@mono-lit/helper 0.0.9 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accordion-core-DaNyPWRr.js +277 -0
- package/dist/alert-core-DWtHbX_-.js +134 -0
- package/dist/breadcrumb-list-core-Dh68T8Cf.js +747 -0
- package/dist/{mono-button-CLkHg3WW.js → button-core-DerXg84Q.js} +3 -276
- package/dist/button-dropdown-core-DKYEOyjy.js +654 -0
- package/dist/button-utils-D5-66M02.js +63 -0
- package/dist/card-utils-l80qAmrN.js +312 -0
- package/dist/chart-core-DV7hD3ZY.js +801 -0
- package/dist/checkbox-utils-DKdrhAzF.js +337 -0
- package/dist/chip-utils-DpRrd7Zj.js +403 -0
- package/dist/{data-source-controller-C6C4ej_M.js → data-source-controller-DS_JLrmS.js} +1 -1
- package/dist/{data-source-options-fxT5QIX4.js → data-source-options-DJUNU26C.js} +6 -6
- package/dist/{data-source-read-BL266IW0.js → data-source-read-uKJpRAzx.js} +2 -2
- package/dist/date-core-CyuSyvvG.js +734 -0
- package/dist/drawer-core-DbFWvNln.js +759 -0
- package/dist/dropdown-core-BCH1pK14.js +511 -0
- package/dist/dropdown-table-core-SfHUJVFq.js +1342 -0
- package/dist/export.js +1 -1
- package/dist/{field-path-BabiMrrA.js → field-path-DK2VBWHl.js} +1 -1
- package/dist/field-tooltip.css_raw-DVblekCl.js +5 -0
- package/dist/{field-validation.css_raw-Cx6SGrfP.js → field-validation.css_raw-BU7C6173.js} +1 -170
- package/dist/file-upload-core-Cz_dBQMx.js +624 -0
- package/dist/filter-builder-core-DyWjbBP7.js +381 -0
- package/dist/{filter-builder-B6Y2mmbM.js → filter-builder-vbqIuPhX.js} +1 -1
- package/dist/{filter-eval-ChDSaAUH.js → filter-eval-CqeBnMYh.js} +1 -1
- package/dist/{filter-odata-Bi27XUpQ.js → filter-odata-GsczYKNC.js} +1 -1
- package/dist/{hydration-flush-BaE9gN3C.js → hydration-flush-Volt9X7Q.js} +2 -1071
- package/dist/index.js +11 -11
- package/dist/{index.node-DTqDBca7.js → index.node-Csy9zrr1.js} +4 -4
- package/dist/index.node.js +11 -11
- package/dist/input-utils-BglY-k0H.js +1145 -0
- package/dist/menu-core-DkAYuCZ2.js +831 -0
- package/dist/modal-core-CSBSPN_m.js +844 -0
- package/dist/mono-button-DVvZ03P-.js +280 -0
- package/dist/{mono-button.shadow-DqZMpcqY.js → mono-button.shadow-BANxMGln.js} +6 -594
- package/dist/{mono-data-chart-NKPdAgVq.js → mono-data-chart-ChF4J9RT.js} +6 -6
- package/dist/{mono-data-dropdown-B2iRpJuI.js → mono-data-dropdown-Bgj2PDfm.js} +1 -1
- package/dist/{mono-data-grid-llJCX9MC.js → mono-data-grid-DJkp96N-.js} +144 -46
- package/dist/{mono-event-BPku9HpS.js → mono-event-CAZf0F_k.js} +14 -9
- package/dist/{mono-form-controller-BrA10erm.js → mono-form-controller-DxZLvyg6.js} +2 -1
- package/dist/mono-table-checkbox-core-BKOMx8R5.js +5288 -0
- package/dist/nav-core-B5qU2dzn.js +225 -0
- package/dist/{normalize-error-D93O4q8g.js → normalize-error-BEcd1tmS.js} +2 -2
- package/dist/nuxt.js +1 -1
- package/dist/otp-core-DXg3vFLh.js +758 -0
- package/dist/radio-utils-Dk9Tl1jz.js +319 -0
- package/dist/rich-text-editor-core-Ddd_QaLn.js +1280 -0
- package/dist/select-utils-BgArYHk8.js +2140 -0
- package/dist/sidebar-core-CD6txDo3.js +583 -0
- package/dist/slider-core-vcWr321-.js +1014 -0
- package/dist/src/components/dropdown-table/dropdown-table-core.d.ts +2 -2
- package/dist/src/components/dropdown-table/dropdown-table-types.d.ts +1 -1
- package/dist/src/components/dropdown-table/index.d.ts +1 -1
- package/dist/src/components/dropdown-table/index.shadow.d.ts +1 -1
- package/dist/src/components/dropdown-table/mono-data-dropdown.d.ts +1 -1
- package/dist/src/components/form/form-types.d.ts +9 -3
- package/dist/src/components/input/input-core.d.ts +1 -1
- package/dist/src/components/input/input-types.d.ts +1 -1
- package/dist/src/components/otp/otp-core.d.ts +1 -1
- package/dist/src/components/select/select-core.d.ts +1 -1
- package/dist/src/components/select/select-types.d.ts +1 -1
- package/dist/src/components/slider/slider-core.d.ts +1 -1
- package/dist/src/components/slider/slider-types.d.ts +1 -1
- package/dist/src/components/table/filter-suggestion.d.ts +171 -0
- package/dist/src/components/table/index.d.ts +5 -4
- package/dist/src/components/table/index.shadow.d.ts +5 -4
- package/dist/src/components/table/mono-data-grid.d.ts +22 -3
- package/dist/src/components/table/mono-table-empty-core.d.ts +37 -0
- package/dist/src/components/table/mono-table-search-core.d.ts +22 -3
- package/dist/src/components/table/table-types.d.ts +74 -4
- package/dist/src/components/tag-input/tag-input-core.d.ts +1 -1
- package/dist/src/components/tag-input/tag-input-types.d.ts +1 -1
- package/dist/src/components/textarea/textarea-core.d.ts +1 -1
- package/dist/src/composables/mono-event.d.ts +7 -1
- package/dist/src/entries/index.d.ts +8 -6
- package/dist/src/entries/index.node.d.ts +8 -6
- package/dist/src/search/index.d.ts +1 -1
- package/dist/src/utils/group-count.d.ts +36 -0
- package/dist/switch-utils-CTZ4id2V.js +338 -0
- package/dist/tabs-core-r5-B8k_N.js +317 -0
- package/dist/tag-input-utils-D2JA90DQ.js +3060 -0
- package/dist/textarea-utils-VU1KeXm8.js +821 -0
- package/dist/ui/accordion.js +2 -274
- package/dist/ui/alert.js +2 -131
- package/dist/ui/breadcrumb.js +4 -745
- package/dist/ui/button-dropdown.js +4 -652
- package/dist/ui/button.js +2 -63
- package/dist/ui/card.js +2 -310
- package/dist/ui/chart.js +4 -800
- package/dist/ui/checkbox.js +4 -335
- package/dist/ui/chip.js +4 -402
- package/dist/ui/date.js +3 -732
- package/dist/ui/drawer.js +3 -757
- package/dist/ui/dropdown-table.js +5 -1341
- package/dist/ui/dropdown.js +4 -509
- package/dist/ui/file-upload.js +3 -622
- package/dist/ui/filter.js +5 -380
- package/dist/ui/form.js +1 -1
- package/dist/ui/index.css +118 -1
- package/dist/ui/input.js +3 -1141
- package/dist/ui/menu.js +4 -828
- package/dist/ui/modal.js +4 -843
- package/dist/ui/nav.js +4 -223
- package/dist/ui/otp.js +4 -756
- package/dist/ui/radio.js +4 -317
- package/dist/ui/rich-text-editor.js +4 -1278
- package/dist/ui/select.js +5 -2138
- package/dist/ui/shadow/accordion.js +4 -273
- package/dist/ui/shadow/alert.js +4 -130
- package/dist/ui/shadow/breadcrumb.js +5 -744
- package/dist/ui/shadow/button-dropdown.js +6 -652
- package/dist/ui/shadow/button.js +3 -63
- package/dist/ui/shadow/card.js +4 -309
- package/dist/ui/shadow/chart.js +6 -1576
- package/dist/ui/shadow/checkbox.js +4 -334
- package/dist/ui/shadow/chip.js +4 -396
- package/dist/ui/shadow/date.js +7 -731
- package/dist/ui/shadow/drawer.js +4 -756
- package/dist/ui/shadow/dropdown-table.js +8 -1762
- package/dist/ui/shadow/dropdown.js +4 -508
- package/dist/ui/shadow/file-upload.js +4 -620
- package/dist/ui/shadow/filter.js +7 -379
- package/dist/ui/shadow/input.js +6 -1140
- package/dist/ui/shadow/menu.js +5 -828
- package/dist/ui/shadow/modal.js +6 -1063
- package/dist/ui/shadow/nav.js +5 -223
- package/dist/ui/shadow/otp.js +6 -754
- package/dist/ui/shadow/radio.js +4 -316
- package/dist/ui/shadow/rich-text-editor.js +4 -1276
- package/dist/ui/shadow/select.js +6 -2136
- package/dist/ui/shadow/sidebar.js +5 -581
- package/dist/ui/shadow/slider.js +5 -1010
- package/dist/ui/shadow/switch.js +4 -335
- package/dist/ui/shadow/table.js +10 -4501
- package/dist/ui/shadow/tabs.js +6 -315
- package/dist/ui/shadow/tag-input.js +7 -3057
- package/dist/ui/shadow/textarea.js +6 -817
- package/dist/ui/sidebar.js +3 -581
- package/dist/ui/slider.js +4 -1012
- package/dist/ui/switch.js +4 -336
- package/dist/ui/table.js +9 -4502
- package/dist/ui/tabs.js +4 -315
- package/dist/ui/tag-input.js +5 -3058
- package/dist/ui/textarea.js +4 -819
- package/dist/{vite-CivRCJaf.js → vite-CfW7z2wn.js} +21 -14
- package/dist/vite.js +1 -1
- package/dist/vue.d.ts +3 -2
- package/package.json +10 -11
- package/dist/chip-strip-bNfLn52Z.js +0 -215
- package/dist/data-source-controller-Dx6IQ2CN.js +0 -243
- package/dist/data-source-options-BM4r4PRw.js +0 -558
- package/dist/data-source-read-HyBCRg1t.js +0 -242
- package/dist/excel-BXzr3uI1.js +0 -784
- package/dist/export-ixRdCS2y.js +0 -2388
- package/dist/field-tooltip-addon-CWCp19mJ.js +0 -702
- package/dist/field-tooltip.css_raw-U5UeTa2d.js +0 -269
- package/dist/filter-builder-C0T3pMx2.js +0 -208
- package/dist/form-control-core-BbiACMtL.js +0 -226
- package/dist/icon-ClxnYhBG.js +0 -19
- package/dist/import-Dc4eXC02.js +0 -612
- package/dist/light-slots-_no0r2Z-.js +0 -246
- package/dist/markdown-uGlE_OfJ.js +0 -20191
- package/dist/mono-data-grid-e9ZVBlNN.js +0 -4240
- package/dist/normalize-error-BmLbgANw.js +0 -1048
|
@@ -0,0 +1,1342 @@
|
|
|
1
|
+
import { d as buildSizeStyle, f as toCssSize, y as monoPendingGrace } from "./mono-ui-kvLVIf1V.js";
|
|
2
|
+
import { n as detachEventHandlers, t as applyProps } from "./element-props-xvw0EB7H.js";
|
|
3
|
+
import { a as defineHybridPropAliases, i as defineHybridPropAlias, o as numberStringConverter, r as booleanStringConverter, s as optionalNumberConverter, t as __decorate } from "./decorate-DbYFpi8R.js";
|
|
4
|
+
import { t as dispatchMonoEvent } from "./mono-event-CAZf0F_k.js";
|
|
5
|
+
import { n as applyPopupPlacement, r as computePopupPlacement, t as PopupPortalController } from "./popup-portal-CINuVjNY.js";
|
|
6
|
+
import { a as closeIcon, i as chevronIcon, n as caretIcon } from "./field-icons-t8rXRCKs.js";
|
|
7
|
+
import { t as MonoFormControlCore } from "./form-control-core-CRZPZ1ru.js";
|
|
8
|
+
import { t as FieldTooltipController } from "./field-tooltip-Csk4s2ve.js";
|
|
9
|
+
import { t as FieldValidationController } from "./field-validation-CFmUmuDx.js";
|
|
10
|
+
import { n as visibleChipCap, r as ChipStripController, t as resolveChipLimit } from "./chip-limits-CFABydIL.js";
|
|
11
|
+
import { html, isServer, nothing } from "lit";
|
|
12
|
+
import { property } from "lit/decorators.js";
|
|
13
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
14
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
15
|
+
|
|
16
|
+
//#region src/components/dropdown-table/dropdown-table-core.ts
|
|
17
|
+
/** Sentinel so an initial `model-value === our last emit` guard never matches by accident. */
|
|
18
|
+
const SYMBOL_INIT = Symbol("init");
|
|
19
|
+
/**
|
|
20
|
+
* `MonoDropdownTableCore` — the field + popup shell for `<mono-dropdown-table>`. The
|
|
21
|
+
* FIELD mirrors `<mono-select>` (same size/color/variant/validation/label props and
|
|
22
|
+
* an identical look); the selection/value/display magic lives in the bound
|
|
23
|
+
* {@link MonoDropdownController} (`monoDataDropdown`). The consumer's native `<table>`
|
|
24
|
+
* + `mono-table-*` are slotted into the body-portaled panel; row clicks delegate to
|
|
25
|
+
* `dd.toggleRow`. The panel is sized independently of the field via the `dropdown`
|
|
26
|
+
* object (`matchWidth` is intentionally off). Reuses `PopupPortalController`,
|
|
27
|
+
* `buildSizeStyle`, and the global `.mono-chip` classes.
|
|
28
|
+
*/
|
|
29
|
+
const MonoDropdownTableCore = (superClass) => {
|
|
30
|
+
class MonoDropdownTableCoreClass extends MonoFormControlCore(superClass) {
|
|
31
|
+
static {
|
|
32
|
+
this.monoPendingAuto = "data";
|
|
33
|
+
}
|
|
34
|
+
_monoPendingReady() {
|
|
35
|
+
const grid = this._dd?.grid;
|
|
36
|
+
if (!grid) return monoPendingGrace(this);
|
|
37
|
+
if (grid.hasLoaded || grid.error) return true;
|
|
38
|
+
return monoPendingGrace(this) && !grid.loading;
|
|
39
|
+
}
|
|
40
|
+
constructor(...args) {
|
|
41
|
+
super(...args);
|
|
42
|
+
this.size = "md";
|
|
43
|
+
this.color = "primary";
|
|
44
|
+
this.variant = "outlined";
|
|
45
|
+
this.labelPosition = "top";
|
|
46
|
+
this.validationPosition = "bottom";
|
|
47
|
+
this.label = "";
|
|
48
|
+
this.placeholder = "Select…";
|
|
49
|
+
this.helperText = "";
|
|
50
|
+
this.tooltip = null;
|
|
51
|
+
this._tooltip = new FieldTooltipController(this, () => this.tooltip);
|
|
52
|
+
this._validation = new FieldValidationController(this, () => ({
|
|
53
|
+
position: this.validationPosition,
|
|
54
|
+
...this._validationNotice ?? {
|
|
55
|
+
state: void 0,
|
|
56
|
+
text: void 0
|
|
57
|
+
},
|
|
58
|
+
suppressed: this._isOpen || this._moreOpen
|
|
59
|
+
}), () => this.renderRoot?.querySelector?.("[mono-dropdown-table] > [mono-dd-control] > [mono-dd-trigger]"));
|
|
60
|
+
this.validationState = "default";
|
|
61
|
+
this.validationMessage = "";
|
|
62
|
+
this.errorMessage = "";
|
|
63
|
+
this.successMessage = "";
|
|
64
|
+
this.required = false;
|
|
65
|
+
this.disabled = false;
|
|
66
|
+
this.readonly = false;
|
|
67
|
+
this.clearable = false;
|
|
68
|
+
this.multiple = false;
|
|
69
|
+
this.maxVisible = 5;
|
|
70
|
+
this.chip = {};
|
|
71
|
+
this.modelValue = void 0;
|
|
72
|
+
this.placement = "bottom-start";
|
|
73
|
+
this.flip = true;
|
|
74
|
+
this.shift = true;
|
|
75
|
+
this.offset = 6;
|
|
76
|
+
this.autoFocusSearch = true;
|
|
77
|
+
this.stayOpen = false;
|
|
78
|
+
this.cssClass = {};
|
|
79
|
+
this.cssClassName = "";
|
|
80
|
+
this._moreOpen = false;
|
|
81
|
+
this._resolvedSide = "bottom";
|
|
82
|
+
this._lastEmitted = SYMBOL_INIT;
|
|
83
|
+
this._chipStrip = new ChipStripController(this, {
|
|
84
|
+
strip: () => this.renderRoot?.querySelector(".mono-dropdown-table-chip-strip"),
|
|
85
|
+
enabled: () => this._inlineChips
|
|
86
|
+
});
|
|
87
|
+
this._popup = new PopupPortalController(this, {
|
|
88
|
+
getPanel: () => this.renderRoot.querySelector(".mono-dropdown-table-panel"),
|
|
89
|
+
getAnchor: () => this.renderRoot.querySelector(".mono-dropdown-table-trigger"),
|
|
90
|
+
getStyleScope: () => this.renderRoot.querySelector(".mono-dropdown-table"),
|
|
91
|
+
isOpen: () => this._isOpen,
|
|
92
|
+
matchWidth: false,
|
|
93
|
+
...this._placementOpts(),
|
|
94
|
+
onSideResolved: (side) => {
|
|
95
|
+
this._resolvedSide = side;
|
|
96
|
+
this._applySideClass();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
this._morePopup = new PopupPortalController(this, {
|
|
100
|
+
getPanel: () => this.renderRoot.querySelector(".mono-dropdown-table-more"),
|
|
101
|
+
getAnchor: () => this.renderRoot.querySelector(".mono-dropdown-table-more-chip"),
|
|
102
|
+
getStyleScope: () => this.renderRoot.querySelector(".mono-dropdown-table"),
|
|
103
|
+
isOpen: () => this._moreOpen,
|
|
104
|
+
matchWidth: false,
|
|
105
|
+
...this._morePlacementOpts()
|
|
106
|
+
});
|
|
107
|
+
this._ddPropsQueued = false;
|
|
108
|
+
this._onTriggerKeydown = (e) => {
|
|
109
|
+
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
this.open();
|
|
112
|
+
} else if (e.key === "Escape" && this._isOpen) {
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
this.close();
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
this._onDocPointer = (e) => {
|
|
118
|
+
if (this.stayOpen) return;
|
|
119
|
+
if (!this._isOpen && !this._moreOpen) return;
|
|
120
|
+
const path = e.composedPath();
|
|
121
|
+
if (path.includes(this)) return;
|
|
122
|
+
if (this._popup.containsInPath(path)) return;
|
|
123
|
+
if (this._morePopup.containsInPath(path)) return;
|
|
124
|
+
this.close();
|
|
125
|
+
};
|
|
126
|
+
this._onDocKey = (e) => {
|
|
127
|
+
if (e.key !== "Escape") return;
|
|
128
|
+
if (!this._isOpen && !this._moreOpen) return;
|
|
129
|
+
const path = e.composedPath();
|
|
130
|
+
if (this._popup.ownsNestedInPath(path) || this._popup.inLayerAbove(path)) return;
|
|
131
|
+
this.close();
|
|
132
|
+
};
|
|
133
|
+
this._onPanelClick = (e) => {
|
|
134
|
+
if (this.disabled || this.readonly) return;
|
|
135
|
+
const path = e.composedPath();
|
|
136
|
+
if (path.some((n) => n?.matches?.("mono-table-checkbox, mono-shadow-table-checkbox, mono-checkbox, mono-shadow-checkbox, .mono-checkbox, input, button, a, select, textarea"))) return;
|
|
137
|
+
const row = path.find((n) => n?.matches?.("[data-row-key]"));
|
|
138
|
+
if (!row) return;
|
|
139
|
+
const key = row.getAttribute("data-row-key");
|
|
140
|
+
if (key == null) return;
|
|
141
|
+
this._activeRowKey = key;
|
|
142
|
+
this._focusZone = "list";
|
|
143
|
+
this._dd?.toggleRow(key);
|
|
144
|
+
this._reflectSelection();
|
|
145
|
+
};
|
|
146
|
+
this._onViewportChange = () => {
|
|
147
|
+
if (!this._isOpen && !this._moreOpen || this._viewportFrame) return;
|
|
148
|
+
this._viewportFrame = requestAnimationFrame(() => {
|
|
149
|
+
this._viewportFrame = 0;
|
|
150
|
+
if (this._isOpen) this._positionPanel();
|
|
151
|
+
if (this._moreOpen) this._positionMorePanel();
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
this._viewportBound = false;
|
|
155
|
+
this._viewportFrame = 0;
|
|
156
|
+
this._docBound = false;
|
|
157
|
+
this._wasOpen = false;
|
|
158
|
+
this._lastSelectedCount = 0;
|
|
159
|
+
this._searchFocused = false;
|
|
160
|
+
this._focusZone = "search";
|
|
161
|
+
this._activeRowKey = null;
|
|
162
|
+
this._panelTabHeld = false;
|
|
163
|
+
this._panelTabConsumed = false;
|
|
164
|
+
this._onPanelKeydown = (e) => {
|
|
165
|
+
if (this.disabled || this.readonly || !this._isOpen) return;
|
|
166
|
+
if (e.key === "Tab") {
|
|
167
|
+
if (!this._searchEl()) return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
if (!e.repeat) {
|
|
170
|
+
this._panelTabHeld = true;
|
|
171
|
+
this._panelTabConsumed = false;
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (this._panelTabHeld && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
this._panelTabConsumed = true;
|
|
178
|
+
if (e.key === "ArrowDown") this._enterList();
|
|
179
|
+
else this._enterSearch();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
183
|
+
if (this._focusZone !== "list") return;
|
|
184
|
+
e.preventDefault();
|
|
185
|
+
this._moveActiveRow(e.key === "ArrowDown" ? 1 : -1);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (e.key === "Enter" && this._focusZone === "list") {
|
|
189
|
+
e.preventDefault();
|
|
190
|
+
this._selectActiveRow();
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
this._onPanelKeyup = (e) => {
|
|
194
|
+
if (e.key !== "Tab" || !this._panelTabHeld) return;
|
|
195
|
+
this._panelTabHeld = false;
|
|
196
|
+
if (this._panelTabConsumed) return;
|
|
197
|
+
if (this._focusZone === "search") this._enterList();
|
|
198
|
+
else this._enterSearch();
|
|
199
|
+
};
|
|
200
|
+
defineHybridPropAliases(this, [
|
|
201
|
+
"labelPosition",
|
|
202
|
+
"validationPosition",
|
|
203
|
+
"dataDropdown",
|
|
204
|
+
"modelValue",
|
|
205
|
+
"cssClass",
|
|
206
|
+
"autoFocusSearch",
|
|
207
|
+
"stayOpen",
|
|
208
|
+
"maxVisible",
|
|
209
|
+
"minVisible"
|
|
210
|
+
]);
|
|
211
|
+
defineHybridPropAlias(this, "controlDataDropdown", "dataDropdown");
|
|
212
|
+
for (const alias of ["css-class", "cssclass"]) Object.defineProperty(this, alias, {
|
|
213
|
+
get: () => this.cssClass,
|
|
214
|
+
set: (value) => this._setCssClass(value),
|
|
215
|
+
configurable: true,
|
|
216
|
+
enumerable: false
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/** Normalize an object | JSON string | plain-string `css-class` value. */
|
|
220
|
+
_setCssClass(value) {
|
|
221
|
+
if (value == null) {
|
|
222
|
+
this.cssClass = {};
|
|
223
|
+
this.cssClassName = "";
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (typeof value === "object") {
|
|
227
|
+
this.cssClass = value;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (typeof value === "string") {
|
|
231
|
+
const trimmed = value.trim();
|
|
232
|
+
if (!trimmed) {
|
|
233
|
+
this.cssClass = {};
|
|
234
|
+
this.cssClassName = "";
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) try {
|
|
238
|
+
this.cssClass = JSON.parse(trimmed);
|
|
239
|
+
return;
|
|
240
|
+
} catch {}
|
|
241
|
+
this.cssClassName = trimmed;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/** Append the consumer's per-part override to a built-in class. */
|
|
245
|
+
_cls(base, key) {
|
|
246
|
+
const extra = this.cssClass?.[key];
|
|
247
|
+
return extra ? `${base} ${extra}` : base;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* `chip` is an object prop, so the real binding is `:chip.prop="{…}"`. This
|
|
251
|
+
* covers the two ways a *string* can arrive: a hand-written JSON attribute
|
|
252
|
+
* (`chip='{"size":"md"}'`, incl. DSD/SSR), and Vue's `String(value)` mirror
|
|
253
|
+
* of a plain `:chip="{…}"` binding — which yields `"[object Object]"` and
|
|
254
|
+
* must be ignored, or it would wipe the property set moments later.
|
|
255
|
+
*/
|
|
256
|
+
_setChip(value) {
|
|
257
|
+
if (value == null) {
|
|
258
|
+
this.chip = {};
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (typeof value === "object") {
|
|
262
|
+
this.chip = value;
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (typeof value !== "string") return;
|
|
266
|
+
const trimmed = value.trim();
|
|
267
|
+
if (!trimmed) {
|
|
268
|
+
this.chip = {};
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return;
|
|
272
|
+
try {
|
|
273
|
+
this.chip = JSON.parse(trimmed);
|
|
274
|
+
} catch {}
|
|
275
|
+
}
|
|
276
|
+
static get observedAttributes() {
|
|
277
|
+
return [
|
|
278
|
+
...super.observedAttributes ?? [],
|
|
279
|
+
"css-class",
|
|
280
|
+
"cssclass",
|
|
281
|
+
"chip"
|
|
282
|
+
];
|
|
283
|
+
}
|
|
284
|
+
attributeChangedCallback(name, old, value) {
|
|
285
|
+
super.attributeChangedCallback(name, old, value);
|
|
286
|
+
if (name === "css-class" || name === "cssclass") this._setCssClass(value);
|
|
287
|
+
if (name === "chip") this._setChip(value);
|
|
288
|
+
}
|
|
289
|
+
/** The validation message and its state — the same precedence the message row uses. */
|
|
290
|
+
get _validationNotice() {
|
|
291
|
+
if (this.validationMessage) return {
|
|
292
|
+
state: this._validationState,
|
|
293
|
+
text: this.validationMessage
|
|
294
|
+
};
|
|
295
|
+
if (this.errorMessage) return {
|
|
296
|
+
state: "invalid",
|
|
297
|
+
text: this.errorMessage
|
|
298
|
+
};
|
|
299
|
+
if (this.successMessage) return {
|
|
300
|
+
state: "valid",
|
|
301
|
+
text: this.successMessage
|
|
302
|
+
};
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
/** Imperatively sync the `is-<side>` class — see `_resolvedSide`. */
|
|
306
|
+
_applySideClass() {
|
|
307
|
+
const root = this.renderRoot.querySelector(".mono-dropdown-table");
|
|
308
|
+
if (!root) return;
|
|
309
|
+
root.classList.toggle("is-top", this._resolvedSide === "top");
|
|
310
|
+
root.classList.toggle("is-bottom", this._resolvedSide === "bottom");
|
|
311
|
+
root.classList.toggle("is-left", this._resolvedSide === "left");
|
|
312
|
+
root.classList.toggle("is-right", this._resolvedSide === "right");
|
|
313
|
+
if (root.getAttribute("mono-side") !== this._resolvedSide) root.setAttribute("mono-side", this._resolvedSide);
|
|
314
|
+
}
|
|
315
|
+
/** Shared by the light path (the controller) and the shadow path (`_positionMorePanel`). */
|
|
316
|
+
_morePlacementOpts() {
|
|
317
|
+
return {
|
|
318
|
+
side: () => "bottom",
|
|
319
|
+
align: () => "start",
|
|
320
|
+
offset: () => 4,
|
|
321
|
+
flip: () => true,
|
|
322
|
+
shift: () => true,
|
|
323
|
+
constrainSize: () => true
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Placement options shared by the light path (the controller) and the shadow
|
|
328
|
+
* path (`_positionPanel`), so both run the identical algorithm.
|
|
329
|
+
*
|
|
330
|
+
* `constrainSize` is on: a field low in a dense grid can have less room than
|
|
331
|
+
* the panel wants on BOTH sides, so flipping alone isn't enough — the panel
|
|
332
|
+
* also has to shrink and scroll internally (see dropdown-table.css).
|
|
333
|
+
*/
|
|
334
|
+
_placementOpts() {
|
|
335
|
+
return {
|
|
336
|
+
side: () => this._sideFromPlacement(this.placement),
|
|
337
|
+
align: () => this._alignFromPlacement(this.placement),
|
|
338
|
+
offset: () => this.offset,
|
|
339
|
+
flip: () => this.flip,
|
|
340
|
+
shift: () => this.shift,
|
|
341
|
+
constrainSize: () => true
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
_sideFromPlacement(p) {
|
|
345
|
+
if (p.startsWith("top")) return "top";
|
|
346
|
+
if (p.startsWith("left")) return "left";
|
|
347
|
+
if (p.startsWith("right")) return "right";
|
|
348
|
+
return "bottom";
|
|
349
|
+
}
|
|
350
|
+
_alignFromPlacement(p) {
|
|
351
|
+
if (p.endsWith("-start")) return "start";
|
|
352
|
+
if (p.endsWith("-end")) return "end";
|
|
353
|
+
return "center";
|
|
354
|
+
}
|
|
355
|
+
get _dd() {
|
|
356
|
+
return this.dataDropdown;
|
|
357
|
+
}
|
|
358
|
+
get _isOpen() {
|
|
359
|
+
return !!this._dd?.open;
|
|
360
|
+
}
|
|
361
|
+
get _multi() {
|
|
362
|
+
return this.multiple || !!this._dd?.multiple;
|
|
363
|
+
}
|
|
364
|
+
get _hasValue() {
|
|
365
|
+
const v = this._dd?.value;
|
|
366
|
+
return this._multi ? Array.isArray(v) && v.length > 0 : v != null;
|
|
367
|
+
}
|
|
368
|
+
/** Resolve the effective validation state (message props force it). */
|
|
369
|
+
get _validationState() {
|
|
370
|
+
if (this.validationState && this.validationState !== "default") return this.validationState;
|
|
371
|
+
if (this.errorMessage) return "invalid";
|
|
372
|
+
if (this.successMessage) return "valid";
|
|
373
|
+
return "default";
|
|
374
|
+
}
|
|
375
|
+
get _wrapperClasses() {
|
|
376
|
+
const v = this._validationState;
|
|
377
|
+
return [
|
|
378
|
+
"mono-dropdown-table",
|
|
379
|
+
this.size,
|
|
380
|
+
this.color,
|
|
381
|
+
this.variant,
|
|
382
|
+
`is-${this._resolvedSide}`,
|
|
383
|
+
this._isOpen ? "open" : "",
|
|
384
|
+
this._moreOpen ? "more-open" : "",
|
|
385
|
+
this.disabled ? "disabled" : "",
|
|
386
|
+
this.readonly ? "readonly" : "",
|
|
387
|
+
v !== "default" ? `is-${v}` : "",
|
|
388
|
+
this._hasValue ? "has-value" : "",
|
|
389
|
+
this._multi ? "multiple" : "",
|
|
390
|
+
this.cssClassName,
|
|
391
|
+
this.cssClass?.root
|
|
392
|
+
].filter(Boolean).join(" ");
|
|
393
|
+
}
|
|
394
|
+
get _triggerClasses() {
|
|
395
|
+
return [
|
|
396
|
+
"mono-dropdown-table-trigger",
|
|
397
|
+
this.size,
|
|
398
|
+
this.color,
|
|
399
|
+
this.variant,
|
|
400
|
+
this.disabled ? "disabled" : "",
|
|
401
|
+
this.readonly ? "readonly" : "",
|
|
402
|
+
this.cssClass?.trigger
|
|
403
|
+
].filter(Boolean).join(" ");
|
|
404
|
+
}
|
|
405
|
+
connectedCallback() {
|
|
406
|
+
super.connectedCallback();
|
|
407
|
+
this._subscribe();
|
|
408
|
+
this._wireDropdown();
|
|
409
|
+
if (!isServer) this._syncViewportListeners();
|
|
410
|
+
}
|
|
411
|
+
disconnectedCallback() {
|
|
412
|
+
this._off?.();
|
|
413
|
+
this._off = void 0;
|
|
414
|
+
this._panelObserver?.disconnect();
|
|
415
|
+
this._panelObserver = void 0;
|
|
416
|
+
if (this._dropdownWired) this._dropdownWired.onValueChange = null;
|
|
417
|
+
if (!isServer) {
|
|
418
|
+
document.removeEventListener("pointerdown", this._onDocPointer, true);
|
|
419
|
+
document.removeEventListener("keydown", this._onDocKey, true);
|
|
420
|
+
this._docBound = false;
|
|
421
|
+
window.removeEventListener("scroll", this._onViewportChange, true);
|
|
422
|
+
window.removeEventListener("resize", this._onViewportChange);
|
|
423
|
+
if (this._viewportFrame) cancelAnimationFrame(this._viewportFrame);
|
|
424
|
+
this._viewportFrame = 0;
|
|
425
|
+
this._viewportBound = false;
|
|
426
|
+
}
|
|
427
|
+
super.disconnectedCallback();
|
|
428
|
+
}
|
|
429
|
+
willUpdate(changed) {
|
|
430
|
+
if (changed.has("dataDropdown")) {
|
|
431
|
+
this._subscribe();
|
|
432
|
+
this._wireDropdown();
|
|
433
|
+
}
|
|
434
|
+
if (changed.has("modelValue")) this._pushModelToDd();
|
|
435
|
+
if (changed.has("dataDropdown") || changed.has("max") || changed.has("min") || changed.has("chip")) this._pushLimits();
|
|
436
|
+
super.willUpdate?.(changed);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Second entry point for the props apply. `MonoFormControlCore` (composed
|
|
440
|
+
* below this core) also overrides `update`, so chain `super`.
|
|
441
|
+
*/
|
|
442
|
+
update(changed) {
|
|
443
|
+
this._scheduleApplyDropdownProps();
|
|
444
|
+
super.update(changed);
|
|
445
|
+
}
|
|
446
|
+
_subscribe() {
|
|
447
|
+
this._off?.();
|
|
448
|
+
detachEventHandlers(this);
|
|
449
|
+
this._off = this._dd?.subscribe(() => {
|
|
450
|
+
this._scheduleApplyDropdownProps();
|
|
451
|
+
this.requestUpdate();
|
|
452
|
+
});
|
|
453
|
+
this._scheduleApplyDropdownProps();
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Deferred + de-duplicated, and driven from `update()` as well as
|
|
457
|
+
* `_subscribe()` — the same belt-and-braces the table base needed once two
|
|
458
|
+
* cores were found replacing `_subscribe` without chaining `super`. The
|
|
459
|
+
* deferral keeps the reactive writes out of the update cycle, so they can't
|
|
460
|
+
* trip Lit's change-in-update warning.
|
|
461
|
+
*/
|
|
462
|
+
_scheduleApplyDropdownProps() {
|
|
463
|
+
if (this._ddPropsQueued) return;
|
|
464
|
+
if (typeof queueMicrotask !== "function") {
|
|
465
|
+
this._applyDropdownProps();
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
this._ddPropsQueued = true;
|
|
469
|
+
queueMicrotask(() => {
|
|
470
|
+
this._ddPropsQueued = false;
|
|
471
|
+
this._applyDropdownProps();
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Pull `monoDataDropdown({ props: { dropdownTable } })` onto this element, so
|
|
476
|
+
* the field needs nothing but `:data-dropdown.prop`. The CONTROLLER WINS for
|
|
477
|
+
* keys it declares; the rest stay with the template.
|
|
478
|
+
*/
|
|
479
|
+
_applyDropdownProps() {
|
|
480
|
+
const dd = this._dd;
|
|
481
|
+
if (!dd?.props) return;
|
|
482
|
+
applyProps(this, dd.props().dropdownTable);
|
|
483
|
+
}
|
|
484
|
+
_wireDropdown() {
|
|
485
|
+
const dd = this._dd;
|
|
486
|
+
if (this._dropdownWired && this._dropdownWired !== dd) this._dropdownWired.onValueChange = null;
|
|
487
|
+
this._dropdownWired = dd;
|
|
488
|
+
if (!dd) return;
|
|
489
|
+
dd.onValueChange = (v) => {
|
|
490
|
+
this._lastEmitted = v;
|
|
491
|
+
this.modelValue = v;
|
|
492
|
+
this._emitChange(v);
|
|
493
|
+
};
|
|
494
|
+
this._pushModelToDd();
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* The selection limits the element asks for: `chip.max` / `chip.min` pin,
|
|
498
|
+
* `max` / `min` are the fallback, and `undefined` leaves the controller's own
|
|
499
|
+
* option in force. They are ENFORCED by the controller's check store (the one
|
|
500
|
+
* place a row click, the keyboard, a `<mono-table-checkbox>` and the
|
|
501
|
+
* select-all drain all go through), so the element only forwards them.
|
|
502
|
+
*/
|
|
503
|
+
_pushLimits() {
|
|
504
|
+
const chip = this._chipProps;
|
|
505
|
+
this._dd?.setLimits({
|
|
506
|
+
max: resolveChipLimit(chip, "max", this.max),
|
|
507
|
+
min: resolveChipLimit(chip, "min", this.min)
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
/** Push an external `model-value` into the controller (guarding our own echo). */
|
|
511
|
+
_pushModelToDd() {
|
|
512
|
+
const dd = this._dd;
|
|
513
|
+
if (!dd || this.modelValue === void 0) return;
|
|
514
|
+
if (this._sameValue(this.modelValue, this._lastEmitted)) return;
|
|
515
|
+
if (this._sameValue(this.modelValue, dd.value)) return;
|
|
516
|
+
dd.setValue(this.modelValue);
|
|
517
|
+
}
|
|
518
|
+
_sameValue(a, b) {
|
|
519
|
+
if (a === b) return true;
|
|
520
|
+
if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((x, i) => String(x) === String(b[i]));
|
|
521
|
+
return String(a) === String(b);
|
|
522
|
+
}
|
|
523
|
+
_emitChange(value) {
|
|
524
|
+
const detail = {
|
|
525
|
+
modelValue: value,
|
|
526
|
+
value,
|
|
527
|
+
selectedItems: this._dd?.selectedItems() ?? []
|
|
528
|
+
};
|
|
529
|
+
dispatchMonoEvent(this, "change", detail);
|
|
530
|
+
}
|
|
531
|
+
/** Whether the panel is currently open. */
|
|
532
|
+
get isOpen() {
|
|
533
|
+
return this._isOpen;
|
|
534
|
+
}
|
|
535
|
+
open() {
|
|
536
|
+
if (this.disabled || this.readonly) return;
|
|
537
|
+
this._dd?.setOpen(true);
|
|
538
|
+
}
|
|
539
|
+
close() {
|
|
540
|
+
this._moreOpen = false;
|
|
541
|
+
this._dd?.setOpen(false);
|
|
542
|
+
this.requestUpdate();
|
|
543
|
+
}
|
|
544
|
+
toggle() {
|
|
545
|
+
if (this.disabled || this.readonly) return;
|
|
546
|
+
this._moreOpen = false;
|
|
547
|
+
this._dd?.setOpen(!this._isOpen);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Where the `[data-row-key]` rows live: light = the captured table inside the
|
|
551
|
+
* (portaled) panel; shadow = the SLOTTED table (host's light-DOM children).
|
|
552
|
+
*/
|
|
553
|
+
get _rowScope() {
|
|
554
|
+
return this.renderRoot === this ? this._popup.panelRoot : this;
|
|
555
|
+
}
|
|
556
|
+
_reflectSelection(rows = this._rowEls()) {
|
|
557
|
+
const dd = this._dd;
|
|
558
|
+
if (!dd) return;
|
|
559
|
+
if ((this._focusZone === "list" || this._activeRowKey != null) && rows.length && !rows.some((el) => el.getAttribute("data-row-key") === this._activeRowKey)) this._activeRowKey = rows[0].getAttribute("data-row-key");
|
|
560
|
+
rows.forEach((el) => {
|
|
561
|
+
const key = el.getAttribute("data-row-key");
|
|
562
|
+
const on = key != null && dd.isSelected(key);
|
|
563
|
+
const active = key != null && key === this._activeRowKey;
|
|
564
|
+
el.classList.toggle("mono-dd-row-selected", on);
|
|
565
|
+
el.classList.toggle("mono-dd-row-active", active);
|
|
566
|
+
el.toggleAttribute("mono-selected", on);
|
|
567
|
+
el.toggleAttribute("mono-dd-selected", on);
|
|
568
|
+
el.toggleAttribute("mono-dd-active", active);
|
|
569
|
+
if (on !== el.hasAttribute("aria-selected")) {
|
|
570
|
+
if (on) el.setAttribute("aria-selected", "true");
|
|
571
|
+
else el.removeAttribute("aria-selected");
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
/** Re-reflect after the consumer's Vue re-renders rows (paging/search). */
|
|
576
|
+
_observePanel() {
|
|
577
|
+
const root = this._rowScope;
|
|
578
|
+
if (!root || this._panelObserver) return;
|
|
579
|
+
const touchesRows = (nodes) => {
|
|
580
|
+
for (const n of Array.from(nodes)) {
|
|
581
|
+
if (n.nodeType !== 1) continue;
|
|
582
|
+
const el = n;
|
|
583
|
+
if (el.matches("[data-row-key]") || el.querySelector("[data-row-key]")) return true;
|
|
584
|
+
}
|
|
585
|
+
return false;
|
|
586
|
+
};
|
|
587
|
+
this._panelObserver = new MutationObserver((records) => {
|
|
588
|
+
if (records.some((r) => touchesRows(r.addedNodes) || touchesRows(r.removedNodes))) this._reflectSelection();
|
|
589
|
+
});
|
|
590
|
+
this._panelObserver.observe(root, {
|
|
591
|
+
childList: true,
|
|
592
|
+
subtree: true
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
/** True for the shadow build — a shadow host keeps its panel in the shadow root. */
|
|
596
|
+
get _isShadowHost() {
|
|
597
|
+
return this.renderRoot !== this;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Position the panel for the SHADOW build (renderRoot ≠ this): the popup
|
|
601
|
+
* controller only portals/positions LIGHT hosts (`_canPortal`), so a shadow
|
|
602
|
+
* host places its own `position:fixed` panel. It runs the SAME shared math as
|
|
603
|
+
* the light path — flip, shift and size-constrain included. Light is untouched.
|
|
604
|
+
*/
|
|
605
|
+
_positionPanel() {
|
|
606
|
+
if (isServer || !this._isShadowHost) return;
|
|
607
|
+
if (!this._isOpen) return;
|
|
608
|
+
const root = this.renderRoot;
|
|
609
|
+
const panel = root.querySelector(".mono-dropdown-table-panel");
|
|
610
|
+
const anchor = root.querySelector(".mono-dropdown-table-trigger");
|
|
611
|
+
if (!panel || !anchor) return;
|
|
612
|
+
const o = this._placementOpts();
|
|
613
|
+
const placement = computePopupPlacement(anchor, panel, {
|
|
614
|
+
side: o.side(),
|
|
615
|
+
align: o.align(),
|
|
616
|
+
offset: o.offset(),
|
|
617
|
+
flip: o.flip(),
|
|
618
|
+
shift: o.shift(),
|
|
619
|
+
constrain: true
|
|
620
|
+
});
|
|
621
|
+
applyPopupPlacement(panel, placement, true);
|
|
622
|
+
this._resolvedSide = placement.side;
|
|
623
|
+
this._applySideClass();
|
|
624
|
+
}
|
|
625
|
+
/** The shadow build's in-place placement of the "+N more" panel — see `_positionPanel`. */
|
|
626
|
+
_positionMorePanel() {
|
|
627
|
+
if (isServer || !this._isShadowHost) return;
|
|
628
|
+
if (!this._moreOpen) return;
|
|
629
|
+
const root = this.renderRoot;
|
|
630
|
+
const panel = root.querySelector(".mono-dropdown-table-more");
|
|
631
|
+
const anchor = root.querySelector(".mono-dropdown-table-more-chip");
|
|
632
|
+
if (!panel || !anchor) return;
|
|
633
|
+
const o = this._morePlacementOpts();
|
|
634
|
+
const placement = computePopupPlacement(anchor, panel, {
|
|
635
|
+
side: o.side(),
|
|
636
|
+
align: o.align(),
|
|
637
|
+
offset: o.offset(),
|
|
638
|
+
flip: o.flip(),
|
|
639
|
+
shift: o.shift(),
|
|
640
|
+
constrain: true
|
|
641
|
+
});
|
|
642
|
+
applyPopupPlacement(panel, placement, true);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Outside-dismiss listeners, both builds, bound while a panel is OPEN only (select's rule).
|
|
646
|
+
* Both handlers bail when the list and "+N more" panels are closed, so at rest they cost
|
|
647
|
+
* only dispatch — two capture-phase `document` listeners per field.
|
|
648
|
+
*/
|
|
649
|
+
_syncDocListeners() {
|
|
650
|
+
const want = this.isConnected && (this._isOpen || this._moreOpen);
|
|
651
|
+
if (want === this._docBound) return;
|
|
652
|
+
this._docBound = want;
|
|
653
|
+
if (want) {
|
|
654
|
+
document.addEventListener("pointerdown", this._onDocPointer, true);
|
|
655
|
+
document.addEventListener("keydown", this._onDocKey, true);
|
|
656
|
+
} else {
|
|
657
|
+
document.removeEventListener("pointerdown", this._onDocPointer, true);
|
|
658
|
+
document.removeEventListener("keydown", this._onDocKey, true);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Shadow build only, and only while a panel is OPEN — the popup controller's rule. They used
|
|
663
|
+
* to be bound at connect for every shadow instance: a capture-phase, non-passive `scroll`
|
|
664
|
+
* listener on `window` per field, open or not, which also keeps the page's scrolling off the
|
|
665
|
+
* compositor thread.
|
|
666
|
+
*/
|
|
667
|
+
_syncViewportListeners() {
|
|
668
|
+
if (isServer) return;
|
|
669
|
+
this._syncDocListeners();
|
|
670
|
+
const want = this._isShadowHost && this.isConnected && (this._isOpen || this._moreOpen);
|
|
671
|
+
if (want === this._viewportBound) return;
|
|
672
|
+
if (want) {
|
|
673
|
+
window.addEventListener("scroll", this._onViewportChange, {
|
|
674
|
+
capture: true,
|
|
675
|
+
passive: true
|
|
676
|
+
});
|
|
677
|
+
window.addEventListener("resize", this._onViewportChange, { passive: true });
|
|
678
|
+
} else {
|
|
679
|
+
window.removeEventListener("scroll", this._onViewportChange, true);
|
|
680
|
+
window.removeEventListener("resize", this._onViewportChange);
|
|
681
|
+
if (this._viewportFrame) cancelAnimationFrame(this._viewportFrame);
|
|
682
|
+
this._viewportFrame = 0;
|
|
683
|
+
}
|
|
684
|
+
this._viewportBound = want;
|
|
685
|
+
}
|
|
686
|
+
updated(changed) {
|
|
687
|
+
super.updated(changed);
|
|
688
|
+
if (isServer) return;
|
|
689
|
+
this._syncViewportListeners();
|
|
690
|
+
if (this._isOpen) {
|
|
691
|
+
this._observePanel();
|
|
692
|
+
this._reflectSelection();
|
|
693
|
+
this._positionPanel();
|
|
694
|
+
} else {
|
|
695
|
+
this._panelObserver?.disconnect();
|
|
696
|
+
this._panelObserver = void 0;
|
|
697
|
+
}
|
|
698
|
+
if (this._moreOpen) {
|
|
699
|
+
if (!this._overflowItems(this._dd?.selectedItems() ?? []).length) {
|
|
700
|
+
this._moreOpen = false;
|
|
701
|
+
this.requestUpdate();
|
|
702
|
+
} else this._positionMorePanel();
|
|
703
|
+
}
|
|
704
|
+
const selectedCount = this._dd?.selectedItems?.()?.length ?? 0;
|
|
705
|
+
const capChanged = changed.has("maxVisible") || changed.has("minVisible") || changed.has("chip");
|
|
706
|
+
if (selectedCount !== this._lastSelectedCount) {
|
|
707
|
+
if (this._isOpen && selectedCount > this._lastSelectedCount && this._inlineChips) this._chipStrip.scrollToEnd();
|
|
708
|
+
else this._chipStrip.invalidate();
|
|
709
|
+
} else if (capChanged) this._chipStrip.invalidate();
|
|
710
|
+
this._lastSelectedCount = selectedCount;
|
|
711
|
+
if (this._isOpen !== this._wasOpen) {
|
|
712
|
+
this._wasOpen = this._isOpen;
|
|
713
|
+
if (this._isOpen) this._initPanelFocus();
|
|
714
|
+
else {
|
|
715
|
+
this._activeRowKey = null;
|
|
716
|
+
this._focusZone = "search";
|
|
717
|
+
this._panelTabHeld = false;
|
|
718
|
+
this._restoreTriggerFocus();
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/** The focusable trigger (`tabindex=0`), in both builds. */
|
|
723
|
+
_triggerElement() {
|
|
724
|
+
return this.renderRoot.querySelector(".mono-dropdown-table-trigger");
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Delegate focus to the trigger — the host itself isn't focusable. Needed by
|
|
728
|
+
* anything that focuses the component from outside, e.g. the data grid
|
|
729
|
+
* focusing the editor in a clicked cell (`focusRowCell`), which looks for an
|
|
730
|
+
* inner `input/textarea/select/button` and otherwise falls back to calling
|
|
731
|
+
* `focus()` on the element itself.
|
|
732
|
+
*/
|
|
733
|
+
focus(options) {
|
|
734
|
+
this._triggerElement()?.focus(options);
|
|
735
|
+
}
|
|
736
|
+
blur() {
|
|
737
|
+
this._triggerElement()?.blur();
|
|
738
|
+
}
|
|
739
|
+
/** The search region's slotted content — light: captured nodes; shadow: `<slot>`. */
|
|
740
|
+
_searchRegionElements() {
|
|
741
|
+
if (this._isShadowHost) {
|
|
742
|
+
const slot = this.renderRoot.querySelector("slot[name=\"search\"]");
|
|
743
|
+
return slot ? slot.assignedElements({ flatten: true }) : [];
|
|
744
|
+
}
|
|
745
|
+
const sel = "[data-mono-slot=\"search\"]";
|
|
746
|
+
const region = this.querySelector(sel) ?? this._popup.panelRoot?.querySelector?.(sel);
|
|
747
|
+
return region ? Array.from(region.children) : [];
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Focus the panel's search box once the panel is rendered, placed and (light
|
|
751
|
+
* build) portaled. Deferred a microtask so a slotted element that hasn't
|
|
752
|
+
* rendered its input yet gets a chance to — `updateComplete` on the search
|
|
753
|
+
* element itself, since it updates independently of this host.
|
|
754
|
+
*/
|
|
755
|
+
/**
|
|
756
|
+
* The element to focus for "the search box", or null when the panel has no
|
|
757
|
+
* search region. `mono-table-search` / `mono-shadow-table-search` expose
|
|
758
|
+
* `focus()` and delegate to their own input, so neither build needs a DOM
|
|
759
|
+
* reach-in; anything else slotted in its place falls back to its first field.
|
|
760
|
+
*/
|
|
761
|
+
_searchEl() {
|
|
762
|
+
for (const el of this._searchRegionElements()) {
|
|
763
|
+
if (el.matches("mono-table-search, mono-shadow-table-search")) return el;
|
|
764
|
+
const sel = "input:not([type=\"hidden\"]), textarea, select";
|
|
765
|
+
const field = el.matches(sel) ? el : el.querySelector(sel) ?? el.shadowRoot?.querySelector(sel);
|
|
766
|
+
if (field) return field;
|
|
767
|
+
}
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
async _focusSearch() {
|
|
771
|
+
await this.updateComplete;
|
|
772
|
+
if (!this._isOpen) return;
|
|
773
|
+
const el = this._searchEl();
|
|
774
|
+
if (!el) return;
|
|
775
|
+
const pending = el.updateComplete;
|
|
776
|
+
if (pending) await pending;
|
|
777
|
+
if (!this._isOpen) return;
|
|
778
|
+
el.focus({ preventScroll: true });
|
|
779
|
+
this._focusZone = "search";
|
|
780
|
+
this._searchFocused = true;
|
|
781
|
+
}
|
|
782
|
+
_rowEls() {
|
|
783
|
+
const root = this._rowScope;
|
|
784
|
+
if (!root?.querySelectorAll) return [];
|
|
785
|
+
return Array.from(root.querySelectorAll("[data-row-key]"));
|
|
786
|
+
}
|
|
787
|
+
_panelEl() {
|
|
788
|
+
return (this._popup.panelRoot ?? this.renderRoot)?.querySelector?.(".mono-dropdown-table-panel") ?? null;
|
|
789
|
+
}
|
|
790
|
+
/** `rows`: the caller's own `_rowEls()` — one query per arrow key, not three. */
|
|
791
|
+
_setActiveRow(key, reveal = false, rows = this._rowEls()) {
|
|
792
|
+
this._activeRowKey = key;
|
|
793
|
+
this._reflectSelection(rows);
|
|
794
|
+
if (!reveal || key == null) return;
|
|
795
|
+
rows.find((r) => r.getAttribute("data-row-key") === key)?.scrollIntoView?.({ block: "nearest" });
|
|
796
|
+
}
|
|
797
|
+
_moveActiveRow(delta) {
|
|
798
|
+
const rows = this._rowEls();
|
|
799
|
+
const keys = rows.map((r) => r.getAttribute("data-row-key") ?? "");
|
|
800
|
+
if (!keys.length) return;
|
|
801
|
+
const i = this._activeRowKey == null ? -1 : keys.indexOf(this._activeRowKey);
|
|
802
|
+
const next = i < 0 ? delta > 0 ? 0 : keys.length - 1 : Math.min(Math.max(i + delta, 0), keys.length - 1);
|
|
803
|
+
this._setActiveRow(keys[next], true, rows);
|
|
804
|
+
}
|
|
805
|
+
/** Move the keyboard into the row list, highlighting the first row. */
|
|
806
|
+
_enterList() {
|
|
807
|
+
this._focusZone = "list";
|
|
808
|
+
if (this._activeRowKey == null) this._setActiveRow(this._rowEls()[0]?.getAttribute("data-row-key") ?? null, true);
|
|
809
|
+
this._panelEl()?.focus({ preventScroll: true });
|
|
810
|
+
}
|
|
811
|
+
/** Move the keyboard back to the search box (no-op when there isn't one). */
|
|
812
|
+
_enterSearch() {
|
|
813
|
+
const el = this._searchEl();
|
|
814
|
+
if (!el) return;
|
|
815
|
+
this._focusZone = "search";
|
|
816
|
+
el.focus({ preventScroll: true });
|
|
817
|
+
}
|
|
818
|
+
_selectActiveRow() {
|
|
819
|
+
if (this._activeRowKey == null) return;
|
|
820
|
+
this._dd?.toggleRow(this._activeRowKey);
|
|
821
|
+
this._reflectSelection();
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Where the keyboard starts when the panel opens: the search box if there is
|
|
825
|
+
* one, otherwise straight into the list with the first row highlighted.
|
|
826
|
+
*/
|
|
827
|
+
async _initPanelFocus() {
|
|
828
|
+
await this.updateComplete;
|
|
829
|
+
if (!this._isOpen) return;
|
|
830
|
+
const selected = this._dd?.selectedItems?.() ?? [];
|
|
831
|
+
if (selected.length) this._setActiveRow(String(selected[selected.length - 1].key), true);
|
|
832
|
+
if (this._searchEl()) {
|
|
833
|
+
this._focusZone = "search";
|
|
834
|
+
if (this.autoFocusSearch) this._focusSearch();
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
this._enterList();
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Hand focus back to the trigger when a panel that HAD focus closes — hiding
|
|
841
|
+
* the panel blurs its input to `<body>`, which would otherwise strand the
|
|
842
|
+
* keyboard. Covers every close path (Escape, outside pointerdown, a
|
|
843
|
+
* controller-driven auto-close after a single-select pick) because it hangs
|
|
844
|
+
* off the state edge rather than off `close()`.
|
|
845
|
+
*/
|
|
846
|
+
_restoreTriggerFocus() {
|
|
847
|
+
if (!this._searchFocused) return;
|
|
848
|
+
this._searchFocused = false;
|
|
849
|
+
const active = document.activeElement;
|
|
850
|
+
const panel = this._popup.panelRoot?.querySelector?.(".mono-dropdown-table-panel");
|
|
851
|
+
if (!(!active || active === document.body || this.contains(active) || !!panel?.contains(active))) return;
|
|
852
|
+
this.focus({ preventScroll: true });
|
|
853
|
+
}
|
|
854
|
+
/** A panel region — light: a `data-mono-slot` capture target; shadow overrides with `<slot>`. */
|
|
855
|
+
_renderRegion(cls, name) {
|
|
856
|
+
return html`<div class="mono-dropdown-table-region ${cls}" mono-dd-region=${cls} data-mono-slot="${name}"></div>`;
|
|
857
|
+
}
|
|
858
|
+
_caretIcon() {
|
|
859
|
+
return caretIcon();
|
|
860
|
+
}
|
|
861
|
+
_chevronIcon(dir) {
|
|
862
|
+
return chevronIcon(dir);
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* One scroll button. `active` false keeps the box — and therefore the row's
|
|
866
|
+
* width — but hides it (`is-idle` → `visibility: hidden`), so reaching an end
|
|
867
|
+
* of the strip costs no layout. `aria-hidden` keeps the hidden one out of
|
|
868
|
+
* reach of assistive tech; `pointer-events: none` handles the pointer.
|
|
869
|
+
*/
|
|
870
|
+
_renderScrollButton(dir, active) {
|
|
871
|
+
const back = dir === -1;
|
|
872
|
+
const base = `mono-dropdown-table-scroll mono-dropdown-table-scroll-${back ? "prev" : "next"}${active ? "" : " is-idle"}`;
|
|
873
|
+
return html`<span
|
|
874
|
+
role="button"
|
|
875
|
+
tabindex="-1"
|
|
876
|
+
class=${this._cls(base, back ? "scrollPrev" : "scrollNext")}
|
|
877
|
+
mono-dd-scroll=${back ? "prev" : "next"}
|
|
878
|
+
?mono-idle=${!active}
|
|
879
|
+
aria-label=${back ? "Scroll selection backward" : "Scroll selection forward"}
|
|
880
|
+
aria-hidden=${active ? "false" : "true"}
|
|
881
|
+
@mousedown=${(e) => e.preventDefault()}
|
|
882
|
+
@click=${(e) => {
|
|
883
|
+
e.stopPropagation();
|
|
884
|
+
this._chipStrip.page(dir);
|
|
885
|
+
}}
|
|
886
|
+
>${this._chevronIcon(dir)}</span
|
|
887
|
+
>`;
|
|
888
|
+
}
|
|
889
|
+
/** The `chip` prop, always an object (it can be assigned null from a binding). */
|
|
890
|
+
get _chipProps() {
|
|
891
|
+
return this.chip ?? {};
|
|
892
|
+
}
|
|
893
|
+
/** Chip layout: `inline` = one scrolling line, anything else = today's wrap. */
|
|
894
|
+
get _chipBehaviour() {
|
|
895
|
+
return this._chipProps.behaviour === "inline" ? "inline" : "flex";
|
|
896
|
+
}
|
|
897
|
+
/** True where a scrolling strip actually renders: multi, inline, with chips. */
|
|
898
|
+
get _inlineChips() {
|
|
899
|
+
return this._multi && this._hasValue && this._chipBehaviour === "inline";
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* How many chips to draw for `total` selected rows before the rest collapse
|
|
903
|
+
* into "+N more" — Infinity when uncapped or under the collapse floor.
|
|
904
|
+
* `chip.maxVisible` / `chip.minVisible` pin, the element props are the
|
|
905
|
+
* fallback (`resolveChipLimit`). Applies to `inline` as well: the "+N more"
|
|
906
|
+
* chip then sits in the strip after the visible ones and its panel lists the
|
|
907
|
+
* rest, exactly as in `flex`.
|
|
908
|
+
*/
|
|
909
|
+
_visibleCap(total) {
|
|
910
|
+
const chip = this._chipProps;
|
|
911
|
+
return visibleChipCap(total, resolveChipLimit(chip, "maxVisible", this.maxVisible), resolveChipLimit(chip, "minVisible", this.minVisible));
|
|
912
|
+
}
|
|
913
|
+
/** The selection floor in force (element / chip / controller), 0 when none. */
|
|
914
|
+
get _minSelected() {
|
|
915
|
+
return this._dd?.limits().min ?? 0;
|
|
916
|
+
}
|
|
917
|
+
/** A chip can be removed while the selection sits above its floor. */
|
|
918
|
+
get _canRemove() {
|
|
919
|
+
return (this._dd?.selectedItems?.()?.length ?? 0) > this._minSelected;
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Chip skin matching the field skin: a `filled` field already tints its own
|
|
923
|
+
* surface, so a soft chip would vanish into it — go solid; `underlined` is
|
|
924
|
+
* minimal, so the chip stays outline. `chip.variant` pins one instead.
|
|
925
|
+
*/
|
|
926
|
+
get _chipVariantClass() {
|
|
927
|
+
const pinned = this._chipProps.variant;
|
|
928
|
+
if (pinned) return pinned;
|
|
929
|
+
if (this.variant === "filled") return "solid";
|
|
930
|
+
if (this.variant === "underlined") return "outline";
|
|
931
|
+
return "soft";
|
|
932
|
+
}
|
|
933
|
+
/** Chips wear the control's color unless `chip.color` pins another. */
|
|
934
|
+
get _chipColorClass() {
|
|
935
|
+
return this._chipProps.color ?? this.color;
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Classes shared by the selection chips and the "+N more" chip, in
|
|
939
|
+
* `mono-chip`'s own order (`mono-chip <size> <variant>-<color> <shape>`), so
|
|
940
|
+
* chip.css paints them exactly as it paints a real `<mono-chip>`.
|
|
941
|
+
*
|
|
942
|
+
* `mono-dropdown-table-chip` is a marker only — it scopes the `--mono-chip-*`
|
|
943
|
+
* bridge in dropdown-table.css so the bridge never reaches a `<mono-chip>` a
|
|
944
|
+
* consumer slots into the panel. `chip.color` deliberately picks a hue OTHER
|
|
945
|
+
* than the control accent, so it adds the `-pinned` marker that switches the
|
|
946
|
+
* bridge back off.
|
|
947
|
+
*/
|
|
948
|
+
get _chipBaseClasses() {
|
|
949
|
+
const chip = this._chipProps;
|
|
950
|
+
return [
|
|
951
|
+
"mono-dropdown-table-chip",
|
|
952
|
+
chip.color ? "mono-dropdown-table-chip-pinned" : "",
|
|
953
|
+
chip.size ?? (this.size === "xs" || this.size === "sm" ? "xs" : "sm"),
|
|
954
|
+
`${this._chipVariantClass}-${this._chipColorClass}`,
|
|
955
|
+
"",
|
|
956
|
+
chip.dot ? "has-dot" : ""
|
|
957
|
+
].filter(Boolean).join(" ");
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Class for one chip part. Both class APIs append: the field-level key
|
|
961
|
+
* (`cssClass.chip` / `.chipLabel` / `.chipRemove`) and the chip-level one
|
|
962
|
+
* (`chip.cssClass.*`, which additionally reaches the parts the field has no
|
|
963
|
+
* key for — `main`, `content`, `dot`).
|
|
964
|
+
*/
|
|
965
|
+
_chipCls(base, chipKey, key) {
|
|
966
|
+
return [
|
|
967
|
+
base,
|
|
968
|
+
key ? this.cssClass?.[key] : void 0,
|
|
969
|
+
this._chipProps.cssClass?.[chipKey]
|
|
970
|
+
].filter(Boolean).join(" ");
|
|
971
|
+
}
|
|
972
|
+
_renderChip(it) {
|
|
973
|
+
const chip = this._chipProps;
|
|
974
|
+
const removable = !this.disabled && !this.readonly && this._canRemove;
|
|
975
|
+
return html`
|
|
976
|
+
<div
|
|
977
|
+
class=${this._chipCls(`${this._chipBaseClasses}${removable ? " removable" : ""}`, "root", "chip")}
|
|
978
|
+
mono-dd-chip
|
|
979
|
+
?mono-dd-removable=${removable}
|
|
980
|
+
mono-dd-chip-color=${chip.color ?? nothing}
|
|
981
|
+
mono-dd-chip-rounded=${chip.rounded ?? nothing}
|
|
982
|
+
>
|
|
983
|
+
<span class=${ifDefined(chip.cssClass?.main)} mono-dd-chip-main>
|
|
984
|
+
<span class=${this._chipCls("chip-content", "content")} mono-dd-chip-content>
|
|
985
|
+
${chip.dot ? html`<span class=${this._chipCls("chip-dot", "dot")} mono-dd-chip-dot aria-hidden="true"></span>` : nothing}
|
|
986
|
+
<span class=${this._chipCls("chip-label", "label", "chipLabel")} mono-dd-chip-label>${it.text}</span>
|
|
987
|
+
${removable ? html`<button
|
|
988
|
+
type="button"
|
|
989
|
+
class=${this._chipCls("chip-close", "close", "chipRemove")}
|
|
990
|
+
mono-dd-chip-close
|
|
991
|
+
aria-label=${chip.closeLabel ?? `Remove ${it.text}`}
|
|
992
|
+
@click=${(e) => {
|
|
993
|
+
e.stopPropagation();
|
|
994
|
+
this._dd?.removeKey(it.key);
|
|
995
|
+
}}
|
|
996
|
+
>
|
|
997
|
+
${closeIcon()}
|
|
998
|
+
</button>` : nothing}
|
|
999
|
+
</span>
|
|
1000
|
+
</span>
|
|
1001
|
+
</div>
|
|
1002
|
+
`;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* The chip region. `flex` returns the chips as bare children of the value
|
|
1006
|
+
* span, so they wrap — the historical markup, unchanged. `inline` nests them
|
|
1007
|
+
* in one `overflow-x: hidden` strip instead, which is what makes the row
|
|
1008
|
+
* scroll as a unit.
|
|
1009
|
+
*/
|
|
1010
|
+
_renderChipRegion(items) {
|
|
1011
|
+
const chips = this._renderChips(items);
|
|
1012
|
+
if (this._chipBehaviour !== "inline") return chips;
|
|
1013
|
+
return html`
|
|
1014
|
+
<span class=${this._cls("mono-dropdown-table-chip-strip", "chipStrip")} mono-dd-chip-strip>${chips}</span>
|
|
1015
|
+
`;
|
|
1016
|
+
}
|
|
1017
|
+
_renderChips(items) {
|
|
1018
|
+
const max = this._visibleCap(items.length);
|
|
1019
|
+
const visible = max === Infinity ? items : items.slice(0, max);
|
|
1020
|
+
const overflow = this._overflowItems(items);
|
|
1021
|
+
const chips = visible.map((it) => this._renderChip(it));
|
|
1022
|
+
if (overflow.length) chips.push(html`
|
|
1023
|
+
<div
|
|
1024
|
+
class=${this._cls(`${this._chipBaseClasses.replace(" has-dot", "")} clickable mono-dropdown-table-more-chip`, "moreChip")}
|
|
1025
|
+
mono-dd-chip
|
|
1026
|
+
mono-dd-more
|
|
1027
|
+
mono-dd-chip-color=${this._chipProps.color ?? nothing}
|
|
1028
|
+
mono-dd-chip-rounded=${this._chipProps.rounded ?? nothing}
|
|
1029
|
+
>
|
|
1030
|
+
<span
|
|
1031
|
+
class=${ifDefined(this._chipProps.cssClass?.main)}
|
|
1032
|
+
mono-dd-chip-main
|
|
1033
|
+
role="button"
|
|
1034
|
+
tabindex="0"
|
|
1035
|
+
@mousedown=${(e) => e.preventDefault()}
|
|
1036
|
+
@click=${(e) => {
|
|
1037
|
+
e.stopPropagation();
|
|
1038
|
+
const next = !this._moreOpen;
|
|
1039
|
+
this._dd?.setOpen(false);
|
|
1040
|
+
this._moreOpen = next;
|
|
1041
|
+
this.requestUpdate();
|
|
1042
|
+
}}
|
|
1043
|
+
>
|
|
1044
|
+
<span class=${this._chipCls("chip-content", "content")} mono-dd-chip-content
|
|
1045
|
+
><span class=${this._chipCls("chip-label", "label")} mono-dd-chip-label
|
|
1046
|
+
>+${overflow.length} more</span
|
|
1047
|
+
></span
|
|
1048
|
+
>
|
|
1049
|
+
</span>
|
|
1050
|
+
</div>
|
|
1051
|
+
`);
|
|
1052
|
+
return chips;
|
|
1053
|
+
}
|
|
1054
|
+
/** Selected rows past the visible cap — empty when uncapped or under the collapse floor. */
|
|
1055
|
+
_overflowItems(items) {
|
|
1056
|
+
const max = this._visibleCap(items.length);
|
|
1057
|
+
return max === Infinity ? [] : items.slice(max);
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Always rendered, shown by the root's `more-open` class: the panel lives in
|
|
1061
|
+
* a body portal while open and a portaled node cannot be removed by a
|
|
1062
|
+
* conditional render (Lit's ChildPart no longer contains it).
|
|
1063
|
+
*/
|
|
1064
|
+
_renderMore(items) {
|
|
1065
|
+
const overflow = this._moreOpen ? this._overflowItems(items) : [];
|
|
1066
|
+
return html`<div class="mono-dropdown-table-more" mono-dd-more>${overflow.map((it) => this._renderChip(it))}</div>`;
|
|
1067
|
+
}
|
|
1068
|
+
_renderLabel() {
|
|
1069
|
+
if (!this.label) return nothing;
|
|
1070
|
+
return html`<label class=${this._cls("mono-dropdown-table-label", "label")} mono-dd-label
|
|
1071
|
+
>${this.label}${this.required ? html`<span class=${this._cls("mono-dropdown-table-required", "required")} mono-dd-required-mark>*</span>` : nothing}${this._tooltip.render()}</label
|
|
1072
|
+
>`;
|
|
1073
|
+
}
|
|
1074
|
+
_renderMessage() {
|
|
1075
|
+
const base = this._cls("mono-dropdown-table-message", "message");
|
|
1076
|
+
if (this._validation.moved) {
|
|
1077
|
+
const notice = this._validationNotice;
|
|
1078
|
+
return html`${this.helperText ? html`<div class="${base} helper" mono-dd-message="helper">${this.helperText}</div>` : nothing}<div
|
|
1079
|
+
class="${base} ${notice.state}"
|
|
1080
|
+
mono-dd-message=${notice.state}
|
|
1081
|
+
mono-sr-only
|
|
1082
|
+
role=${notice.state === "invalid" ? "alert" : nothing}
|
|
1083
|
+
>${notice.text}</div>`;
|
|
1084
|
+
}
|
|
1085
|
+
if (this.validationMessage) {
|
|
1086
|
+
const state = this._validationState;
|
|
1087
|
+
return html`<div class="${base} ${state}" mono-dd-message=${state} role=${state === "invalid" ? "alert" : nothing}>
|
|
1088
|
+
${this.validationMessage}
|
|
1089
|
+
</div>`;
|
|
1090
|
+
}
|
|
1091
|
+
if (this.errorMessage) return html`<div class="${base} invalid" mono-dd-message="invalid" role="alert">${this.errorMessage}</div>`;
|
|
1092
|
+
if (this.successMessage) return html`<div class="${base} valid" mono-dd-message="valid">${this.successMessage}</div>`;
|
|
1093
|
+
if (this.helperText) return html`<div class="${base} helper" mono-dd-message="helper">${this.helperText}</div>`;
|
|
1094
|
+
return nothing;
|
|
1095
|
+
}
|
|
1096
|
+
_renderTrigger(items) {
|
|
1097
|
+
const dd = this._dd;
|
|
1098
|
+
const hasValue = this._hasValue;
|
|
1099
|
+
const inert = this.disabled || this.readonly;
|
|
1100
|
+
const canClear = this.clearable && hasValue && !inert && !(this._multi && this._minSelected > 0);
|
|
1101
|
+
const inlineChips = this._inlineChips;
|
|
1102
|
+
return html`
|
|
1103
|
+
<div
|
|
1104
|
+
class=${this._triggerClasses}
|
|
1105
|
+
mono-dd-trigger
|
|
1106
|
+
role="combobox"
|
|
1107
|
+
tabindex=${this.disabled ? -1 : 0}
|
|
1108
|
+
aria-expanded=${this._isOpen ? "true" : "false"}
|
|
1109
|
+
@click=${() => this.toggle()}
|
|
1110
|
+
@keydown=${this._onTriggerKeydown}
|
|
1111
|
+
>
|
|
1112
|
+
<span
|
|
1113
|
+
class="${this._cls("mono-dropdown-table-value", "value")}${hasValue ? "" : ` mono-dropdown-table-placeholder${this.cssClass?.placeholder ? ` ${this.cssClass.placeholder}` : ""}`}${inlineChips ? " is-inline" : ""}"
|
|
1114
|
+
mono-dd-value
|
|
1115
|
+
?mono-dd-placeholder=${!hasValue}
|
|
1116
|
+
?mono-inline=${inlineChips}
|
|
1117
|
+
>
|
|
1118
|
+
${this._multi ? hasValue ? this._renderChipRegion(items) : this.placeholder : hasValue ? html`<span>${dd?.displayText()}</span>` : this.placeholder}
|
|
1119
|
+
</span>
|
|
1120
|
+
${this._validation.renderIcon()}
|
|
1121
|
+
<span class=${this._cls("mono-dropdown-table-actions", "actions")} mono-dd-actions>
|
|
1122
|
+
${inlineChips && this._chipStrip.overflowing ? html`${this._renderScrollButton(-1, this._chipStrip.canScrollStart)}
|
|
1123
|
+
${this._renderScrollButton(1, this._chipStrip.canScrollEnd)}` : nothing}
|
|
1124
|
+
${canClear ? html`<span
|
|
1125
|
+
role="button"
|
|
1126
|
+
tabindex="-1"
|
|
1127
|
+
class=${this._cls("mono-dropdown-table-clear", "clear")}
|
|
1128
|
+
mono-dd-clear
|
|
1129
|
+
aria-label="Clear"
|
|
1130
|
+
@mousedown=${(e) => e.preventDefault()}
|
|
1131
|
+
@click=${(e) => {
|
|
1132
|
+
e.stopPropagation();
|
|
1133
|
+
this._dd?.clear();
|
|
1134
|
+
this.close();
|
|
1135
|
+
}}
|
|
1136
|
+
>${closeIcon()}</span
|
|
1137
|
+
>` : nothing}
|
|
1138
|
+
${!inert && !canClear ? html`<span
|
|
1139
|
+
role="button"
|
|
1140
|
+
tabindex="-1"
|
|
1141
|
+
class=${this._cls("mono-dropdown-table-arrow", "arrow")}
|
|
1142
|
+
mono-dd-arrow
|
|
1143
|
+
aria-label="Toggle"
|
|
1144
|
+
aria-expanded=${this._isOpen ? "true" : "false"}
|
|
1145
|
+
@mousedown=${(e) => e.preventDefault()}
|
|
1146
|
+
@click=${(e) => {
|
|
1147
|
+
e.stopPropagation();
|
|
1148
|
+
this.toggle();
|
|
1149
|
+
}}
|
|
1150
|
+
>${this._caretIcon()}</span
|
|
1151
|
+
>` : nothing}
|
|
1152
|
+
</span>
|
|
1153
|
+
</div>
|
|
1154
|
+
`;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Panel-only sizing from the `dropdown` object.
|
|
1158
|
+
*
|
|
1159
|
+
* `maxHeight` is published as the custom property `--_mono-dropdown-table-panel-max-h` rather than a direct
|
|
1160
|
+
* `max-height`: the popup controller also constrains the panel (via `--mono-popup-avail-h`),
|
|
1161
|
+
* and two writers on the same declaration would clobber each other across renders. The CSS
|
|
1162
|
+
* combines both with `min()`. Everything else is a plain declaration.
|
|
1163
|
+
*
|
|
1164
|
+
* `height` is an EXACT height here now. It used to be the cap, because this object had no
|
|
1165
|
+
* `maxHeight`; it does now, and the three popup components share one meaning.
|
|
1166
|
+
*/
|
|
1167
|
+
_panelStyle() {
|
|
1168
|
+
const d = this.dropdown;
|
|
1169
|
+
if (!d) return {};
|
|
1170
|
+
const s = {};
|
|
1171
|
+
const w = toCssSize(d.width);
|
|
1172
|
+
const mw = toCssSize(d.minWidth);
|
|
1173
|
+
const xw = toCssSize(d.maxWidth);
|
|
1174
|
+
const h = toCssSize(d.height);
|
|
1175
|
+
const mh = toCssSize(d.minHeight);
|
|
1176
|
+
const xh = toCssSize(d.maxHeight);
|
|
1177
|
+
if (w) s.width = w;
|
|
1178
|
+
if (mw) s["min-width"] = mw;
|
|
1179
|
+
if (xw) s["max-width"] = xw;
|
|
1180
|
+
if (h) s.height = h;
|
|
1181
|
+
if (mh) s["min-height"] = mh;
|
|
1182
|
+
if (xh) s["--_mono-dropdown-table-panel-max-h"] = xh;
|
|
1183
|
+
return s;
|
|
1184
|
+
}
|
|
1185
|
+
render() {
|
|
1186
|
+
const items = this._dd?.selectedItems() ?? [];
|
|
1187
|
+
const state = this._validationState;
|
|
1188
|
+
return html`
|
|
1189
|
+
<div
|
|
1190
|
+
class=${this._wrapperClasses}
|
|
1191
|
+
style=${styleMap(buildSizeStyle(this))}
|
|
1192
|
+
mono-dropdown-table
|
|
1193
|
+
mono-size=${this.size === "md" ? nothing : this.size}
|
|
1194
|
+
mono-color=${this.color === "primary" ? nothing : this.color}
|
|
1195
|
+
mono-variant=${this.variant === "outlined" ? nothing : this.variant}
|
|
1196
|
+
mono-label-position=${this.labelPosition === "top" ? nothing : this.labelPosition}
|
|
1197
|
+
mono-validation-position=${this.validationPosition === "bottom" ? nothing : this.validationPosition}
|
|
1198
|
+
mono-validation-state=${state === "default" ? nothing : state}
|
|
1199
|
+
mono-side=${this._resolvedSide}
|
|
1200
|
+
?mono-open=${this._isOpen}
|
|
1201
|
+
?mono-more-open=${this._moreOpen}
|
|
1202
|
+
?mono-disabled=${this.disabled}
|
|
1203
|
+
?mono-readonly=${this.readonly}
|
|
1204
|
+
?mono-required=${this.required}
|
|
1205
|
+
?mono-clearable=${this.clearable}
|
|
1206
|
+
?mono-multiple=${this._multi}
|
|
1207
|
+
?mono-has-value=${this._hasValue}
|
|
1208
|
+
>
|
|
1209
|
+
${this._renderLabel()}
|
|
1210
|
+
<div
|
|
1211
|
+
class="mono-dropdown-table-control${this._inlineChips ? " is-inline" : ""}"
|
|
1212
|
+
mono-dd-control
|
|
1213
|
+
?mono-inline=${this._inlineChips}
|
|
1214
|
+
>
|
|
1215
|
+
${this._renderTrigger(items)} ${this._renderMore(items)}
|
|
1216
|
+
</div>
|
|
1217
|
+
${this._renderMessage()}
|
|
1218
|
+
${this._validation.renderFloat()}
|
|
1219
|
+
<div
|
|
1220
|
+
class="mono-dropdown-table-panel ${this._isOpen ? "open" : ""}"
|
|
1221
|
+
mono-dd-panel
|
|
1222
|
+
role="dialog"
|
|
1223
|
+
tabindex="-1"
|
|
1224
|
+
style=${styleMap(this._panelStyle())}
|
|
1225
|
+
@click=${this._onPanelClick}
|
|
1226
|
+
@keydown=${this._onPanelKeydown}
|
|
1227
|
+
@keyup=${this._onPanelKeyup}
|
|
1228
|
+
>
|
|
1229
|
+
${this._renderRegion("search", "search")}
|
|
1230
|
+
${this._renderRegion("body", "body")}
|
|
1231
|
+
${this._renderRegion("foot", "footer")}
|
|
1232
|
+
</div>
|
|
1233
|
+
</div>
|
|
1234
|
+
`;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
__decorate([property({ attribute: false })], MonoDropdownTableCoreClass.prototype, "dataDropdown", void 0);
|
|
1238
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "size", void 0);
|
|
1239
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "color", void 0);
|
|
1240
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "variant", void 0);
|
|
1241
|
+
__decorate([property({
|
|
1242
|
+
type: String,
|
|
1243
|
+
attribute: "label-position"
|
|
1244
|
+
})], MonoDropdownTableCoreClass.prototype, "labelPosition", void 0);
|
|
1245
|
+
__decorate([property({
|
|
1246
|
+
type: String,
|
|
1247
|
+
attribute: "validation-position"
|
|
1248
|
+
})], MonoDropdownTableCoreClass.prototype, "validationPosition", void 0);
|
|
1249
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "label", void 0);
|
|
1250
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "placeholder", void 0);
|
|
1251
|
+
__decorate([property({
|
|
1252
|
+
type: String,
|
|
1253
|
+
attribute: "helper-text"
|
|
1254
|
+
})], MonoDropdownTableCoreClass.prototype, "helperText", void 0);
|
|
1255
|
+
__decorate([property()], MonoDropdownTableCoreClass.prototype, "tooltip", void 0);
|
|
1256
|
+
__decorate([property({
|
|
1257
|
+
type: String,
|
|
1258
|
+
attribute: "validation-state"
|
|
1259
|
+
})], MonoDropdownTableCoreClass.prototype, "validationState", void 0);
|
|
1260
|
+
__decorate([property({
|
|
1261
|
+
type: String,
|
|
1262
|
+
attribute: "validation-message"
|
|
1263
|
+
})], MonoDropdownTableCoreClass.prototype, "validationMessage", void 0);
|
|
1264
|
+
__decorate([property({
|
|
1265
|
+
type: String,
|
|
1266
|
+
attribute: "error-message"
|
|
1267
|
+
})], MonoDropdownTableCoreClass.prototype, "errorMessage", void 0);
|
|
1268
|
+
__decorate([property({
|
|
1269
|
+
type: String,
|
|
1270
|
+
attribute: "success-message"
|
|
1271
|
+
})], MonoDropdownTableCoreClass.prototype, "successMessage", void 0);
|
|
1272
|
+
__decorate([property({
|
|
1273
|
+
reflect: true,
|
|
1274
|
+
converter: booleanStringConverter
|
|
1275
|
+
})], MonoDropdownTableCoreClass.prototype, "required", void 0);
|
|
1276
|
+
__decorate([property({
|
|
1277
|
+
reflect: true,
|
|
1278
|
+
converter: booleanStringConverter
|
|
1279
|
+
})], MonoDropdownTableCoreClass.prototype, "disabled", void 0);
|
|
1280
|
+
__decorate([property({
|
|
1281
|
+
reflect: true,
|
|
1282
|
+
converter: booleanStringConverter
|
|
1283
|
+
})], MonoDropdownTableCoreClass.prototype, "readonly", void 0);
|
|
1284
|
+
__decorate([property({
|
|
1285
|
+
reflect: true,
|
|
1286
|
+
converter: booleanStringConverter
|
|
1287
|
+
})], MonoDropdownTableCoreClass.prototype, "clearable", void 0);
|
|
1288
|
+
__decorate([property({
|
|
1289
|
+
reflect: true,
|
|
1290
|
+
converter: booleanStringConverter
|
|
1291
|
+
})], MonoDropdownTableCoreClass.prototype, "multiple", void 0);
|
|
1292
|
+
__decorate([property({ converter: optionalNumberConverter })], MonoDropdownTableCoreClass.prototype, "max", void 0);
|
|
1293
|
+
__decorate([property({ converter: optionalNumberConverter })], MonoDropdownTableCoreClass.prototype, "min", void 0);
|
|
1294
|
+
__decorate([property({
|
|
1295
|
+
attribute: "max-visible",
|
|
1296
|
+
converter: optionalNumberConverter
|
|
1297
|
+
})], MonoDropdownTableCoreClass.prototype, "maxVisible", void 0);
|
|
1298
|
+
__decorate([property({
|
|
1299
|
+
attribute: "min-visible",
|
|
1300
|
+
converter: optionalNumberConverter
|
|
1301
|
+
})], MonoDropdownTableCoreClass.prototype, "minVisible", void 0);
|
|
1302
|
+
__decorate([property({ attribute: false })], MonoDropdownTableCoreClass.prototype, "chip", void 0);
|
|
1303
|
+
__decorate([property({ attribute: "model-value" })], MonoDropdownTableCoreClass.prototype, "modelValue", void 0);
|
|
1304
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "width", void 0);
|
|
1305
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "height", void 0);
|
|
1306
|
+
__decorate([property({
|
|
1307
|
+
type: String,
|
|
1308
|
+
attribute: "min-width"
|
|
1309
|
+
})], MonoDropdownTableCoreClass.prototype, "minWidth", void 0);
|
|
1310
|
+
__decorate([property({
|
|
1311
|
+
type: String,
|
|
1312
|
+
attribute: "max-width"
|
|
1313
|
+
})], MonoDropdownTableCoreClass.prototype, "maxWidth", void 0);
|
|
1314
|
+
__decorate([property({
|
|
1315
|
+
type: String,
|
|
1316
|
+
attribute: "min-height"
|
|
1317
|
+
})], MonoDropdownTableCoreClass.prototype, "minHeight", void 0);
|
|
1318
|
+
__decorate([property({
|
|
1319
|
+
type: String,
|
|
1320
|
+
attribute: "max-height"
|
|
1321
|
+
})], MonoDropdownTableCoreClass.prototype, "maxHeight", void 0);
|
|
1322
|
+
__decorate([property({ attribute: false })], MonoDropdownTableCoreClass.prototype, "dropdown", void 0);
|
|
1323
|
+
__decorate([property({ type: String })], MonoDropdownTableCoreClass.prototype, "placement", void 0);
|
|
1324
|
+
__decorate([property({ converter: booleanStringConverter })], MonoDropdownTableCoreClass.prototype, "flip", void 0);
|
|
1325
|
+
__decorate([property({ converter: booleanStringConverter })], MonoDropdownTableCoreClass.prototype, "shift", void 0);
|
|
1326
|
+
__decorate([property({ converter: numberStringConverter })], MonoDropdownTableCoreClass.prototype, "offset", void 0);
|
|
1327
|
+
__decorate([property({
|
|
1328
|
+
attribute: "auto-focus-search",
|
|
1329
|
+
converter: booleanStringConverter
|
|
1330
|
+
})], MonoDropdownTableCoreClass.prototype, "autoFocusSearch", void 0);
|
|
1331
|
+
__decorate([property({
|
|
1332
|
+
attribute: "stay-open",
|
|
1333
|
+
reflect: true,
|
|
1334
|
+
converter: booleanStringConverter
|
|
1335
|
+
})], MonoDropdownTableCoreClass.prototype, "stayOpen", void 0);
|
|
1336
|
+
__decorate([property({ attribute: false })], MonoDropdownTableCoreClass.prototype, "cssClass", void 0);
|
|
1337
|
+
__decorate([property({ attribute: false })], MonoDropdownTableCoreClass.prototype, "cssClassName", void 0);
|
|
1338
|
+
return MonoDropdownTableCoreClass;
|
|
1339
|
+
};
|
|
1340
|
+
|
|
1341
|
+
//#endregion
|
|
1342
|
+
export { MonoDropdownTableCore as t };
|