@astryxdesign/core 0.7.1-canary.ff40b78 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -0
- package/dist/Button/Button.d.ts +1 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +5 -2
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +191 -32
- package/dist/Chat/ChatComposerInput.d.ts +9 -0
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +5 -0
- package/dist/Chat/ChatMessage.d.ts +11 -0
- package/dist/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.js +18 -9
- package/dist/Chat/useTriggerMenu.d.ts +9 -2
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +222 -50
- package/dist/CodeBlock/CodeBlock.d.ts +10 -1
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +158 -38
- package/dist/CodeBlock/tokenizer.d.ts +39 -0
- package/dist/CodeBlock/tokenizer.d.ts.map +1 -1
- package/dist/CodeBlock/tokenizer.js +108 -0
- package/dist/CommandPalette/CommandPalette.d.ts +9 -5
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +38 -20
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts +4 -3
- package/dist/CommandPalette/CommandPaletteEmpty.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteEmpty.js +4 -3
- package/dist/CommandPalette/CommandPaletteInput.d.ts +5 -4
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +35 -18
- package/dist/CommandPalette/CommandPaletteItem.d.ts +11 -6
- package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteItem.js +8 -9
- package/dist/CommandPalette/CommandPaletteList.d.ts +4 -2
- package/dist/CommandPalette/CommandPaletteList.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteList.js +10 -5
- package/dist/ComplexSelector/ComplexSelector.d.ts +8 -5
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +12 -3
- package/dist/ContextMenu/ContextMenu.d.ts +3 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +62 -48
- package/dist/DateInput/DateInput.js +1 -1
- package/dist/DateInput/MonthScroller.d.ts.map +1 -1
- package/dist/DateInput/MonthScroller.js +12 -4
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +12 -2
- package/dist/Dialog/Dialog.d.ts +1 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +16 -5
- package/dist/Dialog/DialogHeader.d.ts +1 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +7 -7
- package/dist/Divider/Divider.d.ts +1 -0
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +5 -4
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +161 -65
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +7 -5
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +12 -5
- package/dist/Field/InputClearButton.d.ts +8 -4
- package/dist/Field/InputClearButton.d.ts.map +1 -1
- package/dist/Field/InputClearButton.js +23 -7
- package/dist/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/FormLayout/FormLayout.js +1 -0
- package/dist/Grid/Grid.d.ts +13 -4
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +7 -1
- package/dist/HoverCard/useHoverCard.d.ts +1 -1
- package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
- package/dist/HoverCard/useHoverCard.js +67 -49
- package/dist/InputGroup/InputGroup.d.ts +2 -4
- package/dist/InputGroup/InputGroup.d.ts.map +1 -1
- package/dist/InputGroup/InputGroup.js +2 -4
- package/dist/InputGroup/groupStyles.d.ts +10 -2
- package/dist/InputGroup/groupStyles.d.ts.map +1 -1
- package/dist/InputGroup/groupStyles.js +19 -2
- package/dist/Layer/useLayerDismissal.d.ts +9 -9
- package/dist/Layer/useLayerDismissal.js +9 -9
- package/dist/Markdown/Markdown.d.ts.map +1 -1
- package/dist/Markdown/Markdown.js +25 -4
- package/dist/MobileNav/MobileNav.d.ts +7 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +45 -2
- package/dist/MultiSelector/MultiSelector.d.ts +7 -4
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +24 -3
- package/dist/NumberInput/NumberInput.js +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +6 -0
- package/dist/Resizable/ResizeHandle.d.ts +1 -1
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +83 -15
- package/dist/Resizable/useResizable.d.ts +23 -0
- package/dist/Resizable/useResizable.d.ts.map +1 -1
- package/dist/Resizable/useResizable.js +78 -0
- package/dist/Selector/Selector.d.ts +6 -3
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +28 -6
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +10 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +18 -7
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +16 -4
- package/dist/Toolbar/Toolbar.d.ts +2 -1
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +3 -1
- package/dist/TreeList/TreeList.d.ts.map +1 -1
- package/dist/TreeList/TreeList.js +6 -3
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +7 -1
- package/dist/astryx.css +22 -0
- package/dist/hooks/useScrollOverflow.d.ts.map +1 -1
- package/dist/hooks/useScrollOverflow.js +55 -12
- package/dist/hooks/useTreeFocus.d.ts +21 -0
- package/dist/hooks/useTreeFocus.d.ts.map +1 -1
- package/dist/hooks/useTreeFocus.js +40 -2
- package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/en.generated.js +3 -0
- package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
- package/dist/i18n/generated-locales/pseudo.generated.js +3 -0
- package/dist/reset.a11y.chromium.spec.js +6 -1
- package/dist/reset.until-found.a11y.browser.spec.d.ts +2 -0
- package/dist/reset.until-found.a11y.browser.spec.d.ts.map +1 -0
- package/dist/reset.until-found.a11y.browser.spec.js +71 -0
- package/dist/theme/syntax/SyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/SyntaxTheme.js +43 -2
- package/dist/theme/syntax/defineSyntaxTheme.d.ts +18 -5
- package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
- package/dist/theme/syntax/defineSyntaxTheme.js +38 -9
- package/dist/theme/syntax/index.d.ts +1 -1
- package/dist/theme/syntax/index.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.d.ts +12 -2
- package/dist/theme/syntax/tokens.d.ts.map +1 -1
- package/dist/theme/syntax/tokens.js +25 -4
- package/dist/utils/plainDate.d.ts +12 -0
- package/dist/utils/plainDate.d.ts.map +1 -1
- package/dist/utils/plainDate.js +63 -10
- package/locales/en.json +13 -1
- package/locales/pseudo.json +9 -0
- package/package.json +4 -4
- package/src/AlertDialog/AlertDialog.doc.mjs +6 -0
- package/src/AlertDialog/AlertDialog.spec.md +205 -0
- package/src/AlertDialog/AlertDialog.test.tsx +3 -14
- package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +115 -0
- package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +52 -0
- package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +68 -0
- package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +144 -0
- package/src/Button/Button.test.tsx +37 -0
- package/src/Button/Button.tsx +5 -1
- package/src/Calendar/Calendar.doc.mjs +234 -60
- package/src/Calendar/Calendar.spec.md +209 -0
- package/src/Calendar/Calendar.test.tsx +559 -20
- package/src/Calendar/Calendar.tsx +258 -41
- package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +126 -0
- package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +12 -0
- package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +57 -0
- package/src/Calendar/__tests__/Calendar.a11y.states.ts +139 -0
- package/src/Calendar/__tests__/Calendar.a11y.test.tsx +137 -0
- package/src/Carousel/Carousel.test.tsx +54 -71
- package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +192 -0
- package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +27 -0
- package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +39 -0
- package/src/Carousel/__tests__/Carousel.a11y.states.ts +139 -0
- package/src/Carousel/__tests__/Carousel.a11y.test.tsx +127 -0
- package/src/Chat/ChatComposerInput.doc.mjs +1 -1
- package/src/Chat/ChatComposerInput.spec.md +94 -55
- package/src/Chat/ChatComposerInput.test.tsx +202 -1
- package/src/Chat/ChatComposerInput.tsx +13 -0
- package/src/Chat/ChatMessage.doc.mjs +31 -16
- package/src/Chat/ChatMessage.spec.md +265 -0
- package/src/Chat/ChatMessage.test.tsx +246 -0
- package/src/Chat/ChatMessage.tsx +30 -7
- package/src/Chat/ChatSystemMessage.spec.md +36 -37
- package/src/Chat/ChatSystemMessage.test.tsx +4 -4
- package/src/Chat/useTriggerMenu.test.tsx +994 -0
- package/src/Chat/useTriggerMenu.tsx +278 -46
- package/src/CodeBlock/CodeBlock.doc.mjs +7 -0
- package/src/CodeBlock/CodeBlock.spec.md +40 -41
- package/src/CodeBlock/CodeBlock.test.tsx +89 -9
- package/src/CodeBlock/CodeBlock.tsx +205 -25
- package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +125 -0
- package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +93 -0
- package/src/CodeBlock/tokenizer.test.ts +112 -0
- package/src/CodeBlock/tokenizer.ts +106 -0
- package/src/Collapsible/CollapsibleGroup.spec.md +17 -8
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +45 -0
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +139 -0
- package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +135 -0
- package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +128 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +126 -23
- package/src/CommandPalette/CommandPalette.spec.md +100 -49
- package/src/CommandPalette/CommandPalette.test.tsx +241 -2
- package/src/CommandPalette/CommandPalette.tsx +47 -21
- package/src/CommandPalette/CommandPaletteEmpty.tsx +4 -3
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +61 -4
- package/src/CommandPalette/CommandPaletteInput.spec.md +215 -0
- package/src/CommandPalette/CommandPaletteInput.test.tsx +375 -51
- package/src/CommandPalette/CommandPaletteInput.tsx +74 -21
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +73 -22
- package/src/CommandPalette/CommandPaletteItem.spec.md +223 -0
- package/src/CommandPalette/CommandPaletteItem.test.tsx +200 -5
- package/src/CommandPalette/CommandPaletteItem.tsx +16 -11
- package/src/CommandPalette/CommandPaletteList.doc.mjs +3 -3
- package/src/CommandPalette/CommandPaletteList.spec.md +41 -36
- package/src/CommandPalette/CommandPaletteList.test.tsx +80 -0
- package/src/CommandPalette/CommandPaletteList.tsx +10 -5
- package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +599 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +10 -8
- package/src/ComplexSelector/ComplexSelector.spec.md +14 -9
- package/src/ComplexSelector/ComplexSelector.test.tsx +64 -16
- package/src/ComplexSelector/ComplexSelector.tsx +37 -8
- package/src/ContextMenu/ContextMenu.doc.mjs +6 -0
- package/src/ContextMenu/ContextMenu.spec.md +51 -53
- package/src/ContextMenu/ContextMenu.test.tsx +132 -1
- package/src/ContextMenu/ContextMenu.tsx +78 -48
- package/src/DateInput/DateInput.doc.mjs +52 -0
- package/src/DateInput/DateInput.spec.md +27 -15
- package/src/DateInput/DateInput.test.tsx +38 -0
- package/src/DateInput/DateInput.tsx +1 -1
- package/src/DateInput/DateInputTouch.test.tsx +22 -0
- package/src/DateInput/MonthScroller.tsx +12 -4
- package/src/DateRangeInput/DateRangeInput.test.tsx +49 -0
- package/src/DateRangeInput/DateRangeInput.tsx +20 -2
- package/src/Dialog/Dialog.test.tsx +27 -1
- package/src/Dialog/Dialog.tsx +16 -5
- package/src/Dialog/DialogHeader.test.tsx +27 -2
- package/src/Dialog/DialogHeader.tsx +7 -8
- package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +21 -14
- package/src/Divider/Divider.doc.mjs +5 -3
- package/src/Divider/Divider.spec.md +140 -54
- package/src/Divider/Divider.test.tsx +15 -0
- package/src/Divider/Divider.tsx +6 -4
- package/src/DropdownMenu/DropdownMenu.doc.mjs +2 -2
- package/src/DropdownMenu/DropdownMenu.spec.md +121 -45
- package/src/DropdownMenu/DropdownMenu.test.tsx +288 -23
- package/src/DropdownMenu/DropdownMenu.tsx +212 -81
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +38 -2
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +32 -18
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +146 -1
- package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +136 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +12 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +67 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +141 -0
- package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +138 -0
- package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +40 -5
- package/src/Field/InputClearButton.test.tsx +29 -4
- package/src/Field/InputClearButton.tsx +26 -8
- package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +21 -14
- package/src/FormLayout/FormLayout.tsx +1 -0
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +1 -1
- package/src/Grid/Grid.doc.mjs +14 -11
- package/src/Grid/Grid.spec.md +50 -32
- package/src/Grid/Grid.tsx +13 -4
- package/src/HoverCard/HoverCard.test.tsx +46 -1
- package/src/HoverCard/useHoverCard.tsx +68 -48
- package/src/InputGroup/InputGroup.doc.mjs +11 -1
- package/src/InputGroup/InputGroup.tsx +2 -4
- package/src/InputGroup/groupStyles.ts +40 -2
- package/src/Layer/useLayerDismissal.ts +9 -9
- package/src/Link/__tests__/Link.a11y.chromium.spec.ts +142 -0
- package/src/Link/__tests__/Link.a11y.renders.tsx +97 -0
- package/src/Link/__tests__/Link.a11y.states.ts +104 -0
- package/src/Link/__tests__/Link.a11y.test.tsx +127 -0
- package/src/Markdown/Markdown.doc.mjs +1 -1
- package/src/Markdown/Markdown.fr23.perf.test.ts +161 -40
- package/src/Markdown/Markdown.spec.md +45 -23
- package/src/Markdown/Markdown.test.tsx +38 -0
- package/src/Markdown/Markdown.tsx +24 -2
- package/src/Markdown/plugins/semanticFence.test.tsx +3 -2
- package/src/MobileNav/MobileNav.tsx +50 -2
- package/src/MobileNav/MobileNavFocus.test.tsx +553 -0
- package/src/MoreMenu/MoreMenu.spec.md +18 -7
- package/src/MoreMenu/MoreMenu.test.tsx +38 -0
- package/src/MultiSelector/MultiSelector.doc.mjs +8 -8
- package/src/MultiSelector/MultiSelector.spec.md +1 -1
- package/src/MultiSelector/MultiSelector.test.tsx +188 -21
- package/src/MultiSelector/MultiSelector.tsx +50 -11
- package/src/NumberInput/NumberInput.test.tsx +24 -0
- package/src/NumberInput/NumberInput.tsx +1 -1
- package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +22 -15
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +78 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +6 -0
- package/src/Resizable/Resizable.spec.md +41 -9
- package/src/Resizable/ResizeHandle.test.tsx +565 -2
- package/src/Resizable/ResizeHandle.tsx +126 -21
- package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +119 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +39 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +80 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +197 -0
- package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +123 -0
- package/src/Resizable/useResizable.ts +95 -0
- package/src/Selector/Selector.doc.mjs +7 -6
- package/src/Selector/Selector.spec.md +37 -24
- package/src/Selector/Selector.test.tsx +176 -10
- package/src/Selector/Selector.tsx +59 -14
- package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +27 -0
- package/src/Selector/__tests__/Combobox.a11y.states.ts +145 -0
- package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +108 -0
- package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +126 -0
- package/src/SideNav/SideNav.test.tsx +35 -0
- package/src/SideNav/SideNav.tsx +16 -2
- package/src/Slider/Slider.spec.md +43 -17
- package/src/Slider/Slider.test.tsx +134 -0
- package/src/Slider/Slider.tsx +29 -6
- package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +112 -0
- package/src/Slider/__tests__/Slider.a11y.known-failures.ts +12 -0
- package/src/Slider/__tests__/Slider.a11y.renders.tsx +42 -0
- package/src/Slider/__tests__/Slider.a11y.states.ts +106 -0
- package/src/Slider/__tests__/Slider.a11y.test.tsx +105 -0
- package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +25 -18
- package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +19 -12
- package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +21 -14
- package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +18 -11
- package/src/Tokenizer/Tokenizer.test.tsx +87 -0
- package/src/Tokenizer/Tokenizer.tsx +31 -3
- package/src/Toolbar/Toolbar.test.tsx +131 -4
- package/src/Toolbar/Toolbar.tsx +3 -1
- package/src/TreeList/TreeList.spec.md +33 -8
- package/src/TreeList/TreeList.test.tsx +72 -0
- package/src/TreeList/TreeList.tsx +10 -6
- package/src/TreeList/TreeListItem.tsx +5 -0
- package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +125 -0
- package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +44 -0
- package/src/TreeList/__tests__/TreeList.a11y.states.ts +164 -0
- package/src/TreeList/__tests__/TreeList.a11y.test.tsx +138 -0
- package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +82 -0
- package/src/hooks/useScrollOverflow.test.tsx +222 -0
- package/src/hooks/useScrollOverflow.ts +55 -13
- package/src/hooks/useTreeFocus.test.tsx +37 -0
- package/src/hooks/useTreeFocus.ts +55 -2
- package/src/i18n/generated-locales/en.generated.ts +3 -0
- package/src/i18n/generated-locales/pseudo.generated.ts +3 -0
- package/src/reset.a11y.chromium.spec.ts +9 -1
- package/src/reset.css +8 -3
- package/src/reset.until-found.a11y.browser.spec.ts +75 -0
- package/src/reset.until-found.test.ts +32 -0
- package/src/stylexValidation.source-build.test.mjs +42 -0
- package/src/theme/syntax/SyntaxTheme.tsx +46 -5
- package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +91 -0
- package/src/theme/syntax/defineSyntaxTheme.ts +75 -15
- package/src/theme/syntax/index.ts +5 -1
- package/src/theme/syntax/tokens.ts +32 -2
- package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +40 -0
- package/src/utils/plainDate.test.ts +147 -0
- package/src/utils/plainDate.ts +75 -10
|
@@ -114,6 +114,53 @@ const MENU_TRIGGER_OPEN_BACKGROUND = `linear-gradient(${colorVars['--color-overl
|
|
|
114
114
|
* aria-hidden subtrees) are not reachable, judged by the popover focus trap's
|
|
115
115
|
* own test so the two never disagree about where Tab can go.
|
|
116
116
|
*/
|
|
117
|
+
/**
|
|
118
|
+
* Collapse each named radio group to the one Tab stop a browser gives it: the
|
|
119
|
+
* focused radio, else the checked one, else the first radio moving forward or
|
|
120
|
+
* the last moving backward. The stop takes the group's first position.
|
|
121
|
+
*/
|
|
122
|
+
function collapseRadioGroups(
|
|
123
|
+
elements: HTMLElement[],
|
|
124
|
+
active: Element | null,
|
|
125
|
+
isBackward: boolean,
|
|
126
|
+
): HTMLElement[] {
|
|
127
|
+
const groups = new Map<string, HTMLInputElement[]>();
|
|
128
|
+
const groupKey = (el: HTMLElement): string | null =>
|
|
129
|
+
el instanceof HTMLInputElement &&
|
|
130
|
+
el.getAttribute('type') === 'radio' &&
|
|
131
|
+
el.name !== ''
|
|
132
|
+
? `${el.form?.id ?? ''}\u0000${el.name}`
|
|
133
|
+
: null;
|
|
134
|
+
for (const el of elements) {
|
|
135
|
+
const key = groupKey(el);
|
|
136
|
+
if (key != null) {
|
|
137
|
+
const members = groups.get(key) ?? [];
|
|
138
|
+
members.push(el as HTMLInputElement);
|
|
139
|
+
groups.set(key, members);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const placed = new Set<string>();
|
|
143
|
+
const result: HTMLElement[] = [];
|
|
144
|
+
for (const el of elements) {
|
|
145
|
+
const key = groupKey(el);
|
|
146
|
+
if (key == null) {
|
|
147
|
+
result.push(el);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (placed.has(key)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
placed.add(key);
|
|
154
|
+
const members = groups.get(key) ?? [];
|
|
155
|
+
const stop =
|
|
156
|
+
members.find(radio => radio === active) ??
|
|
157
|
+
members.find(radio => radio.checked) ??
|
|
158
|
+
(isBackward ? members[members.length - 1] : members[0]);
|
|
159
|
+
result.push(stop);
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
117
164
|
function tabbablesIn(region: HTMLElement | null): HTMLElement[] {
|
|
118
165
|
if (region == null) {
|
|
119
166
|
return [];
|
|
@@ -148,8 +195,9 @@ function isInsideHiddenWrapper(
|
|
|
148
195
|
}
|
|
149
196
|
|
|
150
197
|
/**
|
|
151
|
-
* Move focus to a
|
|
152
|
-
* caller can
|
|
198
|
+
* Move focus to a Tab stop in the dialog. Reports whether focus actually
|
|
199
|
+
* arrived, so a caller can fall back to the menu instead of stranding focus on
|
|
200
|
+
* the page.
|
|
153
201
|
*/
|
|
154
202
|
function focusRegionControl(element: HTMLElement): boolean {
|
|
155
203
|
element.focus();
|
|
@@ -551,6 +599,17 @@ function visibleText(element: Element): string {
|
|
|
551
599
|
for (const hidden of copy.querySelectorAll('[aria-hidden="true"]')) {
|
|
552
600
|
hidden.remove();
|
|
553
601
|
}
|
|
602
|
+
// An icon-only control is named by its image's alt, an inner aria-label, or
|
|
603
|
+
// an svg title rather than by text, so those count as its contents too.
|
|
604
|
+
for (const named of copy.querySelectorAll('img[alt], [aria-label]')) {
|
|
605
|
+
const name =
|
|
606
|
+
named.getAttribute(named.tagName === 'IMG' ? 'alt' : 'aria-label') ?? '';
|
|
607
|
+
named.replaceWith(copy.ownerDocument.createTextNode(` ${name} `));
|
|
608
|
+
}
|
|
609
|
+
for (const svg of copy.querySelectorAll('svg')) {
|
|
610
|
+
const title = svg.querySelector('title')?.textContent ?? '';
|
|
611
|
+
svg.replaceWith(copy.ownerDocument.createTextNode(` ${title} `));
|
|
612
|
+
}
|
|
554
613
|
return (copy.textContent ?? '').replace(/\s+/g, ' ').trim();
|
|
555
614
|
}
|
|
556
615
|
|
|
@@ -580,6 +639,24 @@ function readAccessibleName(element: HTMLElement | null): string | undefined {
|
|
|
580
639
|
if (label) {
|
|
581
640
|
return label;
|
|
582
641
|
}
|
|
642
|
+
// A form control is named by its own <label>s; an input button by its value.
|
|
643
|
+
const labels =
|
|
644
|
+
'labels' in element
|
|
645
|
+
? Array.from((element as HTMLInputElement).labels ?? [])
|
|
646
|
+
: [];
|
|
647
|
+
const labelText = labels.map(visibleText).join(' ').trim();
|
|
648
|
+
if (labelText !== '') {
|
|
649
|
+
return labelText;
|
|
650
|
+
}
|
|
651
|
+
if (
|
|
652
|
+
element instanceof HTMLInputElement &&
|
|
653
|
+
['button', 'submit', 'reset'].includes(element.getAttribute('type') ?? '')
|
|
654
|
+
) {
|
|
655
|
+
const value = element.value.trim();
|
|
656
|
+
if (value !== '') {
|
|
657
|
+
return value;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
583
660
|
const text = visibleText(element);
|
|
584
661
|
if (text !== '') {
|
|
585
662
|
return text;
|
|
@@ -716,7 +793,15 @@ function DropdownMenuBottomSheet({
|
|
|
716
793
|
openModalityRef.current = 'keyboard';
|
|
717
794
|
}
|
|
718
795
|
};
|
|
719
|
-
const handleSheetTriggerClick = () => {
|
|
796
|
+
const handleSheetTriggerClick = (event: React.MouseEvent<HTMLElement>) => {
|
|
797
|
+
// A caller-rendered trigger that is natively or aria-disabled must not
|
|
798
|
+
// open the sheet; the default Button already refuses its own click.
|
|
799
|
+
// The DOM check covers the built-in Button too (it renders aria-disabled
|
|
800
|
+
// when focusable-disabled); a caller's own enabled control wins over a
|
|
801
|
+
// `button.isDisabled` left on the props.
|
|
802
|
+
if (event.currentTarget.matches(':disabled, [aria-disabled="true"]')) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
720
805
|
onClick?.();
|
|
721
806
|
setOpen(!isOpen);
|
|
722
807
|
};
|
|
@@ -954,6 +1039,16 @@ function DropdownMenuPopover({
|
|
|
954
1039
|
// ArrowUp opens with the LAST enabled row highlighted.
|
|
955
1040
|
const focusLastOnOpenRef = useRef(false);
|
|
956
1041
|
|
|
1042
|
+
// Synchronizes the dialog's name from the custom trigger's DOM, an external
|
|
1043
|
+
// system React does not own: the rendered control's accessible name is only
|
|
1044
|
+
// readable once it mounts. With a header or footer the dialog is named like
|
|
1045
|
+
// the menu, so that name is needed before the first open, not only at it.
|
|
1046
|
+
useIsomorphicLayoutEffect(() => {
|
|
1047
|
+
if (renderTrigger != null && hasRegions && triggerName == null) {
|
|
1048
|
+
setTriggerName(readAccessibleName(buttonRef.current));
|
|
1049
|
+
}
|
|
1050
|
+
}, [renderTrigger, hasRegions, triggerName]);
|
|
1051
|
+
|
|
957
1052
|
const handleLayerShow = useCallback(() => {
|
|
958
1053
|
acceptedOpenRef.current = true;
|
|
959
1054
|
resetFocusReturn();
|
|
@@ -970,15 +1065,38 @@ function DropdownMenuPopover({
|
|
|
970
1065
|
}
|
|
971
1066
|
}, [isControlled, onClick, onOpenChange, renderTrigger, resetFocusReturn]);
|
|
972
1067
|
|
|
1068
|
+
// The menu's name: the dialog around it with a header or footer, and the
|
|
1069
|
+
// Back row of a drilled-in view ("Back to <this>"), both read it.
|
|
1070
|
+
// The design system's button names the menu by its label. A custom trigger
|
|
1071
|
+
// names it by reference (or the caller's `aria-label` does), so that name
|
|
1072
|
+
// is read off the control as the menu opens.
|
|
1073
|
+
const callerMenuLabel = rest['aria-label'];
|
|
1074
|
+
const menuLabel =
|
|
1075
|
+
renderTrigger != null
|
|
1076
|
+
? typeof callerMenuLabel === 'string' && callerMenuLabel.trim() !== ''
|
|
1077
|
+
? callerMenuLabel
|
|
1078
|
+
: triggerName
|
|
1079
|
+
: typeof button.label === 'string'
|
|
1080
|
+
? button.label
|
|
1081
|
+
: undefined;
|
|
1082
|
+
|
|
973
1083
|
const popover = usePopover({
|
|
974
1084
|
onHide: handleLayerHide,
|
|
975
1085
|
onShow: handleLayerShow,
|
|
976
1086
|
hasLightDismiss: true,
|
|
977
1087
|
hasCloseButton: false,
|
|
978
1088
|
hasAutoFocus: false,
|
|
979
|
-
//
|
|
980
|
-
//
|
|
981
|
-
|
|
1089
|
+
// Without a header or footer the popup's own role="menu" is the exposed
|
|
1090
|
+
// semantics; a dialog around it would announce an extra container. With
|
|
1091
|
+
// either, the popover holds controls that are not menu items, so it is a
|
|
1092
|
+
// modal dialog named like the menu, with the menu inside it. A custom
|
|
1093
|
+
// trigger's name is read off the control once it mounts; until then, or
|
|
1094
|
+
// when the control exposes no readable name, the dialog takes the default
|
|
1095
|
+
// menu label, so it is never unnamed and never loses its role.
|
|
1096
|
+
role: hasRegions ? 'dialog' : 'none',
|
|
1097
|
+
dialogLabel: hasRegions
|
|
1098
|
+
? (menuLabel ?? t(DEFAULT_BUTTON_I18N_KEY))
|
|
1099
|
+
: undefined,
|
|
982
1100
|
});
|
|
983
1101
|
|
|
984
1102
|
const closeMenu = useCallback(() => {
|
|
@@ -1077,6 +1195,55 @@ function DropdownMenuPopover({
|
|
|
1077
1195
|
});
|
|
1078
1196
|
}, [popover.isOpen, focusFirst, focusLast, listRef]);
|
|
1079
1197
|
|
|
1198
|
+
// With a header or footer the popover is a modal dialog: Tab and Shift+Tab
|
|
1199
|
+
// walk the header's controls, the menu as one stop, then the footer's
|
|
1200
|
+
// controls, and wrap at either end. The key is handled here in full, so the
|
|
1201
|
+
// popover's own focus trap, which listens on the document, never reads it a
|
|
1202
|
+
// second time.
|
|
1203
|
+
const moveDialogTab = useCallback(
|
|
1204
|
+
(e: React.KeyboardEvent, isFromFlyout = false) => {
|
|
1205
|
+
const menu = listRef.current;
|
|
1206
|
+
if (menu == null) {
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
e.preventDefault();
|
|
1210
|
+
e.stopPropagation();
|
|
1211
|
+
// A named radio group is one stop, as the browser's own Tab order has it.
|
|
1212
|
+
const cycle = collapseRadioGroups(
|
|
1213
|
+
[
|
|
1214
|
+
...tabbablesIn(headerRef.current),
|
|
1215
|
+
menu,
|
|
1216
|
+
...tabbablesIn(footerRef.current),
|
|
1217
|
+
],
|
|
1218
|
+
document.activeElement,
|
|
1219
|
+
e.shiftKey,
|
|
1220
|
+
);
|
|
1221
|
+
if (cycle.length === 1) {
|
|
1222
|
+
// Nothing else to reach: focus stays in the menu. From a flyout that
|
|
1223
|
+
// has just closed itself, that is the menu's own stop.
|
|
1224
|
+
if (isFromFlyout) {
|
|
1225
|
+
menu.focus();
|
|
1226
|
+
}
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const active = document.activeElement;
|
|
1230
|
+
const menuIndex = cycle.indexOf(menu);
|
|
1231
|
+
// A row, or a row of an open flyout, counts as the menu's stop.
|
|
1232
|
+
let index = menu.contains(active)
|
|
1233
|
+
? menuIndex
|
|
1234
|
+
: cycle.findIndex(el => el === active || el.contains(active));
|
|
1235
|
+
if (index < 0) {
|
|
1236
|
+
index = menuIndex;
|
|
1237
|
+
}
|
|
1238
|
+
const step = e.shiftKey ? -1 : 1;
|
|
1239
|
+
const next = cycle[(index + step + cycle.length) % cycle.length];
|
|
1240
|
+
if (!focusRegionControl(next)) {
|
|
1241
|
+
menu.focus();
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
[listRef],
|
|
1245
|
+
);
|
|
1246
|
+
|
|
1080
1247
|
// Extend useListFocus with Enter/Space activation + typeahead
|
|
1081
1248
|
const listKeyDown = useCallback(
|
|
1082
1249
|
(e: React.KeyboardEvent) => {
|
|
@@ -1087,18 +1254,7 @@ function DropdownMenuPopover({
|
|
|
1087
1254
|
// itself as the key bubbled up, and the root moves focus on as Tab
|
|
1088
1255
|
// from one of its own rows would.
|
|
1089
1256
|
if (e.key === 'Tab' && hasRegions) {
|
|
1090
|
-
|
|
1091
|
-
const tabbables = tabbablesIn(region);
|
|
1092
|
-
e.stopPropagation();
|
|
1093
|
-
const target = e.shiftKey
|
|
1094
|
-
? tabbables[tabbables.length - 1]
|
|
1095
|
-
: tabbables[0];
|
|
1096
|
-
if (target != null) {
|
|
1097
|
-
e.preventDefault();
|
|
1098
|
-
}
|
|
1099
|
-
if (target == null || !focusRegionControl(target)) {
|
|
1100
|
-
closeMenu();
|
|
1101
|
-
}
|
|
1257
|
+
moveDialogTab(e, true);
|
|
1102
1258
|
}
|
|
1103
1259
|
return;
|
|
1104
1260
|
}
|
|
@@ -1120,30 +1276,15 @@ function DropdownMenuPopover({
|
|
|
1120
1276
|
}
|
|
1121
1277
|
return;
|
|
1122
1278
|
}
|
|
1123
|
-
//
|
|
1124
|
-
//
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
//
|
|
1128
|
-
//
|
|
1129
|
-
// takes the Tab instead, and the menu stays open.
|
|
1279
|
+
// With a header or footer the popover is a dialog and Tab wraps inside
|
|
1280
|
+
// it. Without either, the APG menu-button pattern: Tab closes the menu.
|
|
1281
|
+
// Menu items are tabIndex={-1} so the focus trap has nothing trappable
|
|
1282
|
+
// and Tab would otherwise leak into the page while the menu stayed open
|
|
1283
|
+
// (menus-5). Do NOT preventDefault — closing restores focus to the
|
|
1284
|
+
// trigger, and the browser's default Tab then continues from there.
|
|
1130
1285
|
if (e.key === 'Tab') {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
if (tabbables.length > 0) {
|
|
1134
|
-
// From a row, the browser's Shift+Tab would stop on the menu element
|
|
1135
|
-
// itself — the row's ancestor and a Tab stop — before reaching the
|
|
1136
|
-
// header. The menu is one stop, so go straight to the header.
|
|
1137
|
-
if (e.shiftKey && e.target !== listRef.current) {
|
|
1138
|
-
e.preventDefault();
|
|
1139
|
-
// The popover's focus trap listens on the document and would read
|
|
1140
|
-
// this same Shift+Tab against the field just focused — the trap's
|
|
1141
|
-
// first element — and wrap to the footer. This key is handled.
|
|
1142
|
-
e.stopPropagation();
|
|
1143
|
-
if (!focusRegionControl(tabbables[tabbables.length - 1])) {
|
|
1144
|
-
closeMenu();
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1286
|
+
if (hasRegions) {
|
|
1287
|
+
moveDialogTab(e);
|
|
1147
1288
|
return;
|
|
1148
1289
|
}
|
|
1149
1290
|
closeMenu();
|
|
@@ -1156,33 +1297,24 @@ function DropdownMenuPopover({
|
|
|
1156
1297
|
}
|
|
1157
1298
|
listNavKeyDown(e);
|
|
1158
1299
|
},
|
|
1159
|
-
[
|
|
1300
|
+
[
|
|
1301
|
+
listNavKeyDown,
|
|
1302
|
+
closeMenu,
|
|
1303
|
+
typeahead,
|
|
1304
|
+
ownsEvent,
|
|
1305
|
+
hasRegions,
|
|
1306
|
+
moveDialogTab,
|
|
1307
|
+
],
|
|
1160
1308
|
);
|
|
1161
1309
|
|
|
1162
|
-
// Tab
|
|
1163
|
-
// control of the header, leaves the menu as Tab from its rows does: the
|
|
1164
|
-
// menu closes, focus returns to the trigger, and the browser's own Tab
|
|
1165
|
-
// continues from there. Between the regions, Tab moves through the menu
|
|
1166
|
-
// as one stop.
|
|
1310
|
+
// Tab from a header or footer control moves through the same dialog cycle.
|
|
1167
1311
|
const handleRegionKeyDown = useCallback(
|
|
1168
1312
|
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
1169
|
-
if (e.key
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
const tabbables = tabbablesIn(e.currentTarget);
|
|
1173
|
-
const isHeader = e.currentTarget === headerRef.current;
|
|
1174
|
-
const exit = isHeader
|
|
1175
|
-
? e.shiftKey
|
|
1176
|
-
? tabbables[0]
|
|
1177
|
-
: undefined
|
|
1178
|
-
: e.shiftKey
|
|
1179
|
-
? undefined
|
|
1180
|
-
: tabbables[tabbables.length - 1];
|
|
1181
|
-
if (exit != null && exit === document.activeElement) {
|
|
1182
|
-
closeMenu();
|
|
1313
|
+
if (e.key === 'Tab') {
|
|
1314
|
+
moveDialogTab(e);
|
|
1183
1315
|
}
|
|
1184
1316
|
},
|
|
1185
|
-
[
|
|
1317
|
+
[moveDialogTab],
|
|
1186
1318
|
);
|
|
1187
1319
|
|
|
1188
1320
|
const openAndFocus = useCallback(
|
|
@@ -1190,6 +1322,15 @@ function DropdownMenuPopover({
|
|
|
1190
1322
|
modality: 'keyboard' | 'pointer' = 'keyboard',
|
|
1191
1323
|
notifyClick = false,
|
|
1192
1324
|
): boolean => {
|
|
1325
|
+
// An unavailable trigger never opens the menu. Button keeps an
|
|
1326
|
+
// aria-disabled trigger focusable for its tooltip and swallows only
|
|
1327
|
+
// Enter and Space, so the arrow keys, a press and a click would still
|
|
1328
|
+
// arrive here; a custom trigger may be natively disabled instead.
|
|
1329
|
+
if (
|
|
1330
|
+
buttonRef.current?.matches(':disabled, [aria-disabled="true"]') === true
|
|
1331
|
+
) {
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1193
1334
|
acceptedOpenRef.current = false;
|
|
1194
1335
|
notifyClickOnShowRef.current = notifyClick;
|
|
1195
1336
|
pendingControlledOpenRef.current = isControlled;
|
|
@@ -1328,7 +1469,11 @@ function DropdownMenuPopover({
|
|
|
1328
1469
|
return;
|
|
1329
1470
|
}
|
|
1330
1471
|
focusLastOnOpenRef.current = e.key === 'ArrowUp';
|
|
1331
|
-
openAndFocus()
|
|
1472
|
+
if (!openAndFocus()) {
|
|
1473
|
+
// A refused open (disabled trigger) must not leave ArrowUp's
|
|
1474
|
+
// "land on the last row" armed for a later open.
|
|
1475
|
+
focusLastOnOpenRef.current = false;
|
|
1476
|
+
}
|
|
1332
1477
|
}
|
|
1333
1478
|
return;
|
|
1334
1479
|
}
|
|
@@ -1368,20 +1513,6 @@ function DropdownMenuPopover({
|
|
|
1368
1513
|
|
|
1369
1514
|
// The drill-in view stack for sub-menus on a phone.
|
|
1370
1515
|
const {drillIn, wrapContent} = useMenuDrillIn(isOpen);
|
|
1371
|
-
// The name a drilled-in view's Back row returns to: the menu's own name.
|
|
1372
|
-
// The design system's button names the menu by its label. A custom trigger
|
|
1373
|
-
// names it by reference (or the caller's `aria-label` does), so that name
|
|
1374
|
-
// is read off the control as the menu opens.
|
|
1375
|
-
const callerMenuLabel = rest['aria-label'];
|
|
1376
|
-
const menuLabel =
|
|
1377
|
-
renderTrigger != null
|
|
1378
|
-
? typeof callerMenuLabel === 'string' && callerMenuLabel.trim() !== ''
|
|
1379
|
-
? callerMenuLabel
|
|
1380
|
-
: triggerName
|
|
1381
|
-
: typeof button.label === 'string'
|
|
1382
|
-
? button.label
|
|
1383
|
-
: undefined;
|
|
1384
|
-
|
|
1385
1516
|
const resolvedMaxHeight = menuMaxHeight == null ? null : `${menuMaxHeight}px`;
|
|
1386
1517
|
// Context for compound items
|
|
1387
1518
|
const contextValue = useMemo<DropdownMenuContextValue>(
|
|
@@ -1442,9 +1573,9 @@ function DropdownMenuPopover({
|
|
|
1442
1573
|
// keeps the browser from drawing one after pointer input (see
|
|
1443
1574
|
// handleLayerHide), so its focus needs no bookkeeping here.
|
|
1444
1575
|
onFocus: () => {},
|
|
1445
|
-
'aria-haspopup': 'menu',
|
|
1576
|
+
'aria-haspopup': hasRegions ? 'dialog' : 'menu',
|
|
1446
1577
|
'aria-expanded': isOpen,
|
|
1447
|
-
'aria-controls': menuId,
|
|
1578
|
+
'aria-controls': hasRegions ? popover.id : menuId,
|
|
1448
1579
|
};
|
|
1449
1580
|
|
|
1450
1581
|
return (
|
|
@@ -1492,9 +1623,9 @@ function DropdownMenuPopover({
|
|
|
1492
1623
|
button.onFocus?.(event);
|
|
1493
1624
|
onFocusReturnTargetFocus();
|
|
1494
1625
|
}}
|
|
1495
|
-
aria-haspopup=
|
|
1626
|
+
aria-haspopup={hasRegions ? 'dialog' : 'menu'}
|
|
1496
1627
|
aria-expanded={isOpen}
|
|
1497
|
-
aria-controls={menuId}
|
|
1628
|
+
aria-controls={hasRegions ? popover.id : menuId}
|
|
1498
1629
|
data-testid={testId}
|
|
1499
1630
|
/>
|
|
1500
1631
|
)}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
|
|
4
|
+
const anatomy = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Checkable row',
|
|
7
|
+
required: true,
|
|
8
|
+
description:
|
|
9
|
+
'The menuitemcheckbox root that owns the accessible name, checked state, focus, and activation.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'Label/content',
|
|
13
|
+
required: true,
|
|
14
|
+
description:
|
|
15
|
+
'Caller content laid out by Item: the required label plus optional description, icon, and end content.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Checkbox marker',
|
|
19
|
+
required: true,
|
|
20
|
+
description:
|
|
21
|
+
'Decorative shared Indicator chrome that visualizes checked and disabled state without adding a second control.',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
3
25
|
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
26
|
|
|
5
27
|
export const docs = {
|
|
@@ -9,6 +31,11 @@ export const docs = {
|
|
|
9
31
|
isHiddenFromOverview: true,
|
|
10
32
|
description:
|
|
11
33
|
'A checkable menu item (role="menuitemcheckbox") that toggles an independent boolean. Use for on/off options inside a DropdownMenu; for a one-of-N choice use DropdownMenuRadioGroup.',
|
|
34
|
+
usage: {
|
|
35
|
+
anatomy,
|
|
36
|
+
description:
|
|
37
|
+
'An independently checkable option inside DropdownMenu. The row owns menuitemcheckbox semantics while the shared Indicator supplies decorative checkbox paint.',
|
|
38
|
+
},
|
|
12
39
|
props: [
|
|
13
40
|
{
|
|
14
41
|
name: 'label',
|
|
@@ -43,7 +70,7 @@ export const docs = {
|
|
|
43
70
|
type: 'boolean',
|
|
44
71
|
default: 'false',
|
|
45
72
|
description:
|
|
46
|
-
'Whether the item is disabled. Disabled items
|
|
73
|
+
'Whether the item is disabled. Disabled items expose aria-disabled, block activation, and are currently skipped by DropdownMenu arrow-key navigation.',
|
|
47
74
|
},
|
|
48
75
|
{
|
|
49
76
|
name: 'hasCloseOnSelect',
|
|
@@ -65,6 +92,14 @@ export const docs = {
|
|
|
65
92
|
'Content to render after the label and description, such as a keyboard shortcut hint or badge.',
|
|
66
93
|
},
|
|
67
94
|
],
|
|
95
|
+
theming: {
|
|
96
|
+
targets: [
|
|
97
|
+
{
|
|
98
|
+
className: 'astryx-dropdown-menu-item',
|
|
99
|
+
visualProps: ['size'],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
68
103
|
};
|
|
69
104
|
|
|
70
105
|
export const docsZh = {
|
|
@@ -98,7 +133,8 @@ export const docsDense = {
|
|
|
98
133
|
icon: 'icon before label',
|
|
99
134
|
value: 'checked state (controlled, w/ onChange)',
|
|
100
135
|
onChange: 'fired w/ next checked state on toggle',
|
|
101
|
-
isDisabled:
|
|
136
|
+
isDisabled:
|
|
137
|
+
'disabled; aria-disabled, no activation; current menu focus skips the row',
|
|
102
138
|
hasCloseOnSelect: 'close menu on toggle (default false, stays open)',
|
|
103
139
|
ref: 'forwarded to the row root (the role="menuitemcheckbox" element)',
|
|
104
140
|
endContent: 'content after label+description',
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DropdownMenuCheckboxItem.tsx
|
|
7
|
-
* @input React, stylex, Item +
|
|
7
|
+
* @input React, stylex, Item + Indicator registry + DropdownMenu context + tokens from core
|
|
8
8
|
* @output DropdownMenuCheckboxItem — a standalone checkable menu item.
|
|
9
9
|
* @position Sub-component; used inside DropdownMenu.
|
|
10
10
|
*
|
|
@@ -26,7 +26,12 @@
|
|
|
26
26
|
* mobile.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
useCallback,
|
|
31
|
+
type MouseEvent,
|
|
32
|
+
type PointerEvent,
|
|
33
|
+
type ReactNode,
|
|
34
|
+
} from 'react';
|
|
30
35
|
import * as stylex from '@stylexjs/stylex';
|
|
31
36
|
import {renderIconSlot, type IconType} from '../Icon';
|
|
32
37
|
import {useIndicator} from '../Indicator';
|
|
@@ -34,7 +39,7 @@ import {Item} from '../Item';
|
|
|
34
39
|
import {useDropdownMenuContext} from './DropdownMenuContext';
|
|
35
40
|
import {focusMenuItemOnHover} from './menuItemHover';
|
|
36
41
|
import {colorVars, spacingVars} from '../theme/tokens.stylex';
|
|
37
|
-
import {mergeProps, themeProps} from '../utils';
|
|
42
|
+
import {composeEventHandlers, mergeProps, themeProps} from '../utils';
|
|
38
43
|
import type {BaseProps} from '../BaseProps';
|
|
39
44
|
|
|
40
45
|
const styles = stylex.create({
|
|
@@ -58,8 +63,8 @@ const styles = stylex.create({
|
|
|
58
63
|
},
|
|
59
64
|
// Rendered in Item's `marker` slot as a raw flex child, so `order` moves it
|
|
60
65
|
// relative to the label within the row. On touch it moves to the inline-end.
|
|
61
|
-
// `aria-hidden` + `inert` + pointer-events:none keep the composed
|
|
62
|
-
// decorative: it adds no accessible name, and clicks fall through to the row,
|
|
66
|
+
// `aria-hidden` + `inert` + pointer-events:none keep the composed checkbox
|
|
67
|
+
// indicator decorative: it adds no accessible name, and clicks fall through to the row,
|
|
63
68
|
// which owns the role and activation.
|
|
64
69
|
// Placement of the marker within the row. The indicator draws its own box
|
|
65
70
|
// (size, fill, border) — these are only the rules the MENU owns: where the
|
|
@@ -105,9 +110,8 @@ export interface DropdownMenuCheckboxItemProps extends Omit<
|
|
|
105
110
|
*/
|
|
106
111
|
onChange?: (checked: boolean) => void;
|
|
107
112
|
/**
|
|
108
|
-
* Whether the item is disabled. Disabled items
|
|
109
|
-
*
|
|
110
|
-
* technology, but activation is blocked.
|
|
113
|
+
* Whether the item is disabled. Disabled items expose `aria-disabled`, block
|
|
114
|
+
* activation, and are currently skipped by DropdownMenu arrow-key navigation.
|
|
111
115
|
* @default false
|
|
112
116
|
*/
|
|
113
117
|
isDisabled?: boolean;
|
|
@@ -132,6 +136,9 @@ export interface DropdownMenuCheckboxItemProps extends Omit<
|
|
|
132
136
|
*
|
|
133
137
|
* Must be used inside a DropdownMenu. Toggles an independent boolean; for a
|
|
134
138
|
* one-of-N choice use DropdownMenuRadioGroup + DropdownMenuRadioItem instead.
|
|
139
|
+
* Consumer click and pointer-move handlers compose after the row's own
|
|
140
|
+
* activation and hover-focus behavior; disabled activation stops before the
|
|
141
|
+
* consumer click handler.
|
|
135
142
|
*
|
|
136
143
|
* @example
|
|
137
144
|
* ```
|
|
@@ -157,6 +164,8 @@ export function DropdownMenuCheckboxItem({
|
|
|
157
164
|
xstyle,
|
|
158
165
|
className,
|
|
159
166
|
style,
|
|
167
|
+
onClick,
|
|
168
|
+
onPointerMove,
|
|
160
169
|
...rest
|
|
161
170
|
}: DropdownMenuCheckboxItemProps) {
|
|
162
171
|
const ctx = useDropdownMenuContext();
|
|
@@ -164,15 +173,20 @@ export function DropdownMenuCheckboxItem({
|
|
|
164
173
|
const controlSize = menuSize === 'sm' ? 'sm' : 'md';
|
|
165
174
|
const CheckboxControl = useIndicator('checkbox');
|
|
166
175
|
|
|
167
|
-
const handleClick = useCallback(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
const handleClick = useCallback(
|
|
177
|
+
(event: MouseEvent) => {
|
|
178
|
+
if (isDisabled) {
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
onChange?.(!value);
|
|
183
|
+
if (hasCloseOnSelect) {
|
|
184
|
+
ctx?.closeMenu();
|
|
185
|
+
}
|
|
186
|
+
onClick?.(event as MouseEvent<HTMLElement>);
|
|
187
|
+
},
|
|
188
|
+
[isDisabled, onChange, value, hasCloseOnSelect, ctx, onClick],
|
|
189
|
+
);
|
|
176
190
|
|
|
177
191
|
const handlePointerMove = useCallback(
|
|
178
192
|
(e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
|
|
@@ -185,7 +199,7 @@ export function DropdownMenuCheckboxItem({
|
|
|
185
199
|
role="menuitemcheckbox"
|
|
186
200
|
aria-checked={value}
|
|
187
201
|
tabIndex={isDisabled ? undefined : -1}
|
|
188
|
-
onPointerMove={handlePointerMove}
|
|
202
|
+
onPointerMove={composeEventHandlers(handlePointerMove, onPointerMove)}
|
|
189
203
|
marker={
|
|
190
204
|
// No wrapper, and no menu-specific theme target: the shared
|
|
191
205
|
// `astryx-checkbox-indicator` target is already on this element (main
|