diff --git a/src/frontend/src/features/rooms/livekit/components/controls/ChatToggle.tsx b/src/frontend/src/features/rooms/livekit/components/controls/ChatToggle.tsx
index 9f260643..3b56a141 100644
--- a/src/frontend/src/features/rooms/livekit/components/controls/ChatToggle.tsx
+++ b/src/frontend/src/features/rooms/livekit/components/controls/ChatToggle.tsx
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next'
import { RiChat1Line } from '@remixicon/react'
-import { Button } from '@/primitives'
+import { ToggleButton } from '@/primitives'
import { css } from '@/styled-system/css'
import { useLayoutContext } from '@livekit/components-react'
import { useSnapshot } from 'valtio'
@@ -22,8 +22,7 @@ export const ChatToggle = () => {
display: 'inline-block',
})}
>
-
+
{!!state?.unreadMessages && (
{
display: 'inline-block',
})}
>
-
+
&
+export type ButtonProps = RecipeVariantProps &
RACButtonsProps &
- Tooltip & {
- toggle?: boolean
- isSelected?: boolean
- }
+ TooltipWrapperProps
-type LinkButtonProps = RecipeVariantProps & LinkProps & Tooltip
+type LinkButtonProps = RecipeVariantProps &
+ LinkProps &
+ TooltipWrapperProps
type ButtonOrLinkProps = ButtonProps | LinkButtonProps
@@ -154,22 +23,11 @@ export const Button = ({
tooltipType = 'instant',
...props
}: ButtonOrLinkProps) => {
- const [variantProps, componentProps] = button.splitVariantProps(props)
+ const [variantProps, componentProps] = buttonRecipe.splitVariantProps(props)
if ((props as LinkButtonProps).href !== undefined) {
return (
-
-
- )
- }
-
- if ((props as ButtonProps).toggle) {
- return (
-
-
+
)
}
@@ -177,26 +35,9 @@ export const Button = ({
return (
)
}
-
-const TooltipWrapper = ({
- tooltip,
- tooltipType,
- children,
-}: {
- children: ReactNode
-} & Tooltip) => {
- return tooltip ? (
-
- {children}
- {tooltip}
-
- ) : (
- children
- )
-}
diff --git a/src/frontend/src/primitives/ToggleButton.tsx b/src/frontend/src/primitives/ToggleButton.tsx
new file mode 100644
index 00000000..288a9335
--- /dev/null
+++ b/src/frontend/src/primitives/ToggleButton.tsx
@@ -0,0 +1,25 @@
+import {
+ ToggleButton as RACToggleButton,
+ ToggleButtonProps,
+} from 'react-aria-components'
+import { type ButtonRecipeProps, buttonRecipe } from './buttonRecipe'
+import { TooltipWrapper, TooltipWrapperProps } from './TooltipWrapper'
+
+/**
+ * React aria ToggleButton with our button styles, that can take a tooltip if needed
+ */
+export const ToggleButton = ({
+ tooltip,
+ tooltipType,
+ ...props
+}: ToggleButtonProps & ButtonRecipeProps & TooltipWrapperProps) => {
+ const [variantProps, componentProps] = buttonRecipe.splitVariantProps(props)
+ return (
+
+
+
+ )
+}
diff --git a/src/frontend/src/primitives/Tooltip.tsx b/src/frontend/src/primitives/TooltipWrapper.tsx
similarity index 77%
rename from src/frontend/src/primitives/Tooltip.tsx
rename to src/frontend/src/primitives/TooltipWrapper.tsx
index 2faf6d55..b1818926 100644
--- a/src/frontend/src/primitives/Tooltip.tsx
+++ b/src/frontend/src/primitives/TooltipWrapper.tsx
@@ -2,16 +2,41 @@ import { type ReactNode } from 'react'
import {
OverlayArrow,
Tooltip as RACTooltip,
- TooltipProps,
+ TooltipTrigger,
+ type TooltipProps,
} from 'react-aria-components'
import { styled } from '@/styled-system/jsx'
+export type TooltipWrapperProps = {
+ tooltip?: string
+ tooltipType?: 'instant' | 'delayed'
+}
+
+/**
+ * Wrap a component you want to apply a tooltip on (for example a Button)
+ *
+ * If no tooltip is given, just returns children
+ */
+export const TooltipWrapper = ({
+ tooltip,
+ tooltipType,
+ children,
+}: {
+ children: ReactNode
+} & TooltipWrapperProps) => {
+ return tooltip ? (
+
+ {children}
+ {tooltip}
+
+ ) : (
+ children
+ )
+}
+
/**
* Styled react aria Tooltip component.
*
- * Note that tooltips are directly handled by Buttons via the `tooltip` prop,
- * so you should not need to use this component directly.
- *
* Style taken from example at https://react-spectrum.adobe.com/react-aria/Tooltip.html
*/
const StyledTooltip = styled(RACTooltip, {
@@ -80,7 +105,7 @@ const TooltipArrow = () => {
)
}
-export const Tooltip = ({
+const Tooltip = ({
children,
...props
}: Omit & { children: ReactNode }) => {
diff --git a/src/frontend/src/primitives/buttonRecipe.ts b/src/frontend/src/primitives/buttonRecipe.ts
new file mode 100644
index 00000000..1a590235
--- /dev/null
+++ b/src/frontend/src/primitives/buttonRecipe.ts
@@ -0,0 +1,125 @@
+import { type RecipeVariantProps, cva } from '@/styled-system/css'
+
+export type ButtonRecipe = typeof buttonRecipe
+
+export type ButtonRecipeProps = RecipeVariantProps
+
+export const buttonRecipe = cva({
+ base: {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ transition: 'background 200ms, outline 200ms, border-color 200ms',
+ cursor: 'pointer',
+ border: '1px solid transparent',
+ color: 'colorPalette.text',
+ backgroundColor: 'colorPalette',
+ '&[data-hovered]': {
+ backgroundColor: 'colorPalette.hover',
+ },
+ '&[data-pressed]': {
+ backgroundColor: 'colorPalette.active',
+ },
+ },
+ variants: {
+ size: {
+ default: {
+ borderRadius: 8,
+ paddingX: '1',
+ paddingY: '0.625',
+ '--square-padding': '{spacing.0.625}',
+ },
+ sm: {
+ borderRadius: 4,
+ paddingX: '0.5',
+ paddingY: '0.25',
+ '--square-padding': '{spacing.0.25}',
+ },
+ xs: {
+ borderRadius: 4,
+ '--square-padding': '0',
+ },
+ },
+ square: {
+ true: {
+ paddingX: 'var(--square-padding)',
+ paddingY: 'var(--square-padding)',
+ },
+ },
+ variant: {
+ default: {
+ colorPalette: 'control',
+ },
+ primary: {
+ colorPalette: 'primary',
+ },
+ // @TODO: better handling of colors… this is a mess
+ success: {
+ colorPalette: 'success',
+ borderColor: 'success.300',
+ color: 'success.subtle-text',
+ backgroundColor: 'success.subtle',
+ '&[data-hovered]': {
+ backgroundColor: 'success.200',
+ },
+ '&[data-pressed]': {
+ backgroundColor: 'success.subtle!',
+ },
+ },
+ },
+ outline: {
+ true: {
+ color: 'colorPalette',
+ backgroundColor: 'transparent!',
+ borderColor: 'currentcolor!',
+ '&[data-hovered]': {
+ backgroundColor: 'colorPalette.subtle!',
+ },
+ '&[data-pressed]': {
+ backgroundColor: 'colorPalette.subtle!',
+ },
+ },
+ },
+ invisible: {
+ true: {
+ borderColor: 'none!',
+ backgroundColor: 'none!',
+ '&[data-hovered]': {
+ backgroundColor: 'none!',
+ borderColor: 'colorPalette.active!',
+ },
+ '&[data-pressed]': {
+ borderColor: 'currentcolor',
+ },
+ },
+ },
+ fullWidth: {
+ true: {
+ width: 'full',
+ },
+ },
+ legacyStyle: {
+ true: {
+ borderColor: 'gray.400',
+ '&[data-hovered]': {
+ borderColor: 'gray.500',
+ },
+ '&[data-pressed]': {
+ borderColor: 'gray.500',
+ },
+ '&[data-selected]': {
+ background: '#e5e7eb',
+ borderColor: 'gray.400',
+ '&[data-hovered]': {
+ backgroundColor: 'gray.300',
+ },
+ },
+ },
+ },
+ },
+ defaultVariants: {
+ size: 'default',
+ variant: 'default',
+ outline: false,
+ },
+})
diff --git a/src/frontend/src/primitives/index.ts b/src/frontend/src/primitives/index.ts
index d7f9c706..16a0ffc0 100644
--- a/src/frontend/src/primitives/index.ts
+++ b/src/frontend/src/primitives/index.ts
@@ -24,5 +24,6 @@ export { MenuList } from './MenuList'
export { P } from './P'
export { Popover } from './Popover'
export { Text } from './Text'
+export { ToggleButton } from './ToggleButton'
export { Ul } from './Ul'
export { VerticallyOffCenter } from './VerticallyOffCenter'