mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-04 14:45:40 +00:00
♻️(frontend) normalize PiP component patterns
Extract OverflowItems, replace `any`, convert function declarations to const arrows.
This commit is contained in:
@@ -30,10 +30,10 @@ const COLLAPSE_ORDER: CollapsibleControl[] = [
|
|||||||
const BUTTON_SLOT = 50
|
const BUTTON_SLOT = 50
|
||||||
const ESSENTIAL_WIDTH = 260
|
const ESSENTIAL_WIDTH = 260
|
||||||
|
|
||||||
function getHiddenControls(
|
const getHiddenControls = (
|
||||||
containerWidth: number,
|
containerWidth: number,
|
||||||
showScreenShare: boolean
|
showScreenShare: boolean
|
||||||
): Set<CollapsibleControl> {
|
): Set<CollapsibleControl> => {
|
||||||
const hidden = new Set<CollapsibleControl>()
|
const hidden = new Set<CollapsibleControl>()
|
||||||
if (containerWidth <= 0) return hidden
|
if (containerWidth <= 0) return hidden
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import { Menu as RACMenu, MenuItem, MenuSection } from 'react-aria-components'
|
import { Menu as RACMenu, MenuSection } from 'react-aria-components'
|
||||||
import {
|
|
||||||
RiHand,
|
|
||||||
RiClosedCaptioningLine,
|
|
||||||
RiArrowUpLine,
|
|
||||||
RiEmotionLine,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Separator } from '@/primitives/Separator'
|
import { Separator } from '@/primitives/Separator'
|
||||||
import { FeedbackMenuItem } from '@/features/rooms/livekit/components/controls/Options/FeedbackMenuItem'
|
import { FeedbackMenuItem } from '@/features/rooms/livekit/components/controls/Options/FeedbackMenuItem'
|
||||||
@@ -12,12 +6,7 @@ import { EffectsMenuItem } from '@/features/rooms/livekit/components/controls/Op
|
|||||||
import { SupportMenuItem } from '@/features/rooms/livekit/components/controls/Options/SupportMenuItem'
|
import { SupportMenuItem } from '@/features/rooms/livekit/components/controls/Options/SupportMenuItem'
|
||||||
import { PictureInPictureMenuItem } from '@/features/rooms/livekit/components/controls/Options/PictureInPictureMenuItem'
|
import { PictureInPictureMenuItem } from '@/features/rooms/livekit/components/controls/Options/PictureInPictureMenuItem'
|
||||||
import { pipLayoutStore } from '@/features/pip/stores/pipLayoutStore'
|
import { pipLayoutStore } from '@/features/pip/stores/pipLayoutStore'
|
||||||
import { menuRecipe } from '@/primitives/menuRecipe'
|
import { PipOverflowItems } from './PipOverflowItems'
|
||||||
import { useRoomContext } from '@livekit/components-react'
|
|
||||||
import { useRaisedHand } from '@/features/rooms/livekit/hooks/useRaisedHand'
|
|
||||||
import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
|
|
||||||
import { useAreSubtitlesAvailable } from '@/features/subtitle/hooks/useAreSubtitlesAvailable'
|
|
||||||
import { useSnapshot } from 'valtio'
|
|
||||||
import type { CollapsibleControl } from '../PipControlBar'
|
import type { CollapsibleControl } from '../PipControlBar'
|
||||||
|
|
||||||
type PipOptionsMenuItemsProps = {
|
type PipOptionsMenuItemsProps = {
|
||||||
@@ -40,7 +29,7 @@ export const PipOptionsMenuItems = ({
|
|||||||
{hasOverflow && (
|
{hasOverflow && (
|
||||||
<>
|
<>
|
||||||
<MenuSection>
|
<MenuSection>
|
||||||
<OverflowItems overflowControls={overflowControls} t={t} />
|
<PipOverflowItems overflowControls={overflowControls} t={t} />
|
||||||
</MenuSection>
|
</MenuSection>
|
||||||
<Separator />
|
<Separator />
|
||||||
</>
|
</>
|
||||||
@@ -57,60 +46,3 @@ export const PipOptionsMenuItems = ({
|
|||||||
</RACMenu>
|
</RACMenu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const OverflowItems = ({
|
|
||||||
overflowControls,
|
|
||||||
t,
|
|
||||||
}: {
|
|
||||||
overflowControls: Set<CollapsibleControl>
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
t: any
|
|
||||||
}) => {
|
|
||||||
const room = useRoomContext()
|
|
||||||
const { isHandRaised, toggleRaisedHand } = useRaisedHand({
|
|
||||||
participant: room.localParticipant,
|
|
||||||
})
|
|
||||||
const { areSubtitlesOpen, toggleSubtitles } = useSubtitles()
|
|
||||||
const areSubtitlesAvailable = useAreSubtitlesAvailable()
|
|
||||||
const pipSnap = useSnapshot(pipLayoutStore)
|
|
||||||
const toggleReactions = () => {
|
|
||||||
pipLayoutStore.showReactionsToolbar = !pipSnap.showReactionsToolbar
|
|
||||||
}
|
|
||||||
const itemClass = menuRecipe({ icon: true, variant: 'dark' }).item
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{overflowControls.has('reactions') && (
|
|
||||||
<MenuItem onAction={toggleReactions} className={itemClass}>
|
|
||||||
<RiEmotionLine size={20} />
|
|
||||||
{t('controls.reactions.button')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
{overflowControls.has('screenShare') && (
|
|
||||||
<MenuItem
|
|
||||||
onAction={() => {
|
|
||||||
/* screen share requires track toggle, handled externally */
|
|
||||||
}}
|
|
||||||
className={itemClass}
|
|
||||||
>
|
|
||||||
<RiArrowUpLine size={20} />
|
|
||||||
{t('controls.screenShare.start')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
{overflowControls.has('subtitles') && areSubtitlesAvailable && (
|
|
||||||
<MenuItem onAction={toggleSubtitles} className={itemClass}>
|
|
||||||
<RiClosedCaptioningLine size={20} />
|
|
||||||
{areSubtitlesOpen
|
|
||||||
? t('controls.subtitles.open')
|
|
||||||
: t('controls.subtitles.closed')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
{overflowControls.has('hand') && (
|
|
||||||
<MenuItem onAction={toggleRaisedHand} className={itemClass}>
|
|
||||||
<RiHand size={20} />
|
|
||||||
{isHandRaised ? t('controls.hand.lower') : t('controls.hand.raise')}
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { MenuItem } from 'react-aria-components'
|
||||||
|
import {
|
||||||
|
RiHand,
|
||||||
|
RiClosedCaptioningLine,
|
||||||
|
RiArrowUpLine,
|
||||||
|
RiEmotionLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import { TFunction } from 'i18next'
|
||||||
|
import { pipLayoutStore } from '@/features/pip/stores/pipLayoutStore'
|
||||||
|
import { menuRecipe } from '@/primitives/menuRecipe'
|
||||||
|
import { useRoomContext } from '@livekit/components-react'
|
||||||
|
import { useRaisedHand } from '@/features/rooms/livekit/hooks/useRaisedHand'
|
||||||
|
import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
|
||||||
|
import { useAreSubtitlesAvailable } from '@/features/subtitle/hooks/useAreSubtitlesAvailable'
|
||||||
|
import { useSnapshot } from 'valtio'
|
||||||
|
import type { CollapsibleControl } from '../PipControlBar'
|
||||||
|
|
||||||
|
type PipOverflowItemsProps = {
|
||||||
|
overflowControls: Set<CollapsibleControl>
|
||||||
|
t: TFunction<'rooms'>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PipOverflowItems = ({
|
||||||
|
overflowControls,
|
||||||
|
t,
|
||||||
|
}: PipOverflowItemsProps) => {
|
||||||
|
const room = useRoomContext()
|
||||||
|
const { isHandRaised, toggleRaisedHand } = useRaisedHand({
|
||||||
|
participant: room.localParticipant,
|
||||||
|
})
|
||||||
|
const { areSubtitlesOpen, toggleSubtitles } = useSubtitles()
|
||||||
|
const areSubtitlesAvailable = useAreSubtitlesAvailable()
|
||||||
|
const pipSnap = useSnapshot(pipLayoutStore)
|
||||||
|
const toggleReactions = () => {
|
||||||
|
pipLayoutStore.showReactionsToolbar = !pipSnap.showReactionsToolbar
|
||||||
|
}
|
||||||
|
const itemClass = menuRecipe({ icon: true, variant: 'dark' }).item
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{overflowControls.has('reactions') && (
|
||||||
|
<MenuItem onAction={toggleReactions} className={itemClass}>
|
||||||
|
<RiEmotionLine size={20} />
|
||||||
|
{t('controls.reactions.button')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{overflowControls.has('screenShare') && (
|
||||||
|
<MenuItem
|
||||||
|
onAction={() => {
|
||||||
|
/* screen share requires track toggle, handled externally */
|
||||||
|
}}
|
||||||
|
className={itemClass}
|
||||||
|
>
|
||||||
|
<RiArrowUpLine size={20} />
|
||||||
|
{t('controls.screenShare.start')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{overflowControls.has('subtitles') && areSubtitlesAvailable && (
|
||||||
|
<MenuItem onAction={toggleSubtitles} className={itemClass}>
|
||||||
|
<RiClosedCaptioningLine size={20} />
|
||||||
|
{areSubtitlesOpen
|
||||||
|
? t('controls.subtitles.open')
|
||||||
|
: t('controls.subtitles.closed')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
{overflowControls.has('hand') && (
|
||||||
|
<MenuItem onAction={toggleRaisedHand} className={itemClass}>
|
||||||
|
<RiHand size={20} />
|
||||||
|
{isHandRaised ? t('controls.hand.lower') : t('controls.hand.raise')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -17,31 +17,31 @@ type PipFocusLayoutProps = {
|
|||||||
* above/below when the window shape doesn't match the source.
|
* above/below when the window shape doesn't match the source.
|
||||||
* The thumbnail keeps the usual cover fill.
|
* The thumbnail keeps the usual cover fill.
|
||||||
*/
|
*/
|
||||||
export const PipFocusLayout = memo(function PipFocusLayout({
|
export const PipFocusLayout = memo(
|
||||||
mainTrack,
|
({ mainTrack, thumbnailTrack }: PipFocusLayoutProps) => {
|
||||||
thumbnailTrack,
|
return (
|
||||||
}: PipFocusLayoutProps) {
|
<FocusContainer>
|
||||||
return (
|
<MainSlot>
|
||||||
<FocusContainer>
|
|
||||||
<MainSlot>
|
|
||||||
<ParticipantTile
|
|
||||||
key={getTrackKey(mainTrack)}
|
|
||||||
trackRef={mainTrack}
|
|
||||||
disableMetadata
|
|
||||||
/>
|
|
||||||
</MainSlot>
|
|
||||||
{thumbnailTrack && (
|
|
||||||
<Thumbnail>
|
|
||||||
<ParticipantTile
|
<ParticipantTile
|
||||||
key={getTrackKey(thumbnailTrack)}
|
key={getTrackKey(mainTrack)}
|
||||||
trackRef={thumbnailTrack}
|
trackRef={mainTrack}
|
||||||
disableMetadata
|
disableMetadata
|
||||||
/>
|
/>
|
||||||
</Thumbnail>
|
</MainSlot>
|
||||||
)}
|
{thumbnailTrack && (
|
||||||
</FocusContainer>
|
<Thumbnail>
|
||||||
)
|
<ParticipantTile
|
||||||
})
|
key={getTrackKey(thumbnailTrack)}
|
||||||
|
trackRef={thumbnailTrack}
|
||||||
|
disableMetadata
|
||||||
|
/>
|
||||||
|
</Thumbnail>
|
||||||
|
)}
|
||||||
|
</FocusContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
PipFocusLayout.displayName = 'PipFocusLayout'
|
||||||
|
|
||||||
const FocusContainer = styled('div', {
|
const FocusContainer = styled('div', {
|
||||||
base: {
|
base: {
|
||||||
|
|||||||
@@ -21,9 +21,7 @@ type PipGridLayoutProps = {
|
|||||||
*
|
*
|
||||||
* Tiles keep a stable key so resizing never remounts <video> elements.
|
* Tiles keep a stable key so resizing never remounts <video> elements.
|
||||||
*/
|
*/
|
||||||
export const PipGridLayout = memo(function PipGridLayout({
|
export const PipGridLayout = memo(({ tracks }: PipGridLayoutProps) => {
|
||||||
tracks,
|
|
||||||
}: PipGridLayoutProps) {
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const { width, height } = usePipElementSize(containerRef)
|
const { width, height } = usePipElementSize(containerRef)
|
||||||
|
|
||||||
@@ -54,6 +52,7 @@ export const PipGridLayout = memo(function PipGridLayout({
|
|||||||
</GridContainer>
|
</GridContainer>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
PipGridLayout.displayName = 'PipGridLayout'
|
||||||
|
|
||||||
const GridContainer = styled('div', {
|
const GridContainer = styled('div', {
|
||||||
base: {
|
base: {
|
||||||
|
|||||||
Reference in New Issue
Block a user