♿️(frontend) shortcuts table: semantic structure and kbd badge
caption, th scope, <kbd> for keys, no Tab stops on rows
This commit is contained in:
@@ -41,6 +41,7 @@ and this project adheres to
|
|||||||
- ♿️(frontend) improve accessibility of the IntroSlider carousel #1026
|
- ♿️(frontend) improve accessibility of the IntroSlider carousel #1026
|
||||||
- ♿️(frontend) add skip link component for keyboard navigation #1019
|
- ♿️(frontend) add skip link component for keyboard navigation #1019
|
||||||
- ♿️(frontend) announce mic/camera state to SR on shortcut toggle #1052
|
- ♿️(frontend) announce mic/camera state to SR on shortcut toggle #1052
|
||||||
|
- ✨(frontend) add Ctrl+Shift+/ to open shortcuts settings #1050
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
|
|||||||
import { useConnectionObserver } from '../hooks/useConnectionObserver'
|
import { useConnectionObserver } from '../hooks/useConnectionObserver'
|
||||||
import { useNoiseReduction } from '../hooks/useNoiseReduction'
|
import { useNoiseReduction } from '../hooks/useNoiseReduction'
|
||||||
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
|
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
|
||||||
import { settingsStore } from '@/stores/settings'
|
import { useSettingsDialog } from '@/features/settings'
|
||||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||||
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
|
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
|
||||||
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
|
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
|
||||||
@@ -100,6 +100,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
|||||||
const { t: tRooms } = useTranslation('rooms')
|
const { t: tRooms } = useTranslation('rooms')
|
||||||
const room = useRoomContext()
|
const room = useRoomContext()
|
||||||
const announce = useScreenReaderAnnounce()
|
const announce = useScreenReaderAnnounce()
|
||||||
|
const { toggleSettingsDialog } = useSettingsDialog()
|
||||||
|
|
||||||
const getAnnouncementName = useCallback(
|
const getAnnouncementName = useCallback(
|
||||||
(participant?: Participant | null) => {
|
(participant?: Participant | null) => {
|
||||||
@@ -117,9 +118,8 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
|||||||
useRegisterKeyboardShortcut({
|
useRegisterKeyboardShortcut({
|
||||||
id: 'open-shortcuts',
|
id: 'open-shortcuts',
|
||||||
handler: useCallback(() => {
|
handler: useCallback(() => {
|
||||||
settingsStore.defaultSelectedTab = SettingsDialogExtendedKey.SHORTCUTS
|
toggleSettingsDialog(SettingsDialogExtendedKey.SHORTCUTS)
|
||||||
settingsStore.areSettingsOpen = true
|
}, [toggleSettingsDialog]),
|
||||||
}, []),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const tracks = useTracks(
|
const tracks = useTracks(
|
||||||
|
|||||||
@@ -3,15 +3,20 @@ import { ShortcutRow } from '@/features/shortcuts/components/ShortcutRow'
|
|||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { TabPanel, type TabPanelProps } from '@/primitives/Tabs'
|
import { TabPanel, type TabPanelProps } from '@/primitives/Tabs'
|
||||||
import { H } from '@/primitives'
|
|
||||||
|
|
||||||
const tableStyle = css({
|
const tableStyle = css({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
borderCollapse: 'collapse',
|
borderCollapse: 'collapse',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
|
'& caption': {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: '0.75rem',
|
||||||
|
textAlign: 'left',
|
||||||
|
},
|
||||||
'& th, & td': {
|
'& th, & td': {
|
||||||
padding: '0.65rem 0',
|
padding: '0.65rem 0',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
|
fontWeight: 'normal',
|
||||||
},
|
},
|
||||||
'& tbody tr': {
|
'& tbody tr': {
|
||||||
borderBottom: '1px solid rgba(255,255,255,0.08)',
|
borderBottom: '1px solid rgba(255,255,255,0.08)',
|
||||||
@@ -29,12 +34,11 @@ export const ShortcutTab = ({ id }: Pick<TabPanelProps, 'id'>) => {
|
|||||||
className={css({
|
className={css({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '0.75rem',
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<H lvl={2}>{t('shortcuts.listLabel')}</H>
|
|
||||||
<table className={tableStyle}>
|
<table className={tableStyle}>
|
||||||
<thead className="sr-only">
|
<caption>{t('shortcuts.listLabel')}</caption>
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">{t('shortcuts.columnAction')}</th>
|
<th scope="col">{t('shortcuts.columnAction')}</th>
|
||||||
<th scope="col">{t('shortcuts.columnShortcut')}</th>
|
<th scope="col">{t('shortcuts.columnShortcut')}</th>
|
||||||
|
|||||||
@@ -14,7 +14,25 @@ export const useSettingsDialog = () => {
|
|||||||
settingsStore.areSettingsOpen = true
|
settingsStore.areSettingsOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeSettingsDialog = () => {
|
||||||
|
settingsStore.areSettingsOpen = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleSettingsDialog = (
|
||||||
|
defaultSelectedTab?: SettingsDialogExtendedKey
|
||||||
|
) => {
|
||||||
|
if (areSettingsOpen) {
|
||||||
|
closeSettingsDialog()
|
||||||
|
} else {
|
||||||
|
if (defaultSelectedTab)
|
||||||
|
settingsStore.defaultSelectedTab = defaultSelectedTab
|
||||||
|
settingsStore.areSettingsOpen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openSettingsDialog,
|
openSettingsDialog,
|
||||||
|
closeSettingsDialog,
|
||||||
|
toggleSettingsDialog,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ export const ShortcutBadge: React.FC<ShortcutBadgeProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={cx(badgeStyle, className)} aria-hidden="true">
|
<kbd className={cx(badgeStyle, className)} aria-hidden="true">
|
||||||
<span>{visualLabel}</span>
|
{visualLabel}
|
||||||
</div>
|
</kbd>
|
||||||
{srLabel && <span className="sr-only">{srLabel}</span>}
|
{srLabel && <span className="sr-only">{srLabel}</span>}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ export const ShortcutRow: React.FC<ShortcutRowProps> = ({ descriptor }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td className={text({ variant: 'body' })}>
|
<th scope="row" className={text({ variant: 'body' })}>
|
||||||
{t(`actions.${descriptor.id}`)}
|
{t(`actions.${descriptor.id}`)}
|
||||||
</td>
|
</th>
|
||||||
<td className={shortcutCellStyle}>
|
<td className={shortcutCellStyle}>
|
||||||
<ShortcutBadge visualLabel={visualShortcut} srLabel={srShortcut} />
|
<ShortcutBadge visualLabel={visualShortcut} srLabel={srShortcut} />
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user