@xmesh/system-design 0.0.3 → 0.0.5
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/README.md +2 -1
- package/custom-elements.json +18382 -0
- package/dist/lit/components/alert/index.d.ts +6 -1
- package/dist/lit/components/alert/index.js +9 -2
- package/dist/lit/components/alert/index.styles.js +215 -0
- package/dist/lit/components/app-bar/index.d.ts +2 -1
- package/dist/lit/components/app-bar/index.js +5 -2
- package/dist/lit/components/app-bar/index.styles.js +94 -0
- package/dist/lit/components/artifact/index.d.ts +6 -1
- package/dist/lit/components/artifact/index.js +20 -2
- package/dist/lit/components/artifact/index.styles.js +180 -0
- package/dist/lit/components/autocomplete/index.d.ts +5 -0
- package/dist/lit/components/autocomplete/index.js +11 -2
- package/dist/lit/components/autocomplete/index.styles.js +185 -0
- package/dist/lit/components/avatar/index.d.ts +2 -1
- package/dist/lit/components/avatar/index.js +5 -2
- package/dist/lit/components/avatar/index.styles.js +76 -0
- package/dist/lit/components/avatar-group/index.d.ts +2 -1
- package/dist/lit/components/avatar-group/index.js +5 -2
- package/dist/lit/components/avatar-group/index.styles.js +74 -0
- package/dist/lit/components/badge/index.d.ts +2 -2
- package/dist/lit/components/badge/index.js +5 -2
- package/dist/lit/components/badge/index.styles.js +86 -0
- package/dist/lit/components/brand-mark/index.d.ts +2 -2
- package/dist/lit/components/brand-mark/index.js +13 -0
- package/dist/lit/components/brand-mark/index.styles.js +123 -0
- package/dist/lit/components/breadcrumbs/index.d.ts +2 -1
- package/dist/lit/components/breadcrumbs/index.js +6 -4
- package/dist/lit/components/breadcrumbs/index.styles.js +105 -0
- package/dist/lit/components/bubble/index.d.ts +8 -4
- package/dist/lit/components/bubble/index.js +21 -0
- package/dist/lit/components/bubble/index.styles.js +196 -0
- package/dist/lit/components/button/index.d.ts +2 -1
- package/dist/lit/components/button/index.js +7 -13
- package/dist/lit/components/button/index.styles.js +356 -0
- package/dist/lit/components/card/index.d.ts +2 -1
- package/dist/lit/components/card/index.js +5 -2
- package/dist/lit/components/card/index.styles.js +113 -0
- package/dist/lit/components/chat/index.d.ts +7 -2
- package/dist/lit/components/chat/index.js +21 -2
- package/dist/lit/components/chat/index.styles.js +306 -0
- package/dist/lit/components/checkbox/index.d.ts +2 -2
- package/dist/lit/components/checkbox/index.js +8 -4
- package/dist/lit/components/checkbox/index.styles.js +140 -0
- package/dist/lit/components/chip/index.d.ts +6 -1
- package/dist/lit/components/chip/index.js +9 -2
- package/dist/lit/components/chip/index.styles.js +159 -0
- package/dist/lit/components/chip-group/index.d.ts +5 -1
- package/dist/lit/components/chip-group/index.js +8 -2
- package/dist/lit/components/chip-group/index.styles.js +33 -0
- package/dist/lit/components/code/index.d.ts +2 -2
- package/dist/lit/components/code/index.js +5 -3
- package/dist/lit/components/code/index.styles.js +56 -0
- package/dist/lit/components/composer/index.d.ts +7 -2
- package/dist/lit/components/composer/index.js +19 -0
- package/dist/lit/components/composer/index.styles.js +562 -0
- package/dist/lit/components/data-table/index.css +18 -0
- package/dist/lit/components/data-table/index.d.ts +30 -0
- package/dist/lit/components/data-table/index.js +120 -34
- package/dist/lit/components/data-table/index.styles.js +198 -0
- package/dist/lit/components/date-range/index.d.ts +4 -0
- package/dist/lit/components/date-range/index.js +7 -2
- package/dist/lit/components/date-range/index.styles.js +338 -0
- package/dist/lit/components/dialog/index.d.ts +4 -0
- package/dist/lit/components/dialog/index.js +7 -2
- package/dist/lit/components/dialog/index.styles.js +138 -0
- package/dist/lit/components/divider/index.d.ts +2 -1
- package/dist/lit/components/divider/index.js +5 -2
- package/dist/lit/components/divider/index.styles.js +41 -0
- package/dist/lit/components/empty-state/index.d.ts +1 -0
- package/dist/lit/components/empty-state/index.js +4 -2
- package/dist/lit/components/empty-state/index.styles.js +83 -0
- package/dist/lit/components/expansion-panel/index.d.ts +6 -2
- package/dist/lit/components/expansion-panel/index.js +9 -2
- package/dist/lit/components/expansion-panel/index.styles.js +134 -0
- package/dist/lit/components/field/index.d.ts +4 -0
- package/dist/lit/components/field/index.js +9 -4
- package/dist/lit/components/field/index.styles.js +237 -0
- package/dist/lit/components/file-input/index.d.ts +4 -0
- package/dist/lit/components/file-input/index.js +10 -4
- package/dist/lit/components/file-input/index.styles.js +271 -0
- package/dist/lit/components/form/index.d.ts +4 -0
- package/dist/lit/components/form/index.js +7 -2
- package/dist/lit/components/form/index.styles.js +43 -0
- package/dist/lit/components/grid/index.d.ts +2 -1
- package/dist/lit/components/grid/index.js +5 -2
- package/dist/lit/components/grid/index.styles.js +67 -0
- package/dist/lit/components/kbd/index.d.ts +2 -2
- package/dist/lit/components/kbd/index.js +5 -2
- package/dist/lit/components/kbd/index.styles.js +49 -0
- package/dist/lit/components/list/index.d.ts +5 -1
- package/dist/lit/components/list/index.js +8 -2
- package/dist/lit/components/list/index.styles.js +29 -0
- package/dist/lit/components/list-item/index.d.ts +2 -2
- package/dist/lit/components/list-item/index.js +5 -2
- package/dist/lit/components/list-item/index.styles.js +133 -0
- package/dist/lit/components/menu/index.d.ts +8 -0
- package/dist/lit/components/menu/index.js +11 -3
- package/dist/lit/components/menu/index.styles.js +108 -0
- package/dist/lit/components/multi-select/index.css +156 -0
- package/dist/lit/components/multi-select/index.d.ts +70 -0
- package/dist/lit/components/multi-select/index.js +497 -0
- package/dist/lit/components/multi-select/index.styles.js +170 -0
- package/dist/lit/components/navigation-drawer/index.d.ts +5 -2
- package/dist/lit/components/navigation-drawer/index.js +8 -2
- package/dist/lit/components/navigation-drawer/index.styles.js +128 -0
- package/dist/lit/components/overlay/index.d.ts +5 -0
- package/dist/lit/components/overlay/index.js +8 -2
- package/dist/lit/components/overlay/index.styles.js +185 -0
- package/dist/lit/components/pagination/index.d.ts +18 -1
- package/dist/lit/components/pagination/index.js +54 -8
- package/dist/lit/components/pagination/index.styles.js +116 -0
- package/dist/lit/components/popover/index.d.ts +5 -0
- package/dist/lit/components/popover/index.js +8 -2
- package/dist/lit/components/popover/index.styles.js +48 -0
- package/dist/lit/components/primitives/index.d.ts +2 -2
- package/dist/lit/components/primitives/index.js +13 -0
- package/dist/lit/components/primitives/index.styles.js +518 -0
- package/dist/lit/components/progress/index.d.ts +1 -0
- package/dist/lit/components/progress/index.js +4 -2
- package/dist/lit/components/progress/index.styles.js +157 -0
- package/dist/lit/components/radio-group/index.d.ts +5 -0
- package/dist/lit/components/radio-group/index.js +10 -6
- package/dist/lit/components/radio-group/index.styles.js +192 -0
- package/dist/lit/components/select/index.d.ts +4 -0
- package/dist/lit/components/select/index.js +10 -2
- package/dist/lit/components/select/index.styles.js +165 -0
- package/dist/lit/components/sidebar-item/index.d.ts +1 -1
- package/dist/lit/components/sidebar-item/index.js +12 -0
- package/dist/lit/components/sidebar-item/index.styles.js +147 -0
- package/dist/lit/components/skeleton/index.d.ts +1 -0
- package/dist/lit/components/skeleton/index.js +4 -2
- package/dist/lit/components/skeleton/index.styles.js +95 -0
- package/dist/lit/components/slider/index.d.ts +5 -0
- package/dist/lit/components/slider/index.js +11 -4
- package/dist/lit/components/slider/index.styles.js +185 -0
- package/dist/lit/components/snackbar/index.d.ts +5 -2
- package/dist/lit/components/snackbar/index.js +20 -2
- package/dist/lit/components/snackbar/index.styles.js +293 -0
- package/dist/lit/components/stack/index.d.ts +2 -1
- package/dist/lit/components/stack/index.js +5 -2
- package/dist/lit/components/stack/index.styles.js +55 -0
- package/dist/lit/components/switch/index.d.ts +1 -0
- package/dist/lit/components/switch/index.js +7 -4
- package/dist/lit/components/switch/index.styles.js +140 -0
- package/dist/lit/components/table/index.d.ts +2 -2
- package/dist/lit/components/table/index.js +5 -2
- package/dist/lit/components/table/index.styles.js +99 -0
- package/dist/lit/components/tabs/index.d.ts +7 -3
- package/dist/lit/components/tabs/index.js +11 -4
- package/dist/lit/components/tabs/index.styles.js +130 -0
- package/dist/lit/components/text-field/index.d.ts +1 -0
- package/dist/lit/components/text-field/index.js +7 -2
- package/dist/lit/components/text-field/index.styles.js +104 -0
- package/dist/lit/components/textarea/index.d.ts +1 -0
- package/dist/lit/components/textarea/index.js +7 -2
- package/dist/lit/components/textarea/index.styles.js +69 -0
- package/dist/lit/components/tooltip/index.d.ts +1 -0
- package/dist/lit/components/tooltip/index.js +4 -2
- package/dist/lit/components/tooltip/index.styles.js +51 -0
- package/dist/lit/components/validation/index.d.ts +7 -2
- package/dist/lit/components/validation/index.js +21 -1
- package/dist/lit/components/validation/index.styles.js +400 -0
- package/dist/lit/index.d.ts +1 -0
- package/dist/lit/index.js +1 -0
- package/dist/react/XmAlert.d.ts +99 -0
- package/dist/react/XmAlert.js +45 -0
- package/dist/react/XmAppBar.d.ts +59 -0
- package/dist/react/XmAppBar.js +34 -0
- package/dist/react/XmArtifact.d.ts +113 -0
- package/dist/react/XmArtifact.js +45 -0
- package/dist/react/XmArtifactChip.d.ts +56 -0
- package/dist/react/XmArtifactChip.js +32 -0
- package/dist/react/XmAutocomplete.d.ts +153 -0
- package/dist/react/XmAutocomplete.js +68 -0
- package/dist/react/XmAvatar.d.ts +71 -0
- package/dist/react/XmAvatar.js +40 -0
- package/dist/react/XmAvatarGroup.d.ts +59 -0
- package/dist/react/XmAvatarGroup.js +34 -0
- package/dist/react/XmBadge.d.ts +67 -0
- package/dist/react/XmBadge.js +38 -0
- package/dist/react/XmBrandGlyph.d.ts +46 -0
- package/dist/react/XmBrandGlyph.js +24 -0
- package/dist/react/XmBrandMark.d.ts +71 -0
- package/dist/react/XmBrandMark.js +40 -0
- package/dist/react/XmBreadcrumbs.d.ts +56 -0
- package/dist/react/XmBreadcrumbs.js +32 -0
- package/dist/react/XmBubble.d.ts +69 -0
- package/dist/react/XmBubble.js +38 -0
- package/dist/react/XmBubbleActions.d.ts +59 -0
- package/dist/react/XmBubbleActions.js +34 -0
- package/dist/react/XmBubbleGroup.d.ts +82 -0
- package/dist/react/XmBubbleGroup.js +36 -0
- package/dist/react/XmButton.d.ts +89 -0
- package/dist/react/XmButton.js +48 -0
- package/dist/react/XmCard.d.ts +59 -0
- package/dist/react/XmCard.js +34 -0
- package/dist/react/XmChatShell.d.ts +110 -0
- package/dist/react/XmChatShell.js +44 -0
- package/dist/react/XmCheckbox.d.ts +145 -0
- package/dist/react/XmCheckbox.js +58 -0
- package/dist/react/XmChip.d.ts +99 -0
- package/dist/react/XmChip.js +46 -0
- package/dist/react/XmChipGroup.d.ts +79 -0
- package/dist/react/XmChipGroup.js +35 -0
- package/dist/react/XmCode.d.ts +55 -0
- package/dist/react/XmCode.js +32 -0
- package/dist/react/XmComposer.d.ts +123 -0
- package/dist/react/XmComposer.js +52 -0
- package/dist/react/XmDataTable.d.ts +125 -0
- package/dist/react/XmDataTable.js +65 -0
- package/dist/react/XmDateRange.d.ts +93 -0
- package/dist/react/XmDateRange.js +41 -0
- package/dist/react/XmDialog.d.ts +87 -0
- package/dist/react/XmDialog.js +40 -0
- package/dist/react/XmDivider.d.ts +55 -0
- package/dist/react/XmDivider.js +32 -0
- package/dist/react/XmEmptyState.d.ts +61 -0
- package/dist/react/XmEmptyState.js +34 -0
- package/dist/react/XmExpansionPanel.d.ts +101 -0
- package/dist/react/XmExpansionPanel.js +48 -0
- package/dist/react/XmFileInput.d.ts +151 -0
- package/dist/react/XmFileInput.js +68 -0
- package/dist/react/XmFileValidationBlock.d.ts +111 -0
- package/dist/react/XmFileValidationBlock.js +45 -0
- package/dist/react/XmForm.d.ts +91 -0
- package/dist/react/XmForm.js +37 -0
- package/dist/react/XmGrid.d.ts +59 -0
- package/dist/react/XmGrid.js +34 -0
- package/dist/react/XmKbd.d.ts +46 -0
- package/dist/react/XmKbd.js +24 -0
- package/dist/react/XmList.d.ts +83 -0
- package/dist/react/XmList.js +37 -0
- package/dist/react/XmListItem.d.ts +67 -0
- package/dist/react/XmListItem.js +38 -0
- package/dist/react/XmMenu.d.ts +98 -0
- package/dist/react/XmMenu.js +42 -0
- package/dist/react/XmMenuItem.d.ts +63 -0
- package/dist/react/XmMenuItem.js +36 -0
- package/dist/react/XmMultiSelect.d.ts +161 -0
- package/dist/react/XmMultiSelect.js +72 -0
- package/dist/react/XmNavigationDrawer.d.ts +93 -0
- package/dist/react/XmNavigationDrawer.js +41 -0
- package/dist/react/XmOverlay.d.ts +120 -0
- package/dist/react/XmOverlay.js +54 -0
- package/dist/react/XmPagination.d.ts +117 -0
- package/dist/react/XmPagination.js +57 -0
- package/dist/react/XmPopover.d.ts +90 -0
- package/dist/react/XmPopover.js +40 -0
- package/dist/react/XmProgress.d.ts +75 -0
- package/dist/react/XmProgress.js +42 -0
- package/dist/react/XmRadio.d.ts +88 -0
- package/dist/react/XmRadio.js +41 -0
- package/dist/react/XmRadioGroup.d.ts +139 -0
- package/dist/react/XmRadioGroup.js +56 -0
- package/dist/react/XmSelect.d.ts +152 -0
- package/dist/react/XmSelect.js +68 -0
- package/dist/react/XmSidebarItem.d.ts +75 -0
- package/dist/react/XmSidebarItem.js +42 -0
- package/dist/react/XmSkeleton.d.ts +71 -0
- package/dist/react/XmSkeleton.js +40 -0
- package/dist/react/XmSlider.d.ts +160 -0
- package/dist/react/XmSlider.js +74 -0
- package/dist/react/XmSnackbar.d.ts +110 -0
- package/dist/react/XmSnackbar.js +49 -0
- package/dist/react/XmStack.d.ts +71 -0
- package/dist/react/XmStack.js +40 -0
- package/dist/react/XmSwitch.d.ts +136 -0
- package/dist/react/XmSwitch.js +56 -0
- package/dist/react/XmTab.d.ts +79 -0
- package/dist/react/XmTab.js +37 -0
- package/dist/react/XmTabPanel.d.ts +55 -0
- package/dist/react/XmTabPanel.js +32 -0
- package/dist/react/XmTable.d.ts +57 -0
- package/dist/react/XmTable.js +32 -0
- package/dist/react/XmTabs.d.ts +82 -0
- package/dist/react/XmTabs.js +37 -0
- package/dist/react/XmTextField.d.ts +147 -0
- package/dist/react/XmTextField.js +60 -0
- package/dist/react/XmTextarea.d.ts +155 -0
- package/dist/react/XmTextarea.js +64 -0
- package/dist/react/XmTooltip.d.ts +67 -0
- package/dist/react/XmTooltip.js +38 -0
- package/dist/react/index.d.ts +59 -0
- package/dist/react/index.js +59 -0
- package/dist/react/react-utils.js +67 -0
- package/package.json +37 -9
- package/styles/_base-typography.css +86 -0
- package/styles/_primitives.css +54 -0
- package/styles/_reset.css +58 -0
- package/styles/base.css +23 -0
- package/vscode.css-custom-data.json +6 -0
- 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
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @fires close - Fired when the element requests to close.
|
|
5
|
+
*/
|
|
6
|
+
export declare class XmNavigationDrawer extends LitElement {
|
|
7
|
+
static styles: CSSStyleSheet[];
|
|
4
8
|
static shadowRootOptions: ShadowRootInit;
|
|
5
9
|
open: boolean;
|
|
6
10
|
collapsed: boolean;
|
|
@@ -26,4 +30,3 @@ declare global {
|
|
|
26
30
|
"xm-navigation-drawer": XmNavigationDrawer;
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
|
-
export {};
|