@astryxdesign/core 0.5.2-canary.c9c8564 → 0.5.2-canary.e03511b
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/dist/BottomSheet/BottomSheet.d.ts +3 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +4 -3
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
- package/dist/Button/Button.d.ts +2 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -4
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +34 -17
- package/dist/ContextMenu/ContextMenu.d.ts +14 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +149 -26
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +340 -26
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
- package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
- package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/menuWidth.d.ts +14 -0
- package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
- package/dist/DropdownMenu/menuWidth.js +35 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
- package/dist/DropdownMenu/useMenuOverflow.js +60 -0
- package/dist/MoreMenu/MoreMenu.d.ts +8 -2
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +2 -0
- package/dist/MultiSelector/MultiSelector.d.ts +11 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +97 -68
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/Popover/Popover.d.ts +10 -6
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +141 -16
- package/dist/Popover/usePopover.d.ts +4 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +52 -5
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +106 -80
- package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
- package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
- package/dist/Selector/SelectorBottomSheet.js +83 -0
- package/dist/Selector/index.d.ts +1 -1
- package/dist/Selector/index.d.ts.map +1 -1
- package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
- package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
- package/dist/Selector/selectorPresentation.stylex.js +21 -0
- package/dist/Selector/useSelectorPresentation.d.ts +32 -0
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
- package/dist/Selector/useSelectorPresentation.js +90 -0
- package/dist/Toast/useToastGesture.d.ts.map +1 -1
- package/dist/Toast/useToastGesture.js +46 -9
- package/dist/astryx.css +19 -1
- package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
- package/dist/hooks/useAdaptivePresentation.js +19 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
- package/dist/hooks/useFocusReturnVisibility.js +35 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +12 -0
- package/locales/en.json +20 -0
- package/locales/pseudo.json +15 -0
- package/package.json +2 -2
- package/src/BottomSheet/BottomSheet.doc.mjs +37 -0
- package/src/BottomSheet/BottomSheet.spec.md +177 -0
- package/src/BottomSheet/BottomSheet.test.tsx +29 -0
- package/src/BottomSheet/BottomSheet.tsx +7 -4
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
- package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
- package/src/Button/Button.doc.mjs +56 -0
- package/src/Button/Button.test.tsx +10 -0
- package/src/Button/Button.tsx +13 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/Chat/ChatToolCalls.test.tsx +44 -0
- package/src/Chat/ChatToolCalls.tsx +36 -15
- package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
- package/src/CheckboxList/CheckboxList.spec.md +214 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
- package/src/CommandPalette/CommandPalette.spec.md +223 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
- package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +61 -1
- package/src/ContextMenu/ContextMenu.spec.md +206 -0
- package/src/ContextMenu/ContextMenu.test.tsx +131 -1
- package/src/ContextMenu/ContextMenu.tsx +199 -32
- package/src/ContextMenu/index.ts +1 -0
- package/src/Divider/Divider.doc.mjs +24 -0
- package/src/Divider/Divider.spec.md +150 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +305 -40
- package/src/DropdownMenu/DropdownMenu.spec.md +245 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
- package/src/DropdownMenu/DropdownMenu.tsx +459 -28
- package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
- package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
- package/src/DropdownMenu/index.ts +2 -0
- package/src/DropdownMenu/menuWidth.ts +51 -0
- package/src/DropdownMenu/useMenuOverflow.ts +77 -0
- package/src/FieldStatus/FieldStatus.doc.mjs +21 -0
- package/src/FieldStatus/FieldStatus.spec.md +212 -0
- package/src/Grid/Grid.doc.mjs +19 -0
- package/src/Grid/Grid.spec.md +161 -0
- package/src/Icon/Icon.doc.mjs +196 -48
- package/src/Icon/Icon.spec.md +191 -0
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/Kbd/Kbd.doc.mjs +18 -0
- package/src/Kbd/Kbd.spec.md +141 -0
- package/src/Layout/Layout.doc.mjs +32 -0
- package/src/Layout/Layout.spec.md +199 -0
- package/src/Lightbox/Lightbox.doc.mjs +149 -33
- package/src/Lightbox/Lightbox.spec.md +197 -0
- package/src/Markdown/Markdown.doc.mjs +60 -0
- package/src/Markdown/Markdown.spec.md +200 -0
- package/src/MobileNav/MobileNav.doc.mjs +140 -32
- package/src/MobileNav/MobileNav.spec.md +208 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +59 -0
- package/src/MoreMenu/MoreMenu.spec.md +221 -0
- package/src/MoreMenu/MoreMenu.test.tsx +47 -0
- package/src/MoreMenu/MoreMenu.tsx +13 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +128 -0
- package/src/MultiSelector/MultiSelector.spec.md +266 -0
- package/src/MultiSelector/MultiSelector.test.tsx +127 -0
- package/src/MultiSelector/MultiSelector.tsx +121 -73
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavIcon/NavIcon.doc.mjs +17 -0
- package/src/NavIcon/NavIcon.spec.md +133 -0
- package/src/NavMenu/NavHeadingMenu.spec.md +205 -0
- package/src/NavMenu/NavMenu.doc.mjs +37 -0
- package/src/Outline/Outline.doc.mjs +35 -0
- package/src/Outline/Outline.spec.md +161 -0
- package/src/OverflowList/OverflowList.doc.mjs +24 -0
- package/src/OverflowList/OverflowList.spec.md +156 -0
- package/src/Pagination/Pagination.doc.mjs +74 -0
- package/src/Pagination/Pagination.spec.md +225 -0
- package/src/Popover/Popover.doc.mjs +11 -11
- package/src/Popover/Popover.test.tsx +397 -2
- package/src/Popover/Popover.tsx +228 -21
- package/src/Popover/usePopover.tsx +92 -15
- package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
- package/src/ProgressBar/ProgressBar.spec.md +260 -0
- package/src/Resizable/Resizable.doc.mjs +59 -16
- package/src/Resizable/Resizable.spec.md +194 -0
- package/src/Section/Section.doc.mjs +17 -0
- package/src/Section/Section.spec.md +138 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
- package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
- package/src/Selector/Selector.doc.mjs +12 -0
- package/src/Selector/Selector.spec.md +202 -0
- package/src/Selector/Selector.test.tsx +135 -0
- package/src/Selector/Selector.tsx +141 -82
- package/src/Selector/SelectorBottomSheet.tsx +97 -0
- package/src/Selector/index.ts +1 -0
- package/src/Selector/selectorPresentation.stylex.ts +20 -0
- package/src/Selector/useSelectorPresentation.ts +147 -0
- package/src/Skeleton/Skeleton.doc.mjs +12 -0
- package/src/Skeleton/Skeleton.spec.md +126 -0
- package/src/Slider/Slider.doc.mjs +184 -30
- package/src/Slider/Slider.spec.md +223 -0
- package/src/Stack/Stack.doc.mjs +22 -0
- package/src/Stack/Stack.spec.md +154 -0
- package/src/StatusDot/StatusDot.doc.mjs +17 -0
- package/src/StatusDot/StatusDot.spec.md +137 -0
- package/src/Switch/Switch.doc.mjs +40 -0
- package/src/Switch/Switch.spec.md +174 -0
- package/src/Table/Table.doc.mjs +102 -24
- package/src/Table/Table.spec.md +283 -0
- package/src/Text/Text.doc.mjs +190 -43
- package/src/Text/Text.spec.md +205 -0
- package/src/TextArea/TextArea.doc.mjs +255 -45
- package/src/TextArea/TextArea.spec.md +204 -0
- package/src/Toast/ToastViewport.test.tsx +118 -4
- package/src/Toast/useToastGesture.ts +61 -8
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
- package/src/Toolbar/Toolbar.doc.mjs +19 -0
- package/src/Toolbar/Toolbar.spec.md +173 -0
- package/src/Tooltip/Tooltip.doc.mjs +17 -0
- package/src/Tooltip/Tooltip.spec.md +141 -0
- package/src/TreeList/TreeList.doc.mjs +58 -0
- package/src/TreeList/TreeList.spec.md +220 -0
- package/src/Typeahead/Typeahead.doc.mjs +83 -1
- package/src/Typeahead/Typeahead.spec.md +250 -0
- package/src/hooks/useAdaptivePresentation.ts +35 -0
- package/src/hooks/useFocusReturnVisibility.ts +45 -0
- package/src/hooks/useFocusTrap.test.tsx +40 -0
- package/src/hooks/useFocusTrap.ts +13 -0
- package/src/theme/themingTargets.test.ts +91 -25
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
template_version: 3
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:MoreMenu
|
|
6
|
+
authority: draft
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: null
|
|
10
|
+
approved_at: null
|
|
11
|
+
owners: [cixzhang]
|
|
12
|
+
review_triggers: [theming]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/MoreMenu/MoreMenu.test.tsx,
|
|
16
|
+
packages/core/src/DropdownMenu/DropdownMenu.test.tsx,
|
|
17
|
+
packages/core/src/Button/Button.test.tsx,
|
|
18
|
+
packages/core/src/Icon/Icon.test.tsx,
|
|
19
|
+
packages/core/src/List/List.test.tsx,
|
|
20
|
+
scripts/check-knowledge.mjs,
|
|
21
|
+
]
|
|
22
|
+
families: [family:overlay-dismissal]
|
|
23
|
+
design_specs: []
|
|
24
|
+
architecture:
|
|
25
|
+
[
|
|
26
|
+
architecture:component-theming-surface,
|
|
27
|
+
architecture:icon-resolution-and-component-slots,
|
|
28
|
+
architecture:interaction-modality,
|
|
29
|
+
architecture:layer-runtime,
|
|
30
|
+
]
|
|
31
|
+
contributing: []
|
|
32
|
+
system_specs: []
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# MoreMenu component contract
|
|
36
|
+
|
|
37
|
+
## Intent
|
|
38
|
+
|
|
39
|
+
MoreMenu provides a visible icon-only overflow trigger and delegates its action
|
|
40
|
+
presentation to DropdownMenu. This draft records current consumer anatomy and
|
|
41
|
+
theming ownership without changing runtime behavior, styling, targets, or public
|
|
42
|
+
API.
|
|
43
|
+
|
|
44
|
+
## Compatibility and migration
|
|
45
|
+
|
|
46
|
+
- Released default preserved: `yes`
|
|
47
|
+
- Compatibility class: additive documentation only; runtime, DOM, styling,
|
|
48
|
+
targets, and public API remain unchanged
|
|
49
|
+
- Controlled/uncontrolled behavior: unchanged
|
|
50
|
+
- Migration decision: none
|
|
51
|
+
|
|
52
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
53
|
+
|
|
54
|
+
## Ownership boundary
|
|
55
|
+
|
|
56
|
+
**Owns**
|
|
57
|
+
|
|
58
|
+
- The overflow-menu composition and its visible icon-only trigger defaults.
|
|
59
|
+
- The Menu surface's current `more-menu` target, which is a second class on the
|
|
60
|
+
composed DropdownMenu panel.
|
|
61
|
+
|
|
62
|
+
**Does not own / non-goals**
|
|
63
|
+
|
|
64
|
+
- Trigger-button presentation — delegated to `component:Button`.
|
|
65
|
+
- Default semantic trigger artwork resolution — delegated to the Icon registry;
|
|
66
|
+
the current artwork renders directly inside Button and does not carry Icon's
|
|
67
|
+
`icon` target. Arbitrary caller-rendered trigger content remains caller-owned.
|
|
68
|
+
- Menu-panel lifecycle and presentation switching — delegated to
|
|
69
|
+
`component:DropdownMenu`.
|
|
70
|
+
- Anchored action rows, sections, dividers, selectable indicators, and nested
|
|
71
|
+
flyouts — delegated to `component:DropdownMenu`.
|
|
72
|
+
- BottomSheet action rows and lists — delegated through DropdownMenu to
|
|
73
|
+
`component:List`; the touch frame remains owned by `component:BottomSheet`.
|
|
74
|
+
- Shared layer lifecycle or dismissal policy.
|
|
75
|
+
|
|
76
|
+
## Public concepts
|
|
77
|
+
|
|
78
|
+
No new public concept is introduced. Consumer props and usage remain documented
|
|
79
|
+
in `MoreMenu.doc.mjs`; its action data and presentation values remain those of
|
|
80
|
+
DropdownMenu.
|
|
81
|
+
|
|
82
|
+
## Behavioral and layout contract
|
|
83
|
+
|
|
84
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
85
|
+
| --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | --------------------------------------------------- |
|
|
86
|
+
| FR1 | Every current render contains one icon-only Trigger button and one DropdownMenu-owned Menu surface. Anchored presentation uses Pointer action rows; BottomSheet presentation uses Touch action rows. The default trigger icon resolves through the Icon registry, while a caller may replace it with arbitrary React content. | Current source, docs, and tests | Verified current behavior; no new behavior decided |
|
|
87
|
+
| FR2 | The current `more-menu` target is applied to the composed DropdownMenu panel as an additional class. It is not applied to the Trigger button. | Current source, docs, and tests | Verified current target placement; no target change |
|
|
88
|
+
| FR3 | Button owns the Trigger button and the Icon registry owns default semantic trigger artwork resolution. DropdownMenu owns Pointer action rows; List owns Touch action rows rendered through DropdownMenu's BottomSheet path. | Current source and owner docs | Verified current delegation; no ownership change |
|
|
89
|
+
| FR4 | MoreMenu forwards the current open state, placement, alignment, and pointer/touch presentation policy to DropdownMenu without adding a parallel menu runtime. | Current source and tests | Verified composition; no behavior change |
|
|
90
|
+
|
|
91
|
+
### Allowed variation
|
|
92
|
+
|
|
93
|
+
- **AV1 — Trigger icon.** The default semantic three-dot icon may be replaced by
|
|
94
|
+
an icon component or arbitrary React content without moving the MoreMenu target
|
|
95
|
+
onto the trigger.
|
|
96
|
+
- **AV2 — Presentation.** DropdownMenu may resolve the current anchored or
|
|
97
|
+
BottomSheet presentation while MoreMenu keeps the same trigger and Menu-surface
|
|
98
|
+
ownership.
|
|
99
|
+
- **AV3 — Interior.** Action rows, sections, dividers, icons, selectable
|
|
100
|
+
indicators, and nested actions may vary according to DropdownMenu's current
|
|
101
|
+
data contract.
|
|
102
|
+
|
|
103
|
+
### Representative states
|
|
104
|
+
|
|
105
|
+
| State | Required invariant | Allowed variation |
|
|
106
|
+
| ---------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
107
|
+
| Default overflow menu | Trigger button uses the default Icon-resolved glyph and Menu surface carries `more-menu`. | Button size, variant, placement, and alignment vary. |
|
|
108
|
+
| Arbitrary icon content | Trigger button remains Button-owned while caller content bypasses Icon ownership. | Caller owns the rendered content. |
|
|
109
|
+
| Pointer presentation | DropdownMenu owns the anchored Menu interior and Pointer action rows. | Rows, sections, dividers, and nested flyouts vary. |
|
|
110
|
+
| Touch presentation | DropdownMenu owns the BottomSheet-backed Menu interior; List owns Touch action rows. | Touch rows and drill-in depth vary. |
|
|
111
|
+
|
|
112
|
+
### Transformation and precedence order
|
|
113
|
+
|
|
114
|
+
- No new icon-resolution, presentation-resolution, open-state, positioning, or
|
|
115
|
+
styling precedence rule is introduced.
|
|
116
|
+
|
|
117
|
+
### Performance and resources
|
|
118
|
+
|
|
119
|
+
- No new loading, listener, measurement, or render constraint is introduced.
|
|
120
|
+
|
|
121
|
+
## Accessibility contract
|
|
122
|
+
|
|
123
|
+
This draft does not change or extend MoreMenu's existing accessible label,
|
|
124
|
+
tooltip, icon-only Button semantics, focus behavior, keyboard operation, or
|
|
125
|
+
dismissal behavior.
|
|
126
|
+
|
|
127
|
+
## Design relationships
|
|
128
|
+
|
|
129
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
130
|
+
| ------------------------------- | -------------------------------------------------------------------------------- | ------------------------------ | ----------------- | ------------------ |
|
|
131
|
+
| Trigger button | Provides the visible and discoverable entry point for secondary actions. | `component:Button` | Supporting | FR1, FR3 |
|
|
132
|
+
| Icon-resolved trigger icon | Draws semantic three-dot artwork from the active Icon registry. | Icon registry | Supporting | FR1, FR3 |
|
|
133
|
+
| Caller-rendered trigger content | Presents arbitrary caller content supplied through the icon override. | Caller-supplied content | Context-dependent | FR1, FR3 |
|
|
134
|
+
| Menu surface | Adds MoreMenu's target to the composed DropdownMenu panel, not the trigger. | Current source and public docs | Prominent | FR1, FR2 |
|
|
135
|
+
| Pointer action row | Presents an action or nested flyout trigger in the anchored presentation. | `component:DropdownMenu` | Prominent | FR1, FR3, FR4 |
|
|
136
|
+
| Touch action row | Presents an action or drill-in entry through ListItem in the touch presentation. | `component:List` | Prominent | FR1, FR3, FR4 |
|
|
137
|
+
|
|
138
|
+
The Menu surface is the DropdownMenu panel itself. It carries both
|
|
139
|
+
`astryx-more-menu` and DropdownMenu's own panel target; the local class is not a
|
|
140
|
+
wrapper and does not retarget the trigger. DropdownMenu remains the owner of the
|
|
141
|
+
presentation runtime and anchored interior. Its touch path delegates action rows
|
|
142
|
+
to List rather than extending the pointer-row target into the BottomSheet
|
|
143
|
+
presentation.
|
|
144
|
+
|
|
145
|
+
### Theming anatomy
|
|
146
|
+
|
|
147
|
+
<!-- anatomy-theming:v1 -->
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"Trigger button": {
|
|
152
|
+
"delegatesTo": {"owner": "component:Button", "target": "button"}
|
|
153
|
+
},
|
|
154
|
+
"Icon-resolved trigger icon": {
|
|
155
|
+
"none": {
|
|
156
|
+
"reason": "unsettled: The current useIcon artwork renders directly inside Button; whether it should carry Icon's icon target needs an owner decision."
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"Caller-rendered trigger content": {
|
|
160
|
+
"none": {
|
|
161
|
+
"reason": "intentional: Arbitrary React icon content is caller-owned and receives no MoreMenu target."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"Menu surface": {"target": "more-menu"},
|
|
165
|
+
"Pointer action row": {
|
|
166
|
+
"delegatesTo": {
|
|
167
|
+
"owner": "component:DropdownMenu",
|
|
168
|
+
"target": "dropdown-menu-item"
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"Touch action row": {
|
|
172
|
+
"delegatesTo": {"owner": "component:List", "target": "list-item"}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Family and system relationships
|
|
178
|
+
|
|
179
|
+
- `architecture:component-theming-surface` owns anatomy qualification, the local
|
|
180
|
+
Menu-surface target, and composition delegation rules.
|
|
181
|
+
- `architecture:icon-resolution-and-component-slots` owns active-theme icon
|
|
182
|
+
resolution. MoreMenu's default artwork uses that registry but currently
|
|
183
|
+
bypasses Icon's painted `icon` target.
|
|
184
|
+
- `architecture:interaction-modality` owns the shared pointer, touch, keyboard,
|
|
185
|
+
and adaptive-presentation boundaries inherited through DropdownMenu; this
|
|
186
|
+
draft changes none of them.
|
|
187
|
+
- `architecture:layer-runtime` owns the current DropdownMenu Popover host, native
|
|
188
|
+
light-dismiss reconciliation, and BottomSheet host distinction. MoreMenu adds
|
|
189
|
+
no parallel layer runtime.
|
|
190
|
+
- `family:overlay-dismissal` owns shared Escape and platform-close ordering and
|
|
191
|
+
records MoreMenu as a shared owner through its composed DropdownMenu root;
|
|
192
|
+
BottomSheet presentation retains BottomSheet's recorded local-only adoption
|
|
193
|
+
gap.
|
|
194
|
+
|
|
195
|
+
## Verification map
|
|
196
|
+
|
|
197
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
198
|
+
| ------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ------------------------- |
|
|
199
|
+
| FR1, FR4 | `MoreMenu.test.tsx` render, pointer, bottom-sheet, placement, and controlled suites | Default, custom icon, pointer, touch | Replacing the composition or changing forwarded behavior fails existing render/interaction tests. | `audit:MoreMenu/anatomy` |
|
|
200
|
+
| FR2 | MoreMenu target test, source inspection, and theming target inventories | Closed trigger and composed menu panel | Moving `more-menu` onto the trigger or removing it from the panel fails target evidence. | `audit:MoreMenu/theming` |
|
|
201
|
+
| FR3 | MoreMenu, DropdownMenu, Button, Icon, and List source/owner tests | Trigger, icon branches, pointer rows, touch rows | A composed part loses its owner target or is documented as a new MoreMenu-owned descendant. | `audit:MoreMenu/theming` |
|
|
202
|
+
| Layer relationships | `MoreMenu.test.tsx`, `DropdownMenu.test.tsx`, and current layer/dismissal records | Light dismiss, Escape, pointer, touch | Documentation gives MoreMenu a parallel runtime or misstates the current dismissal owner. | `audit:MoreMenu/behavior` |
|
|
203
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current target | Missing, extra, prefixed, stale, or unclassified mappings fail repository validation. | `audit:MoreMenu/theming` |
|
|
204
|
+
|
|
205
|
+
## Decision log
|
|
206
|
+
|
|
207
|
+
None. This draft records current facts and introduces no component-local design,
|
|
208
|
+
API, theming, modality, or layer-system decision.
|
|
209
|
+
|
|
210
|
+
## Open questions
|
|
211
|
+
|
|
212
|
+
- **OQ1 — Should the default trigger artwork render through Icon's public
|
|
213
|
+
target?** (`human-api`) The current registry lookup preserves semantic artwork
|
|
214
|
+
replacement but leaves the painted glyph outside the `icon` target; this
|
|
215
|
+
draft does not change that reachability.
|
|
216
|
+
|
|
217
|
+
## Content boundary
|
|
218
|
+
|
|
219
|
+
This file does not duplicate consumer prop tables, DropdownMenu item examples,
|
|
220
|
+
icon-resolution rules, implementation steps, or shared modality, layer,
|
|
221
|
+
dismissal, and theming rules. It links to their owners.
|
|
@@ -17,6 +17,30 @@ import {DropdownMenu} from '../DropdownMenu/DropdownMenu';
|
|
|
17
17
|
|
|
18
18
|
// Mock showPopover and hidePopover methods since they're not implemented in jsdom
|
|
19
19
|
beforeEach(() => {
|
|
20
|
+
HTMLDialogElement.prototype.showModal = vi.fn(function (
|
|
21
|
+
this: HTMLDialogElement,
|
|
22
|
+
) {
|
|
23
|
+
this.setAttribute('open', '');
|
|
24
|
+
});
|
|
25
|
+
HTMLDialogElement.prototype.show = vi.fn(function (this: HTMLDialogElement) {
|
|
26
|
+
this.setAttribute('open', '');
|
|
27
|
+
});
|
|
28
|
+
HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
|
|
29
|
+
this.removeAttribute('open');
|
|
30
|
+
});
|
|
31
|
+
vi.stubGlobal(
|
|
32
|
+
'matchMedia',
|
|
33
|
+
vi.fn().mockImplementation((query: string) => ({
|
|
34
|
+
matches: false,
|
|
35
|
+
media: query,
|
|
36
|
+
onchange: null,
|
|
37
|
+
addEventListener: vi.fn(),
|
|
38
|
+
removeEventListener: vi.fn(),
|
|
39
|
+
addListener: vi.fn(),
|
|
40
|
+
removeListener: vi.fn(),
|
|
41
|
+
dispatchEvent: vi.fn(),
|
|
42
|
+
})),
|
|
43
|
+
);
|
|
20
44
|
HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
|
|
21
45
|
this.setAttribute('popover-open', '');
|
|
22
46
|
const event = new Event('toggle', {bubbles: false});
|
|
@@ -85,6 +109,29 @@ describe('MoreMenu', () => {
|
|
|
85
109
|
expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
|
|
86
110
|
});
|
|
87
111
|
|
|
112
|
+
it('keeps the trigger visually pressed while the menu is open', async () => {
|
|
113
|
+
const user = userEvent.setup();
|
|
114
|
+
render(<MoreMenu items={defaultItems} />);
|
|
115
|
+
|
|
116
|
+
const trigger = screen.getByRole('button', {name: 'More options'});
|
|
117
|
+
expect(trigger.className).not.toContain('DropdownMenu__styles.triggerOpen');
|
|
118
|
+
|
|
119
|
+
await user.click(trigger);
|
|
120
|
+
|
|
121
|
+
expect(trigger.className).toContain('DropdownMenu__styles.triggerOpen');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('forwards BottomSheet presentation to DropdownMenu', async () => {
|
|
125
|
+
const user = userEvent.setup();
|
|
126
|
+
render(<MoreMenu items={defaultItems} presentation="bottom-sheet" />);
|
|
127
|
+
|
|
128
|
+
await user.click(screen.getByRole('button', {name: 'More options'}));
|
|
129
|
+
await waitFor(() =>
|
|
130
|
+
expect(HTMLDialogElement.prototype.showModal).toHaveBeenCalledOnce(),
|
|
131
|
+
);
|
|
132
|
+
expect(HTMLElement.prototype.showPopover).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
|
|
88
135
|
it('calls onClick when item is clicked', async () => {
|
|
89
136
|
const handleEdit = vi.fn();
|
|
90
137
|
const items = [
|
|
@@ -22,7 +22,10 @@ import type {ReactNode} from 'react';
|
|
|
22
22
|
import {useIcon} from '../Icon';
|
|
23
23
|
import {DropdownMenu} from '../DropdownMenu/DropdownMenu';
|
|
24
24
|
import {useSize} from '../SizeContext/SizeContext';
|
|
25
|
-
import type {
|
|
25
|
+
import type {
|
|
26
|
+
DropdownMenuOption,
|
|
27
|
+
DropdownMenuPresentation,
|
|
28
|
+
} from '../DropdownMenu';
|
|
26
29
|
import type {LayerAlignment, LayerPlacement} from '../Layer';
|
|
27
30
|
import type {ButtonVariant, ButtonSize} from '../Button';
|
|
28
31
|
import type {BaseProps} from '../BaseProps';
|
|
@@ -87,6 +90,13 @@ export interface MoreMenuProps extends Pick<
|
|
|
87
90
|
*/
|
|
88
91
|
alignment?: LayerAlignment;
|
|
89
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Menu presentation policy. `adaptive` uses a BottomSheet on compact
|
|
95
|
+
* coarse-pointer viewports and an anchored popover elsewhere.
|
|
96
|
+
* @default 'popover'
|
|
97
|
+
*/
|
|
98
|
+
presentation?: DropdownMenuPresentation;
|
|
99
|
+
|
|
90
100
|
/**
|
|
91
101
|
* Controlled open state for the menu.
|
|
92
102
|
*/
|
|
@@ -125,6 +135,7 @@ export function MoreMenu({
|
|
|
125
135
|
isDisabled = false,
|
|
126
136
|
placement,
|
|
127
137
|
alignment,
|
|
138
|
+
presentation,
|
|
128
139
|
isMenuOpen,
|
|
129
140
|
onOpenChange,
|
|
130
141
|
xstyle,
|
|
@@ -151,6 +162,7 @@ export function MoreMenu({
|
|
|
151
162
|
onOpenChange={onOpenChange}
|
|
152
163
|
placement={placement}
|
|
153
164
|
alignment={alignment}
|
|
165
|
+
presentation={presentation}
|
|
154
166
|
button={{
|
|
155
167
|
label,
|
|
156
168
|
icon: icon ?? moreIcon,
|
|
@@ -1,5 +1,116 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
|
|
4
|
+
const anatomy = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Field',
|
|
7
|
+
required: false,
|
|
8
|
+
description:
|
|
9
|
+
'Standalone Field shell that provides the label and optional supporting content; omitted inside InputGroup.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'Trigger',
|
|
13
|
+
required: true,
|
|
14
|
+
description:
|
|
15
|
+
'Painted control that displays the current selection or placeholder and opens the selection surface.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Icon-rendered start icon',
|
|
19
|
+
required: false,
|
|
20
|
+
description:
|
|
21
|
+
'Optional leading semantic icon or icon component rendered through Icon.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'Caller-rendered start content',
|
|
25
|
+
required: false,
|
|
26
|
+
description:
|
|
27
|
+
'Optional arbitrary React content rendered directly at the start of the trigger.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Trigger clear button',
|
|
31
|
+
required: false,
|
|
32
|
+
description:
|
|
33
|
+
'Shared clear action that removes every selected value when hasClear is enabled.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Status icon',
|
|
37
|
+
required: false,
|
|
38
|
+
description:
|
|
39
|
+
'Status glyph shown in place of the disclosure indicator for attached or tooltip status.',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'Indicator icon',
|
|
43
|
+
required: false,
|
|
44
|
+
description:
|
|
45
|
+
'Trailing chevron shown when status presentation does not replace it; reflects collapsed or expanded state.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Search row',
|
|
49
|
+
required: false,
|
|
50
|
+
description:
|
|
51
|
+
'Panel header with a borderless search input and optional clear action.',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Search icon',
|
|
55
|
+
required: false,
|
|
56
|
+
description:
|
|
57
|
+
'Leading magnifier rendered through Icon inside the search row.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'Search clear button',
|
|
61
|
+
required: false,
|
|
62
|
+
description:
|
|
63
|
+
'Shared clear action shown in the search row while a query is present.',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Option row',
|
|
67
|
+
required: false,
|
|
68
|
+
description:
|
|
69
|
+
'Selectable row for an option or the optional select-all choice.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Option checkbox indicator',
|
|
73
|
+
required: false,
|
|
74
|
+
description:
|
|
75
|
+
'CheckboxInput indicator that presents each row’s selected, unselected, or indeterminate state.',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Option divider',
|
|
79
|
+
required: false,
|
|
80
|
+
description:
|
|
81
|
+
'Divider supplied in the public options data to separate adjacent option groups.',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'Section heading',
|
|
85
|
+
required: false,
|
|
86
|
+
description: 'Visible heading for a labeled group of option rows.',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Empty state',
|
|
90
|
+
required: false,
|
|
91
|
+
description:
|
|
92
|
+
'Message shown when the shared panel content has no options or no search matches.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'Pointer popup',
|
|
96
|
+
required: false,
|
|
97
|
+
description:
|
|
98
|
+
'Anchored painted surface that hosts the shared panel content for popover presentation.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'Touch sheet heading',
|
|
102
|
+
required: false,
|
|
103
|
+
description:
|
|
104
|
+
'Heading above the shared panel content in bottom-sheet presentation.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'Touch sheet',
|
|
108
|
+
required: false,
|
|
109
|
+
description:
|
|
110
|
+
'BottomSheet surface that hosts the same panel content for bottom-sheet presentation.',
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
|
|
3
114
|
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
115
|
|
|
5
116
|
export const docs = {
|
|
@@ -224,6 +335,13 @@ export const docs = {
|
|
|
224
335
|
'Which edge of the option row carries the checkbox. end pushes it to the far edge of the row, including on the select-all row.',
|
|
225
336
|
default: "'start'",
|
|
226
337
|
},
|
|
338
|
+
{
|
|
339
|
+
name: 'presentation',
|
|
340
|
+
type: "'popover' | 'bottom-sheet' | 'adaptive'",
|
|
341
|
+
description:
|
|
342
|
+
'How the option list is presented. adaptive uses a bottom sheet on compact touch screens and an anchored popover otherwise.',
|
|
343
|
+
default: "'popover'",
|
|
344
|
+
},
|
|
227
345
|
{
|
|
228
346
|
name: 'width',
|
|
229
347
|
type: 'SizeValue',
|
|
@@ -257,6 +375,7 @@ export const docs = {
|
|
|
257
375
|
},
|
|
258
376
|
],
|
|
259
377
|
usage: {
|
|
378
|
+
anatomy,
|
|
260
379
|
description:
|
|
261
380
|
'A checkbox dropdown for selecting multiple values from a list. Selected items can display as a count, labels, or badges. Use it for filtering or when presenting a finite set of options where multiple choices are needed.',
|
|
262
381
|
bestPractices: [
|
|
@@ -290,6 +409,11 @@ export const docs = {
|
|
|
290
409
|
description:
|
|
291
410
|
'Use variant="ghost" when a multi-selector sits in a toolbar with ghost buttons. If validation status is needed there, prefer statusVariant="tooltip" so the toolbar height stays compact.',
|
|
292
411
|
},
|
|
412
|
+
{
|
|
413
|
+
guidance: true,
|
|
414
|
+
description:
|
|
415
|
+
'Use presentation="adaptive" when the multi-selector should become a bottom sheet on compact touch screens.',
|
|
416
|
+
},
|
|
293
417
|
{
|
|
294
418
|
guidance: false,
|
|
295
419
|
description: 'Use for single-value selection; use Selector instead.',
|
|
@@ -349,6 +473,8 @@ export const docsZh = {
|
|
|
349
473
|
'状态消息的放置方式:attached 直接叠加在输入框下方;detached 作为独立元素浮于下方并留有间距。',
|
|
350
474
|
renderOption:
|
|
351
475
|
'每个可选选项的自定义渲染函数。不会用于分隔线、分组或全选行。',
|
|
476
|
+
presentation:
|
|
477
|
+
'选项列表的呈现方式:锚定弹出框、底部抽屉,或根据紧凑触控屏自适应。',
|
|
352
478
|
xstyle: '布局自定义的 StyleX 样式,必须是 stylex.create() 值。',
|
|
353
479
|
},
|
|
354
480
|
},
|
|
@@ -487,6 +613,8 @@ export const docsDense = {
|
|
|
487
613
|
'status message placement; ghost detaches attached by default; use tooltip for compact toolbars.',
|
|
488
614
|
renderOption:
|
|
489
615
|
'custom render fn per selectable option; not dividers/sections/select-all',
|
|
616
|
+
presentation:
|
|
617
|
+
'popover, bottom-sheet, or compact-touch adaptive presentation',
|
|
490
618
|
xstyle: 'StyleX layout styles; stylex.create() only',
|
|
491
619
|
},
|
|
492
620
|
},
|