Merge pull request #245 from numerique-gouv/feat/video-conference-ui
Enhance Conference UI
This commit is contained in:
BIN
src/backend/locale/en_US/LC_MESSAGES/django.mo
Normal file
BIN
src/backend/locale/en_US/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
BIN
src/backend/locale/fr_FR/LC_MESSAGES/django.mo
Normal file
BIN
src/backend/locale/fr_FR/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
@@ -93,6 +93,66 @@ const config: Config = {
|
|||||||
* This way we'll only add the things we need step by step and prevent using lots of differents things.
|
* This way we'll only add the things we need step by step and prevent using lots of differents things.
|
||||||
*/
|
*/
|
||||||
...pandaPreset.theme.tokens,
|
...pandaPreset.theme.tokens,
|
||||||
|
colors: defineTokens.colors({
|
||||||
|
...pandaPreset.theme.tokens.colors,
|
||||||
|
primaryDark: {
|
||||||
|
50: { value: '#1B1B35' },
|
||||||
|
100: { value: '#2B2B5B' },
|
||||||
|
200: { value: '#3B3B81' },
|
||||||
|
300: { value: '#4A4AA8' },
|
||||||
|
400: { value: '#5A5ACE' },
|
||||||
|
500: { value: '#6A6AF4' },
|
||||||
|
600: { value: '#8585F6' },
|
||||||
|
700: { value: '#CACAFB' },
|
||||||
|
800: { value: '#E3E3FB' },
|
||||||
|
900: { value: '#ECECFE' },
|
||||||
|
950: { value: '#F5F5FE' },
|
||||||
|
action: { value: '#C1C1FB' },
|
||||||
|
},
|
||||||
|
primary: {
|
||||||
|
50: { value: '#F5F5FE' },
|
||||||
|
100: { value: '#ECECFE' },
|
||||||
|
200: { value: '#E3E3FB' },
|
||||||
|
300: { value: '#CACAFB' },
|
||||||
|
400: { value: '#8585F6' },
|
||||||
|
500: { value: '#6A6AF4' },
|
||||||
|
600: { value: '#313178' },
|
||||||
|
700: { value: '#272747' },
|
||||||
|
800: { value: '#000091' },
|
||||||
|
900: { value: '#21213F' },
|
||||||
|
950: { value: '#1B1B35' },
|
||||||
|
action: { value: '#1212FF' },
|
||||||
|
},
|
||||||
|
greyscale: {
|
||||||
|
'000': { value: '#FFFFFF' },
|
||||||
|
50: { value: '#F6F6F6' },
|
||||||
|
100: { value: '#EEEEEE' },
|
||||||
|
200: { value: '#E5E5E5' },
|
||||||
|
250: { value: '#DDDDDD' },
|
||||||
|
300: { value: '#CECECE' },
|
||||||
|
400: { value: '#929292' },
|
||||||
|
500: { value: '#7C7C7C' },
|
||||||
|
600: { value: '#666666' },
|
||||||
|
700: { value: '#3A3A3A' },
|
||||||
|
750: { value: '#353535' },
|
||||||
|
800: { value: '#2A2A2A' },
|
||||||
|
900: { value: '#242424' },
|
||||||
|
950: { value: '#1E1E1E' },
|
||||||
|
1000: { value: '#161616' },
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
100: {value: '#391C1C'},
|
||||||
|
200: {value: '#412121'},
|
||||||
|
300: {value: '#642626'},
|
||||||
|
400: {value: '#CE0500'},
|
||||||
|
500: {value: '#F60700'},
|
||||||
|
600: {value: '#FF5655'},
|
||||||
|
700: {value: '#FFBDBD'},
|
||||||
|
800: {value: '#FFDDDD'},
|
||||||
|
900: {value: '#FFE9E9'},
|
||||||
|
950: {value: '#FFF4F4'},
|
||||||
|
}
|
||||||
|
}),
|
||||||
animations: {},
|
animations: {},
|
||||||
blurs: {},
|
blurs: {},
|
||||||
/* just directly use values as tokens. This allows us to follow a specific design scale,
|
/* just directly use values as tokens. This allows us to follow a specific design scale,
|
||||||
@@ -194,7 +254,7 @@ const config: Config = {
|
|||||||
semanticTokens: defineSemanticTokens({
|
semanticTokens: defineSemanticTokens({
|
||||||
colors: {
|
colors: {
|
||||||
default: {
|
default: {
|
||||||
text: { value: '{colors.gray.900}' },
|
text: { value: '{colors.greyscale.1000}' },
|
||||||
bg: { value: 'white' },
|
bg: { value: 'white' },
|
||||||
subtle: { value: '{colors.gray.100}' },
|
subtle: { value: '{colors.gray.100}' },
|
||||||
'subtle-text': { value: '{colors.gray.600}' },
|
'subtle-text': { value: '{colors.gray.600}' },
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const SoundTester = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
invisible
|
variant="secondaryText"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
audioRef?.current?.play()
|
audioRef?.current?.play()
|
||||||
setIsPlaying(true)
|
setIsPlaying(true)
|
||||||
|
|||||||
@@ -185,8 +185,8 @@ export const IntroSlider = () => {
|
|||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<ButtonVerticalCenter>
|
<ButtonVerticalCenter>
|
||||||
<Button
|
<Button
|
||||||
|
variant="greyscale"
|
||||||
square
|
square
|
||||||
invisible
|
|
||||||
aria-label={t('previous.label')}
|
aria-label={t('previous.label')}
|
||||||
tooltip={t('previous.tooltip')}
|
tooltip={t('previous.tooltip')}
|
||||||
onPress={() => setSlideIndex(slideIndex - 1)}
|
onPress={() => setSlideIndex(slideIndex - 1)}
|
||||||
@@ -221,8 +221,8 @@ export const IntroSlider = () => {
|
|||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<ButtonVerticalCenter>
|
<ButtonVerticalCenter>
|
||||||
<Button
|
<Button
|
||||||
|
variant="greyscale"
|
||||||
square
|
square
|
||||||
invisible
|
|
||||||
aria-label={t('next.label')}
|
aria-label={t('next.label')}
|
||||||
tooltip={t('next.tooltip')}
|
tooltip={t('next.tooltip')}
|
||||||
onPress={() => setSlideIndex(slideIndex + 1)}
|
onPress={() => setSlideIndex(slideIndex + 1)}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { JoinMeetingDialog } from '../components/JoinMeetingDialog'
|
|||||||
import { ProConnectButton } from '@/components/ProConnectButton'
|
import { ProConnectButton } from '@/components/ProConnectButton'
|
||||||
import { useCreateRoom } from '@/features/rooms'
|
import { useCreateRoom } from '@/features/rooms'
|
||||||
import { usePersistentUserChoices } from '@livekit/components-react'
|
import { usePersistentUserChoices } from '@livekit/components-react'
|
||||||
import { menuItemRecipe } from '@/primitives/menuItemRecipe'
|
|
||||||
import { RiAddLine, RiLink } from '@remixicon/react'
|
import { RiAddLine, RiLink } from '@remixicon/react'
|
||||||
import { LaterMeetingDialog } from '@/features/home/components/LaterMeetingDialog'
|
import { LaterMeetingDialog } from '@/features/home/components/LaterMeetingDialog'
|
||||||
import { IntroSlider } from '@/features/home/components/IntroSlider'
|
import { IntroSlider } from '@/features/home/components/IntroSlider'
|
||||||
@@ -18,6 +17,7 @@ import { MoreLink } from '@/features/home/components/MoreLink'
|
|||||||
import { ReactNode, useState } from 'react'
|
import { ReactNode, useState } from 'react'
|
||||||
|
|
||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
|
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
||||||
|
|
||||||
const Columns = ({ children }: { children?: ReactNode }) => {
|
const Columns = ({ children }: { children?: ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
@@ -173,7 +173,9 @@ export const Home = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<RACMenu>
|
<RACMenu>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={menuItemRecipe({ icon: true })}
|
className={
|
||||||
|
menuRecipe({ icon: true, variant: 'light' }).item
|
||||||
|
}
|
||||||
onAction={async () => {
|
onAction={async () => {
|
||||||
const slug = generateRoomId()
|
const slug = generateRoomId()
|
||||||
createRoom({ slug, username }).then((data) =>
|
createRoom({ slug, username }).then((data) =>
|
||||||
@@ -188,7 +190,9 @@ export const Home = () => {
|
|||||||
{t('createMenu.instantOption')}
|
{t('createMenu.instantOption')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={menuItemRecipe({ icon: true })}
|
className={
|
||||||
|
menuRecipe({ icon: true, variant: 'light' }).item
|
||||||
|
}
|
||||||
onAction={() => {
|
onAction={() => {
|
||||||
const slug = generateRoomId()
|
const slug = generateRoomId()
|
||||||
createRoom({ slug, username }).then((data) =>
|
createRoom({ slug, username }).then((data) =>
|
||||||
@@ -207,8 +211,7 @@ export const Home = () => {
|
|||||||
)}
|
)}
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="secondary"
|
||||||
outline
|
|
||||||
style={{
|
style={{
|
||||||
height: !isLoggedIn ? '56px' : undefined, // Temporary, Align with ProConnect Button fixed height
|
height: !isLoggedIn ? '56px' : undefined, // Temporary, Align with ProConnect Button fixed height
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { InviteDialog } from './InviteDialog'
|
|||||||
|
|
||||||
import { VideoConference } from '../livekit/prefabs/VideoConference'
|
import { VideoConference } from '../livekit/prefabs/VideoConference'
|
||||||
import posthog from 'posthog-js'
|
import posthog from 'posthog-js'
|
||||||
|
import { css } from '@/styled-system/css'
|
||||||
|
|
||||||
export const Conference = ({
|
export const Conference = ({
|
||||||
roomId,
|
roomId,
|
||||||
@@ -107,6 +108,9 @@ export const Conference = ({
|
|||||||
audio={userConfig.audioEnabled}
|
audio={userConfig.audioEnabled}
|
||||||
video={userConfig.videoEnabled}
|
video={userConfig.videoEnabled}
|
||||||
connectOptions={connectOptions}
|
connectOptions={connectOptions}
|
||||||
|
className={css({
|
||||||
|
backgroundColor: 'primaryDark.50 !important',
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<VideoConference />
|
<VideoConference />
|
||||||
{showInviteDialog && (
|
{showInviteDialog && (
|
||||||
|
|||||||
@@ -49,8 +49,6 @@ export const InviteDialog = ({
|
|||||||
}
|
}
|
||||||
}, [isCopied])
|
}, [isCopied])
|
||||||
|
|
||||||
const [isHovered, setIsHovered] = useState(false)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRACDialog {...dialogProps}>
|
<StyledRACDialog {...dialogProps}>
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
@@ -65,7 +63,7 @@ export const InviteDialog = ({
|
|||||||
</Heading>
|
</Heading>
|
||||||
<Div position="absolute" top="5" right="5">
|
<Div position="absolute" top="5" right="5">
|
||||||
<Button
|
<Button
|
||||||
invisible
|
variant="greyscale"
|
||||||
size="xs"
|
size="xs"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
dialogProps.onClose?.()
|
dialogProps.onClose?.()
|
||||||
@@ -78,45 +76,24 @@ export const InviteDialog = ({
|
|||||||
</Div>
|
</Div>
|
||||||
<P>{t('shareDialog.description')}</P>
|
<P>{t('shareDialog.description')}</P>
|
||||||
<Button
|
<Button
|
||||||
variant={isCopied ? 'success' : 'primary'}
|
variant={isCopied ? 'success' : 'tertiary'}
|
||||||
size="sm"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
aria-label={t('shareDialog.copy')}
|
aria-label={t('shareDialog.copy')}
|
||||||
style={{
|
|
||||||
justifyContent: 'start',
|
|
||||||
}}
|
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigator.clipboard.writeText(roomUrl)
|
navigator.clipboard.writeText(roomUrl)
|
||||||
setIsCopied(true)
|
setIsCopied(true)
|
||||||
}}
|
}}
|
||||||
onHoverChange={setIsHovered}
|
|
||||||
data-attr="share-dialog-copy"
|
data-attr="share-dialog-copy"
|
||||||
>
|
>
|
||||||
{isCopied ? (
|
{isCopied ? (
|
||||||
<>
|
<>
|
||||||
<RiCheckLine size={18} style={{ marginRight: '8px' }} />
|
<RiCheckLine size={24} style={{ marginRight: '8px' }} />
|
||||||
{t('shareDialog.copied')}
|
{t('shareDialog.copied')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<RiFileCopyLine
|
<RiFileCopyLine size={24} style={{ marginRight: '8px' }} />
|
||||||
size={18}
|
{t('shareDialog.copyButton')}
|
||||||
style={{ marginRight: '8px', minWidth: '18px' }}
|
|
||||||
/>
|
|
||||||
{isHovered ? (
|
|
||||||
t('shareDialog.copy')
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
overflow: 'hidden',
|
|
||||||
userSelect: 'none',
|
|
||||||
textWrap: 'nowrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{roomUrl.replace(/^https?:\/\//, '')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const ratingButtonRecipe = cva({
|
|||||||
variants: {
|
variants: {
|
||||||
selected: {
|
selected: {
|
||||||
true: {
|
true: {
|
||||||
backgroundColor: '#1d4ed8',
|
backgroundColor: 'primary.800',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
false: {
|
false: {
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ export const Effects = () => {
|
|||||||
<HStack>
|
<HStack>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
legacyStyle
|
|
||||||
aria-label={tooltipLabel(BlurRadius.LIGHT)}
|
aria-label={tooltipLabel(BlurRadius.LIGHT)}
|
||||||
tooltip={tooltipLabel(BlurRadius.LIGHT)}
|
tooltip={tooltipLabel(BlurRadius.LIGHT)}
|
||||||
isDisabled={processorPending}
|
isDisabled={processorPending}
|
||||||
@@ -162,7 +161,6 @@ export const Effects = () => {
|
|||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
legacyStyle
|
|
||||||
aria-label={tooltipLabel(BlurRadius.NORMAL)}
|
aria-label={tooltipLabel(BlurRadius.NORMAL)}
|
||||||
tooltip={tooltipLabel(BlurRadius.NORMAL)}
|
tooltip={tooltipLabel(BlurRadius.NORMAL)}
|
||||||
isDisabled={processorPending}
|
isDisabled={processorPending}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const StyledSidePanel = ({
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
invisible
|
invisible
|
||||||
|
variant="greyscale"
|
||||||
size="xs"
|
size="xs"
|
||||||
onPress={onClose}
|
onPress={onClose}
|
||||||
aria-label={closeButtonTooltip}
|
aria-label={closeButtonTooltip}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export const ChatInput = ({
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
square
|
square
|
||||||
invisible
|
variant="secondaryText"
|
||||||
size="sm"
|
size="sm"
|
||||||
onPress={handleSubmit}
|
onPress={handleSubmit}
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const ChatToggle = () => {
|
|||||||
>
|
>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
square
|
square
|
||||||
legacyStyle
|
variant="primaryTextDark"
|
||||||
aria-label={t(tooltipLabel)}
|
aria-label={t(tooltipLabel)}
|
||||||
tooltip={t(tooltipLabel)}
|
tooltip={t(tooltipLabel)}
|
||||||
isSelected={isChatOpen}
|
isSelected={isChatOpen}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const HandToggle = () => {
|
|||||||
>
|
>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
square
|
square
|
||||||
legacyStyle
|
variant="primaryDark"
|
||||||
aria-label={t(tooltipLabel)}
|
aria-label={t(tooltipLabel)}
|
||||||
tooltip={t(tooltipLabel)}
|
tooltip={t(tooltipLabel)}
|
||||||
isSelected={isHandRaised}
|
isSelected={isHandRaised}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const OptionsButton = () => {
|
|||||||
<Menu>
|
<Menu>
|
||||||
<Button
|
<Button
|
||||||
square
|
square
|
||||||
legacyStyle
|
variant="primaryDark"
|
||||||
aria-label={t('options.buttonLabel')}
|
aria-label={t('options.buttonLabel')}
|
||||||
tooltip={t('options.buttonLabel')}
|
tooltip={t('options.buttonLabel')}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { menuItemRecipe } from '@/primitives/menuItemRecipe'
|
|
||||||
import {
|
import {
|
||||||
RiAccountBoxLine,
|
RiAccountBoxLine,
|
||||||
RiMegaphoneLine,
|
RiMegaphoneLine,
|
||||||
@@ -10,6 +9,7 @@ import { Dispatch, SetStateAction } from 'react'
|
|||||||
import { DialogState } from './OptionsButton'
|
import { DialogState } from './OptionsButton'
|
||||||
import { Separator } from '@/primitives/Separator'
|
import { Separator } from '@/primitives/Separator'
|
||||||
import { useSidePanel } from '../../../hooks/useSidePanel'
|
import { useSidePanel } from '../../../hooks/useSidePanel'
|
||||||
|
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
||||||
|
|
||||||
// @todo try refactoring it to use MenuList component
|
// @todo try refactoring it to use MenuList component
|
||||||
export const OptionsMenuItems = ({
|
export const OptionsMenuItems = ({
|
||||||
@@ -29,7 +29,7 @@ export const OptionsMenuItems = ({
|
|||||||
<Section>
|
<Section>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onAction={() => toggleEffects()}
|
onAction={() => toggleEffects()}
|
||||||
className={menuItemRecipe({ icon: true })}
|
className={menuRecipe({ icon: true }).item}
|
||||||
>
|
>
|
||||||
<RiAccountBoxLine size={20} />
|
<RiAccountBoxLine size={20} />
|
||||||
{t('effects')}
|
{t('effects')}
|
||||||
@@ -40,13 +40,13 @@ export const OptionsMenuItems = ({
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
|
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className={menuItemRecipe({ icon: true })}
|
className={menuRecipe({ icon: true }).item}
|
||||||
>
|
>
|
||||||
<RiMegaphoneLine size={20} />
|
<RiMegaphoneLine size={20} />
|
||||||
{t('feedbacks')}
|
{t('feedbacks')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={menuItemRecipe({ icon: true })}
|
className={menuRecipe({ icon: true }).item}
|
||||||
onAction={() => onOpenDialog('settings')}
|
onAction={() => onOpenDialog('settings')}
|
||||||
>
|
>
|
||||||
<RiSettings3Line size={20} />
|
<RiSettings3Line size={20} />
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const HandRaisedListItem = ({
|
|||||||
</HStack>
|
</HStack>
|
||||||
<Button
|
<Button
|
||||||
square
|
square
|
||||||
invisible
|
variant="greyscale"
|
||||||
size="sm"
|
size="sm"
|
||||||
onPress={() => lowerHandParticipant(participant)}
|
onPress={() => lowerHandParticipant(participant)}
|
||||||
tooltip={t('participants.lowerParticipantHand', { name })}
|
tooltip={t('participants.lowerParticipantHand', { name })}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const MicIndicator = ({ participant }: MicIndicatorProps) => {
|
|||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
square
|
square
|
||||||
invisible
|
variant="greyscale"
|
||||||
size="sm"
|
size="sm"
|
||||||
tooltip={
|
tooltip={
|
||||||
isLocal(participant)
|
isLocal(participant)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const ParticipantsToggle = () => {
|
|||||||
>
|
>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
square
|
square
|
||||||
legacyStyle
|
variant="primaryTextDark"
|
||||||
aria-label={t(tooltipLabel)}
|
aria-label={t(tooltipLabel)}
|
||||||
tooltip={t(tooltipLabel)}
|
tooltip={t(tooltipLabel)}
|
||||||
isSelected={isParticipantsOpen}
|
isSelected={isParticipantsOpen}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const ScreenShareToggle = (
|
|||||||
<ToggleButton
|
<ToggleButton
|
||||||
isSelected={enabled}
|
isSelected={enabled}
|
||||||
square
|
square
|
||||||
legacyStyle
|
variant="primaryDark"
|
||||||
tooltip={t(tooltipLabel)}
|
tooltip={t(tooltipLabel)}
|
||||||
onPress={(e) =>
|
onPress={(e) =>
|
||||||
buttonProps.onClick?.(
|
buttonProps.onClick?.(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
useTrackToggle,
|
useTrackToggle,
|
||||||
UseTrackToggleProps,
|
UseTrackToggleProps,
|
||||||
} from '@livekit/components-react'
|
} from '@livekit/components-react'
|
||||||
import { HStack } from '@/styled-system/jsx'
|
|
||||||
import { Button, Menu, MenuList } from '@/primitives'
|
import { Button, Menu, MenuList } from '@/primitives'
|
||||||
import {
|
import {
|
||||||
RemixiconComponentType,
|
RemixiconComponentType,
|
||||||
@@ -19,6 +18,7 @@ import { Track } from 'livekit-client'
|
|||||||
import { Shortcut } from '@/features/shortcuts/types'
|
import { Shortcut } from '@/features/shortcuts/types'
|
||||||
|
|
||||||
import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice.tsx'
|
import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice.tsx'
|
||||||
|
import { css } from '@/styled-system/css'
|
||||||
|
|
||||||
export type ToggleSource = Exclude<
|
export type ToggleSource = Exclude<
|
||||||
Track.Source,
|
Track.Source,
|
||||||
@@ -86,7 +86,12 @@ export const SelectToggleDevice = <T extends ToggleSource>({
|
|||||||
const selectLabel = t('choose', { keyPrefix: `join.${config.kind}` })
|
const selectLabel = t('choose', { keyPrefix: `join.${config.kind}` })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack gap={0}>
|
<div
|
||||||
|
className={css({
|
||||||
|
display: 'flex',
|
||||||
|
gap: '1px',
|
||||||
|
})}
|
||||||
|
>
|
||||||
<ToggleDevice {...trackProps} config={config} />
|
<ToggleDevice {...trackProps} config={config} />
|
||||||
<Menu>
|
<Menu>
|
||||||
<Button
|
<Button
|
||||||
@@ -94,6 +99,7 @@ export const SelectToggleDevice = <T extends ToggleSource>({
|
|||||||
aria-label={selectLabel}
|
aria-label={selectLabel}
|
||||||
groupPosition="right"
|
groupPosition="right"
|
||||||
square
|
square
|
||||||
|
variant={trackProps.enabled ? 'primaryDark' : 'error2'}
|
||||||
>
|
>
|
||||||
<RiArrowDownSLine />
|
<RiArrowDownSLine />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -109,6 +115,6 @@ export const SelectToggleDevice = <T extends ToggleSource>({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
</HStack>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ export const ToggleDevice = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
isSelected={enabled}
|
isSelected={!enabled}
|
||||||
variant={enabled ? undefined : 'danger'}
|
variant={enabled ? 'primaryDark' : 'error2'}
|
||||||
toggledStyles={false}
|
shySelected
|
||||||
onPress={() => toggle()}
|
onPress={() => toggle()}
|
||||||
aria-label={toggleLabel}
|
aria-label={toggleLabel}
|
||||||
tooltip={toggleLabel}
|
tooltip={toggleLabel}
|
||||||
|
|||||||
@@ -103,54 +103,77 @@ export function ControlBar({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={css({
|
className={css({
|
||||||
display: 'flex',
|
|
||||||
gap: '.5rem',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'space-between',
|
||||||
padding: '.75rem',
|
padding: '.75rem',
|
||||||
borderTop: '1px solid var(--lk-border-color)',
|
|
||||||
maxHeight: 'var(--lk-control-bar-height)',
|
maxHeight: 'var(--lk-control-bar-height)',
|
||||||
height: '80px',
|
height: '80px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
backgroundColor: '#d1d5db',
|
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
|
display: 'flex',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<SelectToggleDevice
|
<div
|
||||||
source={Track.Source.Microphone}
|
className={css({
|
||||||
onChange={microphoneOnChange}
|
display: 'flex',
|
||||||
onDeviceError={(error) =>
|
gap: '.5rem',
|
||||||
onDeviceError?.({ source: Track.Source.Microphone, error })
|
alignItems: 'center',
|
||||||
}
|
lg: {
|
||||||
onActiveDeviceChange={(deviceId) =>
|
position: 'absolute',
|
||||||
saveAudioInputDeviceId(deviceId ?? '')
|
left: '50%',
|
||||||
}
|
transform: 'translateX(-50%)',
|
||||||
/>
|
},
|
||||||
<SelectToggleDevice
|
})}
|
||||||
source={Track.Source.Camera}
|
>
|
||||||
onChange={cameraOnChange}
|
<SelectToggleDevice
|
||||||
onDeviceError={(error) =>
|
source={Track.Source.Microphone}
|
||||||
onDeviceError?.({ source: Track.Source.Camera, error })
|
onChange={microphoneOnChange}
|
||||||
}
|
|
||||||
onActiveDeviceChange={(deviceId) =>
|
|
||||||
saveVideoInputDeviceId(deviceId ?? '')
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{browserSupportsScreenSharing && (
|
|
||||||
<ScreenShareToggle
|
|
||||||
onDeviceError={(error) =>
|
onDeviceError={(error) =>
|
||||||
onDeviceError?.({ source: Track.Source.ScreenShare, error })
|
onDeviceError?.({ source: Track.Source.Microphone, error })
|
||||||
|
}
|
||||||
|
onActiveDeviceChange={(deviceId) =>
|
||||||
|
saveAudioInputDeviceId(deviceId ?? '')
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
<SelectToggleDevice
|
||||||
<HandToggle />
|
source={Track.Source.Camera}
|
||||||
<ChatToggle />
|
onChange={cameraOnChange}
|
||||||
<ParticipantsToggle />
|
onDeviceError={(error) =>
|
||||||
<OptionsButton />
|
onDeviceError?.({ source: Track.Source.Camera, error })
|
||||||
<LeaveButton />
|
}
|
||||||
<StartMediaButton />
|
onActiveDeviceChange={(deviceId) =>
|
||||||
|
saveVideoInputDeviceId(deviceId ?? '')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{browserSupportsScreenSharing && (
|
||||||
|
<ScreenShareToggle
|
||||||
|
onDeviceError={(error) =>
|
||||||
|
onDeviceError?.({ source: Track.Source.ScreenShare, error })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<HandToggle />
|
||||||
|
<OptionsButton />
|
||||||
|
<LeaveButton />
|
||||||
|
<StartMediaButton />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={css({
|
||||||
|
display: 'flex',
|
||||||
|
gap: '.5rem',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: '6.25rem',
|
||||||
|
lg: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ChatToggle />
|
||||||
|
<ParticipantsToggle />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,7 @@ export const FeedbackRoute = () => {
|
|||||||
<VStack>
|
<VStack>
|
||||||
<Heading>{t('feedback.heading')}</Heading>
|
<Heading>{t('feedback.heading')}</Heading>
|
||||||
<HStack>
|
<HStack>
|
||||||
<Button
|
<Button variant="secondary" onPress={() => window.history.back()}>
|
||||||
outline
|
|
||||||
variant="primary"
|
|
||||||
onPress={() => window.history.back()}
|
|
||||||
>
|
|
||||||
{t('feedback.back')}
|
{t('feedback.back')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="primary" onPress={() => setLocation('/')}>
|
<Button variant="primary" onPress={() => setLocation('/')}>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const SettingsButton = () => {
|
|||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<Button
|
<Button
|
||||||
square
|
square
|
||||||
invisible
|
variant="greyscale"
|
||||||
aria-label={t('settingsButtonLabel')}
|
aria-label={t('settingsButtonLabel')}
|
||||||
tooltip={t('settingsButtonLabel')}
|
tooltip={t('settingsButtonLabel')}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const AccountTab = ({ id, onOpenChange }: AccountTabProps) => {
|
|||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Button onPress={handleOnCancel}>
|
<Button variant="secondary" onPress={handleOnCancel}>
|
||||||
{t('cancel', { ns: 'global' })}
|
{t('cancel', { ns: 'global' })}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant={'primary'} onPress={handleOnSubmit}>
|
<Button variant={'primary'} onPress={handleOnSubmit}>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export const Header = () => {
|
|||||||
<Menu>
|
<Menu>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
invisible
|
variant="greyscale"
|
||||||
tooltip={t('loggedInUserTooltip')}
|
tooltip={t('loggedInUserTooltip')}
|
||||||
tooltipType="delayed"
|
tooltipType="delayed"
|
||||||
>
|
>
|
||||||
@@ -83,6 +83,7 @@ export const Header = () => {
|
|||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
<MenuList
|
<MenuList
|
||||||
|
variant={'light'}
|
||||||
items={[{ value: 'logout', label: t('logout') }]}
|
items={[{ value: 'logout', label: t('logout') }]}
|
||||||
onAction={(value) => {
|
onAction={(value) => {
|
||||||
if (value === 'logout') {
|
if (value === 'logout') {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"leaveRoomPrompt": "This will make you leave the meeting.",
|
"leaveRoomPrompt": "This will make you leave the meeting.",
|
||||||
"shareDialog": {
|
"shareDialog": {
|
||||||
"copy": "Copy the meeting link",
|
"copy": "Copy the meeting link",
|
||||||
|
"copyButton": "Copy link",
|
||||||
"copied": "Link copied to clipboard",
|
"copied": "Link copied to clipboard",
|
||||||
"heading": "Your meeting is ready",
|
"heading": "Your meeting is ready",
|
||||||
"description": "Share this link with people you want to invite to the meeting.",
|
"description": "Share this link with people you want to invite to the meeting.",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"leaveRoomPrompt": "Revenir à l'accueil vous fera quitter la réunion.",
|
"leaveRoomPrompt": "Revenir à l'accueil vous fera quitter la réunion.",
|
||||||
"shareDialog": {
|
"shareDialog": {
|
||||||
"copy": "Copier le lien de la réunion",
|
"copy": "Copier le lien de la réunion",
|
||||||
|
"copyButton": "Copier le lien",
|
||||||
"copied": "Lien copié dans le presse-papiers",
|
"copied": "Lien copié dans le presse-papiers",
|
||||||
"heading": "Votre réunion est prête",
|
"heading": "Votre réunion est prête",
|
||||||
"description": "Partagez ce lien avec les personnes que vous souhaitez inviter à la réunion.",
|
"description": "Partagez ce lien avec les personnes que vous souhaitez inviter à la réunion.",
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ export const Dialog = ({
|
|||||||
{!isAlert && (
|
{!isAlert && (
|
||||||
<Div position="absolute" top="5" right="5">
|
<Div position="absolute" top="5" right="5">
|
||||||
<Button
|
<Button
|
||||||
|
variant="greyscale"
|
||||||
invisible
|
invisible
|
||||||
size="xs"
|
size="xs"
|
||||||
onPress={() => close()}
|
onPress={() => close()}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const Form = ({
|
|||||||
{submitLabel}
|
{submitLabel}
|
||||||
</Button>
|
</Button>
|
||||||
{!!onCancel && (
|
{!!onCancel && (
|
||||||
<Button variant="primary" outline onPress={() => onCancel()}>
|
<Button variant="secondary" onPress={() => onCancel()}>
|
||||||
{t('cancel')}
|
{t('cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
import { Menu, MenuProps, MenuItem } from 'react-aria-components'
|
import { Menu, MenuProps, MenuItem } from 'react-aria-components'
|
||||||
import { menuItemRecipe } from './menuItemRecipe'
|
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
||||||
|
import type { RecipeVariantProps } from '@/styled-system/types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* render a Button primitive that shows a popover showing a list of pressable items
|
* render a Button primitive that shows a popover showing a list of pressable items
|
||||||
@@ -14,11 +15,15 @@ export const MenuList = <T extends string | number = string>({
|
|||||||
onAction: (key: T) => void
|
onAction: (key: T) => void
|
||||||
selectedItem?: T
|
selectedItem?: T
|
||||||
items: Array<string | { value: T; label: ReactNode }>
|
items: Array<string | { value: T; label: ReactNode }>
|
||||||
} & MenuProps<unknown>) => {
|
} & MenuProps<unknown> &
|
||||||
|
RecipeVariantProps<typeof menuRecipe>) => {
|
||||||
|
const [variantProps] = menuRecipe.splitVariantProps(menuProps)
|
||||||
|
const classes = menuRecipe({ extraPadding: true, ...variantProps })
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
selectionMode={selectedItem !== undefined ? 'single' : undefined}
|
selectionMode={selectedItem !== undefined ? 'single' : undefined}
|
||||||
selectedKeys={selectedItem !== undefined ? [selectedItem] : undefined}
|
selectedKeys={selectedItem !== undefined ? [selectedItem] : undefined}
|
||||||
|
className={classes.root}
|
||||||
{...menuProps}
|
{...menuProps}
|
||||||
>
|
>
|
||||||
{items.map((item) => {
|
{items.map((item) => {
|
||||||
@@ -26,7 +31,7 @@ export const MenuList = <T extends string | number = string>({
|
|||||||
const label = typeof item === 'string' ? item : item.label
|
const label = typeof item === 'string' ? item : item.label
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={menuItemRecipe({ extraPadding: true })}
|
className={classes.item}
|
||||||
key={value}
|
key={value}
|
||||||
id={value as string}
|
id={value as string}
|
||||||
onAction={() => {
|
onAction={() => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from 'react-aria-components'
|
} from 'react-aria-components'
|
||||||
import { Box } from './Box'
|
import { Box } from './Box'
|
||||||
import { StyledPopover } from './Popover'
|
import { StyledPopover } from './Popover'
|
||||||
import { menuItemRecipe } from './menuItemRecipe'
|
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
||||||
|
|
||||||
const StyledButton = styled(Button, {
|
const StyledButton = styled(Button, {
|
||||||
base: {
|
base: {
|
||||||
@@ -75,7 +75,9 @@ export const Select = <T extends string | number>({
|
|||||||
<ListBox>
|
<ListBox>
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<ListBoxItem
|
<ListBoxItem
|
||||||
className={menuItemRecipe({ extraPadding: true })}
|
className={
|
||||||
|
menuRecipe({ extraPadding: true, variant: 'light' }).item
|
||||||
|
}
|
||||||
id={item.value}
|
id={item.value}
|
||||||
key={item.value}
|
key={item.value}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -66,12 +66,8 @@ const StyledTab = styled(RACTab, {
|
|||||||
color: 'box.text',
|
color: 'box.text',
|
||||||
},
|
},
|
||||||
'&[data-selected]': {
|
'&[data-selected]': {
|
||||||
backgroundColor: 'primary',
|
backgroundColor: 'primary.800',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
'&[data-hovered]': {
|
|
||||||
backgroundColor: 'primary',
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const StyledTooltip = styled(RACTooltip, {
|
|||||||
base: {
|
base: {
|
||||||
boxShadow: '0 8px 20px rgba(0 0 0 / 0.1)',
|
boxShadow: '0 8px 20px rgba(0 0 0 / 0.1)',
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
backgroundColor: 'gray.800',
|
backgroundColor: 'primaryDark.100',
|
||||||
color: 'gray.100',
|
color: 'gray.100',
|
||||||
forcedColorAdjust: 'none',
|
forcedColorAdjust: 'none',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
|
|||||||
@@ -12,25 +12,11 @@ export const buttonRecipe = cva({
|
|||||||
transition: 'background 200ms, outline 200ms, border-color 200ms',
|
transition: 'background 200ms, outline 200ms, border-color 200ms',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
border: '1px solid transparent',
|
border: '1px solid transparent',
|
||||||
color: 'colorPalette.text',
|
|
||||||
backgroundColor: 'colorPalette',
|
|
||||||
'&[data-hovered]': {
|
|
||||||
backgroundColor: 'colorPalette.hover',
|
|
||||||
},
|
|
||||||
'&[data-pressed]': {
|
|
||||||
backgroundColor: 'colorPalette.active',
|
|
||||||
},
|
|
||||||
'&[data-selected]': {
|
|
||||||
backgroundColor: 'colorPalette.active',
|
|
||||||
},
|
|
||||||
'&[data-disabled]': {
|
|
||||||
cursor: 'auto',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
size: {
|
size: {
|
||||||
default: {
|
default: {
|
||||||
borderRadius: 8,
|
borderRadius: 4,
|
||||||
paddingX: '1',
|
paddingX: '1',
|
||||||
paddingY: '0.625',
|
paddingY: '0.625',
|
||||||
'--square-padding': '{spacing.0.625}',
|
'--square-padding': '{spacing.0.625}',
|
||||||
@@ -53,20 +39,137 @@ export const buttonRecipe = cva({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
default: {
|
|
||||||
colorPalette: 'control',
|
|
||||||
borderColor: 'control.subtle',
|
|
||||||
},
|
|
||||||
primary: {
|
primary: {
|
||||||
colorPalette: 'primary',
|
backgroundColor: 'primary.800',
|
||||||
|
color: 'white',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'primary.action',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'primary.action',
|
||||||
|
},
|
||||||
'&[data-disabled]': {
|
'&[data-disabled]': {
|
||||||
opacity: 0.3,
|
backgroundColor: 'greyscale.100',
|
||||||
|
color: 'greyscale.400',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
color: 'primary.800',
|
||||||
|
borderColor: 'primary.800',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'greyscale.100',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'greyscale.100',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
secondaryText: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: 'primary.800',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'greyscale.100',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'greyscale.100',
|
||||||
|
},
|
||||||
|
'&[data-disabled]': {
|
||||||
|
color: 'greyscale.400',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tertiary: {
|
||||||
|
backgroundColor: 'primary.100',
|
||||||
|
color: 'primary.800',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'primary.300',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'primary.300',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
primaryDark: {
|
||||||
|
backgroundColor: 'primaryDark.100',
|
||||||
|
color: 'white',
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'primaryDark.900',
|
||||||
|
color: 'primaryDark.100',
|
||||||
|
},
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'primaryDark.300',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
'&[data-selected]': {
|
||||||
|
backgroundColor: 'primaryDark.900 !important',
|
||||||
|
color: 'primaryDark.100 !important',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
primaryTextDark: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: 'primaryDark.800',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'primaryDark.100',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'primaryDark.700',
|
||||||
|
color: 'primaryDark.100',
|
||||||
|
},
|
||||||
|
'&[data-selected]': {
|
||||||
|
backgroundColor: 'primaryDark.700',
|
||||||
|
color: 'primaryDark.100',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
greyscale: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: 'greyscale.400',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
color: 'greyscale.800',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
color: 'greyscale.800',
|
||||||
|
},
|
||||||
|
'&[data-selected]': {
|
||||||
|
color: 'greyscale.800',
|
||||||
|
},
|
||||||
|
'&[data-disabled]': {
|
||||||
|
color: 'greyscale.200',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
danger: {
|
||||||
|
backgroundColor: 'error.400',
|
||||||
|
color: 'white',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'error.600',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'error.700',
|
||||||
|
color: 'error.200',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error2: {
|
||||||
|
backgroundColor: 'error.200',
|
||||||
|
color: 'error.900',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'error.300',
|
||||||
|
},
|
||||||
|
'&[data-focused]': {
|
||||||
|
backgroundColor: 'error.200',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'error.900',
|
||||||
|
color: 'error.100',
|
||||||
|
},
|
||||||
|
'&[data-selected]': {
|
||||||
|
backgroundColor: 'error.900 !important',
|
||||||
|
color: 'error.100 !important',
|
||||||
|
},
|
||||||
|
'&[data-disabled]': {
|
||||||
|
backgroundColor: 'error.200',
|
||||||
|
color: 'error.300',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// @TODO: better handling of colors… this is a mess
|
// @TODO: better handling of colors… this is a mess
|
||||||
success: {
|
success: {
|
||||||
colorPalette: 'success',
|
colorPalette: 'success',
|
||||||
borderColor: 'success.300',
|
|
||||||
color: 'success.subtle-text',
|
color: 'success.subtle-text',
|
||||||
backgroundColor: 'success.subtle',
|
backgroundColor: 'success.subtle',
|
||||||
'&[data-hovered]': {
|
'&[data-hovered]': {
|
||||||
@@ -83,31 +186,6 @@ export const buttonRecipe = cva({
|
|||||||
color: 'primary !important',
|
color: 'primary !important',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
danger: {
|
|
||||||
colorPalette: 'danger',
|
|
||||||
borderColor: 'danger.600',
|
|
||||||
color: 'danger.subtle-text',
|
|
||||||
backgroundColor: 'danger.subtle',
|
|
||||||
'&[data-hovered]': {
|
|
||||||
backgroundColor: 'danger.200',
|
|
||||||
},
|
|
||||||
'&[data-pressed]': {
|
|
||||||
backgroundColor: 'danger.subtle!',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
outline: {
|
|
||||||
true: {
|
|
||||||
color: 'colorPalette',
|
|
||||||
backgroundColor: 'transparent!',
|
|
||||||
borderColor: 'currentcolor!',
|
|
||||||
'&[data-hovered]': {
|
|
||||||
backgroundColor: 'colorPalette.subtle!',
|
|
||||||
},
|
|
||||||
'&[data-pressed]': {
|
|
||||||
backgroundColor: 'colorPalette.subtle!',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
invisible: {
|
invisible: {
|
||||||
true: {
|
true: {
|
||||||
@@ -130,6 +208,10 @@ export const buttonRecipe = cva({
|
|||||||
width: 'full',
|
width: 'full',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// some toggle buttons make more sense without a "pushed button" style when selected because their content changes to mark the state
|
||||||
|
shySelected: {
|
||||||
|
true: {},
|
||||||
|
},
|
||||||
// if the button is next to other ones to make a "button group", tell where the button is to handle radius
|
// if the button is next to other ones to make a "button group", tell where the button is to handle radius
|
||||||
groupPosition: {
|
groupPosition: {
|
||||||
left: {
|
left: {
|
||||||
@@ -145,40 +227,21 @@ export const buttonRecipe = cva({
|
|||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// some toggle buttons make more sense without a "pushed button" style when selected because their content changes to mark the state
|
|
||||||
toggledStyles: {
|
|
||||||
false: {
|
|
||||||
'&[data-selected]': {
|
|
||||||
backgroundColor: 'colorPalette',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legacyStyle: {
|
|
||||||
true: {
|
|
||||||
borderColor: 'gray.400',
|
|
||||||
transition: 'border 200ms, background 200ms, color 200ms',
|
|
||||||
'&[data-hovered]': {
|
|
||||||
borderColor: 'gray.500',
|
|
||||||
},
|
|
||||||
'&[data-pressed]': {
|
|
||||||
borderColor: 'gray.500',
|
|
||||||
},
|
|
||||||
'&[data-selected]': {
|
|
||||||
backgroundColor: '#1d4ed8',
|
|
||||||
color: 'white',
|
|
||||||
borderColor: 'gray.500',
|
|
||||||
'&[data-hovered]': {
|
|
||||||
borderColor: '#6b7280',
|
|
||||||
backgroundColor: '#1e40af',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
compoundVariants: [
|
||||||
|
{
|
||||||
|
variant: 'primaryDark',
|
||||||
|
shySelected: true,
|
||||||
|
css: {
|
||||||
|
'&[data-selected]': {
|
||||||
|
backgroundColor: 'primaryDark.100',
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
size: 'default',
|
size: 'default',
|
||||||
variant: 'default',
|
variant: 'primary',
|
||||||
outline: false,
|
|
||||||
toggledStyles: true,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import { cva } from '@/styled-system/css'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reusable styles for a menu item, select item, etc… to be used with panda `css()` or `styled()`
|
|
||||||
*
|
|
||||||
* these are in their own files because react hot refresh doesn't like exporting stuff
|
|
||||||
* that aren't components in component files
|
|
||||||
*/
|
|
||||||
export const menuItemRecipe = cva({
|
|
||||||
base: {
|
|
||||||
paddingY: 0.125,
|
|
||||||
paddingX: 0.5,
|
|
||||||
textAlign: 'left',
|
|
||||||
width: 'full',
|
|
||||||
borderRadius: 4,
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: 'box.text',
|
|
||||||
border: '1px solid transparent',
|
|
||||||
position: 'relative',
|
|
||||||
'&[data-selected]': {
|
|
||||||
'&::before': {
|
|
||||||
content: '"✓"',
|
|
||||||
position: 'absolute',
|
|
||||||
top: '2px',
|
|
||||||
left: '6px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'&[data-focused]': {
|
|
||||||
color: 'primary.text',
|
|
||||||
backgroundColor: 'primary',
|
|
||||||
outline: 'none!',
|
|
||||||
},
|
|
||||||
'&[data-hovered]': {
|
|
||||||
color: 'primary.text',
|
|
||||||
backgroundColor: 'primary',
|
|
||||||
outline: 'none!',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
variants: {
|
|
||||||
icon: {
|
|
||||||
true: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '1rem',
|
|
||||||
paddingY: '0.4rem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
extraPadding: {
|
|
||||||
true: {
|
|
||||||
paddingLeft: 1.5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
72
src/frontend/src/primitives/menuRecipe.ts
Normal file
72
src/frontend/src/primitives/menuRecipe.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { sva } from '@/styled-system/css'
|
||||||
|
|
||||||
|
export const menuRecipe = sva({
|
||||||
|
slots: ['root', 'item'],
|
||||||
|
base: {
|
||||||
|
root: {},
|
||||||
|
item: {
|
||||||
|
paddingY: 0.125,
|
||||||
|
paddingX: 0.5,
|
||||||
|
textAlign: 'left',
|
||||||
|
width: 'full',
|
||||||
|
borderRadius: 4,
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'box.text',
|
||||||
|
border: '1px solid transparent',
|
||||||
|
position: 'relative',
|
||||||
|
'&[data-selected]': {
|
||||||
|
'&::before': {
|
||||||
|
content: '"✓"',
|
||||||
|
position: 'absolute',
|
||||||
|
top: '2px',
|
||||||
|
left: '6px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&[data-focused]': {
|
||||||
|
color: 'primary.text',
|
||||||
|
backgroundColor: 'primaryDark.100',
|
||||||
|
outline: 'none!',
|
||||||
|
},
|
||||||
|
'&[data-hovered]': {
|
||||||
|
color: 'primary.text',
|
||||||
|
backgroundColor: 'primaryDark.100',
|
||||||
|
outline: 'none!',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
light: {
|
||||||
|
item: {
|
||||||
|
'&[data-focused]': {
|
||||||
|
backgroundColor: 'primary.800',
|
||||||
|
},
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'primary.800',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {},
|
||||||
|
},
|
||||||
|
extraPadding: {
|
||||||
|
true: {
|
||||||
|
item: {
|
||||||
|
paddingLeft: 1.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
true: {
|
||||||
|
item: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '1rem',
|
||||||
|
paddingY: '0.4rem',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'dark',
|
||||||
|
},
|
||||||
|
})
|
||||||
6059
src/frontend/yarn.lock
Normal file
6059
src/frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user