@xmesh/system-design 0.0.4 → 0.0.6

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 (290) hide show
  1. package/README.md +2 -1
  2. package/custom-elements.json +18382 -0
  3. package/dist/lit/components/alert/index.d.ts +5 -0
  4. package/dist/lit/components/alert/index.js +8 -2
  5. package/dist/lit/components/alert/index.styles.js +215 -0
  6. package/dist/lit/components/app-bar/index.d.ts +1 -0
  7. package/dist/lit/components/app-bar/index.js +4 -2
  8. package/dist/lit/components/app-bar/index.styles.js +94 -0
  9. package/dist/lit/components/artifact/index.d.ts +5 -0
  10. package/dist/lit/components/artifact/index.js +14 -10
  11. package/dist/lit/components/artifact/index.styles.js +180 -0
  12. package/dist/lit/components/autocomplete/index.d.ts +5 -0
  13. package/dist/lit/components/autocomplete/index.js +11 -2
  14. package/dist/lit/components/autocomplete/index.styles.js +185 -0
  15. package/dist/lit/components/avatar/index.d.ts +1 -0
  16. package/dist/lit/components/avatar/index.js +4 -2
  17. package/dist/lit/components/avatar/index.styles.js +76 -0
  18. package/dist/lit/components/avatar-group/index.d.ts +1 -0
  19. package/dist/lit/components/avatar-group/index.js +4 -2
  20. package/dist/lit/components/avatar-group/index.styles.js +74 -0
  21. package/dist/lit/components/badge/index.d.ts +1 -0
  22. package/dist/lit/components/badge/index.js +4 -2
  23. package/dist/lit/components/badge/index.styles.js +86 -0
  24. package/dist/lit/components/brand-mark/index.js +8 -9
  25. package/dist/lit/components/brand-mark/index.styles.js +123 -0
  26. package/dist/lit/components/breadcrumbs/index.d.ts +1 -0
  27. package/dist/lit/components/breadcrumbs/index.js +5 -4
  28. package/dist/lit/components/breadcrumbs/index.styles.js +105 -0
  29. package/dist/lit/components/bubble/index.d.ts +4 -0
  30. package/dist/lit/components/bubble/index.js +14 -11
  31. package/dist/lit/components/bubble/index.styles.js +196 -0
  32. package/dist/lit/components/button/index.d.ts +1 -0
  33. package/dist/lit/components/button/index.js +6 -13
  34. package/dist/lit/components/button/index.styles.js +356 -0
  35. package/dist/lit/components/card/index.d.ts +1 -0
  36. package/dist/lit/components/card/index.js +4 -2
  37. package/dist/lit/components/card/index.styles.js +113 -0
  38. package/dist/lit/components/chat/index.d.ts +6 -0
  39. package/dist/lit/components/chat/index.js +15 -10
  40. package/dist/lit/components/chat/index.styles.js +306 -0
  41. package/dist/lit/components/checkbox/index.d.ts +1 -0
  42. package/dist/lit/components/checkbox/index.js +7 -4
  43. package/dist/lit/components/checkbox/index.styles.js +140 -0
  44. package/dist/lit/components/chip/index.d.ts +5 -0
  45. package/dist/lit/components/chip/index.js +8 -2
  46. package/dist/lit/components/chip/index.styles.js +159 -0
  47. package/dist/lit/components/chip-group/index.d.ts +4 -0
  48. package/dist/lit/components/chip-group/index.js +7 -2
  49. package/dist/lit/components/chip-group/index.styles.js +33 -0
  50. package/dist/lit/components/code/index.d.ts +1 -0
  51. package/dist/lit/components/code/index.js +4 -3
  52. package/dist/lit/components/code/index.styles.js +56 -0
  53. package/dist/lit/components/composer/index.d.ts +6 -0
  54. package/dist/lit/components/composer/index.js +15 -10
  55. package/dist/lit/components/composer/index.styles.js +562 -0
  56. package/dist/lit/components/data-table/index.css +18 -0
  57. package/dist/lit/components/data-table/index.d.ts +30 -0
  58. package/dist/lit/components/data-table/index.js +120 -34
  59. package/dist/lit/components/data-table/index.styles.js +198 -0
  60. package/dist/lit/components/date-range/index.d.ts +4 -0
  61. package/dist/lit/components/date-range/index.js +7 -2
  62. package/dist/lit/components/date-range/index.styles.js +338 -0
  63. package/dist/lit/components/dialog/index.d.ts +4 -0
  64. package/dist/lit/components/dialog/index.js +7 -2
  65. package/dist/lit/components/dialog/index.styles.js +138 -0
  66. package/dist/lit/components/divider/index.d.ts +1 -0
  67. package/dist/lit/components/divider/index.js +4 -2
  68. package/dist/lit/components/divider/index.styles.js +41 -0
  69. package/dist/lit/components/empty-state/index.d.ts +1 -0
  70. package/dist/lit/components/empty-state/index.js +4 -2
  71. package/dist/lit/components/empty-state/index.styles.js +83 -0
  72. package/dist/lit/components/expansion-panel/index.d.ts +5 -0
  73. package/dist/lit/components/expansion-panel/index.js +8 -2
  74. package/dist/lit/components/expansion-panel/index.styles.js +134 -0
  75. package/dist/lit/components/field/index.d.ts +4 -0
  76. package/dist/lit/components/field/index.js +9 -4
  77. package/dist/lit/components/field/index.styles.js +237 -0
  78. package/dist/lit/components/file-input/index.d.ts +4 -0
  79. package/dist/lit/components/file-input/index.js +10 -4
  80. package/dist/lit/components/file-input/index.styles.js +271 -0
  81. package/dist/lit/components/form/index.d.ts +4 -0
  82. package/dist/lit/components/form/index.js +7 -2
  83. package/dist/lit/components/form/index.styles.js +43 -0
  84. package/dist/lit/components/grid/index.d.ts +1 -0
  85. package/dist/lit/components/grid/index.js +4 -2
  86. package/dist/lit/components/grid/index.styles.js +67 -0
  87. package/dist/lit/components/kbd/index.d.ts +1 -0
  88. package/dist/lit/components/kbd/index.js +4 -2
  89. package/dist/lit/components/kbd/index.styles.js +49 -0
  90. package/dist/lit/components/list/index.d.ts +4 -0
  91. package/dist/lit/components/list/index.js +7 -2
  92. package/dist/lit/components/list/index.styles.js +29 -0
  93. package/dist/lit/components/list-item/index.d.ts +1 -0
  94. package/dist/lit/components/list-item/index.js +4 -2
  95. package/dist/lit/components/list-item/index.styles.js +133 -0
  96. package/dist/lit/components/menu/index.d.ts +8 -0
  97. package/dist/lit/components/menu/index.js +11 -3
  98. package/dist/lit/components/menu/index.styles.js +108 -0
  99. package/dist/lit/components/multi-select/index.css +156 -0
  100. package/dist/lit/components/multi-select/index.d.ts +70 -0
  101. package/dist/lit/components/multi-select/index.js +497 -0
  102. package/dist/lit/components/multi-select/index.styles.js +170 -0
  103. package/dist/lit/components/navigation-drawer/index.d.ts +4 -0
  104. package/dist/lit/components/navigation-drawer/index.js +7 -2
  105. package/dist/lit/components/navigation-drawer/index.styles.js +128 -0
  106. package/dist/lit/components/overlay/index.d.ts +5 -0
  107. package/dist/lit/components/overlay/index.js +8 -2
  108. package/dist/lit/components/overlay/index.styles.js +185 -0
  109. package/dist/lit/components/pagination/index.d.ts +17 -0
  110. package/dist/lit/components/pagination/index.js +53 -8
  111. package/dist/lit/components/pagination/index.styles.js +116 -0
  112. package/dist/lit/components/popover/index.d.ts +5 -0
  113. package/dist/lit/components/popover/index.js +8 -2
  114. package/dist/lit/components/popover/index.styles.js +48 -0
  115. package/dist/lit/components/primitives/index.js +8 -9
  116. package/dist/lit/components/primitives/index.styles.js +518 -0
  117. package/dist/lit/components/progress/index.d.ts +1 -0
  118. package/dist/lit/components/progress/index.js +4 -2
  119. package/dist/lit/components/progress/index.styles.js +157 -0
  120. package/dist/lit/components/radio-group/index.d.ts +5 -0
  121. package/dist/lit/components/radio-group/index.js +10 -6
  122. package/dist/lit/components/radio-group/index.styles.js +192 -0
  123. package/dist/lit/components/select/index.d.ts +4 -0
  124. package/dist/lit/components/select/index.js +10 -2
  125. package/dist/lit/components/select/index.styles.js +165 -0
  126. package/dist/lit/components/sidebar-item/index.js +8 -9
  127. package/dist/lit/components/sidebar-item/index.styles.js +147 -0
  128. package/dist/lit/components/skeleton/index.d.ts +1 -0
  129. package/dist/lit/components/skeleton/index.js +4 -2
  130. package/dist/lit/components/skeleton/index.styles.js +95 -0
  131. package/dist/lit/components/slider/index.d.ts +5 -0
  132. package/dist/lit/components/slider/index.js +11 -4
  133. package/dist/lit/components/slider/index.styles.js +185 -0
  134. package/dist/lit/components/snackbar/index.d.ts +4 -0
  135. package/dist/lit/components/snackbar/index.js +14 -10
  136. package/dist/lit/components/snackbar/index.styles.js +293 -0
  137. package/dist/lit/components/stack/index.d.ts +1 -0
  138. package/dist/lit/components/stack/index.js +4 -2
  139. package/dist/lit/components/stack/index.styles.js +55 -0
  140. package/dist/lit/components/switch/index.d.ts +1 -0
  141. package/dist/lit/components/switch/index.js +7 -4
  142. package/dist/lit/components/switch/index.styles.js +140 -0
  143. package/dist/lit/components/table/index.d.ts +1 -0
  144. package/dist/lit/components/table/index.js +4 -2
  145. package/dist/lit/components/table/index.styles.js +99 -0
  146. package/dist/lit/components/tabs/index.d.ts +4 -0
  147. package/dist/lit/components/tabs/index.js +8 -4
  148. package/dist/lit/components/tabs/index.styles.js +130 -0
  149. package/dist/lit/components/text-field/index.d.ts +1 -0
  150. package/dist/lit/components/text-field/index.js +7 -2
  151. package/dist/lit/components/text-field/index.styles.js +104 -0
  152. package/dist/lit/components/textarea/index.d.ts +1 -0
  153. package/dist/lit/components/textarea/index.js +7 -2
  154. package/dist/lit/components/textarea/index.styles.js +69 -0
  155. package/dist/lit/components/tooltip/index.d.ts +1 -0
  156. package/dist/lit/components/tooltip/index.js +4 -2
  157. package/dist/lit/components/tooltip/index.styles.js +51 -0
  158. package/dist/lit/components/validation/index.d.ts +6 -0
  159. package/dist/lit/components/validation/index.js +15 -10
  160. package/dist/lit/components/validation/index.styles.js +400 -0
  161. package/dist/lit/index.d.ts +1 -0
  162. package/dist/lit/index.js +1 -0
  163. package/dist/react/XmAlert.d.ts +99 -0
  164. package/dist/react/XmAlert.js +47 -0
  165. package/dist/react/XmAppBar.d.ts +59 -0
  166. package/dist/react/XmAppBar.js +34 -0
  167. package/dist/react/XmArtifact.d.ts +113 -0
  168. package/dist/react/XmArtifact.js +48 -0
  169. package/dist/react/XmArtifactChip.d.ts +56 -0
  170. package/dist/react/XmArtifactChip.js +32 -0
  171. package/dist/react/XmAutocomplete.d.ts +153 -0
  172. package/dist/react/XmAutocomplete.js +70 -0
  173. package/dist/react/XmAvatar.d.ts +71 -0
  174. package/dist/react/XmAvatar.js +40 -0
  175. package/dist/react/XmAvatarGroup.d.ts +59 -0
  176. package/dist/react/XmAvatarGroup.js +34 -0
  177. package/dist/react/XmBadge.d.ts +67 -0
  178. package/dist/react/XmBadge.js +38 -0
  179. package/dist/react/XmBrandGlyph.d.ts +46 -0
  180. package/dist/react/XmBrandGlyph.js +24 -0
  181. package/dist/react/XmBrandMark.d.ts +71 -0
  182. package/dist/react/XmBrandMark.js +40 -0
  183. package/dist/react/XmBreadcrumbs.d.ts +56 -0
  184. package/dist/react/XmBreadcrumbs.js +32 -0
  185. package/dist/react/XmBubble.d.ts +69 -0
  186. package/dist/react/XmBubble.js +38 -0
  187. package/dist/react/XmBubbleActions.d.ts +59 -0
  188. package/dist/react/XmBubbleActions.js +34 -0
  189. package/dist/react/XmBubbleGroup.d.ts +82 -0
  190. package/dist/react/XmBubbleGroup.js +38 -0
  191. package/dist/react/XmButton.d.ts +89 -0
  192. package/dist/react/XmButton.js +48 -0
  193. package/dist/react/XmCard.d.ts +59 -0
  194. package/dist/react/XmCard.js +34 -0
  195. package/dist/react/XmChatShell.d.ts +110 -0
  196. package/dist/react/XmChatShell.js +48 -0
  197. package/dist/react/XmCheckbox.d.ts +145 -0
  198. package/dist/react/XmCheckbox.js +60 -0
  199. package/dist/react/XmChip.d.ts +99 -0
  200. package/dist/react/XmChip.js +48 -0
  201. package/dist/react/XmChipGroup.d.ts +79 -0
  202. package/dist/react/XmChipGroup.js +36 -0
  203. package/dist/react/XmCode.d.ts +55 -0
  204. package/dist/react/XmCode.js +32 -0
  205. package/dist/react/XmComposer.d.ts +123 -0
  206. package/dist/react/XmComposer.js +56 -0
  207. package/dist/react/XmDataTable.d.ts +125 -0
  208. package/dist/react/XmDataTable.js +66 -0
  209. package/dist/react/XmDateRange.d.ts +93 -0
  210. package/dist/react/XmDateRange.js +42 -0
  211. package/dist/react/XmDialog.d.ts +87 -0
  212. package/dist/react/XmDialog.js +41 -0
  213. package/dist/react/XmDivider.d.ts +55 -0
  214. package/dist/react/XmDivider.js +32 -0
  215. package/dist/react/XmEmptyState.d.ts +61 -0
  216. package/dist/react/XmEmptyState.js +34 -0
  217. package/dist/react/XmExpansionPanel.d.ts +101 -0
  218. package/dist/react/XmExpansionPanel.js +49 -0
  219. package/dist/react/XmFileInput.d.ts +151 -0
  220. package/dist/react/XmFileInput.js +70 -0
  221. package/dist/react/XmFileValidationBlock.d.ts +111 -0
  222. package/dist/react/XmFileValidationBlock.js +50 -0
  223. package/dist/react/XmForm.d.ts +91 -0
  224. package/dist/react/XmForm.js +38 -0
  225. package/dist/react/XmGrid.d.ts +59 -0
  226. package/dist/react/XmGrid.js +34 -0
  227. package/dist/react/XmKbd.d.ts +46 -0
  228. package/dist/react/XmKbd.js +24 -0
  229. package/dist/react/XmList.d.ts +83 -0
  230. package/dist/react/XmList.js +38 -0
  231. package/dist/react/XmListItem.d.ts +67 -0
  232. package/dist/react/XmListItem.js +38 -0
  233. package/dist/react/XmMenu.d.ts +98 -0
  234. package/dist/react/XmMenu.js +46 -0
  235. package/dist/react/XmMenuItem.d.ts +63 -0
  236. package/dist/react/XmMenuItem.js +36 -0
  237. package/dist/react/XmMultiSelect.d.ts +161 -0
  238. package/dist/react/XmMultiSelect.js +74 -0
  239. package/dist/react/XmNavigationDrawer.d.ts +93 -0
  240. package/dist/react/XmNavigationDrawer.js +42 -0
  241. package/dist/react/XmOverlay.d.ts +120 -0
  242. package/dist/react/XmOverlay.js +56 -0
  243. package/dist/react/XmPagination.d.ts +117 -0
  244. package/dist/react/XmPagination.js +58 -0
  245. package/dist/react/XmPopover.d.ts +90 -0
  246. package/dist/react/XmPopover.js +42 -0
  247. package/dist/react/XmProgress.d.ts +75 -0
  248. package/dist/react/XmProgress.js +42 -0
  249. package/dist/react/XmRadio.d.ts +88 -0
  250. package/dist/react/XmRadio.js +42 -0
  251. package/dist/react/XmRadioGroup.d.ts +139 -0
  252. package/dist/react/XmRadioGroup.js +58 -0
  253. package/dist/react/XmSelect.d.ts +152 -0
  254. package/dist/react/XmSelect.js +70 -0
  255. package/dist/react/XmSidebarItem.d.ts +75 -0
  256. package/dist/react/XmSidebarItem.js +42 -0
  257. package/dist/react/XmSkeleton.d.ts +71 -0
  258. package/dist/react/XmSkeleton.js +40 -0
  259. package/dist/react/XmSlider.d.ts +160 -0
  260. package/dist/react/XmSlider.js +76 -0
  261. package/dist/react/XmSnackbar.d.ts +110 -0
  262. package/dist/react/XmSnackbar.js +51 -0
  263. package/dist/react/XmStack.d.ts +71 -0
  264. package/dist/react/XmStack.js +40 -0
  265. package/dist/react/XmSwitch.d.ts +136 -0
  266. package/dist/react/XmSwitch.js +58 -0
  267. package/dist/react/XmTab.d.ts +79 -0
  268. package/dist/react/XmTab.js +38 -0
  269. package/dist/react/XmTabPanel.d.ts +55 -0
  270. package/dist/react/XmTabPanel.js +32 -0
  271. package/dist/react/XmTable.d.ts +57 -0
  272. package/dist/react/XmTable.js +32 -0
  273. package/dist/react/XmTabs.d.ts +82 -0
  274. package/dist/react/XmTabs.js +38 -0
  275. package/dist/react/XmTextField.d.ts +147 -0
  276. package/dist/react/XmTextField.js +62 -0
  277. package/dist/react/XmTextarea.d.ts +155 -0
  278. package/dist/react/XmTextarea.js +66 -0
  279. package/dist/react/XmTooltip.d.ts +67 -0
  280. package/dist/react/XmTooltip.js +38 -0
  281. package/dist/react/index.d.ts +59 -0
  282. package/dist/react/index.js +59 -0
  283. package/dist/react/react-utils.js +67 -0
  284. package/package.json +37 -9
  285. package/styles/_base-typography.css +86 -0
  286. package/styles/_primitives.css +54 -0
  287. package/styles/_reset.css +58 -0
  288. package/styles/base.css +23 -0
  289. package/vscode.css-custom-data.json +6 -0
  290. package/vscode.html-custom-data.json +979 -0
@@ -0,0 +1,497 @@
1
+ /*
2
+ multi-select/index.ts — <xm-multi-select>, a multiple-value picker (Component-
3
+ Capability Epic 2, ADR 0014).
4
+
5
+ A DEDICATED element (not an xm-select mode — xm-select stays single-value, ADR
6
+ 0014) that composes the inherited foundations and re-implements none of them:
7
+ • XmField — label / helper / error / required / disabled / focus ring / form
8
+ association / ARIA association chrome (AD-7).
9
+ • xm-overlay — the anchored, non-modal listbox panel (AD-5 / AD-12); driven
10
+ through its PUBLIC API only (mode / tier / placement / .anchor / .opener /
11
+ xm-overlay-close). Its shadow root is never touched.
12
+ • xm-checkbox — the per-option selected indicator, rendered PRESENTATIONALLY
13
+ (`inert` + pointer-events:none): the row owns the click and the a11y, the
14
+ checkbox only reflects `checked`. A presentational checkbox is never toggled
15
+ so it never goes dirty, and XmField's uncontrolled-first re-seed keeps its
16
+ box in sync with `?checked` on every render.
17
+ • xm-text-field — an optional in-panel search that filters by label substring.
18
+
19
+ Value contract (AD-6a / AD-8a): form-associated, multiple selection. `selectedValues`
20
+ returns the typed Array<primitive>; `change` carries that array in detail.value
21
+ (never option objects). Form serialization is multiple FormData entries under `name`
22
+ (matching native <select multiple>); the inherited string `value` mirrors a
23
+ comma-joined view so plain consumers still read something stable.
24
+
25
+ `max` caps the selection: at the cap, unselected options disable (can't grow) while
26
+ selected options stay removable.
27
+
28
+ Keyboard (WAI-ARIA listbox APG, AD-9a): focus stays on the combobox trigger,
29
+ consistent with xm-select. Closed → Enter/Space/↓ opens; open → ↑/↓ move the active
30
+ option (skipping disabled + filtered-out), Home/End jump, type-ahead matches labels,
31
+ Enter/Space TOGGLE the active option and keep the list open (multi), Esc closes
32
+ (routed through the overlay's innermost-Esc). The search box is pointer-operated;
33
+ ↑/↓/Enter inside it bridge focus back to the trigger so pointer+keyboard mix cleanly.
34
+
35
+ Shadow DOM. Lit is a bare `import` (peer dep). The composed element modules are
36
+ imported for their registration side-effects, so importing only this element is a
37
+ self-sufficient drop-in.
38
+ */
39
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
40
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
41
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
42
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
43
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
44
+ };
45
+ import { html, nothing } from "lit";
46
+ import { customElement, property, state, query } from "lit/decorators.js";
47
+ import { XmField } from "../field/index.js";
48
+ import "../overlay/index.js";
49
+ import "../checkbox/index.js";
50
+ import "../text-field/index.js";
51
+ import primitivesSheets from "../primitives/index.styles.js";
52
+ import fieldSheets from "../field/index.styles.js";
53
+ import multiSelectSheets from "./index.styles.js";
54
+ /**
55
+ * @fires change - Fired on every toggle; `detail.value` is the full selection as an Array<primitive>.
56
+ */
57
+ let XmMultiSelect = class XmMultiSelect extends XmField {
58
+ constructor() {
59
+ super(...arguments);
60
+ /** The option model — `{ label, value, disabled? }` shared with xm-select / radio-group. */
61
+ this.options = [];
62
+ /** Shown on the trigger when nothing is selected. */
63
+ this.placeholder = "Select…";
64
+ /** Selection cap (0 = uncapped). At the cap, unselected options disable; selected stay removable. */
65
+ this.max = 0;
66
+ /** Render the in-panel search filter. */
67
+ this.searchable = true;
68
+ this._open = false;
69
+ this._activeIndex = -1;
70
+ this._selected = new Set();
71
+ this._query = "";
72
+ this._typeahead = "";
73
+ this._typeaheadTimer = 0;
74
+ // ── Open / close ────────────────────────────────────────────────────
75
+ this._onDocPointerDown = (e) => {
76
+ if (!this._open)
77
+ return;
78
+ if (!e.composedPath().includes(this))
79
+ this._closeList(false);
80
+ };
81
+ this._onOverlayClose = (e) => {
82
+ const detail = e.detail;
83
+ if (this._open) {
84
+ this._open = false;
85
+ this._activeIndex = -1;
86
+ this._query = "";
87
+ document.removeEventListener("pointerdown", this._onDocPointerDown, true);
88
+ if (detail?.reason !== "escape")
89
+ this._control?.focus();
90
+ }
91
+ };
92
+ // ── Keyboard (listbox APG) ──────────────────────────────────────────
93
+ this._onControlKeydown = (e) => {
94
+ if (this.nonInteractive)
95
+ return;
96
+ if (!this._open) {
97
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
98
+ e.preventDefault();
99
+ // Opening must not also submit a surrounding xm-form.
100
+ e.stopPropagation();
101
+ this._openList();
102
+ }
103
+ return;
104
+ }
105
+ this._onListKeydown(e);
106
+ };
107
+ // Inside the pointer-operated search box: bridge arrow/enter back to the trigger so
108
+ // a keyboard user who landed in the search can navigate + toggle without Tab gymnastics.
109
+ this._onSearchKeydown = (e) => {
110
+ switch (e.key) {
111
+ case "ArrowDown":
112
+ e.preventDefault();
113
+ this._control?.focus();
114
+ this._activeIndex = this._nextNavigable(this._activeIndex, 1);
115
+ break;
116
+ case "ArrowUp":
117
+ e.preventDefault();
118
+ this._control?.focus();
119
+ this._activeIndex = this._nextNavigable(this._activeIndex, -1);
120
+ break;
121
+ case "Enter":
122
+ e.preventDefault();
123
+ e.stopPropagation();
124
+ if (this._activeIndex >= 0)
125
+ this._toggleIndex(this._activeIndex);
126
+ break;
127
+ }
128
+ };
129
+ this._onSearchInput = (e) => {
130
+ this._query = e.detail?.value ?? "";
131
+ // Keep the active option valid after the visible set narrows.
132
+ if (this._activeIndex >= 0 && !this._navigable.includes(this._activeIndex)) {
133
+ this._activeIndex = this._navigable[0] ?? -1;
134
+ }
135
+ };
136
+ }
137
+ // Adopted from build-generated sheets (gen-styles.mjs) — no runtime URL fetch
138
+ // (ADR 0012). Lit static styles don't merge on override, so subclasses re-include
139
+ // the chrome (primitives + field) sheets.
140
+ static { this.styles = [...primitivesSheets, ...fieldSheets, ...multiSelectSheets]; }
141
+ connectedCallback() {
142
+ super.connectedCallback();
143
+ this._seedFromValue();
144
+ }
145
+ willUpdate(changed) {
146
+ super.willUpdate(changed);
147
+ // When options arrive after the value seed, resolve the seed once.
148
+ if (changed.has("options") && !this._dirty && this._selected.size === 0) {
149
+ this._seedFromValue();
150
+ }
151
+ }
152
+ disconnectedCallback() {
153
+ super.disconnectedCallback();
154
+ document.removeEventListener("pointerdown", this._onDocPointerDown, true);
155
+ }
156
+ updated(changed) {
157
+ super.updated?.(changed);
158
+ if (this._open &&
159
+ changed.has("_activeIndex") &&
160
+ this._activeIndex >= 0) {
161
+ this.renderRoot
162
+ .querySelector(".multi-select__option--active")
163
+ ?.scrollIntoView({ block: "nearest" });
164
+ }
165
+ }
166
+ // ── Public value accessors ──────────────────────────────────────────
167
+ /** The selected primitives, in option order. The typed multi-value accessor
168
+ (the inherited string `value` stays a comma-joined mirror for plain consumers). */
169
+ get selectedValues() {
170
+ return this.options
171
+ .filter((o) => this._selected.has(o.value))
172
+ .map((o) => o.value);
173
+ }
174
+ // ── Selection state ─────────────────────────────────────────────────
175
+ // Seed selection from the uncontrolled `value` attribute — a comma-separated list (AD-6).
176
+ _seedFromValue() {
177
+ if (this._dirty || this.initialValue === "")
178
+ return;
179
+ const wanted = this.initialValue
180
+ .split(",")
181
+ .map((s) => s.trim())
182
+ .filter(Boolean);
183
+ if (wanted.length === 0)
184
+ return;
185
+ const next = new Set();
186
+ for (const opt of this.options) {
187
+ if (wanted.includes(String(opt.value)))
188
+ next.add(opt.value);
189
+ }
190
+ if (next.size > 0) {
191
+ this._selected = next;
192
+ this._syncForm();
193
+ }
194
+ }
195
+ get _q() {
196
+ return this._query.trim().toLowerCase();
197
+ }
198
+ _matches(opt) {
199
+ return !this._q || opt.label.toLowerCase().includes(this._q);
200
+ }
201
+ get _atCap() {
202
+ return this.max > 0 && this._selected.size >= this.max;
203
+ }
204
+ _optDisabled(opt) {
205
+ return !!opt.disabled || (this._atCap && !this._selected.has(opt.value));
206
+ }
207
+ /** Original indices of options that are visible (match the query) AND togglable. */
208
+ get _navigable() {
209
+ return this.options
210
+ .map((opt, i) => (this._matches(opt) && !this._optDisabled(opt) ? i : -1))
211
+ .filter((i) => i !== -1);
212
+ }
213
+ get _visibleCount() {
214
+ return this.options.reduce((n, o) => (this._matches(o) ? n + 1 : n), 0);
215
+ }
216
+ _nextNavigable(from, dir) {
217
+ const nav = this._navigable;
218
+ if (nav.length === 0)
219
+ return -1;
220
+ const pos = nav.indexOf(from);
221
+ if (pos === -1)
222
+ return dir === 1 ? nav[0] : nav[nav.length - 1];
223
+ return nav[(pos + dir + nav.length) % nav.length];
224
+ }
225
+ // ── Commit ──────────────────────────────────────────────────────────
226
+ _toggleValue(value) {
227
+ if (this.nonInteractive)
228
+ return;
229
+ if (this._selected.has(value)) {
230
+ this._selected.delete(value);
231
+ }
232
+ else {
233
+ if (this._atCap)
234
+ return;
235
+ this._selected.add(value);
236
+ }
237
+ this._selected = new Set(this._selected);
238
+ // Mark touched so the base's uncontrolled-first re-seed can't clobber the set.
239
+ this._dirty = true;
240
+ this._syncForm();
241
+ this._emitChange();
242
+ }
243
+ _toggleIndex(i) {
244
+ const opt = this.options[i];
245
+ if (!opt || this._optDisabled(opt))
246
+ return;
247
+ this._activeIndex = i;
248
+ this._toggleValue(opt.value);
249
+ }
250
+ // Multiple FormData entries under `name` (native <select multiple> parity, AD-6a);
251
+ // the inherited string `value` mirrors a comma-joined view.
252
+ _syncForm() {
253
+ this._value = this.selectedValues.map(String).join(",");
254
+ const fd = new FormData();
255
+ if (this.name) {
256
+ for (const v of this.selectedValues)
257
+ fd.append(this.name, String(v));
258
+ }
259
+ this.internals.setFormValue(fd);
260
+ }
261
+ _emitChange() {
262
+ this.dispatchEvent(new CustomEvent("change", {
263
+ bubbles: true,
264
+ composed: true,
265
+ detail: { value: this.selectedValues },
266
+ }));
267
+ }
268
+ _openList() {
269
+ if (this.nonInteractive || this._open)
270
+ return;
271
+ this._open = true;
272
+ document.addEventListener("pointerdown", this._onDocPointerDown, true);
273
+ this._activeIndex = this._navigable[0] ?? -1;
274
+ this.updateComplete.then(() => {
275
+ const ov = this._overlay;
276
+ const ctrl = this._control;
277
+ if (ov && ctrl) {
278
+ ov.anchor = ctrl;
279
+ ov.opener = ctrl;
280
+ ov.show();
281
+ }
282
+ });
283
+ }
284
+ _closeList(focusControl = true) {
285
+ if (!this._open)
286
+ return;
287
+ this._open = false;
288
+ this._activeIndex = -1;
289
+ this._query = "";
290
+ document.removeEventListener("pointerdown", this._onDocPointerDown, true);
291
+ const ov = this._overlay;
292
+ if (ov?.open)
293
+ ov.hide("api");
294
+ if (focusControl)
295
+ this._control?.focus();
296
+ }
297
+ _onListKeydown(e) {
298
+ const nav = this._navigable;
299
+ switch (e.key) {
300
+ case "ArrowDown":
301
+ e.preventDefault();
302
+ this._activeIndex = this._nextNavigable(this._activeIndex, 1);
303
+ break;
304
+ case "ArrowUp":
305
+ e.preventDefault();
306
+ this._activeIndex = this._nextNavigable(this._activeIndex, -1);
307
+ break;
308
+ case "Home":
309
+ e.preventDefault();
310
+ this._activeIndex = nav[0] ?? -1;
311
+ break;
312
+ case "End":
313
+ e.preventDefault();
314
+ this._activeIndex = nav[nav.length - 1] ?? -1;
315
+ break;
316
+ case "Enter":
317
+ case " ":
318
+ // Toggle the active option; stay open (multi). Don't bubble to xm-form.
319
+ e.preventDefault();
320
+ e.stopPropagation();
321
+ if (this._activeIndex >= 0)
322
+ this._toggleIndex(this._activeIndex);
323
+ break;
324
+ case "Tab":
325
+ this._closeList(false);
326
+ break;
327
+ // Esc handled by the overlay (innermost-only) → _onOverlayClose syncs us.
328
+ default:
329
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
330
+ this._typeAhead(e.key);
331
+ }
332
+ }
333
+ }
334
+ _typeAhead(char) {
335
+ window.clearTimeout(this._typeaheadTimer);
336
+ this._typeahead += char.toLowerCase();
337
+ this._typeaheadTimer = window.setTimeout(() => {
338
+ this._typeahead = "";
339
+ }, 500);
340
+ const match = this._navigable.find((i) => this.options[i].label.toLowerCase().startsWith(this._typeahead));
341
+ if (match !== undefined)
342
+ this._activeIndex = match;
343
+ }
344
+ // ── Render ──────────────────────────────────────────────────────────
345
+ renderControl() {
346
+ const a = this.controlAria;
347
+ const count = this._selected.size;
348
+ const activeId = this._open && this._activeIndex >= 0
349
+ ? `${a.id}-opt-${this._activeIndex}`
350
+ : nothing;
351
+ return html `
352
+ <button
353
+ type="button"
354
+ class="multi-select__control"
355
+ id=${a.id}
356
+ role="combobox"
357
+ aria-haspopup="listbox"
358
+ aria-expanded=${this._open ? "true" : "false"}
359
+ aria-controls="${a.id}-listbox"
360
+ aria-activedescendant=${activeId}
361
+ aria-describedby=${a.describedBy}
362
+ aria-invalid=${a.invalid ?? "false"}
363
+ aria-required=${a.required ?? nothing}
364
+ ?disabled=${this.effectiveDisabled}
365
+ @click=${() => (this._open ? this._closeList(true) : this._openList())}
366
+ @keydown=${this._onControlKeydown}
367
+ >
368
+ <span
369
+ class="multi-select__value ${count === 0
370
+ ? "multi-select__value--placeholder"
371
+ : ""}"
372
+ >
373
+ ${count === 0 ? this.placeholder : `${count} selected`}
374
+ </span>
375
+ <span
376
+ class="multi-select__chevron ${this._open
377
+ ? "multi-select__chevron--open"
378
+ : ""}"
379
+ >
380
+ <xm-chevron-down-icon size="16"></xm-chevron-down-icon>
381
+ </span>
382
+ </button>
383
+
384
+ <xm-overlay
385
+ mode="non-modal"
386
+ tier="menu"
387
+ placement="bottom-start"
388
+ label=${this.label || "Options"}
389
+ @xm-overlay-close=${this._onOverlayClose}
390
+ >
391
+ ${this._open
392
+ ? html `<div class="multi-select__panel">
393
+ ${this.searchable
394
+ ? html `<xm-text-field
395
+ class="multi-select__search"
396
+ size="sm"
397
+ placeholder="Search…"
398
+ aria-label="Filter options"
399
+ @input=${this._onSearchInput}
400
+ @keydown=${this._onSearchKeydown}
401
+ ></xm-text-field>`
402
+ : nothing}
403
+
404
+ <ul
405
+ class="multi-select__listbox"
406
+ id="${a.id}-listbox"
407
+ role="listbox"
408
+ aria-multiselectable="true"
409
+ aria-label=${this.label || "Options"}
410
+ @keydown=${(e) => this._onListKeydown(e)}
411
+ >
412
+ ${this.options.map((opt, i) => this._renderOption(opt, i, a.id))}
413
+ ${this._visibleCount === 0
414
+ ? html `<li class="multi-select__empty" role="presentation">
415
+ No matches
416
+ </li>`
417
+ : nothing}
418
+ </ul>
419
+ </div>`
420
+ : nothing}
421
+ </xm-overlay>
422
+ `;
423
+ }
424
+ _renderOption(opt, index, baseId) {
425
+ if (!this._matches(opt))
426
+ return html `${nothing}`;
427
+ const selected = this._selected.has(opt.value);
428
+ const disabled = this._optDisabled(opt);
429
+ const active = index === this._activeIndex;
430
+ const cls = [
431
+ "multi-select__option",
432
+ active ? "multi-select__option--active" : "",
433
+ disabled ? "multi-select__option--disabled" : "",
434
+ ]
435
+ .filter(Boolean)
436
+ .join(" ");
437
+ return html `
438
+ <li
439
+ class="${cls}"
440
+ id="${baseId}-opt-${index}"
441
+ role="option"
442
+ aria-selected=${selected ? "true" : "false"}
443
+ aria-disabled=${disabled ? "true" : nothing}
444
+ @click=${() => this._toggleIndex(index)}
445
+ @mousemove=${() => {
446
+ if (!disabled)
447
+ this._activeIndex = index;
448
+ }}
449
+ >
450
+ <xm-checkbox
451
+ class="multi-select__check"
452
+ ?checked=${selected}
453
+ inert
454
+ aria-hidden="true"
455
+ ></xm-checkbox>
456
+ <span class="multi-select__option-label">${opt.label}</span>
457
+ </li>
458
+ `;
459
+ }
460
+ };
461
+ __decorate([
462
+ property({ attribute: false })
463
+ ], XmMultiSelect.prototype, "options", void 0);
464
+ __decorate([
465
+ property({ type: String })
466
+ ], XmMultiSelect.prototype, "placeholder", void 0);
467
+ __decorate([
468
+ property({ type: Number })
469
+ ], XmMultiSelect.prototype, "max", void 0);
470
+ __decorate([
471
+ property({ type: Boolean })
472
+ ], XmMultiSelect.prototype, "searchable", void 0);
473
+ __decorate([
474
+ state()
475
+ ], XmMultiSelect.prototype, "_open", void 0);
476
+ __decorate([
477
+ state()
478
+ ], XmMultiSelect.prototype, "_activeIndex", void 0);
479
+ __decorate([
480
+ state()
481
+ ], XmMultiSelect.prototype, "_selected", void 0);
482
+ __decorate([
483
+ state()
484
+ ], XmMultiSelect.prototype, "_query", void 0);
485
+ __decorate([
486
+ query(".multi-select__control")
487
+ ], XmMultiSelect.prototype, "_control", void 0);
488
+ __decorate([
489
+ query("xm-overlay")
490
+ ], XmMultiSelect.prototype, "_overlay", void 0);
491
+ __decorate([
492
+ query(".multi-select__search")
493
+ ], XmMultiSelect.prototype, "_search", void 0);
494
+ XmMultiSelect = __decorate([
495
+ customElement("xm-multi-select")
496
+ ], XmMultiSelect);
497
+ export { XmMultiSelect };
@@ -0,0 +1,170 @@
1
+ // GENERATED by scripts/gen-styles.mjs — do not edit. Source: lit/components/multi-select/index.css
2
+ // SSR-safe: constructable stylesheets need the DOM, so in a non-DOM context
3
+ // (Node/SSR import) this exports [] instead of throwing. The CSS self-declares
4
+ // the @layer order so the override contract holds even without ./base (AD-2).
5
+ let sheets = [];
6
+ if (typeof CSSStyleSheet !== "undefined") {
7
+ const sheet = new CSSStyleSheet();
8
+ sheet.replaceSync(`@layer reset, base, tokens, components, utilities;
9
+ @layer components {
10
+ /* ============================================
11
+ xm-multi-select — multiple-value picker for XmField (Component-Capability
12
+ Epic 2, ADR 0014).
13
+
14
+ Composes XmField (chrome) + xm-overlay (anchored panel) + xm-checkbox (the
15
+ per-option selected indicator) + xm-text-field (search). This file styles ONLY
16
+ the bespoke parts: the closed trigger (a full-bleed <button> inside the base's
17
+ .field__control wrapper), the chevron, and the panel content (search + listbox)
18
+ projected into the overlay. Positioning / elevation of the panel is owned by
19
+ xm-overlay; the option checkbox visual is owned by xm-checkbox.
20
+
21
+ Surface & ink (AD-13):
22
+ • Closed trigger rides the inverse-surface card tier → inverse-on-surface ink,
23
+ inverse-on-surface-muted for the placeholder.
24
+ • The panel rows sit on the overlay's inverse-surface panel → ink stays
25
+ inverse-on-surface; hover/active uses the MD3 state layer.
26
+ • Selection is carried by the row's xm-checkbox (coral fill), NOT a row tint —
27
+ so the active (keyboard/hover) highlight never double-signals with selection.
28
+
29
+ BEM block: \`multi-select\`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
30
+ ============================================ */
31
+
32
+ /* ---------- Closed trigger — full-bleed button in the field wrapper ---------- */
33
+ .multi-select__control {
34
+ display: flex;
35
+ align-items: center;
36
+ gap: var(--s-2);
37
+ flex: 1;
38
+ min-width: 0;
39
+ width: 100%;
40
+ height: 100%;
41
+ box-sizing: border-box;
42
+ appearance: none;
43
+ border: none;
44
+ outline: none;
45
+ background: transparent;
46
+ color: var(--md-sys-color-inverse-on-surface);
47
+ font:
48
+ var(--md-sys-typescale-body-large-weight)
49
+ var(--md-sys-typescale-body-large-size) /
50
+ var(--md-sys-typescale-body-large-line-height)
51
+ var(--md-sys-typescale-body-large-font);
52
+ padding: 0 var(--s-3);
53
+ text-align: left;
54
+ cursor: pointer;
55
+ }
56
+ .multi-select__control:disabled {
57
+ cursor: not-allowed;
58
+ }
59
+
60
+ /* ---------- Value / placeholder ---------- */
61
+ .multi-select__value {
62
+ flex: 1;
63
+ min-width: 0;
64
+ overflow: hidden;
65
+ white-space: nowrap;
66
+ text-overflow: ellipsis;
67
+ }
68
+ .multi-select__value--placeholder {
69
+ color: var(--xm-color-inverse-on-surface-muted);
70
+ }
71
+
72
+ /* ---------- Chevron — rotates on open (short3, standard) ---------- */
73
+ .multi-select__chevron {
74
+ display: inline-flex;
75
+ align-items: center;
76
+ flex-shrink: 0;
77
+ color: var(--md-sys-color-inverse-on-surface);
78
+ transition: transform var(--md-sys-motion-duration-short3)
79
+ var(--md-sys-motion-easing-standard);
80
+ }
81
+ .multi-select__chevron--open {
82
+ transform: rotate(180deg);
83
+ }
84
+
85
+ /* ---------- Panel (inside the overlay): search + listbox ---------- */
86
+ .multi-select__panel {
87
+ display: flex;
88
+ flex-direction: column;
89
+ gap: var(--s-2);
90
+ min-width: 220px;
91
+ }
92
+ .multi-select__search {
93
+ display: block;
94
+ width: 100%;
95
+ }
96
+
97
+ /* ---------- Listbox ---------- */
98
+ .multi-select__listbox {
99
+ list-style: none;
100
+ margin: 0;
101
+ padding: 0;
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: var(--s-0-5);
105
+ max-height: 280px;
106
+ overflow-y: auto;
107
+ }
108
+
109
+ /* ---------- Option rows ---------- */
110
+ .multi-select__option {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: var(--s-2);
114
+ padding: var(--s-1) var(--s-2);
115
+ border-radius: var(--md-sys-shape-corner-small);
116
+ color: var(--md-sys-color-inverse-on-surface);
117
+ font:
118
+ var(--md-sys-typescale-body-medium-weight)
119
+ var(--md-sys-typescale-body-medium-size) /
120
+ var(--md-sys-typescale-body-medium-line-height)
121
+ var(--md-sys-typescale-body-medium-font);
122
+ cursor: pointer;
123
+ user-select: none;
124
+ }
125
+ .multi-select__option-label {
126
+ flex: 1;
127
+ min-width: 0;
128
+ overflow: hidden;
129
+ white-space: nowrap;
130
+ text-overflow: ellipsis;
131
+ }
132
+
133
+ /* The option checkbox is presentational — the row owns the click + a11y. */
134
+ .multi-select__check {
135
+ flex-shrink: 0;
136
+ pointer-events: none;
137
+ }
138
+
139
+ /* Active (keyboard / pointer hover) — the MD3 state layer over the panel ink. */
140
+ .multi-select__option--active {
141
+ background: color-mix(
142
+ in oklab,
143
+ var(--md-sys-color-inverse-on-surface)
144
+ var(--md-sys-state-hover-state-layer-opacity),
145
+ transparent
146
+ );
147
+ }
148
+
149
+ /* Disabled option (explicitly disabled, or unselected at the \`max\` cap). */
150
+ .multi-select__option--disabled {
151
+ opacity: 0.4;
152
+ cursor: not-allowed;
153
+ }
154
+
155
+ /* ---------- Empty (no search matches) ---------- */
156
+ .multi-select__empty {
157
+ list-style: none;
158
+ padding: var(--s-2) var(--s-2);
159
+ color: var(--xm-color-inverse-on-surface-muted);
160
+ font:
161
+ var(--md-sys-typescale-body-medium-weight)
162
+ var(--md-sys-typescale-body-medium-size) /
163
+ var(--md-sys-typescale-body-medium-line-height)
164
+ var(--md-sys-typescale-body-medium-font);
165
+ }
166
+
167
+ }`);
168
+ sheets = [sheet];
169
+ }
170
+ export default sheets;
@@ -1,6 +1,10 @@
1
1
  import { LitElement } from "lit";
2
2
  import type { PropertyValues, TemplateResult } from "lit";
3
+ /**
4
+ * @fires close - Fired when the element requests to close.
5
+ */
3
6
  export declare class XmNavigationDrawer extends LitElement {
7
+ static styles: CSSStyleSheet[];
4
8
  static shadowRootOptions: ShadowRootInit;
5
9
  open: boolean;
6
10
  collapsed: boolean;