@astryxdesign/core 0.1.7-canary.aa8d475 → 0.1.7-canary.cbfb48e

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.
Files changed (124) hide show
  1. package/README.md +24 -15
  2. package/dist/Collapsible/Collapsible.d.ts +11 -1
  3. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  4. package/dist/Collapsible/Collapsible.js +36 -4
  5. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  6. package/dist/ContextMenu/ContextMenu.js +4 -3
  7. package/dist/ContextMenu/index.d.ts +3 -0
  8. package/dist/ContextMenu/index.d.ts.map +1 -1
  9. package/dist/ContextMenu/index.js +7 -1
  10. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  11. package/dist/DropdownMenu/DropdownMenu.js +8 -4
  12. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +89 -0
  13. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
  14. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +170 -0
  15. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
  16. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  17. package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
  18. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +69 -0
  19. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
  20. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +70 -0
  21. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
  22. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
  23. package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
  24. package/dist/DropdownMenu/index.d.ts +5 -1
  25. package/dist/DropdownMenu/index.d.ts.map +1 -1
  26. package/dist/DropdownMenu/index.js +10 -1
  27. package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
  28. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
  29. package/dist/DropdownMenu/menuItemRoles.js +15 -0
  30. package/dist/Outline/Outline.d.ts +69 -3
  31. package/dist/Outline/Outline.d.ts.map +1 -1
  32. package/dist/Outline/Outline.js +129 -26
  33. package/dist/Outline/useScrollSpy.d.ts +27 -7
  34. package/dist/Outline/useScrollSpy.d.ts.map +1 -1
  35. package/dist/Outline/useScrollSpy.js +202 -70
  36. package/dist/TabList/Tab.d.ts.map +1 -1
  37. package/dist/TabList/Tab.js +2 -2
  38. package/dist/TabList/TabList.d.ts.map +1 -1
  39. package/dist/TabList/TabList.js +2 -0
  40. package/dist/TabList/TabMenu.d.ts.map +1 -1
  41. package/dist/TabList/TabMenu.js +1 -1
  42. package/dist/Table/index.d.ts +2 -0
  43. package/dist/Table/index.d.ts.map +1 -1
  44. package/dist/Table/index.js +1 -0
  45. package/dist/Table/plugins/tree/index.d.ts +5 -0
  46. package/dist/Table/plugins/tree/index.d.ts.map +1 -0
  47. package/dist/Table/plugins/tree/index.js +4 -0
  48. package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
  49. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
  50. package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
  51. package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
  52. package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
  53. package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
  54. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  55. package/dist/Table/useBaseTablePlugins.js +6 -3
  56. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  57. package/dist/Toolbar/Toolbar.js +8 -1
  58. package/dist/astryx.css +6 -1
  59. package/dist/astryx.umd.js +48 -48
  60. package/dist/astryx.umd.js.map +4 -4
  61. package/dist/theme/Theme.js +1 -1
  62. package/dist/theme/defineTheme.d.ts +1 -1
  63. package/dist/theme/defineTheme.js +1 -1
  64. package/dist/theme/hct.d.ts +0 -1
  65. package/dist/theme/hct.d.ts.map +1 -1
  66. package/dist/theme/hct.js +0 -3
  67. package/locales/en.json +8 -0
  68. package/locales/pseudo.json +6 -0
  69. package/package.json +3 -1
  70. package/scripts/postinstall.mjs +93 -0
  71. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  72. package/src/Collapsible/Collapsible.doc.mjs +7 -0
  73. package/src/Collapsible/Collapsible.test.tsx +80 -0
  74. package/src/Collapsible/Collapsible.tsx +58 -5
  75. package/src/ContextMenu/ContextMenu.test.tsx +75 -1
  76. package/src/ContextMenu/ContextMenu.tsx +10 -5
  77. package/src/ContextMenu/index.ts +15 -0
  78. package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
  79. package/src/DropdownMenu/DropdownMenu.tsx +11 -6
  80. package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
  81. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +237 -0
  82. package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
  83. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
  84. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
  85. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +119 -0
  86. package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
  87. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
  88. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
  89. package/src/DropdownMenu/index.ts +23 -3
  90. package/src/DropdownMenu/menuItemRoles.ts +22 -0
  91. package/src/Field/Field.doc.mjs +1 -1
  92. package/src/Field/FieldLabel.doc.mjs +1 -1
  93. package/src/NumberInput/NumberInput.doc.mjs +2 -2
  94. package/src/Outline/Outline.doc.mjs +123 -3
  95. package/src/Outline/Outline.test.tsx +735 -0
  96. package/src/Outline/Outline.tsx +184 -33
  97. package/src/Outline/useScrollSpy.ts +248 -72
  98. package/src/Selector/SelectorOption.doc.mjs +1 -1
  99. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  100. package/src/Switch/Switch.doc.mjs +1 -1
  101. package/src/TabList/Tab.tsx +5 -1
  102. package/src/TabList/TabList.doc.mjs +3 -3
  103. package/src/TabList/TabList.test.tsx +66 -0
  104. package/src/TabList/TabList.tsx +8 -0
  105. package/src/TabList/TabMenu.tsx +4 -1
  106. package/src/Table/Table.doc.mjs +2 -0
  107. package/src/Table/index.ts +7 -0
  108. package/src/Table/plugins/tree/index.ts +12 -0
  109. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
  110. package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
  111. package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
  112. package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
  113. package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
  114. package/src/Table/useBaseTablePlugins.ts +6 -2
  115. package/src/Table/useTableTreeData.doc.mjs +75 -0
  116. package/src/Table/useTableTreeState.doc.mjs +98 -0
  117. package/src/TextArea/TextArea.doc.mjs +1 -1
  118. package/src/TextInput/TextInput.doc.mjs +1 -1
  119. package/src/Toolbar/Toolbar.tsx +10 -3
  120. package/src/postinstall.test.mjs +71 -0
  121. package/src/theme/Theme.doc.mjs +4 -4
  122. package/src/theme/Theme.tsx +1 -1
  123. package/src/theme/defineTheme.ts +1 -1
  124. package/src/theme/hct.ts +0 -4
package/README.md CHANGED
@@ -2,7 +2,14 @@
2
2
 
3
3
  Core UI components, theme system, and utilities for the Astryx design system. For project setup, see [Quick Start](#quick-start) below.
4
4
 
5
- > **Building with an AI agent?** Run **`npx astryx init`** first. It installs the Astryx CLI's component index into your `AGENTS.md`/`CLAUDE.md` so your agent discovers components, templates, and design tokens instead of guessing. Without it, agents can't see the CLI — see [XDS CLI](#xds-cli).
5
+ > **Building with an AI agent?** Add the CLI, then run `init`:
6
+ >
7
+ > ```bash
8
+ > npm install -D @astryxdesign/cli # or: pnpm add -D / yarn add -D / bun add -d
9
+ > npx astryx init # resolves to the CLI you just installed
10
+ > ```
11
+ >
12
+ > `init` writes the Astryx component index into your `AGENTS.md`/`CLAUDE.md` so your agent discovers components, templates, and design tokens instead of guessing. Need a single command without installing? Use the scoped package directly — `npx @astryxdesign/cli <cmd>` (or `pnpm dlx`/`bunx @astryxdesign/cli`). Bare `npx astryx` only works once `@astryxdesign/cli` is a dependency; before that npm resolves it to an unrelated package. See [XDS CLI](#xds-cli).
6
13
 
7
14
  ## Component Docs
8
15
 
@@ -25,9 +32,9 @@ global navigation.
25
32
  Requires `@astryxdesign/cli` (`npm install -D @astryxdesign/cli`):
26
33
 
27
34
  ```bash
28
- npx astryx template --list # browse all page and block templates
29
- npx astryx template dashboard # emit full page source
30
- npx astryx template settings --skeleton # layout skeleton with spatial annotations
35
+ astryx template --list # browse all page and block templates
36
+ astryx template dashboard # emit full page source
37
+ astryx template settings --skeleton # layout skeleton with spatial annotations
31
38
  ```
32
39
 
33
40
  ## Astryx CLI
@@ -35,19 +42,21 @@ npx astryx template settings --skeleton # layout skeleton with spatial annotatio
35
42
  The CLI (`@astryxdesign/cli`) provides additional tooling:
36
43
 
37
44
  ```bash
38
- npx astryx --help # full listing of all commands
39
- npx astryx component Button # full docs + related block templates
40
- npx astryx docs # reference docs (principles, tokens, theming, styling)
41
- npx astryx docs theme # theming guide (Theme, defineTheme, light/dark)
42
- npx astryx docs tokens # spacing, color, radius, typography token reference
43
- npx astryx init # initialize Astryx in your project
44
- npx astryx theme build # build theme CSS for production
45
- npx astryx swizzle Button # eject component source for customization
46
- npx astryx upgrade --apply # run codemods to migrate between versions
47
- npx astryx discover # discover external Astryx packages
48
- npx astryx gap-report # report a missing capability
45
+ astryx --help # full listing of all commands
46
+ astryx component Button # full docs + related block templates
47
+ astryx docs # reference docs (principles, tokens, theming, styling)
48
+ astryx docs theme # theming guide (Theme, defineTheme, light/dark)
49
+ astryx docs tokens # spacing, color, radius, typography token reference
50
+ astryx init # initialize Astryx in your project
51
+ astryx theme build # build theme CSS for production
52
+ astryx swizzle Button # eject component source for customization
53
+ astryx upgrade --apply # run codemods to migrate between versions
54
+ astryx discover # discover external Astryx packages
55
+ astryx gap-report # report a missing capability
49
56
  ```
50
57
 
58
+ > Prefix these with your runner: `npx astryx …` / `pnpm exec astryx …` once the CLI is installed, or `npx @astryxdesign/cli …` to run without installing.
59
+
51
60
  ## Related Packages
52
61
 
53
62
  | Package | Description |
@@ -45,6 +45,16 @@ export interface CollapsibleProps extends BaseProps {
45
45
  * Controlled open state. When provided, the component is fully controlled.
46
46
  */
47
47
  isOpen?: boolean;
48
+ /**
49
+ * Whether the collapsible is disabled. A disabled item can't be toggled —
50
+ * its trigger is non-interactive and dimmed. Following the system-wide
51
+ * disabled convention, the trigger uses `aria-disabled` (not the native
52
+ * `disabled` attribute) and drops out of the tab order, staying perceivable
53
+ * to assistive tech. The content stays in whatever open state it was;
54
+ * disabling doesn't collapse an already-open item.
55
+ * @default false
56
+ */
57
+ isDisabled?: boolean;
48
58
  /**
49
59
  * Callback when the open state changes.
50
60
  */
@@ -96,7 +106,7 @@ export interface CollapsibleProps extends BaseProps {
96
106
  * </CollapsibleGroup>
97
107
  * ```
98
108
  */
99
- export declare function Collapsible({ trigger, children, defaultIsOpen, isOpen: controlledIsOpen, onOpenChange, value, ref, xstyle, className, style, ...props }: CollapsibleProps): import("react").JSX.Element;
109
+ export declare function Collapsible({ trigger, children, defaultIsOpen, isOpen: controlledIsOpen, isDisabled, onOpenChange, value, ref, xstyle, className, style, ...props }: CollapsibleProps): import("react").JSX.Element;
100
110
  export declare namespace Collapsible {
101
111
  var displayName: string;
102
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Collapsible.d.ts","sourceRoot":"","sources":["../../src/Collapsible/Collapsible.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAgBjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAkG5C,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,OAAO,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,aAAa,EACb,MAAM,EAAE,gBAAgB,EACxB,YAAY,EACZ,KAAK,EACL,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,gBAAgB,+BAqElB;yBAjFe,WAAW"}
1
+ {"version":3,"file":"Collapsible.d.ts","sourceRoot":"","sources":["../../src/Collapsible/Collapsible.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAiBjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAkH5C,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,OAAO,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAEzC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,aAAa,EACb,MAAM,EAAE,gBAAgB,EACxB,UAAkB,EAClB,YAAY,EACZ,KAAK,EACL,GAAG,EACH,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,gBAAgB,+BA6FlB;yBA1Ge,WAAW"}
@@ -58,12 +58,22 @@ const styles = {
58
58
  kInvED: "x1wfwxd8 x7s97pk",
59
59
  $$css: true
60
60
  },
61
+ triggerDisabled: {
62
+ kkrTdU: "x1h6gzvc",
63
+ kSiTet: "xbyyjgo",
64
+ $$css: true
65
+ },
61
66
  contentHidden: {
62
67
  k1xSpc: "x1s85apg",
63
68
  $$css: true
64
69
  },
65
70
  content: {
66
71
  kLKAdn: "xfsso4q",
72
+ kMv6JI: "x9ynric",
73
+ kGuDYH: "xjm74w1",
74
+ k63SB2: "xxovm9e",
75
+ kLWn49: "xw6l6zx",
76
+ kMwMTN: "x1tgivj0",
67
77
  $$css: true
68
78
  },
69
79
  divided: {
@@ -155,6 +165,7 @@ export function Collapsible({
155
165
  children,
156
166
  defaultIsOpen,
157
167
  isOpen: controlledIsOpen,
168
+ isDisabled = false,
158
169
  onOpenChange,
159
170
  value,
160
171
  ref,
@@ -178,6 +189,17 @@ export function Collapsible({
178
189
  isCollapsible: collapsibleConfig,
179
190
  value
180
191
  });
192
+
193
+ // Activation is blocked by this guard rather than the native `disabled`
194
+ // attribute, so the trigger keeps `aria-disabled` semantics and stays
195
+ // discoverable. A native `disabled` button would silently swallow events
196
+ // (e.g. a wrapping tooltip's hover) — the system-wide disabled convention.
197
+ const handleToggle = () => {
198
+ if (isDisabled) {
199
+ return;
200
+ }
201
+ toggle();
202
+ };
181
203
  const presentation = use(CollapsibleGroupPresentationContext);
182
204
  const isDivided = presentation?.hasDividers ?? false;
183
205
  const density = presentation?.density ?? null;
@@ -194,10 +216,18 @@ export function Collapsible({
194
216
  ...props,
195
217
  children: [/*#__PURE__*/_jsxs("button", {
196
218
  type: "button",
197
- onClick: toggle,
219
+ onClick: handleToggle,
220
+ "aria-disabled": isDisabled || undefined,
198
221
  "aria-expanded": isOpen,
199
- "aria-controls": contentId,
200
- ...stylex.props(styles.trigger, density != null && triggerDensity[density]),
222
+ "aria-controls": contentId
223
+ // A disabled trigger drops out of the tab order so it isn't a silently
224
+ // dead tab stop; activation stays blocked by the handleToggle guard,
225
+ // and aria-disabled keeps the state perceivable to assistive tech —
226
+ // the system-wide disabled convention (never native `disabled`, which
227
+ // would swallow events like a wrapping tooltip's hover).
228
+ ,
229
+ tabIndex: isDisabled ? -1 : undefined,
230
+ ...stylex.props(styles.trigger, density != null && triggerDensity[density], isDisabled && styles.triggerDisabled),
201
231
  children: [/*#__PURE__*/_jsx("span", {
202
232
  ...{
203
233
  className: "x1b2iylo xwgcxoh"
@@ -216,7 +246,9 @@ export function Collapsible({
216
246
  })]
217
247
  }), /*#__PURE__*/_jsx("div", {
218
248
  id: contentId,
219
- ...stylex.props(styles.content, density != null && contentDensity[density], !isOpen && styles.contentHidden),
249
+ ...mergeProps(themeProps('collapsible-content', {
250
+ density: density ?? undefined
251
+ }), stylex.props(styles.content, density != null && contentDensity[density], !isOpen && styles.contentHidden)),
220
252
  children: presentation != null ? /*#__PURE__*/_jsx(CollapsibleGroupPresentationContext, {
221
253
  value: null,
222
254
  children: children
@@ -1 +1 @@
1
- {"version":3,"file":"ContextMenu.d.ts","sourceRoot":"","sources":["../../src/ContextMenu/ContextMenu.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAqBrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAGjD,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AAqDtC,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AAEvD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAErD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAMnD,UAAU,oBAAqB,SAAQ,SAAS;IAC9C,oDAAoD;IACpD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;IAC9C,+DAA+D;IAC/D,QAAQ,EAAE,SAAS,CAAC;IACpB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,wCAAwC;IACxC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,oBAAqB,SAAQ,oBAAoB;IACzD,gDAAgD;IAChD,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB;AAED,UAAU,wBAAyB,SAAQ,oBAAoB;IAC7D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+CAA+C;IAC/C,WAAW,EAAE,SAAS,CAAC;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,wBAAwB,CAAC;AAM/E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,IAAW,EACX,KAAK,EAAE,cAAc,EACrB,UAAkB,EAClB,YAAY,EACZ,GAAG,EACH,SAAS,EACT,KAAK,EACL,MAAM,EACN,aAAa,EACb,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,gBAAgB,qBAiRlB;yBA/Re,WAAW"}
1
+ {"version":3,"file":"ContextMenu.d.ts","sourceRoot":"","sources":["../../src/ContextMenu/ContextMenu.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAON,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAyBrC,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAGjD,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AAqDtC,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AAEvD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAErD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAMnD,UAAU,oBAAqB,SAAQ,SAAS;IAC9C,oDAAoD;IACpD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;IAC9C,+DAA+D;IAC/D,QAAQ,EAAE,SAAS,CAAC;IACpB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,wCAAwC;IACxC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,oBAAqB,SAAQ,oBAAoB;IACzD,gDAAgD;IAChD,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB;AAED,UAAU,wBAAyB,SAAQ,oBAAoB;IAC7D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,+CAA+C;IAC/C,WAAW,EAAE,SAAS,CAAC;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,wBAAwB,CAAC;AAM/E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,IAAW,EACX,KAAK,EAAE,cAAc,EACrB,UAAkB,EAClB,YAAY,EACZ,GAAG,EACH,SAAS,EACT,KAAK,EACL,MAAM,EACN,aAAa,EACb,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,gBAAgB,qBAkRlB;yBAhSe,WAAW"}
@@ -34,6 +34,7 @@ import * as stylex from '@stylexjs/stylex';
34
34
  import { useLayer } from "../Layer/useLayer.js";
35
35
  import { renderDropdownItems } from "../DropdownMenu/renderDropdownItems.js";
36
36
  import { DropdownMenuContext } from "../DropdownMenu/DropdownMenuContext.js";
37
+ import { MENU_ITEM_ROLES, MENU_ITEM_SELECTOR } from "../DropdownMenu/menuItemRoles.js";
37
38
  import { useListFocus } from "../hooks/useListFocus.js";
38
39
  import { useTypeahead } from "../hooks/useTypeahead.js";
39
40
  import { useLongPress } from "../hooks/useLongPress.js";
@@ -178,13 +179,13 @@ export function ContextMenu({
178
179
  focusFirst,
179
180
  focusItem
180
181
  } = useListFocus({
181
- itemSelector: '[role="menuitem"]:not([aria-disabled="true"])',
182
+ itemSelector: MENU_ITEM_SELECTOR,
182
183
  wrap: false,
183
184
  onEscape: closeMenu
184
185
  });
185
186
 
186
187
  // First-character typeahead over the enabled menu items (menus-11).
187
- const getMenuItems = useCallback(() => listRef.current ? Array.from(listRef.current.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')) : [], [listRef]);
188
+ const getMenuItems = useCallback(() => listRef.current ? Array.from(listRef.current.querySelectorAll(MENU_ITEM_SELECTOR)) : [], [listRef]);
188
189
  const typeahead = useTypeahead({
189
190
  getItemLabels: () => getMenuItems().map(el => el.textContent),
190
191
  onMatch: focusItem,
@@ -238,7 +239,7 @@ export function ContextMenu({
238
239
  if (e.key === 'Enter' || e.key === ' ') {
239
240
  e.preventDefault();
240
241
  const focused = document.activeElement;
241
- if (focused?.getAttribute('role') === 'menuitem') {
242
+ if (focused && MENU_ITEM_ROLES.has(focused.getAttribute('role') ?? '')) {
242
243
  focused.click();
243
244
  }
244
245
  return;
@@ -5,4 +5,7 @@
5
5
  */
6
6
  export { ContextMenu, type ContextMenuProps, type ContextMenuItemData, type ContextMenuDivider, type ContextMenuSection, type ContextMenuOption, } from './ContextMenu';
7
7
  export { DropdownMenuItem as ContextMenuItem, type DropdownMenuItemProps as ContextMenuItemProps, } from '../DropdownMenu/DropdownMenuItem';
8
+ export { DropdownMenuCheckboxItem as ContextMenuCheckboxItem, type DropdownMenuCheckboxItemProps as ContextMenuCheckboxItemProps, } from '../DropdownMenu/DropdownMenuCheckboxItem';
9
+ export { DropdownMenuRadioGroup as ContextMenuRadioGroup, type DropdownMenuRadioGroupProps as ContextMenuRadioGroupProps, } from '../DropdownMenu/DropdownMenuRadioGroup';
10
+ export { DropdownMenuRadioItem as ContextMenuRadioItem, type DropdownMenuRadioItemProps as ContextMenuRadioItemProps, } from '../DropdownMenu/DropdownMenuRadioItem';
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ContextMenu/index.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,IAAI,eAAe,EACnC,KAAK,qBAAqB,IAAI,oBAAoB,GACnD,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ContextMenu/index.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,OAAO,EACL,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,IAAI,eAAe,EACnC,KAAK,qBAAqB,IAAI,oBAAoB,GACnD,MAAM,kCAAkC,CAAC;AAI1C,OAAO,EACL,wBAAwB,IAAI,uBAAuB,EACnD,KAAK,6BAA6B,IAAI,4BAA4B,GACnE,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,sBAAsB,IAAI,qBAAqB,EAC/C,KAAK,2BAA2B,IAAI,0BAA0B,GAC/D,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,qBAAqB,IAAI,oBAAoB,EAC7C,KAAK,0BAA0B,IAAI,yBAAyB,GAC7D,MAAM,uCAAuC,CAAC"}
@@ -7,4 +7,10 @@
7
7
  * @position Public API entry point
8
8
  */
9
9
  export { ContextMenu } from "./ContextMenu.js";
10
- export { DropdownMenuItem as ContextMenuItem } from "../DropdownMenu/DropdownMenuItem.js";
10
+ export { DropdownMenuItem as ContextMenuItem } from "../DropdownMenu/DropdownMenuItem.js";
11
+
12
+ // Selectable items work inside a ContextMenu too — re-exported under the
13
+ // ContextMenu name for a coherent API.
14
+ export { DropdownMenuCheckboxItem as ContextMenuCheckboxItem } from "../DropdownMenu/DropdownMenuCheckboxItem.js";
15
+ export { DropdownMenuRadioGroup as ContextMenuRadioGroup } from "../DropdownMenu/DropdownMenuRadioGroup.js";
16
+ export { DropdownMenuRadioItem as ContextMenuRadioItem } from "../DropdownMenu/DropdownMenuRadioItem.js";
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAS,KAAK,WAAW,EAAC,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAC;AAUtC,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAQtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyC5C,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAC5B,oBAAoB,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAMnE,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAEnE,UAAU,qBAAsB,SAAQ,SAAS;IAC/C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAsB,SAAQ,qBAAqB;IAC3D,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,yBAA0B,SAAQ,qBAAqB;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAC3B,qBAAqB,GAAG,yBAAyB,CAAC;AA+BpD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,gBAAgB,EAC5B,YAAY,EACZ,SAAS,EACT,OAAO,EACP,UAAiB,EACjB,SAAmB,EACnB,SAAS,EACT,KAAK,EACL,MAAM,EACN,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,iBAAiB,qBAwQnB;yBArRe,YAAY"}
1
+ {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenu.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,EAAS,KAAK,WAAW,EAAC,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,SAAS,CAAC;AAWtC,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAQtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAyC5C,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAC5B,oBAAoB,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAMnE,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAEnE,UAAU,qBAAsB,SAAQ,SAAS;IAC/C,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAsB,SAAQ,qBAAqB;IAC3D,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,yBAA0B,SAAQ,qBAAqB;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAC3B,qBAAqB,GAAG,yBAAyB,CAAC;AA+BpD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,gBAAgB,EAC5B,YAAY,EACZ,SAAS,EACT,OAAO,EACP,UAAiB,EACjB,SAAmB,EACnB,SAAS,EACT,KAAK,EACL,MAAM,EACN,aAAa,EAAE,MAAM,EACrB,GAAG,KAAK,EACT,EAAE,iBAAiB,qBA4QnB;yBAzRe,YAAY"}
@@ -27,6 +27,7 @@ import { usePopover } from "../Popover/usePopover.js";
27
27
  import { Button } from "../Button/index.js";
28
28
  import { Icon } from "../Icon/index.js";
29
29
  import { renderDropdownItems } from "./renderDropdownItems.js";
30
+ import { MENU_ITEM_ROLES, MENU_ITEM_SELECTOR } from "./menuItemRoles.js";
30
31
  import { DropdownMenuContext } from "./DropdownMenuContext.js";
31
32
  import { useListFocus } from "../hooks/useListFocus.js";
32
33
  import { useTypeahead } from "../hooks/useTypeahead.js";
@@ -186,21 +187,24 @@ export function DropdownMenu({
186
187
  popover.hide();
187
188
  }, [popover]);
188
189
 
189
- // Single keyboard navigation path for both modes
190
+ // Single keyboard navigation path for both modes.
191
+ // The selector matches plain items plus selectable items
192
+ // (menuitemradio/menuitemcheckbox) so lab checkbox/radio rows are reachable
193
+ // and roved to alongside plain items — not just role="menuitem".
190
194
  const {
191
195
  listRef,
192
196
  handleKeyDown: listNavKeyDown,
193
197
  focusFirst,
194
198
  focusItem
195
199
  } = useListFocus({
196
- itemSelector: '[role="menuitem"]:not([aria-disabled="true"])',
200
+ itemSelector: MENU_ITEM_SELECTOR,
197
201
  wrap: false,
198
202
  onEscape: closeMenu
199
203
  });
200
204
 
201
205
  // First-character typeahead over the (enabled) menu items — jump to the next
202
206
  // item whose label starts with the typed text (menus-11).
203
- const getMenuItems = useCallback(() => listRef.current ? Array.from(listRef.current.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')) : [], [listRef]);
207
+ const getMenuItems = useCallback(() => listRef.current ? Array.from(listRef.current.querySelectorAll(MENU_ITEM_SELECTOR)) : [], [listRef]);
204
208
  const typeahead = useTypeahead({
205
209
  getItemLabels: () => getMenuItems().map(el => el.textContent),
206
210
  onMatch: focusItem,
@@ -224,7 +228,7 @@ export function DropdownMenu({
224
228
  if (e.key === 'Enter' || e.key === ' ') {
225
229
  e.preventDefault();
226
230
  const focused = document.activeElement;
227
- if (focused?.getAttribute('role') === 'menuitem') {
231
+ if (focused && MENU_ITEM_ROLES.has(focused.getAttribute('role') ?? '')) {
228
232
  focused.click();
229
233
  }
230
234
  return;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @file DropdownMenuCheckboxItem.tsx
3
+ * @input React, stylex, Item + Icon + DropdownMenu context + tokens from core
4
+ * @output DropdownMenuCheckboxItem — a standalone checkable menu item.
5
+ * @position Sub-component; used inside DropdownMenu.
6
+ *
7
+ * A menu item that toggles an independent boolean (role="menuitemcheckbox").
8
+ * Unlike CheckboxInput, there is no nested native <input>: the row itself owns
9
+ * the role and aria-checked, per the WAI-ARIA menuitemcheckbox pattern.
10
+ * Keyboard navigation (arrows/typeahead) and Enter/Space activation come from
11
+ * the parent DropdownMenu's useListFocus + activation path, which matches
12
+ * menuitemcheckbox alongside plain menuitem rows.
13
+ *
14
+ * The square checkbox visual is decorative (aria-hidden) — the row owns the
15
+ * checked state. Its size is derived from the menu's item size (a `sm` menu
16
+ * gets the compact control; `md`/`lg` get the standard one) and it swaps to the
17
+ * inline-end of the row on coarse-pointer (touch) devices via CSS `order`, so
18
+ * it lands where selection toggles are conventionally placed on mobile. The
19
+ * checkmark uses the `check` icon from the active theme's icon registry.
20
+ */
21
+ import { type ReactNode } from 'react';
22
+ import { type IconType } from '../Icon';
23
+ import type { BaseProps } from '../BaseProps';
24
+ export interface DropdownMenuCheckboxItemProps extends Omit<BaseProps, 'onChange' | 'role' | 'aria-checked' | 'tabIndex'> {
25
+ /**
26
+ * Primary label text identifying the item.
27
+ */
28
+ label: ReactNode;
29
+ /**
30
+ * Secondary description text displayed below the label.
31
+ */
32
+ description?: ReactNode;
33
+ /**
34
+ * Icon to display before the label. Accepts a semantic icon name (see
35
+ * `npx astryx docs icons`) or a rendered node.
36
+ */
37
+ icon?: ReactNode | IconType;
38
+ /**
39
+ * Whether the item is checked. Controlled — pair with `onChange`.
40
+ */
41
+ value: boolean;
42
+ /**
43
+ * Callback fired with the next checked state when the item is toggled.
44
+ */
45
+ onChange?: (checked: boolean) => void;
46
+ /**
47
+ * Whether the item is disabled. Disabled items stay focusable (via
48
+ * `aria-disabled`) so they remain discoverable by keyboard and assistive
49
+ * technology, but activation is blocked.
50
+ * @default false
51
+ */
52
+ isDisabled?: boolean;
53
+ /**
54
+ * Whether toggling the item closes the menu. Checkbox items default to
55
+ * staying open so several can be toggled in a single session, unlike radio
56
+ * items which default to closing on selection.
57
+ * @default false
58
+ */
59
+ hasCloseOnSelect?: boolean;
60
+ /**
61
+ * Content to render after the label and description, such as a keyboard
62
+ * shortcut hint or badge.
63
+ */
64
+ endContent?: ReactNode;
65
+ }
66
+ /**
67
+ * A checkable dropdown menu item (role="menuitemcheckbox").
68
+ *
69
+ * Must be used inside a DropdownMenu. Toggles an independent boolean; for a
70
+ * one-of-N choice use DropdownMenuRadioGroup + DropdownMenuRadioItem instead.
71
+ *
72
+ * @example
73
+ * ```
74
+ * * import {DropdownMenuCheckboxItem} from '@astryxdesign/core/DropdownMenu';
75
+ *
76
+ * <DropdownMenu button={{label: 'View'}}>
77
+ * <DropdownMenuCheckboxItem
78
+ * label="Show archived"
79
+ * value={showArchived}
80
+ * onChange={setShowArchived}
81
+ * />
82
+ * </DropdownMenu>
83
+ * ```
84
+ */
85
+ export declare function DropdownMenuCheckboxItem({ label, description, icon, value, onChange, isDisabled, hasCloseOnSelect, endContent, xstyle, className, style, ...rest }: DropdownMenuCheckboxItemProps): import("react").JSX.Element;
86
+ export declare namespace DropdownMenuCheckboxItem {
87
+ var displayName: string;
88
+ }
89
+ //# sourceMappingURL=DropdownMenuCheckboxItem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropdownMenuCheckboxItem.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuCheckboxItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAc,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAElD,OAAO,EAAuB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAY5D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA6D5C,MAAM,WAAW,6BAA8B,SAAQ,IAAI,CACzD,SAAS,EACT,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAClD;IACC;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,WAAW,EACX,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,UAAkB,EAClB,gBAAwB,EACxB,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,6BAA6B,+BAwD/B;yBArEe,wBAAwB"}
@@ -0,0 +1,170 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file DropdownMenuCheckboxItem.tsx
7
+ * @input React, stylex, Item + Icon + DropdownMenu context + tokens from core
8
+ * @output DropdownMenuCheckboxItem — a standalone checkable menu item.
9
+ * @position Sub-component; used inside DropdownMenu.
10
+ *
11
+ * A menu item that toggles an independent boolean (role="menuitemcheckbox").
12
+ * Unlike CheckboxInput, there is no nested native <input>: the row itself owns
13
+ * the role and aria-checked, per the WAI-ARIA menuitemcheckbox pattern.
14
+ * Keyboard navigation (arrows/typeahead) and Enter/Space activation come from
15
+ * the parent DropdownMenu's useListFocus + activation path, which matches
16
+ * menuitemcheckbox alongside plain menuitem rows.
17
+ *
18
+ * The square checkbox visual is decorative (aria-hidden) — the row owns the
19
+ * checked state. Its size is derived from the menu's item size (a `sm` menu
20
+ * gets the compact control; `md`/`lg` get the standard one) and it swaps to the
21
+ * inline-end of the row on coarse-pointer (touch) devices via CSS `order`, so
22
+ * it lands where selection toggles are conventionally placed on mobile. The
23
+ * checkmark uses the `check` icon from the active theme's icon registry.
24
+ */
25
+ import { useCallback } from 'react';
26
+ import * as stylex from '@stylexjs/stylex';
27
+ import { Icon, renderIconSlot } from "../Icon/index.js";
28
+ import { Item } from "../Item/index.js";
29
+ import { useDropdownMenuContext } from "./DropdownMenuContext.js";
30
+ import "../theme/tokens.stylex.js";
31
+ import { colorVars, spacingVars, radiusVars, durationVars, easeVars, borderVars } from "../theme/tokens.stylex.js";
32
+ import { mergeProps, themeProps } from "../utils/index.js";
33
+ import { jsx as _jsx } from "react/jsx-runtime";
34
+ const styles = {
35
+ root: {
36
+ kzqmXN: "xh8yej3",
37
+ kaIpWk: "x1ws5lxm",
38
+ kMwMTN: "x1tgivj0",
39
+ kWkggS: "xjbqb8w x1c52tdz x1uwx2nv",
40
+ kkrTdU: "x1ypdohk",
41
+ kI3sdo: "x1a2a7pz",
42
+ $$css: true
43
+ },
44
+ disabled: {
45
+ kSiTet: "xbyyjgo",
46
+ kkrTdU: "x1h6gzvc",
47
+ $$css: true
48
+ },
49
+ box: {
50
+ k1xSpc: "x78zum5",
51
+ kGNEyG: "x6s0dn4",
52
+ kjj79g: "xl56j7k",
53
+ kmuXW: "x2lah0s",
54
+ kB7OPa: "x9f619",
55
+ kMzoRj: "x1litavf",
56
+ ksu8eU: "x1y0btm7",
57
+ kaIpWk: "xx3sua9",
58
+ kMwMTN: "x17wrial",
59
+ k1ekBW: "xts7igz",
60
+ kIyJzY: "xuedmi6 x12w9bfk",
61
+ kAMwcw: "xlr8y92",
62
+ kayTVb: "x1g77sc7 xozvky",
63
+ keTefX: "x1lziwak x1jymrmb",
64
+ $$css: true
65
+ },
66
+ unchecked: {
67
+ kVAM5u: "xvy26l8",
68
+ kWkggS: "x10xzikg",
69
+ $$css: true
70
+ },
71
+ checked: {
72
+ kVAM5u: "xad5do",
73
+ kWkggS: "x1ewilqj",
74
+ $$css: true
75
+ }
76
+ };
77
+ const boxSizeStyles = {
78
+ sm: {
79
+ kzqmXN: "x1xp8n7a",
80
+ kZKoxP: "xmix8c7",
81
+ $$css: true
82
+ },
83
+ md: {
84
+ kzqmXN: "x17z2i9w",
85
+ kZKoxP: "x17rw0jw",
86
+ $$css: true
87
+ }
88
+ };
89
+ /**
90
+ * A checkable dropdown menu item (role="menuitemcheckbox").
91
+ *
92
+ * Must be used inside a DropdownMenu. Toggles an independent boolean; for a
93
+ * one-of-N choice use DropdownMenuRadioGroup + DropdownMenuRadioItem instead.
94
+ *
95
+ * @example
96
+ * ```
97
+ * * import {DropdownMenuCheckboxItem} from '@astryxdesign/core/DropdownMenu';
98
+ *
99
+ * <DropdownMenu button={{label: 'View'}}>
100
+ * <DropdownMenuCheckboxItem
101
+ * label="Show archived"
102
+ * value={showArchived}
103
+ * onChange={setShowArchived}
104
+ * />
105
+ * </DropdownMenu>
106
+ * ```
107
+ */
108
+ export function DropdownMenuCheckboxItem({
109
+ label,
110
+ description,
111
+ icon,
112
+ value,
113
+ onChange,
114
+ isDisabled = false,
115
+ hasCloseOnSelect = false,
116
+ endContent,
117
+ xstyle,
118
+ className,
119
+ style,
120
+ ...rest
121
+ }) {
122
+ const ctx = useDropdownMenuContext();
123
+ const menuSize = ctx?.menuSize ?? 'md';
124
+ const controlSize = menuSize === 'sm' ? 'sm' : 'md';
125
+ const handleClick = useCallback(() => {
126
+ if (isDisabled) {
127
+ return;
128
+ }
129
+ onChange?.(!value);
130
+ if (hasCloseOnSelect) {
131
+ ctx?.closeMenu();
132
+ }
133
+ }, [isDisabled, onChange, value, hasCloseOnSelect, ctx]);
134
+ return /*#__PURE__*/_jsx(Item, {
135
+ ...rest,
136
+ role: "menuitemcheckbox",
137
+ "aria-checked": value,
138
+ tabIndex: isDisabled ? undefined : -1,
139
+ marker: /*#__PURE__*/_jsx("span", {
140
+ "aria-hidden": "true",
141
+ ...mergeProps(themeProps('dropdown-menu-checkbox', {
142
+ size: controlSize,
143
+ checked: value ? 'checked' : null,
144
+ disabled: isDisabled ? 'disabled' : null
145
+ }), stylex.props(styles.box, boxSizeStyles[controlSize], value ? styles.checked : styles.unchecked)),
146
+ children: value && /*#__PURE__*/_jsx(Icon, {
147
+ icon: "check",
148
+ size: "sm",
149
+ color: "inherit"
150
+ })
151
+ }),
152
+ startContent: icon ? renderIconSlot(icon, {
153
+ size: 'sm',
154
+ color: 'secondary'
155
+ }) : undefined,
156
+ label: label,
157
+ description: description,
158
+ endContent: endContent,
159
+ onClick: handleClick,
160
+ isDisabled: isDisabled,
161
+ xstyle: [styles.root, isDisabled && styles.disabled, xstyle],
162
+ ...mergeProps(themeProps('dropdown-menu-item', {
163
+ size: menuSize
164
+ }), {
165
+ className,
166
+ style
167
+ })
168
+ });
169
+ }
170
+ DropdownMenuCheckboxItem.displayName = 'DropdownMenuCheckboxItem';
@@ -1,8 +1,10 @@
1
+ /** Menu size, derived from the trigger button size. */
2
+ export type DropdownMenuSize = 'sm' | 'md' | 'lg';
1
3
  export interface DropdownMenuContextValue {
2
4
  /** Close the menu and return focus to trigger */
3
5
  closeMenu: () => void;
4
6
  /** Menu size derived from button size */
5
- menuSize: 'sm' | 'md' | 'lg';
7
+ menuSize: DropdownMenuSize;
6
8
  }
7
9
  export declare const DropdownMenuContext: import("react").Context<DropdownMenuContextValue | null>;
8
10
  /**
@@ -10,4 +12,18 @@ export declare const DropdownMenuContext: import("react").Context<DropdownMenuCo
10
12
  * Returns null outside of a DropdownMenu.
11
13
  */
12
14
  export declare function useDropdownMenuContext(): DropdownMenuContextValue | null;
15
+ export interface DropdownMenuRadioGroupContextValue {
16
+ /** The currently selected value in the group. */
17
+ value: string | undefined;
18
+ /** Select a value. Called by a DropdownMenuRadioItem on activation. */
19
+ onChange: (value: string) => void;
20
+ /** Whether selecting an item should close the menu. @default true */
21
+ hasCloseOnSelect: boolean;
22
+ }
23
+ export declare const DropdownMenuRadioGroupContext: import("react").Context<DropdownMenuRadioGroupContextValue | null>;
24
+ /**
25
+ * Read the enclosing radio group's selection state.
26
+ * Returns null when used outside a DropdownMenuRadioGroup.
27
+ */
28
+ export declare function useDropdownMenuRadioGroupContext(): DropdownMenuRadioGroupContextValue | null;
13
29
  //# sourceMappingURL=DropdownMenuContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenuContext.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuContext.tsx"],"names":[],"mappings":"AAgBA,MAAM,WAAW,wBAAwB;IACvC,iDAAiD;IACjD,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,yCAAyC;IACzC,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC9B;AAED,eAAO,MAAM,mBAAmB,0DACsB,CAAC;AAGvD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,wBAAwB,GAAG,IAAI,CAExE"}
1
+ {"version":3,"file":"DropdownMenuContext.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuContext.tsx"],"names":[],"mappings":"AAgBA,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,iDAAiD;IACjD,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,yCAAyC;IACzC,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,eAAO,MAAM,mBAAmB,0DACsB,CAAC;AAGvD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,wBAAwB,GAAG,IAAI,CAExE;AAMD,MAAM,WAAW,kCAAkC;IACjD,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,6BAA6B,oEACsB,CAAC;AAGjE;;;GAGG;AACH,wBAAgB,gCAAgC,IAAI,kCAAkC,GAAG,IAAI,CAE5F"}
@@ -12,6 +12,9 @@
12
12
  * items don't need to register themselves.
13
13
  */
14
14
  import { createContext, use } from 'react';
15
+
16
+ /** Menu size, derived from the trigger button size. */
17
+
15
18
  export const DropdownMenuContext = /*#__PURE__*/createContext(null);
16
19
  DropdownMenuContext.displayName = 'DropdownMenuContext';
17
20
 
@@ -21,4 +24,19 @@ DropdownMenuContext.displayName = 'DropdownMenuContext';
21
24
  */
22
25
  export function useDropdownMenuContext() {
23
26
  return use(DropdownMenuContext);
27
+ }
28
+
29
+ // =============================================================================
30
+ // Radio group coordination
31
+ // =============================================================================
32
+
33
+ export const DropdownMenuRadioGroupContext = /*#__PURE__*/createContext(null);
34
+ DropdownMenuRadioGroupContext.displayName = 'DropdownMenuRadioGroupContext';
35
+
36
+ /**
37
+ * Read the enclosing radio group's selection state.
38
+ * Returns null when used outside a DropdownMenuRadioGroup.
39
+ */
40
+ export function useDropdownMenuRadioGroupContext() {
41
+ return use(DropdownMenuRadioGroupContext);
24
42
  }