mirror of
https://github.com/suitenumerique/meet.git
synced 2026-10-19 19:21:47 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27b0f44b2a | |||
| 0b33806d52 | |||
| 0fa8e45b3c |
@@ -1,35 +1,3 @@
|
||||
:root {
|
||||
--fonts-sans: 'Marianne', ui-sans-serif, system-ui, sans-serif;
|
||||
--avatar-cap-height: 0.7;
|
||||
}
|
||||
|
||||
.Header-beforeLogo {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.Header-beforeLogo::before {
|
||||
content: '';
|
||||
display: block;
|
||||
background-image: url(/assets/marianne.svg);
|
||||
background-position: 0 -0.046875rem;
|
||||
background-size: 2.0625rem 0.84375rem;
|
||||
height: 0.75rem;
|
||||
margin-bottom: 0.1rem;
|
||||
width: 2.0625rem;
|
||||
}
|
||||
|
||||
.Header-beforeLogo::after {
|
||||
content: '';
|
||||
display: block;
|
||||
background-image: url(/assets/gouvernement.svg), url(/assets/devise.svg);
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-size: 108.8px 10px, 40px 29px;
|
||||
background-position: 0 3px, 0 18.9px;
|
||||
width: 108.8px;
|
||||
height: 48px;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Marianne';
|
||||
src: url('/assets/fonts/Marianne-Regular-subset.woff2') format('woff2');
|
||||
@@ -69,3 +37,196 @@
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
.Header-beforeLogo {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.Header-beforeLogo::before {
|
||||
content: '';
|
||||
display: block;
|
||||
background-image: url(/assets/marianne.svg);
|
||||
background-position: 0 -0.046875rem;
|
||||
background-size: 2.0625rem 0.84375rem;
|
||||
height: 0.75rem;
|
||||
margin-bottom: 0.1rem;
|
||||
width: 2.0625rem;
|
||||
}
|
||||
|
||||
.Header-beforeLogo::after {
|
||||
content: '';
|
||||
display: block;
|
||||
background-image: url(/assets/gouvernement.svg), url(/assets/devise.svg);
|
||||
background-repeat: no-repeat, no-repeat;
|
||||
background-size: 108.8px 10px, 40px 29px;
|
||||
background-position: 0 3px, 0 18.9px;
|
||||
width: 108.8px;
|
||||
height: 48px;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
:root {
|
||||
--fonts-sans: Marianne, "Mona Sans VF", -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans Backtick Fix", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
--global-font-body: var(--fonts-sans);
|
||||
|
||||
--avatar-cap-height: 0.7;
|
||||
|
||||
/* use hand-picked variables from UI-kit and apply them to meet's variables */
|
||||
--c--globals--colors--brand-050: #EDF0FF;
|
||||
--c--globals--colors--brand-100: #DAE2FF;
|
||||
--c--globals--colors--brand-250: #A2B6FF;
|
||||
--c--globals--colors--brand-550: #3E5DE7;
|
||||
--c--globals--colors--brand-600: #304DDF;
|
||||
--c--globals--colors--brand-650: #2845C1;
|
||||
--c--globals--colors--brand-700: #223E9E;
|
||||
--c--globals--colors--gray-050: #F0F0F3;
|
||||
--c--globals--colors--gray-100: #DFE2EA;
|
||||
--c--globals--colors--gray-150: #CFD5DE;
|
||||
--c--globals--colors--gray-250: #B2B9C7;
|
||||
--c--globals--colors--gray-300: #A4ABBC;
|
||||
--c--globals--colors--gray-550: #626A80;
|
||||
--c--globals--colors--gray-600: #555E74;
|
||||
--c--globals--colors--gray-650: #4A5267;
|
||||
--c--globals--colors--gray-700: #3F4759;
|
||||
--c--globals--colors--gray-750: #363B4C;
|
||||
--c--globals--colors--gray-800: #2B303D;
|
||||
--c--globals--colors--gray-850: #222631;
|
||||
--c--globals--colors--gray-900: #181B24;
|
||||
--c--globals--colors--white-050: #F6F8F90D;
|
||||
--c--globals--colors--white-950: #F6F8F9F2;
|
||||
--c--globals--colors--black-050: #181B240D;
|
||||
--c--globals--colors--black-850: #181B24D9;
|
||||
--c--globals--colors--error-550: #CF202D;
|
||||
--c--globals--colors--error-650: #9D2227;
|
||||
|
||||
--colors-primary-800: var(--c--contextuals--background--semantic--brand--primary);
|
||||
--colors-primary: var(--c--contextuals--background--semantic--brand--primary);
|
||||
--colors-primary-action: var(--c--contextuals--background--semantic--brand--primary-hover);
|
||||
--colors-default-subtle-text: var(--c--contextuals--content--semantic--neutral--secondary);
|
||||
--colors-default-text: var(--c--contextuals--content--semantic--neutral--primary);
|
||||
--colors-primary-subtle: var(--c--contextuals--background--semantic--brand--secondary);
|
||||
--colors-primary-subtle-text: var(--c--contextuals--content--semantic--brand--secondary);
|
||||
--colors-greyscale-250: var(--c--globals--colors--gray-100);
|
||||
/* for the room's page background color */
|
||||
--colors-primary-dark-50: var(--c--globals--colors--gray-900);
|
||||
|
||||
/* light theme specific UI-kit semantic values */
|
||||
--c--contextuals--background--semantic--brand--primary: var(--c--globals--colors--brand-550);
|
||||
--c--contextuals--background--semantic--brand--primary-hover: var(--c--globals--colors--brand-650);
|
||||
--c--contextuals--content--semantic--brand--on-brand: var(--c--globals--colors--brand-050);
|
||||
--c--contextuals--content--semantic--neutral--secondary: var(--c--globals--colors--gray-600);
|
||||
--c--contextuals--content--semantic--neutral--primary: var(--c--globals--colors--gray-850);
|
||||
--c--contextuals--background--semantic--brand--secondary: var(--c--globals--colors--brand-100);
|
||||
--c--contextuals--content--semantic--brand--secondary: var(--c--globals--colors--brand-600);
|
||||
--c--contextuals--content--semantic--brand--tertiary: var(--c--globals--colors--brand-550);
|
||||
--c--contextuals--background--semantic--contextual--primary: var(--c--globals--colors--black-050);
|
||||
--c--contextuals--content--semantic--contextual--primary: var(--c--globals--colors--white-950);
|
||||
--c--contextuals--background--semantic--error--primary: var(--c--globals--colors--error-550);
|
||||
--c--contextuals--background--semantic--error--primary-hover: var(--c--globals--colors--error-650);
|
||||
--c--contextuals--background--semantic--neutral--tertiary: var(--c--globals--colors--gray-050);
|
||||
--c--contextuals--border--semantic--neutral--tertiary: var(--c--globals--colors--gray-150);
|
||||
--c--contextuals--content--semantic--neutral--tertiary: var(--c--globals--colors--gray-550);
|
||||
|
||||
/* try to match the UI-kit's control border color, but keep a 3:1 contrast ratio with white to pass WCAG rules on input borders */
|
||||
--colors-control-border: #8996A9;
|
||||
}
|
||||
|
||||
/* dark theme matching (room page) */
|
||||
.Box--variant-dark,
|
||||
.Tooltip,
|
||||
#control-bar {
|
||||
--c--contextuals--background--semantic--brand--primary: var(--c--globals--colors--brand-550);
|
||||
--c--contextuals--background--semantic--brand--primary-hover: var(--c--globals--colors--brand-650);
|
||||
--c--contextuals--content--semantic--brand--on-brand: var(--c--globals--colors--brand-050);
|
||||
--c--contextuals--content--semantic--neutral--secondary: var(--c--globals--colors--gray-100);
|
||||
--c--contextuals--content--semantic--neutral--primary: var(--c--globals--colors--gray-050);
|
||||
--c--contextuals--background--semantic--brand--secondary: var(--c--globals--colors--brand-700);
|
||||
--c--contextuals--content--semantic--brand--secondary: var(--c--globals--colors--brand-100);
|
||||
--c--contextuals--content--semantic--brand--tertiary: var(--c--globals--colors--brand-250);
|
||||
--c--contextuals--background--semantic--contextual--primary: var(--c--globals--colors--white-050);
|
||||
--c--contextuals--content--semantic--contextual--primary: var(--c--globals--colors--black-850);
|
||||
--c--contextuals--background--semantic--error--primary: var(--c--globals--colors--error-550);
|
||||
--c--contextuals--background--semantic--error--primary-hover: var(--c--globals--colors--error-650);
|
||||
--c--contextuals--background--semantic--neutral--secondary: var(--c--globals--colors--gray-700);
|
||||
--c--contextuals--background--semantic--neutral--secondary-hover: var(--c--globals--colors--gray-750);
|
||||
--c--contextuals--content--semantic--neutral--on-neutral: var(--c--globals--colors--gray-050);
|
||||
--c--contextuals--background--surface--primary: var(--c--globals--colors--gray-800);
|
||||
--c--contextuals--background--semantic--neutral--tertiary: var(--c--globals--colors--gray-750);
|
||||
--c--contextuals--border--semantic--neutral--tertiary: var(--c--globals--colors--gray-700);
|
||||
--c--contextuals--content--semantic--neutral--tertiary: var(--c--globals--colors--gray-250);
|
||||
}
|
||||
|
||||
.Button--variant-primary {
|
||||
background-color: var(--c--contextuals--background--semantic--brand--primary);
|
||||
color: var(--c--contextuals--content--semantic--brand--on-brand);
|
||||
}
|
||||
.Button--variant-primary:is([data-hovered], [data-pressed]) {
|
||||
background-color: var(--c--contextuals--background--semantic--brand--primary-hover);
|
||||
}
|
||||
|
||||
.Button--variant-secondary {
|
||||
border-color: #CFD5DE;
|
||||
}
|
||||
|
||||
.Button--variant-secondaryText {
|
||||
background: transparent;
|
||||
color: var(--c--contextuals--content--semantic--brand--tertiary);
|
||||
}
|
||||
|
||||
.Button--variant-secondaryText:is([data-hovered], [data-pressed]) {
|
||||
background-color: var(--c--contextuals--background--semantic--contextual--primary);
|
||||
}
|
||||
|
||||
.Button--variant-primaryDark {
|
||||
background-color: var(--c--contextuals--background--semantic--neutral--secondary);
|
||||
color: var(--c--contextuals--content--semantic--neutral--secondary);
|
||||
}
|
||||
.Button--variant-primaryDark:is([data-hovered], [data-pressed]) {
|
||||
background-color: var(--c--contextuals--background--semantic--neutral--secondary-hover);
|
||||
}
|
||||
|
||||
.Button--variant-danger {
|
||||
background-color: var(--c--contextuals--background--semantic--error--primary);
|
||||
color: var(--c--contextuals--content--semantic--brand--on-brand);
|
||||
}
|
||||
.Button--variant-danger:is([data-hovered], [data-pressed]) {
|
||||
background-color: var(--c--contextuals--background--semantic--error--primary-hover);
|
||||
}
|
||||
|
||||
.Button--variant-primaryTextDark {
|
||||
background: transparent;
|
||||
color: var(--c--contextuals--content--semantic--neutral--tertiary);
|
||||
}
|
||||
|
||||
.Button--variant-primaryTextDark:is([data-hovered], [data-pressed]) {
|
||||
background-color: var(--c--contextuals--background--semantic--contextual--primary);
|
||||
}
|
||||
|
||||
.Box--variant-dark.Box--type-popover {
|
||||
background-color: var(--c--contextuals--background--surface--primary);
|
||||
}
|
||||
|
||||
.Tooltip {
|
||||
background: var(--c--contextuals--background--semantic--neutral--tertiary);
|
||||
border-color: var(--c--contextuals--border--semantic--neutral--tertiary);
|
||||
color: var(--c--contextuals--content--semantic--neutral--tertiary);
|
||||
}
|
||||
|
||||
/* switch component */
|
||||
.indicator,
|
||||
[data-selected="true"] .indicator::before {
|
||||
background-color: var(--c--contextuals--content--semantic--contextual--primary);
|
||||
}
|
||||
.indicator .cross {
|
||||
color: var(--c--contextuals--content--semantic--contextual--primary);
|
||||
}
|
||||
label:not([data-selected="true"]) .indicator::before,
|
||||
[data-selected="true"] .indicator {
|
||||
background: var(--colors-primary-800);
|
||||
}
|
||||
/* end switch component */
|
||||
|
||||
/* prevent impacting the toolbar buttons (primaryDark variant) */
|
||||
.Button--size-default:is(.Button--variant-primary, .Button--variant-secondary) {
|
||||
padding-block: 0.4375rem;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { LogLevel, setLogLevel } from 'livekit-client'
|
||||
import { useWatchDeviceAvailability } from '@/features/rooms/hooks/useWatchDeviceAvailability'
|
||||
import { useWatchDeviceReleased } from '@/features/rooms/hooks/useWatchDeviceReleased'
|
||||
import { useRoomPageTitle } from '@/features/rooms/livekit/hooks/useRoomPageTitle'
|
||||
import { usePageDataAttribute } from '@/navigation/usePageDataAttribute'
|
||||
|
||||
const BaseRoom = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
@@ -38,10 +39,12 @@ const Room = () => {
|
||||
const initialRoomData = history.state?.initialRoomData
|
||||
const mode = isLoggedIn && history.state?.create ? 'create' : 'join'
|
||||
const skipJoinScreen = isLoggedIn && mode === 'create'
|
||||
const isJoinScreen = !hasSubmittedEntry && !skipJoinScreen
|
||||
|
||||
const { data } = useConfig()
|
||||
|
||||
useRoomPageTitle(roomId)
|
||||
usePageDataAttribute(isJoinScreen ? 'join' : 'conference')
|
||||
|
||||
useEffect(() => {
|
||||
const shouldSilenceLogs = data?.silence_livekit_debug_logs || false
|
||||
@@ -75,7 +78,7 @@ const Room = () => {
|
||||
return <ErrorScreen />
|
||||
}
|
||||
|
||||
if (!hasSubmittedEntry && !skipJoinScreen) {
|
||||
if (isJoinScreen) {
|
||||
return (
|
||||
<BaseRoom>
|
||||
<Join enterRoom={() => setHasSubmittedEntry(true)} roomId={roomId} />
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useSnapshot } from 'valtio'
|
||||
import { Footer } from '@/layout/Footer'
|
||||
import { ScreenReaderAnnouncer } from '@/primitives'
|
||||
import { SkipLink, MAIN_CONTENT_ID } from './SkipLink'
|
||||
import { usePageDataAttribute } from '@/navigation/usePageDataAttribute'
|
||||
|
||||
export type Layout = 'fullpage' | 'centered'
|
||||
|
||||
@@ -16,6 +17,8 @@ export type Layout = 'fullpage' | 'centered'
|
||||
* In a specific page, use the `Screen` component and change its props to change global page layout.
|
||||
*/
|
||||
export const Layout = ({ children }: { children: ReactNode }) => {
|
||||
usePageDataAttribute()
|
||||
|
||||
const layoutSnap = useSnapshot(layoutStore)
|
||||
const showHeader = layoutSnap.showHeader
|
||||
const showFooter = layoutSnap.showFooter
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { useEffect } from 'react'
|
||||
import { matchRoute, useLocation, useRouter } from 'wouter'
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
import { routes } from '@/routes'
|
||||
|
||||
const pageStore = proxy<{ override?: string }>({})
|
||||
|
||||
/**
|
||||
* Apply a [data-page] attribute on the html element, that matches
|
||||
* current route name, or a custom page name if provided.
|
||||
*
|
||||
* Used to allow easier customization of the app with custom CSS.
|
||||
*
|
||||
* This is meant to be used once without arguments in a wrapping component
|
||||
* that renders the whole app. And, on a case by case basis with a `page` argument
|
||||
* when needed.
|
||||
*/
|
||||
export const usePageDataAttribute = (page?: string) => {
|
||||
const router = useRouter()
|
||||
const [location] = useLocation()
|
||||
const { override } = useSnapshot(pageStore)
|
||||
|
||||
useEffect(() => {
|
||||
if (page === undefined) return
|
||||
|
||||
pageStore.override = page
|
||||
return () => {
|
||||
if (pageStore.override === page) {
|
||||
pageStore.override = undefined
|
||||
}
|
||||
}
|
||||
}, [page])
|
||||
|
||||
const routeName = Object.values(routes).find(
|
||||
(route) => matchRoute(router.parser, route.path, location)[0]
|
||||
)?.name
|
||||
|
||||
const pageName = page ?? override ?? routeName
|
||||
|
||||
useEffect(() => {
|
||||
const root = document.documentElement
|
||||
|
||||
if (pageName) {
|
||||
root.setAttribute('data-page', pageName)
|
||||
} else {
|
||||
root.removeAttribute('data-page')
|
||||
}
|
||||
}, [pageName])
|
||||
|
||||
useEffect(() => {
|
||||
if (page !== undefined) return
|
||||
|
||||
return () => {
|
||||
document.documentElement.removeAttribute('data-page')
|
||||
}
|
||||
}, [page])
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
import { ComponentProps, forwardRef } from 'react'
|
||||
import { cva } from '@/styled-system/css'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
|
||||
const boxDefaultVariants = {
|
||||
variant: 'light',
|
||||
size: 'default',
|
||||
} as const
|
||||
|
||||
const box = cva({
|
||||
base: {
|
||||
position: 'relative',
|
||||
@@ -64,10 +70,25 @@ const box = cva({
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'light',
|
||||
size: 'default',
|
||||
},
|
||||
defaultVariants: boxDefaultVariants,
|
||||
})
|
||||
|
||||
export const Box = styled('div', box)
|
||||
const StyledBox = styled('div', box)
|
||||
|
||||
export const Box = forwardRef<HTMLDivElement, ComponentProps<typeof StyledBox>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<StyledBox
|
||||
ref={ref}
|
||||
className={[
|
||||
// These are not necessary for the app, but can help people using custom CSS for theming
|
||||
'Box',
|
||||
`Box--variant-${props.variant || boxDefaultVariants.variant}`,
|
||||
`Box--size-${props.size || boxDefaultVariants.size}`,
|
||||
props.type && `Box--type-${props.type}`,
|
||||
className,
|
||||
].filter(Boolean).join(' ')}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
Box.displayName = 'Box'
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
type ButtonProps as RACButtonsProps,
|
||||
} from 'react-aria-components'
|
||||
import { type RecipeVariantProps } from '@/styled-system/css'
|
||||
import { buttonRecipe, type ButtonRecipe } from './buttonRecipe'
|
||||
import { buttonRecipe, buttonRecipeDefaultVariants, type ButtonRecipe } from './buttonRecipe'
|
||||
import { TooltipWrapper, type TooltipWrapperProps } from './TooltipWrapper'
|
||||
import { ReactNode, forwardRef } from 'react'
|
||||
import { Loader } from './Loader'
|
||||
@@ -26,7 +26,14 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
<TooltipWrapper tooltip={tooltip} tooltipType={tooltipType}>
|
||||
<RACButton
|
||||
ref={ref}
|
||||
className={[buttonRecipe(variantProps), className].join(' ')}
|
||||
className={[
|
||||
buttonRecipe(variantProps),
|
||||
className,
|
||||
// These are not necessary for the app, but can help people using custom CSS for theming
|
||||
'Button',
|
||||
`Button--variant-${variantProps.variant || buttonRecipeDefaultVariants.variant}`,
|
||||
`Button--size-${variantProps.size || buttonRecipeDefaultVariants.size}`,
|
||||
].join(' ')}
|
||||
{...(remainingComponentProps as RACButtonsProps)}
|
||||
>
|
||||
{!props.loading && props.icon}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ToggleButton as RACToggleButton,
|
||||
ToggleButtonProps as RACToggleButtonProps,
|
||||
} from 'react-aria-components'
|
||||
import { type ButtonRecipeProps, buttonRecipe } from './buttonRecipe'
|
||||
import { type ButtonRecipeProps, buttonRecipe, buttonRecipeDefaultVariants } from './buttonRecipe'
|
||||
import { TooltipWrapper, type TooltipWrapperProps } from './TooltipWrapper'
|
||||
|
||||
export type ToggleButtonProps = RACToggleButtonProps &
|
||||
@@ -25,7 +25,14 @@ export const ToggleButton = forwardRef<HTMLButtonElement, ToggleButtonProps>(
|
||||
<RACToggleButton
|
||||
ref={ref}
|
||||
{...componentProps}
|
||||
className={[buttonRecipe(variantProps), props.className].join(' ')}
|
||||
className={[
|
||||
buttonRecipe(variantProps),
|
||||
props.className,
|
||||
// These are not necessary for the app, but can help people using custom CSS for theming
|
||||
'Button Button--toggle',
|
||||
`Button--variant-${variantProps.variant || buttonRecipeDefaultVariants.variant}`,
|
||||
`Button--size-${variantProps.size || buttonRecipeDefaultVariants.size}`,
|
||||
].join(' ')}
|
||||
>
|
||||
<>
|
||||
{componentProps.children as ReactNode}
|
||||
|
||||
@@ -114,10 +114,11 @@ const TooltipArrow = () => {
|
||||
|
||||
const Tooltip = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: Omit<TooltipProps, 'children'> & { children: ReactNode }) => {
|
||||
return (
|
||||
<StyledTooltip {...props}>
|
||||
<StyledTooltip {...props} className={[className, 'Tooltip'].filter(Boolean).join(' ')}>
|
||||
<TooltipArrow />
|
||||
{children}
|
||||
</StyledTooltip>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { type RecipeVariantProps, cva } from '@/styled-system/css'
|
||||
|
||||
|
||||
export const buttonRecipeDefaultVariants = {
|
||||
size: 'default',
|
||||
variant: 'primary',
|
||||
} as const
|
||||
|
||||
export const buttonRecipe = cva({
|
||||
base: {
|
||||
display: 'flex',
|
||||
@@ -393,10 +399,7 @@ export const buttonRecipe = cva({
|
||||
},
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
size: 'default',
|
||||
variant: 'primary',
|
||||
},
|
||||
defaultVariants: buttonRecipeDefaultVariants,
|
||||
})
|
||||
|
||||
export type ButtonRecipe = typeof buttonRecipe
|
||||
|
||||
Reference in New Issue
Block a user