Compare commits

...

1 Commits

Author SHA1 Message Date
lebaudantoine 076d84a5bc 📱(frontend) enhance control bar responsiveness for better UX
Implemented collapsible advanced options to maintain usability on narrow screens
following GMeet's UX pattern. Dialog and popover components were chosen
based on GMeet choices, though this introduces potential accessibility concerns
that should be addressed in future iterations.

Current implementation uses JS for breakpoint handling due to challenges with
Panda CSS's pure CSS approach. This workaround was necessary to resolve a
persistent issue where the popover remained open after window expansion beyond
750px, even after the lateral menu trigger was removed from view.

Technical debt note: Code needs refinement, particularly around breakpoint
management and component architecture. Prioritized shipping over perfection to
meet immediate responsive design needs.
2025-01-27 22:51:42 +01:00
7 changed files with 114 additions and 23 deletions
@@ -7,7 +7,7 @@ import { css } from '@/styled-system/css'
import { ToggleButtonProps } from '@/primitives/ToggleButton'
export const TranscriptToggle = ({
variant = 'primaryDark',
variant = 'primaryTextDark',
onPress,
...props
}: ToggleButtonProps) => {
@@ -8,10 +8,8 @@ import { HandToggle } from '../../components/controls/HandToggle'
import { ScreenShareToggle } from '../../components/controls/ScreenShareToggle'
import { OptionsButton } from '../../components/controls/Options/OptionsButton'
import { StartMediaButton } from '../../components/controls/StartMediaButton'
import { ChatToggle } from '../../components/controls/ChatToggle'
import { ParticipantsToggle } from '../../components/controls/Participants/ParticipantsToggle'
import { SupportToggle } from '../../components/controls/SupportToggle'
import { TranscriptToggle } from '../../components/controls/TranscriptToggle'
import { MoreOptions } from './MoreOptions'
import { useRef } from 'react'
export function DesktopControlBar({
onDeviceError,
@@ -21,9 +19,11 @@ export function DesktopControlBar({
saveVideoInputDeviceId,
}: ControlBarAuxProps) {
const browserSupportsScreenSharing = supportsScreenSharing()
const desktopControlBarEl = useRef<HTMLDivElement>(null)
return (
<>
<div
ref={desktopControlBarEl}
className={css({
width: '100vw',
display: 'flex',
@@ -87,21 +87,7 @@ export function DesktopControlBar({
<LeaveButton />
<StartMediaButton />
</div>
<div
className={css({
display: 'flex',
justifyContent: 'flex-end',
flex: '1 1 33%',
alignItems: 'center',
gap: '0.5rem',
paddingRight: '0.25rem',
})}
>
<ChatToggle />
<ParticipantsToggle />
<TranscriptToggle />
<SupportToggle />
</div>
<MoreOptions parentElement={desktopControlBarEl} />
</div>
</>
)
@@ -0,0 +1,87 @@
import { css } from '@/styled-system/css'
import { ChatToggle } from '../../components/controls/ChatToggle'
import { ParticipantsToggle } from '../../components/controls/Participants/ParticipantsToggle'
import { SupportToggle } from '../../components/controls/SupportToggle'
import { TranscriptToggle } from '../../components/controls/TranscriptToggle'
import { useSize } from '../../hooks/useResizeObserver'
import { useState, RefObject } from 'react'
import { Dialog, DialogTrigger, Popover } from 'react-aria-components'
import { Button } from '@/primitives'
import { ToggleButtonProps } from '@/primitives/ToggleButton'
import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
const CONTROL_BAR_BREAKPOINT = 750
const NavigationControls = ({ onPress }: Partial<ToggleButtonProps>) => (
<>
<ChatToggle onPress={onPress} />
<ParticipantsToggle onPress={onPress} />
<TranscriptToggle onPress={onPress} />
<SupportToggle onPress={onPress} />
</>
)
export const LateralMenu = () => {
const { t } = useTranslation('rooms')
const [isOpen, setIsOpen] = useState(false)
const handlePress = () => setIsOpen(!isOpen)
const handleClose = () => setIsOpen(false)
return (
<DialogTrigger isOpen={isOpen} onOpenChange={setIsOpen}>
<Button
square
variant="secondaryDark"
aria-label={t('controls.moreOptions')}
tooltip={t('controls.moreOptions')}
onPress={handlePress}
>
{isOpen ? <RiArrowDownSLine /> : <RiArrowUpSLine />}
</Button>
<Popover>
<Dialog
className={css({
width: '65px',
backgroundColor: 'primaryDark.50',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
borderRadius: '4px',
paddingTop: '10px',
gap: '0.5rem',
})}
>
<NavigationControls onPress={handleClose} />
</Dialog>
</Popover>
</DialogTrigger>
)
}
export const MoreOptions = ({
parentElement,
}: {
parentElement: RefObject<HTMLDivElement>
}) => {
const { width: parentWidth } = useSize(parentElement)
return (
<div
className={css({
display: 'flex',
justifyContent: 'flex-end',
flex: '1 1 33%',
alignItems: 'center',
gap: '0.5rem',
paddingRight: '0.25rem',
})}
>
{parentWidth > CONTROL_BAR_BREAKPOINT ? (
<NavigationControls />
) : (
<LateralMenu />
)}
</div>
)
}
+2 -1
View File
@@ -75,7 +75,8 @@
"open": "",
"closed": ""
},
"support": ""
"support": "",
"moreOptions": ""
},
"options": {
"buttonLabel": "",
+2 -1
View File
@@ -74,7 +74,8 @@
"open": "Hide AI assistant",
"closed": "Show AI assistant"
},
"support": "Support"
"support": "Support",
"moreOptions": "More options"
},
"options": {
"buttonLabel": "More Options",
+2 -1
View File
@@ -74,7 +74,8 @@
"open": "Masquer l'assistant IA",
"closed": "Afficher l'assistant IA"
},
"support": "Support"
"support": "Support",
"moreOptions": "Plus d'options"
},
"options": {
"buttonLabel": "Plus d'options",
@@ -109,6 +109,21 @@ export const buttonRecipe = cva({
color: 'primaryDark.100 !important',
},
},
secondaryDark: {
backgroundColor: 'primaryDark.50',
color: 'white',
'&[data-pressed]': {
backgroundColor: 'primaryDark.200',
},
'&[data-hovered]': {
backgroundColor: 'primaryDark.100',
color: 'white',
},
'&[data-selected]': {
backgroundColor: 'primaryDark.700 !important',
color: 'primaryDark.100 !important',
},
},
primaryTextDark: {
backgroundColor: 'transparent',
color: 'white',