@mono-lit/helper 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/accordion-core-DaNyPWRr.js +277 -0
  2. package/dist/alert-core-DWtHbX_-.js +134 -0
  3. package/dist/breadcrumb-list-core-Dh68T8Cf.js +747 -0
  4. package/dist/{mono-button-CLkHg3WW.js → button-core-DerXg84Q.js} +3 -276
  5. package/dist/button-dropdown-core-DKYEOyjy.js +654 -0
  6. package/dist/button-utils-D5-66M02.js +63 -0
  7. package/dist/card-utils-l80qAmrN.js +312 -0
  8. package/dist/chart-core-DV7hD3ZY.js +801 -0
  9. package/dist/checkbox-utils-DKdrhAzF.js +337 -0
  10. package/dist/chip-utils-DpRrd7Zj.js +403 -0
  11. package/dist/{data-source-controller-C6C4ej_M.js → data-source-controller-DS_JLrmS.js} +1 -1
  12. package/dist/{data-source-options-fxT5QIX4.js → data-source-options-DJUNU26C.js} +6 -6
  13. package/dist/{data-source-read-BL266IW0.js → data-source-read-uKJpRAzx.js} +2 -2
  14. package/dist/date-core-CyuSyvvG.js +734 -0
  15. package/dist/drawer-core-DbFWvNln.js +759 -0
  16. package/dist/dropdown-core-BCH1pK14.js +511 -0
  17. package/dist/dropdown-table-core-SfHUJVFq.js +1342 -0
  18. package/dist/export.js +1 -1
  19. package/dist/{field-path-BabiMrrA.js → field-path-DK2VBWHl.js} +1 -1
  20. package/dist/field-tooltip.css_raw-DVblekCl.js +5 -0
  21. package/dist/{field-validation.css_raw-Cx6SGrfP.js → field-validation.css_raw-BU7C6173.js} +1 -170
  22. package/dist/file-upload-core-Cz_dBQMx.js +624 -0
  23. package/dist/filter-builder-core-DyWjbBP7.js +381 -0
  24. package/dist/{filter-builder-B6Y2mmbM.js → filter-builder-vbqIuPhX.js} +1 -1
  25. package/dist/{filter-eval-ChDSaAUH.js → filter-eval-CqeBnMYh.js} +1 -1
  26. package/dist/{filter-odata-Bi27XUpQ.js → filter-odata-GsczYKNC.js} +1 -1
  27. package/dist/{hydration-flush-BaE9gN3C.js → hydration-flush-Volt9X7Q.js} +2 -1071
  28. package/dist/index.js +11 -11
  29. package/dist/{index.node-CLqE8F8P.js → index.node-Csy9zrr1.js} +4 -4
  30. package/dist/index.node.js +11 -11
  31. package/dist/input-utils-BglY-k0H.js +1145 -0
  32. package/dist/menu-core-DkAYuCZ2.js +831 -0
  33. package/dist/modal-core-CSBSPN_m.js +844 -0
  34. package/dist/mono-button-DVvZ03P-.js +280 -0
  35. package/dist/{mono-button.shadow-DqZMpcqY.js → mono-button.shadow-BANxMGln.js} +6 -594
  36. package/dist/{mono-data-chart-NKPdAgVq.js → mono-data-chart-ChF4J9RT.js} +6 -6
  37. package/dist/{mono-data-dropdown-DUXo_jxd.js → mono-data-dropdown-Bgj2PDfm.js} +1 -1
  38. package/dist/{mono-data-grid-Bb00QM72.js → mono-data-grid-DJkp96N-.js} +346 -51
  39. package/dist/{mono-event-BPku9HpS.js → mono-event-CAZf0F_k.js} +14 -9
  40. package/dist/{mono-form-controller-BrA10erm.js → mono-form-controller-DxZLvyg6.js} +2 -1
  41. package/dist/mono-table-checkbox-core-BKOMx8R5.js +5288 -0
  42. package/dist/nav-core-B5qU2dzn.js +225 -0
  43. package/dist/{normalize-error-D93O4q8g.js → normalize-error-BEcd1tmS.js} +2 -2
  44. package/dist/otp-core-DXg3vFLh.js +758 -0
  45. package/dist/radio-utils-Dk9Tl1jz.js +319 -0
  46. package/dist/rich-text-editor-core-Ddd_QaLn.js +1280 -0
  47. package/dist/select-utils-BgArYHk8.js +2140 -0
  48. package/dist/sidebar-core-CD6txDo3.js +583 -0
  49. package/dist/slider-core-vcWr321-.js +1014 -0
  50. package/dist/src/components/dropdown-table/dropdown-table-core.d.ts +2 -2
  51. package/dist/src/components/dropdown-table/dropdown-table-types.d.ts +1 -1
  52. package/dist/src/components/dropdown-table/index.d.ts +1 -1
  53. package/dist/src/components/dropdown-table/index.shadow.d.ts +1 -1
  54. package/dist/src/components/dropdown-table/mono-data-dropdown.d.ts +1 -1
  55. package/dist/src/components/form/form-types.d.ts +9 -3
  56. package/dist/src/components/input/input-core.d.ts +1 -1
  57. package/dist/src/components/input/input-types.d.ts +1 -1
  58. package/dist/src/components/otp/otp-core.d.ts +1 -1
  59. package/dist/src/components/select/select-core.d.ts +1 -1
  60. package/dist/src/components/select/select-types.d.ts +1 -1
  61. package/dist/src/components/slider/slider-core.d.ts +1 -1
  62. package/dist/src/components/slider/slider-types.d.ts +1 -1
  63. package/dist/src/components/table/filter-suggestion.d.ts +171 -0
  64. package/dist/src/components/table/index.d.ts +5 -4
  65. package/dist/src/components/table/index.shadow.d.ts +5 -4
  66. package/dist/src/components/table/mono-data-grid.d.ts +58 -3
  67. package/dist/src/components/table/mono-table-empty-core.d.ts +37 -0
  68. package/dist/src/components/table/mono-table-search-core.d.ts +22 -3
  69. package/dist/src/components/table/table-types.d.ts +74 -4
  70. package/dist/src/components/tag-input/tag-input-core.d.ts +1 -1
  71. package/dist/src/components/tag-input/tag-input-types.d.ts +1 -1
  72. package/dist/src/components/textarea/textarea-core.d.ts +1 -1
  73. package/dist/src/composables/mono-event.d.ts +7 -1
  74. package/dist/src/entries/index.d.ts +8 -6
  75. package/dist/src/entries/index.node.d.ts +8 -6
  76. package/dist/src/search/index.d.ts +1 -1
  77. package/dist/src/utils/group-count.d.ts +36 -0
  78. package/dist/switch-utils-CTZ4id2V.js +338 -0
  79. package/dist/tabs-core-r5-B8k_N.js +317 -0
  80. package/dist/tag-input-utils-D2JA90DQ.js +3060 -0
  81. package/dist/textarea-utils-VU1KeXm8.js +821 -0
  82. package/dist/ui/accordion.js +2 -274
  83. package/dist/ui/alert.js +2 -131
  84. package/dist/ui/breadcrumb.js +4 -745
  85. package/dist/ui/button-dropdown.js +4 -652
  86. package/dist/ui/button.js +2 -63
  87. package/dist/ui/card.js +2 -310
  88. package/dist/ui/chart.js +4 -800
  89. package/dist/ui/checkbox.js +4 -335
  90. package/dist/ui/chip.js +4 -402
  91. package/dist/ui/date.js +3 -732
  92. package/dist/ui/drawer.js +3 -757
  93. package/dist/ui/dropdown-table.js +5 -1341
  94. package/dist/ui/dropdown.js +4 -509
  95. package/dist/ui/file-upload.js +3 -622
  96. package/dist/ui/filter.js +5 -380
  97. package/dist/ui/form.js +1 -1
  98. package/dist/ui/index.css +130 -5
  99. package/dist/ui/input.js +3 -1141
  100. package/dist/ui/menu.js +4 -828
  101. package/dist/ui/modal.js +4 -843
  102. package/dist/ui/nav.js +4 -223
  103. package/dist/ui/otp.js +4 -756
  104. package/dist/ui/radio.js +4 -317
  105. package/dist/ui/rich-text-editor.js +4 -1278
  106. package/dist/ui/select.js +5 -2138
  107. package/dist/ui/shadow/accordion.js +4 -273
  108. package/dist/ui/shadow/alert.js +4 -130
  109. package/dist/ui/shadow/breadcrumb.js +5 -744
  110. package/dist/ui/shadow/button-dropdown.js +6 -652
  111. package/dist/ui/shadow/button.js +3 -63
  112. package/dist/ui/shadow/card.js +4 -309
  113. package/dist/ui/shadow/chart.js +6 -1576
  114. package/dist/ui/shadow/checkbox.js +4 -334
  115. package/dist/ui/shadow/chip.js +4 -396
  116. package/dist/ui/shadow/date.js +7 -731
  117. package/dist/ui/shadow/drawer.js +4 -756
  118. package/dist/ui/shadow/dropdown-table.js +8 -1762
  119. package/dist/ui/shadow/dropdown.js +4 -508
  120. package/dist/ui/shadow/file-upload.js +4 -620
  121. package/dist/ui/shadow/filter.js +7 -379
  122. package/dist/ui/shadow/input.js +6 -1140
  123. package/dist/ui/shadow/menu.js +5 -828
  124. package/dist/ui/shadow/modal.js +6 -1063
  125. package/dist/ui/shadow/nav.js +5 -223
  126. package/dist/ui/shadow/otp.js +6 -754
  127. package/dist/ui/shadow/radio.js +4 -316
  128. package/dist/ui/shadow/rich-text-editor.js +4 -1276
  129. package/dist/ui/shadow/select.js +6 -2136
  130. package/dist/ui/shadow/sidebar.js +5 -581
  131. package/dist/ui/shadow/slider.js +5 -1010
  132. package/dist/ui/shadow/switch.js +4 -335
  133. package/dist/ui/shadow/table.js +10 -4501
  134. package/dist/ui/shadow/tabs.js +6 -315
  135. package/dist/ui/shadow/tag-input.js +7 -3057
  136. package/dist/ui/shadow/textarea.js +6 -817
  137. package/dist/ui/sidebar.js +3 -581
  138. package/dist/ui/slider.js +4 -1012
  139. package/dist/ui/switch.js +4 -336
  140. package/dist/ui/table.js +9 -4502
  141. package/dist/ui/tabs.js +4 -315
  142. package/dist/ui/tag-input.js +5 -3058
  143. package/dist/ui/textarea.js +4 -819
  144. package/dist/vue.d.ts +3 -2
  145. package/package.json +3 -3
  146. package/dist/chip-strip-bNfLn52Z.js +0 -215
  147. package/dist/data-source-controller-Dx6IQ2CN.js +0 -243
  148. package/dist/data-source-options-BM4r4PRw.js +0 -558
  149. package/dist/data-source-read-HyBCRg1t.js +0 -242
  150. package/dist/excel-BXzr3uI1.js +0 -784
  151. package/dist/export-ixRdCS2y.js +0 -2388
  152. package/dist/field-tooltip-addon-CWCp19mJ.js +0 -702
  153. package/dist/field-tooltip.css_raw-U5UeTa2d.js +0 -269
  154. package/dist/filter-builder-C0T3pMx2.js +0 -208
  155. package/dist/form-control-core-BbiACMtL.js +0 -226
  156. package/dist/icon-ClxnYhBG.js +0 -19
  157. package/dist/import-Dc4eXC02.js +0 -612
  158. package/dist/light-slots-_no0r2Z-.js +0 -246
  159. package/dist/markdown-uGlE_OfJ.js +0 -20191
  160. package/dist/mono-data-grid-Bkr-e1-i.js +0 -4043
  161. package/dist/normalize-error-BmLbgANw.js +0 -1048
package/dist/ui/select.js CHANGED
@@ -1,2102 +1,10 @@
1
- import { t as getFieldValue } from "../field-path-BabiMrrA.js";
2
- import { a as errorTitle, f as MonoSourceSearch, i as errorText, n as describeError, x as searchRowPredicate, y as resolveSearchFields } from "../normalize-error-D93O4q8g.js";
3
- import { n as unwrapReactive } from "../reactive-Dtd-esHO.js";
4
- import { d as buildSizeStyle, f as toCssSize, g as monoHostChildNodes } from "../mono-ui-kvLVIf1V.js";
1
+ import { g as monoHostChildNodes } from "../mono-ui-kvLVIf1V.js";
5
2
  import { t as customElement } from "../mono-element-CKcT0rwv.js";
6
- import { a as defineHybridPropAliases, n as arrayHasChanged, r as booleanStringConverter, t as __decorate } from "../decorate-DbYFpi8R.js";
7
- import { t as dispatchMonoEvent } from "../mono-event-BPku9HpS.js";
8
- import { s as placeSlotNode, t as LateSlotWatcher } from "../light-slots-B5Ck0t_E.js";
9
- import { a as rateLimitHasChanged } from "../rate-limit-e9Jlpl5l.js";
10
- import { t as PopupPortalController } from "../popup-portal-CINuVjNY.js";
11
- import { c as spinnerIcon, s as refreshIcon } from "../field-icons-t8rXRCKs.js";
12
- import { t as MonoFormControlCore } from "../form-control-core-CRZPZ1ru.js";
13
- import { t as FieldTooltipController } from "../field-tooltip-Csk4s2ve.js";
14
- import { t as FieldValidationController } from "../field-validation-CFmUmuDx.js";
15
- import { t as DataSourceController } from "../data-source-controller-Dx6IQ2CN.js";
16
- import { LitElement, html, isServer, nothing } from "lit";
17
- import { property, query, state } from "lit/decorators.js";
18
- import { ifDefined } from "lit/directives/if-defined.js";
19
- import { styleMap } from "lit/directives/style-map.js";
3
+ import { t as __decorate } from "../decorate-DbYFpi8R.js";
4
+ import { s as placeSlotNode } from "../light-slots-B5Ck0t_E.js";
5
+ import { n as MonoSelectCore, t as validateSelectProps } from "../select-utils-BgArYHk8.js";
6
+ import { LitElement, html } from "lit";
20
7
 
21
- //#region src/components/select/select-core.ts
22
- /**
23
- * `MonoSelectCore` — render-mode-agnostic logic for `mono-select` (props, hybrid
24
- * aliases, value/model sync, DataSource paging, search, grouping, keyboard nav,
25
- * the popup controller, and the full `render()`). SSR-safe: every `document` /
26
- * `window` / focus access is `isServer`-guarded.
27
- *
28
- * Each build supplies `createRenderRoot()` + `static styles`, the slot strategy
29
- * (light captures children into `data-mono-slot` placeholders; shadow uses native
30
- * `<slot>` + a `firstUpdated` scan), and the `_slotOutlet` / `renderIcon` hooks.
31
- * The shared `_has*SlotState` `@state` fields back both strategies.
32
- */
33
- const MonoSelectCore = (superClass) => {
34
- class MonoSelectCoreClass extends MonoFormControlCore(superClass) {
35
- constructor(...args) {
36
- super(...args);
37
- this.size = "md";
38
- this.color = "primary";
39
- this.variant = "outlined";
40
- this.labelPosition = "top";
41
- this.validationPosition = "bottom";
42
- this.modelValue = null;
43
- this.value = null;
44
- this.name = "";
45
- this.label = "";
46
- this.placeholder = "Select option";
47
- this.helperText = "";
48
- this.tooltip = null;
49
- this._tooltip = new FieldTooltipController(this, () => this.tooltip);
50
- this._validation = new FieldValidationController(this, () => ({
51
- position: this.validationPosition,
52
- ...this._validationNotice ?? {
53
- state: void 0,
54
- text: void 0
55
- },
56
- suppressed: this._open
57
- }), () => this.renderRoot?.querySelector?.("[mono-select] > [mono-trigger]"));
58
- this.validationState = "default";
59
- this.validationMessage = "";
60
- this.errorMessage = "";
61
- this.successMessage = "";
62
- this.disabled = false;
63
- this.readonly = false;
64
- this.required = false;
65
- this.clearable = false;
66
- this.items = [];
67
- this.dataSource = null;
68
- this.immediate = true;
69
- this.pageSize = 10;
70
- this.dropdownHeight = "";
71
- this.dropdownMaxHeight = "";
72
- this.flip = true;
73
- this.shift = true;
74
- this.keyValue = "";
75
- this.displayValue = "";
76
- this.displayGroup = [];
77
- this.groupKey = "key";
78
- this.groupItems = "items";
79
- this.group = false;
80
- this.groupSticky = false;
81
- this.searchable = false;
82
- this.stayOpen = false;
83
- this.searchValue = "";
84
- this.searchOperation = "contains";
85
- this.searchDebounce = 300;
86
- this.searchPlaceholder = "";
87
- this.loadingText = "Loading…";
88
- this.retryLabel = "Retry";
89
- this.loading = false;
90
- this.loadingError = null;
91
- this.cssClass = {};
92
- this.cssClassName = "";
93
- this._open = false;
94
- this._query = "";
95
- this._searchActive = false;
96
- this._activeIndex = -1;
97
- this._cursorPending = false;
98
- this._sourceSearch = new MonoSourceSearch();
99
- this._searchVersion = 0;
100
- this._scrollAutoMaxHeight = "";
101
- this._hasLabelSlotState = false;
102
- this._hasHelperSlotState = false;
103
- this._hasPrefixSlotState = false;
104
- this._hasSuffixSlotState = false;
105
- this._hasListSlotState = false;
106
- this._popup = new PopupPortalController(this, {
107
- getPanel: () => this.renderRoot.querySelector(".mono-select-dropdown"),
108
- getAnchor: () => this.renderRoot.querySelector(".mono-select-trigger"),
109
- getStyleScope: () => this.renderRoot.querySelector(".mono-select"),
110
- isOpen: () => this._open,
111
- matchWidth: () => !toCssSize(this.dropdown?.width),
112
- offset: () => 6,
113
- flip: () => this.flip,
114
- shift: () => this.shift,
115
- constrainSize: () => true
116
- });
117
- this._selectId = `mono-select-${Math.random().toString(36).slice(2)}`;
118
- this._messageId = `${this._selectId}-message`;
119
- this._listboxId = `${this._selectId}-listbox`;
120
- this._lateListSlot = new LateSlotWatcher(this, "list", () => this._onLateListSlot());
121
- this._docListenersBound = false;
122
- this._itemsEpoch = 0;
123
- this._handleDropdownScroll = (event) => {
124
- if (this._loadMoreMode !== "scroll") return;
125
- if (this._ds.loadingMore || this._ds.atLastPage) return;
126
- const el = event.currentTarget;
127
- if (!el) return;
128
- if (el.scrollHeight - el.scrollTop - el.clientHeight <= 24) this._ds.loadMore();
129
- };
130
- this._appliedQuery = "";
131
- this._appFailure = null;
132
- this._retry = (event) => {
133
- event.preventDefault();
134
- event.stopPropagation();
135
- const error = this._failure;
136
- if (this.disabled || this._busy || !error) return;
137
- const fromApp = !!this._appFailure;
138
- dispatchMonoEvent(this, "retry", {
139
- error,
140
- source: fromApp ? "app" : "dataSource"
141
- });
142
- if (fromApp || !this.dataSource || !this._ds.error) return;
143
- const clearSearch = !!this._appliedQuery && this._serverSearch();
144
- this._ds.retry(clearSearch ? () => this._pushSearch("") : void 0);
145
- };
146
- this._handleDocumentClick = (event) => {
147
- if (!this._open || this.stayOpen) return;
148
- const path = event.composedPath();
149
- if (path.includes(this) || this._popup.containsInPath(path)) return;
150
- this._close();
151
- };
152
- this._handleDocumentFocusIn = (event) => {
153
- if (!this._open || this.stayOpen) return;
154
- const path = event.composedPath();
155
- if (path.includes(this) || this._popup.containsInPath(path)) return;
156
- this._close();
157
- };
158
- this._handleDocumentKeydown = (event) => {
159
- if (!this._open) return;
160
- if (event.key === "Escape") {
161
- if (this._popup.inLayerAbove(event.composedPath())) return;
162
- event.preventDefault();
163
- this._close();
164
- this._triggerEl?.focus();
165
- }
166
- };
167
- this._publishedEntries = [];
168
- this._onListSlotClick = (event) => {
169
- if (this._inert) return;
170
- const target = event.target;
171
- const item = target ? this._entryFromNode(target)?.item : void 0;
172
- if (!item || item.disabled) return;
173
- this._selectItem(item, event);
174
- };
175
- defineHybridPropAliases(this, [
176
- "labelPosition",
177
- "validationPosition",
178
- "modelValue",
179
- "loadingText",
180
- "loadingError",
181
- "helperText",
182
- "validationState",
183
- "validationMessage",
184
- "errorMessage",
185
- "successMessage",
186
- "ariaLabelText",
187
- "cssClass",
188
- "keyValue",
189
- "displayValue",
190
- "displayGroup",
191
- "groupKey",
192
- "groupItems",
193
- "groupSticky",
194
- "searchValue",
195
- "searchOperation",
196
- "searchDebounce",
197
- "searchPlaceholder",
198
- "dataSource",
199
- "loadMore",
200
- "pageSize",
201
- "dropdownHeight",
202
- "dropdownMaxHeight",
203
- "minWidth",
204
- "maxWidth",
205
- "minHeight",
206
- "maxHeight",
207
- "stayOpen"
208
- ]);
209
- /**
210
- * Makes these Vue usages work:
211
- *
212
- * <mono-select :cssClass="{}" />
213
- * <mono-select :css-class="{}" />
214
- * <mono-select :cssclass="{}" />
215
- */
216
- Object.defineProperty(this, "css-class", {
217
- get: () => this.cssClass,
218
- set: (value) => {
219
- this._setCssClass(value);
220
- },
221
- configurable: true,
222
- enumerable: false
223
- });
224
- Object.defineProperty(this, "cssclass", {
225
- get: () => this.cssClass,
226
- set: (value) => {
227
- this._setCssClass(value);
228
- },
229
- configurable: true,
230
- enumerable: false
231
- });
232
- Object.defineProperty(this, "ariaLabel", {
233
- get: () => this.ariaLabelText,
234
- set: (value) => {
235
- this.ariaLabelText = value == null ? void 0 : String(value);
236
- },
237
- configurable: true,
238
- enumerable: false
239
- });
240
- Object.defineProperty(this, "aria-label", {
241
- get: () => this.ariaLabelText,
242
- set: (value) => {
243
- this.ariaLabelText = value == null ? void 0 : String(value);
244
- },
245
- configurable: true,
246
- enumerable: false
247
- });
248
- Object.defineProperty(this, "arialabel", {
249
- get: () => this.ariaLabelText,
250
- set: (value) => {
251
- this.ariaLabelText = value == null ? void 0 : String(value);
252
- },
253
- configurable: true,
254
- enumerable: false
255
- });
256
- this._ds = new DataSourceController(this, {
257
- getDataSource: () => this.dataSource,
258
- getItems: () => this.items,
259
- getImmediate: () => this.immediate,
260
- getLoadMoreMode: () => this._loadMoreMode,
261
- getPageSize: () => this._pageSize
262
- });
263
- }
264
- static {
265
- this.monoPendingAuto = "data";
266
- }
267
- _monoPendingReady() {
268
- return !this.dataSource || !this._ds.loading;
269
- }
270
- static get observedAttributes() {
271
- return [
272
- ...super.observedAttributes ?? [],
273
- "modelvalue",
274
- "loadingtext",
275
- "loadingerror",
276
- "model-value",
277
- "helpertext",
278
- "validationstate",
279
- "validationmessage",
280
- "errormessage",
281
- "successmessage",
282
- "arialabeltext",
283
- "arialabel",
284
- "css-class",
285
- "cssclass",
286
- "keyvalue",
287
- "displayvalue",
288
- "display-value",
289
- "stayopen"
290
- ];
291
- }
292
- attributeChangedCallback(name, oldValue, newValue) {
293
- super.attributeChangedCallback(name, oldValue, newValue);
294
- if (oldValue === newValue) return;
295
- if (name === "modelvalue" || name === "model-value") {
296
- this.modelValue = this._toSelectValue(newValue);
297
- return;
298
- }
299
- if (name === "loadingtext") {
300
- this.loadingText = newValue ?? "Loading…";
301
- return;
302
- }
303
- if (name === "loadingerror") {
304
- this.loadingError = newValue;
305
- return;
306
- }
307
- if (name === "helpertext") {
308
- this.helperText = newValue ?? "";
309
- return;
310
- }
311
- if (name === "validationstate") {
312
- this.validationState = newValue ?? "default";
313
- return;
314
- }
315
- if (name === "validationmessage") {
316
- this.validationMessage = newValue ?? "";
317
- return;
318
- }
319
- if (name === "errormessage") {
320
- this.errorMessage = newValue ?? "";
321
- return;
322
- }
323
- if (name === "successmessage") {
324
- this.successMessage = newValue ?? "";
325
- return;
326
- }
327
- if (name === "arialabeltext" || name === "arialabel") {
328
- this.ariaLabelText = newValue ?? void 0;
329
- return;
330
- }
331
- if (name === "keyvalue") {
332
- this.keyValue = newValue ?? "";
333
- return;
334
- }
335
- if (name === "displayvalue" || name === "display-value") {
336
- if (typeof this.displayValue === "function") return;
337
- const next = newValue ?? "";
338
- if (this._looksLikeSerializedFunction(next)) return;
339
- this.displayValue = next;
340
- return;
341
- }
342
- if (name === "stayopen") {
343
- this.stayOpen = this._toBoolean(newValue);
344
- return;
345
- }
346
- if (name === "css-class" || name === "cssclass") this._setCssClass(newValue);
347
- }
348
- /** The validation message and its state — the same precedence the message row uses. */
349
- get _validationNotice() {
350
- if (this.validationMessage) return {
351
- state: this._resolvedValidationState,
352
- text: this.validationMessage
353
- };
354
- if (this.errorMessage) return {
355
- state: "invalid",
356
- text: this.errorMessage
357
- };
358
- if (this.successMessage) return {
359
- state: "valid",
360
- text: this.successMessage
361
- };
362
- return null;
363
- }
364
- /** The message that moved to the icon / float bubble, kept for assistive tech only. */
365
- _renderMovedMessage() {
366
- const notice = this._validationNotice;
367
- return html`<div
368
- class=${`${this._cls("mono-select-message", "message")} ${notice.state}`}
369
- mono-message=${notice.state}
370
- mono-sr-only
371
- role=${notice.state === "invalid" ? "alert" : nothing}
372
- >${notice.text}</div>`;
373
- }
374
- /**
375
- * The scroll body lives inside the dropdown panel, which the portal
376
- * controller relocates into a `<body>` portal while open — query it through
377
- * `panelRoot` (the portal when adopted, else the host render root).
378
- */
379
- get _dropdownBodyEl() {
380
- return this._popup.panelRoot.querySelector(".mono-select-dropdown-body");
381
- }
382
- /**
383
- * Where the consumer's list wrapper is parked — through the SAME portal-aware root as the panel,
384
- * since while the dropdown is open it lives in a body portal a host query would miss.
385
- */
386
- get _listSlotTarget() {
387
- return this._popup.panelRoot.querySelector("[data-mono-slot=\"list\"]");
388
- }
389
- /** A late wrapper arrived. Overridden per build — one adopts it, the other rescans. */
390
- _onLateListSlot() {}
391
- connectedCallback() {
392
- super.connectedCallback();
393
- this._lateListSlot.start();
394
- if (isServer) return;
395
- if (this._open) this._bindDocumentListeners();
396
- }
397
- /**
398
- * Outside-dismiss listeners, bound on OPEN rather than on connect.
399
- *
400
- * All three handlers begin with `if (!this._open) return`, so binding them only
401
- * while open is behaviour-preserving — the cost they carried was pure event
402
- * DISPATCH. Every mounted select used to hold three `document` listeners, two of
403
- * them capture-phase, so a grid with one select per row put 600 handlers (400 in
404
- * capture) in the path of every click, focus move and keystroke on the page.
405
- * Measured on the 200-row fixture: 600 listeners before, 0 at rest after.
406
- *
407
- * Same change, and the same reasoning, as the popup viewport listeners in
408
- * `popup-portal.ts` — which this file was explicitly the remaining example of.
409
- */
410
- _bindDocumentListeners() {
411
- if (this._docListenersBound) return;
412
- this._docListenersBound = true;
413
- document.addEventListener("click", this._handleDocumentClick, true);
414
- document.addEventListener("focusin", this._handleDocumentFocusIn, true);
415
- document.addEventListener("keydown", this._handleDocumentKeydown);
416
- }
417
- _unbindDocumentListeners() {
418
- if (!this._docListenersBound) return;
419
- this._docListenersBound = false;
420
- document.removeEventListener("click", this._handleDocumentClick, true);
421
- document.removeEventListener("focusin", this._handleDocumentFocusIn, true);
422
- document.removeEventListener("keydown", this._handleDocumentKeydown);
423
- }
424
- disconnectedCallback() {
425
- if (this._searchTimer) {
426
- clearTimeout(this._searchTimer);
427
- this._searchTimer = void 0;
428
- }
429
- this._sourceSearch.clear(this.dataSource);
430
- this._lateListSlot.stop();
431
- this._stopWatchingListChrome();
432
- if (!isServer) this._unbindDocumentListeners();
433
- super.disconnectedCallback();
434
- }
435
- willUpdate(changed) {
436
- this._itemsEpoch++;
437
- for (const key of [
438
- "disabled",
439
- "loading",
440
- "readonly",
441
- "required",
442
- "clearable",
443
- "immediate",
444
- "group",
445
- "groupSticky",
446
- "searchable",
447
- "stayOpen"
448
- ]) {
449
- const v = this[key];
450
- if (typeof v !== "boolean") this[key] = this._toBoolean(v);
451
- }
452
- if (changed.has("loadingError")) {
453
- const v = this.loadingError;
454
- this._appFailure = v == null || v === false || v === "" ? null : describeError(v === true ? void 0 : v);
455
- }
456
- if (this._open && this._dsInert) {
457
- this._open = false;
458
- this._dropQuery();
459
- }
460
- if (changed.has("modelValue") && !this._isSameValue(this.value, this.modelValue)) this.value = unwrapReactive(this.modelValue);
461
- if (changed.has("value") && !this._isSameValue(this.modelValue, this.value)) this.modelValue = unwrapReactive(this.value);
462
- if (this.group && (changed.has("dataSource") || changed.has("displayGroup") || changed.has("group"))) {
463
- const fields = this._groupSortFields();
464
- if (fields.length) this.dataSource?.sort?.(fields);
465
- }
466
- if (changed.has("dataSource")) {
467
- this._ds.bind(this.dataSource);
468
- this._sourceSearch.bind(this.dataSource);
469
- }
470
- if (changed.has("searchValue") || changed.has("searchOperation") || changed.has("displayValue") || changed.has("keyValue") || changed.has("dataSource") || changed.has("items")) this._searchVersion++;
471
- if (changed.has("immediate")) this._ds.maybeImmediateLoad();
472
- if (changed.has("loadMore")) this._ds.onLoadMoreModeChange();
473
- if (changed.has("items") || changed.has("loadMore") || changed.has("pageSize")) this._ds.resetVisible();
474
- if (changed.has("_open")) {
475
- this._cursorPending = this._open;
476
- if (!this._open) this._activeIndex = -1;
477
- }
478
- if (this._cursorPending && this._open && this._filteredItems.length) {
479
- this._activeIndex = this._initialActiveIndex();
480
- this._cursorPending = false;
481
- }
482
- }
483
- updated(changed) {
484
- super.updated(changed);
485
- this._syncListEntries();
486
- if (isServer) return;
487
- if (changed.has("_open") && this._open && this.searchable) this._searchFieldEl?.focus();
488
- if (changed.has("_activeIndex") && this._open && this._activeIndex >= 0) (this._popup.panelRoot?.querySelectorAll?.(".mono-select-item")?.[this._activeIndex])?.scrollIntoView?.({ block: "nearest" });
489
- this._updateScrollAutoHeight();
490
- if (this._open) this._bindDocumentListeners();
491
- else this._unbindDocumentListeners();
492
- }
493
- /**
494
- * Scroll-mode ergonomics: a short page (e.g. 5 items) won't overflow the
495
- * default dropdown height, so there's nothing to scroll and the next page
496
- * could never be triggered. Rather than eagerly fetching another page on
497
- * open, cap the list height to the loaded rows minus a sliver — enough to
498
- * show a scrollbar so the user scrolls to load more. No height is forced
499
- * when the consumer set `dropdown-height`/`dropdown-max-height`, when the
500
- * last page is reached, or when the rows already overflow.
501
- */
502
- _updateScrollAutoHeight() {
503
- const userSetHeight = this._toCssLength(this.dropdownHeight) != null || this._toCssLength(this.dropdownMaxHeight) != null || toCssSize(this.dropdown?.height) != null || toCssSize(this.dropdown?.maxHeight) != null;
504
- if (!(this._loadMoreMode === "scroll" && this._open && !this._ds.atLastPage && !userSetHeight && this._visibleItems.length > 0)) {
505
- this._setScrollAutoMaxHeight("");
506
- return;
507
- }
508
- const el = this._dropdownBodyEl;
509
- const rowH = (el?.querySelector(".mono-select-item"))?.offsetHeight ?? 0;
510
- if (!el || !rowH) return;
511
- const CAP_ROWS = 7;
512
- const rows = this._visibleItems.length;
513
- let visible = Math.min(rows, CAP_ROWS);
514
- if (rows <= CAP_ROWS) visible -= .5;
515
- this._setScrollAutoMaxHeight(`${Math.round(visible * rowH)}px`);
516
- }
517
- _setScrollAutoMaxHeight(value) {
518
- if (value === this._scrollAutoMaxHeight) return;
519
- queueMicrotask(() => {
520
- this._scrollAutoMaxHeight = value;
521
- });
522
- }
523
- _setCssClass(value) {
524
- if (value == null) {
525
- this.cssClass = {};
526
- this.cssClassName = "";
527
- return;
528
- }
529
- if (typeof value === "object") {
530
- this.cssClass = value;
531
- return;
532
- }
533
- if (typeof value === "string") {
534
- const trimmed = value.trim();
535
- if (!trimmed) {
536
- this.cssClass = {};
537
- this.cssClassName = "";
538
- return;
539
- }
540
- /**
541
- * Optional HTML object support:
542
- * <mono-select css-class='{"root":"...", "trigger":"..."}'>
543
- */
544
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) try {
545
- this.cssClass = JSON.parse(trimmed);
546
- return;
547
- } catch {}
548
- this.cssClassName = trimmed;
549
- }
550
- }
551
- _toSelectValue(value) {
552
- if (value === void 0 || value === null) return null;
553
- if (typeof value !== "string") return value;
554
- const trimmed = value.trim();
555
- if (!trimmed) return null;
556
- if (/^-?\d+(?:\.\d+)?$/.test(trimmed) || trimmed === "true" || trimmed === "false" || trimmed === "null" || trimmed[0] === "{" && trimmed[trimmed.length - 1] === "}" || trimmed[0] === "[" && trimmed[trimmed.length - 1] === "]" || trimmed[0] === "\"" && trimmed[trimmed.length - 1] === "\"") try {
557
- return JSON.parse(trimmed);
558
- } catch {}
559
- return trimmed;
560
- }
561
- /**
562
- * Wrap bare primitives so every row is an object, and pass objects through.
563
- *
564
- * The `.map()` used to be unconditional, which made this an ALLOCATION on the
565
- * overwhelmingly common path (an array of objects, where the map is an identity
566
- * function). It is read through `_normalizedItems` / `_visibleItems` / `_leafItems`
567
- * several times per render, so at one select per table row it was the single
568
- * hottest allocation in the component. The scan below is O(n) reads with no
569
- * allocation, and returns the SAME array when nothing needs wrapping.
570
- *
571
- * Returning the input array is safe: every caller only reads
572
- * (`find`/`filter`/`map`/`findIndex`/`length`), and `resolveSearchFields` takes its
573
- * rows as `readonly unknown[]`. It also makes item identity STABLE across calls for
574
- * primitive lists, which is the safe direction — see the note on
575
- * `_initialActiveIndex`, which explains why it deliberately does not rely on that
576
- * identity holding.
577
- */
578
- _normalizeItems(items) {
579
- if (Array.isArray(items)) {
580
- let needsWrap = false;
581
- for (const item of items) if (typeof item !== "object" || item === null) {
582
- needsWrap = true;
583
- break;
584
- }
585
- if (!needsWrap) return items;
586
- return items.map((item) => {
587
- if (typeof item === "object" && item !== null) return item;
588
- return { value: item };
589
- });
590
- }
591
- if (typeof items === "string") try {
592
- const parsed = JSON.parse(items);
593
- return this._normalizeItems(parsed);
594
- } catch {
595
- return [];
596
- }
597
- return [];
598
- }
599
- /**
600
- * Read `field` off an option row — PATH-AWARE, like every other field name in
601
- * the library.
602
- *
603
- * A plain `field in item` was the old test, and it silently produced
604
- * `undefined` for any nested accessor: an option list grouped by a navigation
605
- * property (`$apply=groupby((PostBudget/Nama))` returns
606
- * `{ PostBudget: { Nama: … } }`) has no top-level `"PostBudget.Nama"` key, so
607
- * every option resolved to no value at all — selectable in appearance, inert in
608
- * practice. `searchExpr`, table columns and summaries have always accepted
609
- * these paths; a select's `key-value` / `display-value` now do too.
610
- *
611
- * The LITERAL key is still tried first, so a row that genuinely carries a
612
- * dotted PROPERTY NAME keeps resolving exactly as it did — only a key that is
613
- * absent falls through to being read as a path, which previously could only
614
- * have yielded `undefined` anyway. Nothing that worked before changes.
615
- */
616
- _readField(item, field) {
617
- if (field in item) return item[field];
618
- return getFieldValue(item, field);
619
- }
620
- _resolveItemValue(item) {
621
- if (item == null) return item;
622
- if (typeof item !== "object") return item;
623
- if (this.keyValue) return this._readField(item, this.keyValue);
624
- return item;
625
- }
626
- _resolveItemDisplay(item) {
627
- if (item == null) return "";
628
- if (typeof this.displayValue === "function") return String(this.displayValue(item));
629
- if (typeof this.displayValue === "string" && this.displayValue) {
630
- if (typeof item !== "object") return "";
631
- const value = this._readField(item, this.displayValue);
632
- return value == null ? "" : String(value);
633
- }
634
- if (typeof item === "object" && "label" in item) {
635
- const label = item.label;
636
- return label == null ? "" : String(label);
637
- }
638
- if (typeof item === "object" && "value" in item) {
639
- const value = item.value;
640
- return value == null ? "" : String(value);
641
- }
642
- return String(item);
643
- }
644
- _looksLikeSerializedFunction(value) {
645
- const trimmed = value.trim();
646
- if (!trimmed) return false;
647
- return /^(?:async\s+)?(?:function\b|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/.test(trimmed);
648
- }
649
- /**
650
- * Value equality. With no `key-value` the whole item IS the value, so this is
651
- * identity — with one correction that is load-bearing under Vue.
652
- *
653
- * The model round-trips through the consumer's `ref`, which hands the item back
654
- * wrapped in a **reactive Proxy**; `items` normally stays raw, so `proxy === row`
655
- * is false and the selected row stops resolving (blank field, no highlight).
656
- * Comparing the raw targets restores it. Still identity, not deep equality:
657
- * two distinct rows with equal contents remain different values.
658
- */
659
- _isSameValue(a, b) {
660
- if (a === b) return true;
661
- if (a == null || b == null) return false;
662
- const rawA = unwrapReactive(a);
663
- const rawB = unwrapReactive(b);
664
- if (rawA === rawB) return true;
665
- if (typeof rawA !== "object" || typeof rawB !== "object") return false;
666
- return false;
667
- }
668
- _cls(base, key) {
669
- const extra = this.cssClass?.[key];
670
- return extra ? `${base} ${extra}` : base;
671
- }
672
- _toCssLength(value) {
673
- if (value == null || value === "") return void 0;
674
- if (typeof value === "number") return `${value}px`;
675
- const trimmed = String(value).trim();
676
- if (!trimmed) return void 0;
677
- return /^-?\d+(?:\.\d+)?$/.test(trimmed) ? `${trimmed}px` : trimmed;
678
- }
679
- /**
680
- * The WIDTH half of `dropdown`, on the panel.
681
- *
682
- * Split from the heights below because this component's popup is two elements: the panel owns
683
- * the box and the border, its body does the scrolling. That split is also why select needs no
684
- * `--_…-panel-max-h` custom property the way tag-input does — the popup controller's own cap
685
- * (`--mono-popup-avail-h`) lives on the panel and the author's on the body, so the two writers
686
- * never meet on one declaration.
687
- *
688
- * The panel is positioned `left: 0; right: 0` against the field, and a width on top of that is
689
- * the over-constrained case, which CSS resolves by dropping `right` — so the panel keeps its
690
- * left edge and takes this size.
691
- */
692
- get _dropdownPanelStyle() {
693
- const style = {};
694
- const panel = this.dropdown;
695
- const width = toCssSize(panel?.width);
696
- if (width) style.width = width;
697
- const minWidth = toCssSize(panel?.minWidth);
698
- if (minWidth) style["min-width"] = minWidth;
699
- const maxWidth = toCssSize(panel?.maxWidth);
700
- if (maxWidth) style["max-width"] = maxWidth;
701
- return style;
702
- }
703
- /** The HEIGHT half of `dropdown`, on the scrolling body — see `_dropdownPanelStyle`. */
704
- get _dropdownBodyStyle() {
705
- const style = {};
706
- const panel = this.dropdown;
707
- const minHeight = toCssSize(panel?.minHeight);
708
- if (minHeight) style.minHeight = minHeight;
709
- const height = toCssSize(panel?.height) ?? this._toCssLength(this.dropdownHeight);
710
- if (height) style.height = height;
711
- const maxHeight = toCssSize(panel?.maxHeight) ?? this._toCssLength(this.dropdownMaxHeight);
712
- if (maxHeight) style.maxHeight = maxHeight;
713
- else if (height) style.maxHeight = height;
714
- else if (this._scrollAutoMaxHeight) style.maxHeight = this._scrollAutoMaxHeight;
715
- return style;
716
- }
717
- get _normalizedItems() {
718
- if (this._normalizedMemo?.epoch !== this._itemsEpoch) this._normalizedMemo = {
719
- epoch: this._itemsEpoch,
720
- items: this._normalizeItems(this._ds.sourceItems)
721
- };
722
- return this._normalizedMemo.items;
723
- }
724
- /** Sanitised chunk size for array-mode paging (>= 1). */
725
- get _pageSize() {
726
- const n = Math.floor(Number(this.pageSize));
727
- return Number.isFinite(n) && n > 0 ? n : 10;
728
- }
729
- /**
730
- * The effective load-more mode. Off when unset/false; otherwise 'button' only
731
- * for the explicit `load-more="button"`, and 'scroll' for everything else that
732
- * enables it (bare attribute, empty string, `true`, or `"scroll"`). Scroll is
733
- * the default so enabling load-more "just works" without picking a mode.
734
- */
735
- get _loadMoreMode() {
736
- const v = this.loadMore;
737
- if (v === void 0 || v === null || v === false) return void 0;
738
- return v === "button" ? "button" : "scroll";
739
- }
740
- /**
741
- * The rows to actually render — the controller's visible slice, normalized.
742
- * (Array load-more reveals a chunk; DataSource mode accumulates pages.)
743
- */
744
- get _visibleItems() {
745
- if (this._visibleMemo?.epoch !== this._itemsEpoch) this._visibleMemo = {
746
- epoch: this._itemsEpoch,
747
- items: this._normalizeItems(this._ds.visibleItems)
748
- };
749
- return this._visibleMemo.items;
750
- }
751
- get _selectedItem() {
752
- return this._leafItems.find((item) => this._isSameValue(this._resolveItemValue(item), this.value));
753
- }
754
- /** Grouping is active when at least one display-group accessor is given. */
755
- get _grouped() {
756
- return Array.isArray(this.displayGroup) && this.displayGroup.length > 0;
757
- }
758
- /** A node is a group when its `groupItems` field holds an array. */
759
- _isGroupNode(x) {
760
- return !!x && typeof x === "object" && Array.isArray(x[this.groupItems]);
761
- }
762
- /** Descend `groupItems[0]` to the first leaf row — the group's representative. */
763
- _firstLeaf(node) {
764
- let cur = node;
765
- let guard = 0;
766
- while (this._isGroupNode(cur) && guard++ < 50) cur = cur[this.groupItems]?.[0];
767
- return cur ?? void 0;
768
- }
769
- /** Resolve a group header's label from `displayGroup[level]` over a leaf row. */
770
- _resolveGroupLabel(node, level) {
771
- const accessor = this.displayGroup[level];
772
- const leaf = this._firstLeaf(node);
773
- if (typeof accessor === "function") return String(accessor(leaf ?? {}) ?? "");
774
- if (typeof accessor === "string" && leaf && accessor in leaf) return String(leaf[accessor] ?? "");
775
- const key = node[this.groupKey];
776
- return key == null ? "" : String(key);
777
- }
778
- /** The string `display-group` entries — usable as server group/sort fields. */
779
- _groupSortFields() {
780
- return (this.displayGroup ?? []).filter((a) => typeof a === "string");
781
- }
782
- /** A `display-group` level's key for a row (string → field, function → call). */
783
- _groupKeyOf(row, level) {
784
- const accessor = this.displayGroup[level];
785
- if (typeof accessor === "function") return accessor(row);
786
- if (typeof accessor === "string") return row[accessor];
787
- }
788
- /**
789
- * `group` mode: bucket flat rows into the nested `{ [groupKey], [groupItems] }`
790
- * shape client-side, by `display-group` level. Buckets keep first-appearance
791
- * order, so each group is contiguous and grows in place as more pages load.
792
- */
793
- _buildClientTree(rows) {
794
- const build = (items, level) => {
795
- if (level >= this.displayGroup.length) return items;
796
- const buckets = /* @__PURE__ */ new Map();
797
- for (const row of items) {
798
- const key = this._groupKeyOf(row, level);
799
- const ks = String(key);
800
- let bucket = buckets.get(ks);
801
- if (!bucket) buckets.set(ks, bucket = {
802
- key,
803
- rows: []
804
- });
805
- bucket.rows.push(row);
806
- }
807
- return [...buckets.values()].map((b) => ({
808
- [this.groupKey]: b.key,
809
- [this.groupItems]: build(b.rows, level + 1)
810
- }));
811
- };
812
- return build(rows, 0);
813
- }
814
- /** The nested group nodes to render — built client-side in `group` mode. */
815
- _groupNodes() {
816
- if (this.group) {
817
- const leaves = this._visibleItems.filter((it) => this._searchMatches(it));
818
- return this._buildClientTree(leaves);
819
- }
820
- return this._ds.visibleItems ?? [];
821
- }
822
- /** Every leaf row — for value/display lookup. */
823
- get _leafItems() {
824
- if (this._leafMemo?.epoch === this._itemsEpoch) return this._leafMemo.items;
825
- const items = this._computeLeafItems();
826
- this._leafMemo = {
827
- epoch: this._itemsEpoch,
828
- items
829
- };
830
- return items;
831
- }
832
- _computeLeafItems() {
833
- if (!this._grouped) return this._normalizedItems;
834
- if (this.group) return [...this._ds.sourceItems];
835
- const out = [];
836
- const walk = (nodes) => {
837
- for (const n of nodes) if (this._isGroupNode(n)) walk(n[this.groupItems]);
838
- else if (n != null) out.push(n);
839
- };
840
- walk(this._ds.sourceItems ?? []);
841
- return out;
842
- }
843
- /**
844
- * Flatten the visible group tree into ordered header / item render rows.
845
- * Leaves are search-filtered (client mode); empty groups are dropped.
846
- */
847
- _groupRows() {
848
- const filterLeaves = !this.group;
849
- const build = (nodes, level, parentKey) => {
850
- const rows = [];
851
- nodes.forEach((node, i) => {
852
- if (!this._isGroupNode(node)) {
853
- const item = node;
854
- if (item == null) return;
855
- if (filterLeaves && !this._searchMatches(item)) return;
856
- rows.push({
857
- kind: "item",
858
- item,
859
- index: -1
860
- });
861
- return;
862
- }
863
- const rec = node;
864
- const path = `${parentKey}/${String(rec[this.groupKey])}#${i}`;
865
- const children = build(rec[this.groupItems] ?? [], level + 1, path);
866
- if (!children.length) return;
867
- rows.push({
868
- kind: "group",
869
- level,
870
- label: this._resolveGroupLabel(rec, level),
871
- key: `g:${level}:${path}`
872
- });
873
- rows.push(...children);
874
- });
875
- return rows;
876
- };
877
- let idx = 0;
878
- return build(this._groupNodes(), 0, "").map((r) => r.kind === "item" ? {
879
- ...r,
880
- index: idx++
881
- } : r);
882
- }
883
- /** Whether `search-value` names anything explicitly (vs falling back). */
884
- get _hasSearchValue() {
885
- const sv = this.searchValue;
886
- if (Array.isArray(sv)) return sv.length > 0;
887
- return typeof sv === "string" && !!sv.trim();
888
- }
889
- /**
890
- * The natural fields searched alongside the `'*'` default when `search-value` is
891
- * empty — the safety net, not the whole list.
892
- *
893
- * `'*'` resolves against loaded rows, so on a bound DataSource it expands to
894
- * NOTHING until the first page lands (nothing loads on open, and a query can
895
- * fire inside the debounce) — these keep that first query working. They are also
896
- * exempt from the remote string-only rule, so a numeric `key-value` stays
897
- * searchable. `display-value` contributes only when it's a string; a function
898
- * builds text that no field expression can reach, which is why `_searchMatches`
899
- * keeps matching the rendered display separately.
900
- */
901
- _fallbackSearchFields() {
902
- return [typeof this.displayValue === "string" ? this.displayValue : void 0, this.keyValue];
903
- }
904
- /**
905
- * A cheap signature of the rows' SHAPE, for the memo keys below.
906
- *
907
- * Row count alone isn't enough once `'*'` is in play: the resolved field list
908
- * depends on the rows' keys, and a remote source paging from one full page to
909
- * the next keeps the same length while the shape can differ. Sampling the first
910
- * and last row keeps this O(1) while catching that case.
911
- */
912
- _rowsShapeKey(rows) {
913
- const keysOf = (row) => row && typeof row === "object" ? Object.keys(row).join(",") : typeof row;
914
- if (!rows.length) return "0";
915
- return `${rows.length}:${keysOf(rows[0])}|${keysOf(rows[rows.length - 1])}`;
916
- }
917
- /**
918
- * `search-value` resolved to concrete entries — `*` patterns expanded against
919
- * the loaded rows. Memoized: a pattern samples up to 20 rows, so resolving per
920
- * item (this feeds `_searchMatches`) would be O(items × fields) per keystroke.
921
- *
922
- * With no `search-value` this defaults to `'*'` (every top-level field) plus the
923
- * natural fallbacks — see {@link _fallbackSearchFields}.
924
- */
925
- _searchEntries(remote) {
926
- const rows = this._normalizedItems;
927
- const key = `${this._searchVersion} ${remote ? 1 : 0} ${this._rowsShapeKey(rows)}`;
928
- if (this._entriesCache?.key === key) return this._entriesCache.entries;
929
- const entries = resolveSearchFields({
930
- searchValue: this.searchValue,
931
- fallbackFields: this._fallbackSearchFields(),
932
- rows,
933
- remote,
934
- defaultToWildcard: true
935
- });
936
- this._entriesCache = {
937
- key,
938
- entries
939
- };
940
- return entries;
941
- }
942
- /** The compiled client-side predicate for the current query (memoized). */
943
- _searchPredicate() {
944
- const query = this._query.trim();
945
- const operation = this.searchOperation || "contains";
946
- const rows = this._normalizedItems;
947
- const key = `${this._searchVersion} ${operation} ${this._rowsShapeKey(rows)} ${query}`;
948
- if (this._predicateCache?.key === key) return this._predicateCache.pred;
949
- const pred = searchRowPredicate(this._searchEntries(false), query, operation);
950
- this._predicateCache = {
951
- key,
952
- pred
953
- };
954
- return pred;
955
- }
956
- /** A bound DataSource is searched on the server; a plain array, client-side. */
957
- _serverSearch() {
958
- return !!this.dataSource;
959
- }
960
- /** Client-side match test (server mode already filtered → always true). */
961
- _searchMatches(item) {
962
- if (this._serverSearch()) return true;
963
- if (!this._searchActive) return true;
964
- const q = this._query.trim().toLowerCase();
965
- if (!q) return true;
966
- if (this._hasSearchValue) {
967
- const pred = this._searchPredicate();
968
- return pred ? pred(item) : true;
969
- }
970
- const display = this._resolveItemDisplay(item).toLowerCase();
971
- const val = this._resolveItemValue(item);
972
- const valStr = val == null ? "" : String(val).toLowerCase();
973
- const pred = this._searchPredicate();
974
- return display.includes(q) || valStr.includes(q) || (pred ? pred(item) : false);
975
- }
976
- _onSearchInput(event) {
977
- if (this._inert) return;
978
- this._query = event.target.value;
979
- this._searchActive = true;
980
- this._open = true;
981
- this._activeIndex = 0;
982
- this._cursorPending = false;
983
- this.requestUpdate();
984
- if (this._searchTimer) clearTimeout(this._searchTimer);
985
- if (!this._serverSearch()) {
986
- this._searchTimer = void 0;
987
- return;
988
- }
989
- const wait = Math.max(0, Number(this.searchDebounce) || 0);
990
- this._searchTimer = setTimeout(() => void this._applySearch(this._query), wait);
991
- }
992
- /** Run the search: server query (debounced) or just re-render for client filter. */
993
- async _applySearch(query) {
994
- if (!this._serverSearch()) {
995
- this.requestUpdate();
996
- return;
997
- }
998
- await this._pushSearch(query).catch((err) => {
999
- this._ds.setError(err);
1000
- });
1001
- }
1002
- /** Push `query` onto the source and reload it; rejects when the load fails. */
1003
- _pushSearch(query) {
1004
- this._appliedQuery = query;
1005
- return this._sourceSearch.apply(this.dataSource, {
1006
- query,
1007
- searchValue: this.searchValue,
1008
- fallbackFields: this._fallbackSearchFields(),
1009
- rows: this._normalizedItems,
1010
- remote: true,
1011
- defaultToWildcard: true,
1012
- operation: this.searchOperation || "contains"
1013
- });
1014
- }
1015
- /** Forget the typed query without touching the source. */
1016
- _dropQuery() {
1017
- if (this._searchTimer) {
1018
- clearTimeout(this._searchTimer);
1019
- this._searchTimer = void 0;
1020
- }
1021
- this._query = "";
1022
- this._searchActive = false;
1023
- this._activeIndex = -1;
1024
- }
1025
- /** Loading: the app says so (`loading`), or the bound source's first load / retry. */
1026
- get _busy() {
1027
- return this.loading || !!this.dataSource && this._ds.busy;
1028
- }
1029
- /** The failure to paint: the app's `loading-error`, else the source's own; none while busy. */
1030
- get _failure() {
1031
- if (this._busy) return null;
1032
- return this._appFailure ?? (this.dataSource ? this._ds.error : null);
1033
- }
1034
- /** Enter / Space on the focused field retries a failure. */
1035
- _retryKey(event) {
1036
- if (event.key !== "Enter" && event.key !== " ") return false;
1037
- if (!this._failure || this.disabled) return false;
1038
- this._retry(event);
1039
- return true;
1040
- }
1041
- /** Loading, or failed: the field refuses every gesture but ↻. */
1042
- get _dsInert() {
1043
- return this._busy || !!this._failure;
1044
- }
1045
- /** Every interaction is off: disabled, read-only, or the source isn't usable. */
1046
- get _inert() {
1047
- return this.disabled || this.readonly || this._dsInert;
1048
- }
1049
- /** Clear the typed query (and the source query, on close / select). */
1050
- _resetSearch() {
1051
- if (this._searchTimer) {
1052
- clearTimeout(this._searchTimer);
1053
- this._searchTimer = void 0;
1054
- }
1055
- const had = this._searchActive && !!this._query;
1056
- this._query = "";
1057
- this._searchActive = false;
1058
- this._activeIndex = -1;
1059
- if (had && this._serverSearch()) this._applySearch("");
1060
- }
1061
- /** The label shown in the field while not actively typing a query. */
1062
- get _displayText() {
1063
- const item = this._selectedItem;
1064
- return item ? this._resolveItemDisplay(item) : "";
1065
- }
1066
- /** Placeholder for the search field — a search hint while open, else the prompt. */
1067
- get _fieldPlaceholder() {
1068
- if (this._busy) return this.loadingText;
1069
- if (this._failure) return errorText(this._failure);
1070
- if ((this._open || this._searchActive) && this.searchPlaceholder) return this.searchPlaceholder;
1071
- return this.placeholder;
1072
- }
1073
- /**
1074
- * The selectable rows in render order — drives keyboard nav (`_activeIndex`)
1075
- * for the searchable combobox. Mirrors what `_renderItems` paints.
1076
- */
1077
- /**
1078
- * Where the keyboard cursor goes when the list opens: the row matching the
1079
- * current value, else the first row.
1080
- *
1081
- * Deliberately runs the SAME predicate that paints `.selected` in
1082
- * `_renderOption`, over `_filteredItems` — the very list ↑/↓ and Enter index
1083
- * into — so the cursor is guaranteed to land on the row drawn as selected.
1084
- * Going via `_selectedItem` + `indexOf` would not work: that searches
1085
- * `_leafItems`, an unfiltered/unpaged array whose `_normalizeItems` wraps
1086
- * primitives in fresh objects per call, so identity would not hold. Grouping
1087
- * needs no special case — `_groupRows()` numbers leaf rows only, which is
1088
- * exactly what `_filteredItems` reconstructs.
1089
- *
1090
- * `-1` when the list is empty; `0` when the selected row is not in it (a page
1091
- * the user hasn't scrolled to, or a server query that didn't return it), which
1092
- * is the behaviour this component has always had.
1093
- */
1094
- _initialActiveIndex() {
1095
- const items = this._filteredItems;
1096
- if (!items.length) return -1;
1097
- const index = items.findIndex((item) => this._isSameValue(this._resolveItemValue(item), this.value));
1098
- return index >= 0 ? index : 0;
1099
- }
1100
- get _filteredItems() {
1101
- if (this._grouped) return this._groupRows().filter((r) => r.kind === "item").map((r) => r.item);
1102
- return this._visibleItems.filter((item) => this._searchMatches(item));
1103
- }
1104
- get _hasValue() {
1105
- return this.value !== null && this.value !== void 0 && this.value !== "";
1106
- }
1107
- get _hasLabelSlot() {
1108
- return this._hasLabelSlotState;
1109
- }
1110
- get _hasHelperSlot() {
1111
- return this._hasHelperSlotState;
1112
- }
1113
- get _hasPrefixSlot() {
1114
- return this._hasPrefixSlotState;
1115
- }
1116
- get _hasSuffixSlot() {
1117
- return this._hasSuffixSlotState;
1118
- }
1119
- get _resolvedValidationState() {
1120
- if (this.validationState && this.validationState !== "default") return this.validationState;
1121
- if (this.errorMessage) return "invalid";
1122
- if (this.successMessage) return "valid";
1123
- return "default";
1124
- }
1125
- get _wrapperClasses() {
1126
- return [
1127
- "mono-select",
1128
- this.size,
1129
- this.color,
1130
- this.variant,
1131
- this._open ? "open" : "",
1132
- this.disabled ? "disabled" : "",
1133
- this.readonly ? "readonly" : "",
1134
- this._resolvedValidationState !== "default" ? `is-${this._resolvedValidationState}` : "",
1135
- this._hasValue ? "has-value" : "",
1136
- this._hasPrefixSlot ? "has-prefix" : "",
1137
- this._hasSuffixSlot ? "has-suffix" : "",
1138
- this.cssClassName,
1139
- this.cssClass?.root
1140
- ].filter(Boolean).join(" ");
1141
- }
1142
- get _triggerClasses() {
1143
- return [
1144
- this._cls("mono-select-trigger", "trigger"),
1145
- this.size,
1146
- this.color,
1147
- this.variant,
1148
- this.disabled ? "disabled" : "",
1149
- this.readonly ? "readonly" : ""
1150
- ].filter(Boolean).join(" ");
1151
- }
1152
- _createModelDetail(args) {
1153
- return {
1154
- modelValue: args.modelValue,
1155
- currentValue: args.modelValue,
1156
- oldValue: args.oldValue,
1157
- value: args.modelValue,
1158
- selectedItem: this._leafItems.find((item) => this._isSameValue(this._resolveItemValue(item), args.modelValue)),
1159
- sourceEvent: args.sourceEvent
1160
- };
1161
- }
1162
- _emitChange(detail) {
1163
- dispatchMonoEvent(this, "change", detail);
1164
- }
1165
- _emitClear(detail) {
1166
- dispatchMonoEvent(this, "clear", detail);
1167
- }
1168
- _toggleOpen(event) {
1169
- event?.stopPropagation();
1170
- if (this._inert) return;
1171
- if (this._open) this._close();
1172
- else this._open = true;
1173
- }
1174
- _close() {
1175
- this._open = false;
1176
- this._resetSearch();
1177
- }
1178
- _handleTriggerKeydown(event) {
1179
- if (this._retryKey(event)) return;
1180
- if (this._inert) return;
1181
- if (!this._open) {
1182
- if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
1183
- event.preventDefault();
1184
- this._open = true;
1185
- return;
1186
- }
1187
- if (event.key === "Escape") {
1188
- event.preventDefault();
1189
- this._close();
1190
- }
1191
- return;
1192
- }
1193
- this._handleListKeydown(event);
1194
- }
1195
- /**
1196
- * ↑/↓/Enter/Esc against the rendered list, shared by the trigger and the
1197
- * search field so both modes navigate identically.
1198
- */
1199
- _handleListKeydown(event) {
1200
- const items = this._filteredItems;
1201
- if (event.key === "ArrowDown") {
1202
- event.preventDefault();
1203
- this._open = true;
1204
- this._activeIndex = items.length ? Math.min(this._activeIndex + 1, items.length - 1) : -1;
1205
- return true;
1206
- }
1207
- if (event.key === "ArrowUp") {
1208
- event.preventDefault();
1209
- this._activeIndex = items.length ? Math.max(this._activeIndex - 1, 0) : -1;
1210
- return true;
1211
- }
1212
- if (event.key === "Enter") {
1213
- const active = this._activeIndex >= 0 ? items[this._activeIndex] : void 0;
1214
- if (!active) return false;
1215
- event.preventDefault();
1216
- if (!active.disabled) this._selectItem(active, event);
1217
- return true;
1218
- }
1219
- if (event.key === "Escape") {
1220
- event.preventDefault();
1221
- this._close();
1222
- return true;
1223
- }
1224
- return false;
1225
- }
1226
- /**
1227
- * Focusing the field selects the shown label so the first keystroke replaces
1228
- * it (type to filter). It does NOT open the dropdown: a Tab into the field is
1229
- * not a request to see the list — typing, ↓, a click in the field or the
1230
- * chevron are.
1231
- */
1232
- _handleSearchFocus(event) {
1233
- if (this._inert) return;
1234
- event.target.select();
1235
- }
1236
- /**
1237
- * Clicking anywhere in a SEARCHABLE field opens it and focuses the input.
1238
- * Opens only — a click in a text input while the list is open is caret
1239
- * placement or text selection, never a toggle; the chevron, Escape and an
1240
- * outside click close. (The plain trigger is a button that toggles instead:
1241
- * nothing to type there, so a second click means "close".)
1242
- */
1243
- _handleFieldClick(event) {
1244
- event.stopPropagation();
1245
- if (this._inert) return;
1246
- this._open = true;
1247
- this._searchFieldEl?.focus();
1248
- }
1249
- /** Keyboard nav for the search field: arrows move, Enter selects, Esc closes. */
1250
- _handleSearchKeydown(event) {
1251
- if (this._retryKey(event)) return;
1252
- if (this._inert) return;
1253
- this._handleListKeydown(event);
1254
- }
1255
- _selectItem(item, event) {
1256
- event?.preventDefault();
1257
- event?.stopPropagation();
1258
- if (this._inert || item.disabled) return;
1259
- const oldValue = this.modelValue;
1260
- const nextValue = this._resolveItemValue(item);
1261
- this.value = nextValue;
1262
- this.modelValue = nextValue;
1263
- this._open = false;
1264
- this._resetSearch();
1265
- const detail = this._createModelDetail({
1266
- modelValue: nextValue,
1267
- oldValue,
1268
- sourceEvent: event
1269
- });
1270
- this._emitChange(detail);
1271
- if (this.searchable) this.updateComplete.then(() => this._searchFieldEl?.select());
1272
- else this._triggerEl?.focus();
1273
- }
1274
- _clear(event) {
1275
- event.preventDefault();
1276
- event.stopPropagation();
1277
- if (this._inert) return;
1278
- const oldValue = this.modelValue;
1279
- const nextValue = null;
1280
- this.value = nextValue;
1281
- this.modelValue = nextValue;
1282
- this._open = false;
1283
- this._resetSearch();
1284
- const detail = this._createModelDetail({
1285
- modelValue: nextValue,
1286
- oldValue,
1287
- sourceEvent: event
1288
- });
1289
- this._emitChange(detail);
1290
- this._emitClear(detail);
1291
- if (this.searchable) this._searchFieldEl?.focus();
1292
- }
1293
- _renderLabel() {
1294
- const hasContent = !!this.label || this._hasLabelSlot;
1295
- if (!hasContent && !this._slotsAlwaysRender) return nothing;
1296
- return html`
1297
- <label
1298
- class=${this._cls("mono-select-label", "label")}
1299
- mono-label
1300
- for=${this._selectId}
1301
- ?mono-empty=${!hasContent}
1302
- >
1303
- ${this._slotOutlet("label", this.label)}
1304
-
1305
- ${this.required ? html`
1306
- <span class=${this._cls("mono-select-required", "required")} mono-required-mark>
1307
- *
1308
- </span>
1309
- ` : nothing}
1310
- ${this._tooltip.render()}
1311
- </label>
1312
- `;
1313
- }
1314
- _renderValue() {
1315
- if (this._dsInert) {
1316
- const err = this._failure;
1317
- return html`
1318
- <span
1319
- class=${`${this._cls("mono-select-value", "value")} ${this._cls("mono-select-placeholder", "placeholder")}`}
1320
- mono-value
1321
- mono-placeholder
1322
- ?mono-ds-error-text=${!!err}
1323
- title=${ifDefined(err ? errorTitle(err) : void 0)}
1324
- >
1325
- ${err ? errorText(err) : this.loadingText}
1326
- </span>
1327
- `;
1328
- }
1329
- if (this._hasValue && this._selectedItem) return html`
1330
- <span class=${this._cls("mono-select-value", "value")} mono-value>
1331
- ${this._hasPrefixSlot || this._slotsAlwaysRender ? this._slotOutlet("prefix") : nothing}
1332
-
1333
- <span>${this._resolveItemDisplay(this._selectedItem)}</span>
1334
-
1335
- ${this._hasSuffixSlot || this._slotsAlwaysRender ? this._slotOutlet("suffix") : nothing}
1336
- </span>
1337
- `;
1338
- return html`
1339
- <span
1340
- class=${`${this._cls("mono-select-value", "value")} ${this._cls("mono-select-placeholder", "placeholder")}`}
1341
- mono-value
1342
- mono-placeholder
1343
- >
1344
- ${this.placeholder}
1345
- </span>
1346
- `;
1347
- }
1348
- /** A single selectable option button. `index` drives the keyboard-active highlight. */
1349
- _renderOption(item, index = -1) {
1350
- const selected = this._isSameValue(this._resolveItemValue(item), this.value);
1351
- const active = index >= 0 && index === this._activeIndex;
1352
- const itemClass = [
1353
- this._cls("mono-select-item", "item"),
1354
- active ? `active ${this.cssClass?.itemActive ?? ""}` : "",
1355
- selected ? `selected ${this.cssClass?.itemSelected ?? ""}` : "",
1356
- item.disabled ? `disabled ${this.cssClass?.itemDisabled ?? ""}` : ""
1357
- ].filter(Boolean).join(" ");
1358
- return html`
1359
- <button
1360
- type="button"
1361
- role="option"
1362
- class=${itemClass}
1363
- mono-item
1364
- ?mono-active=${active}
1365
- ?mono-selected=${selected}
1366
- ?mono-disabled=${!!item.disabled}
1367
- aria-selected=${selected ? "true" : "false"}
1368
- ?disabled=${item.disabled}
1369
- @mousedown=${(event) => event.preventDefault()}
1370
- @click=${(event) => this._selectItem(item, event)}
1371
- >
1372
- ${this._resolveItemDisplay(item)}
1373
- </button>
1374
- `;
1375
- }
1376
- /**
1377
- * The flat entry list published to `form.items()[key].list`.
1378
- *
1379
- * The SAME sequence the component renders — group headers interleaved with their leaves — so a
1380
- * consumer looping it produces one node per row, in order, with nothing to pair up by hand.
1381
- *
1382
- * `key` is `item[keyValue]` via `_resolveItemValue`, the same value selection is keyed by, so
1383
- * the feature introduces no second notion of a key.
1384
- */
1385
- _listEntries() {
1386
- const rowEntry = (item, level, index) => ({
1387
- type: "row",
1388
- key: String(this._resolveItemValue(item) ?? ""),
1389
- item,
1390
- level,
1391
- selected: this._isSameValue(this._resolveItemValue(item), this.value),
1392
- active: index >= 0 && index === this._activeIndex
1393
- });
1394
- if (this._grouped) {
1395
- const rows = this._groupRows();
1396
- const leavesFrom = (start, level) => {
1397
- const out = [];
1398
- for (let i = start + 1; i < rows.length; i++) {
1399
- const row = rows[i];
1400
- if (row.kind === "group") {
1401
- if (row.level <= level) break;
1402
- continue;
1403
- }
1404
- out.push(row.item);
1405
- }
1406
- return out;
1407
- };
1408
- let rowLevel = 0;
1409
- return rows.map((row, i) => {
1410
- if (row.kind === "group") {
1411
- rowLevel = row.level + 1;
1412
- return {
1413
- type: "group",
1414
- key: row.key,
1415
- label: row.label,
1416
- level: row.level,
1417
- items: leavesFrom(i, row.level)
1418
- };
1419
- }
1420
- return rowEntry(row.item, rowLevel, row.index);
1421
- });
1422
- }
1423
- return this._filteredItems.map((item, index) => rowEntry(item, 0, index));
1424
- }
1425
- /**
1426
- * Hand the form the current entries — but only a NEW array when they actually moved.
1427
- *
1428
- * This runs from `updated()`, so it fires on every render; the controller notifies on a changed
1429
- * list, and that notify re-renders this control. Returning the same array reference is what keeps
1430
- * that from looping forever. Identity of `item` counts as a change: a reloaded DataSource hands
1431
- * back fresh row objects under the same keys, and a consumer reading `e.item.Nama` must see them.
1432
- */
1433
- _syncListEntries() {
1434
- if (!this.dataForm || !this.keyForm) return;
1435
- const next = this._listEntries();
1436
- const prev = this._publishedEntries;
1437
- let same = prev.length === next.length;
1438
- if (same) for (let i = 0; i < next.length; i++) {
1439
- const a = prev[i];
1440
- const b = next[i];
1441
- if (a.type !== b.type || a.key !== b.key || a.item !== b.item || a.selected !== b.selected || a.active !== b.active) {
1442
- same = false;
1443
- break;
1444
- }
1445
- }
1446
- if (same) return;
1447
- this._publishedEntries = next;
1448
- this._publishList(next);
1449
- }
1450
- /**
1451
- * Where the consumer's `slot="list"` content goes — overridden per build.
1452
- *
1453
- * mono places that content as ONE block and never reaches inside it. Moving a `v-for`'s children
1454
- * is not survivable: Vue inserts a new row with `parent.insertBefore(node, anchor)` where the
1455
- * anchor is the existing node at that position, so relocating one makes the consumer's next
1456
- * insert throw `NotFoundError`. Hence one block, and hence `data-mono-item-key` — with the rows
1457
- * out of reach, a delegated click is the only way back to the item.
1458
- */
1459
- renderListSlot() {
1460
- return html``;
1461
- }
1462
- /**
1463
- * The entry a click landed on — by POSITION, so a consumer writes a plain `v-for` and nothing else.
1464
- *
1465
- * mono cannot read a node's item off the node: it published the list, but the DOM was built by
1466
- * someone else. What it CAN do is count. The wrapper's Nth element child is the Nth published
1467
- * entry, because that is the contract the slot already states — one flat loop, one node per entry,
1468
- * in order — and here that contract is CHECKED rather than assumed: if the two lengths disagree,
1469
- * this refuses the click instead of picking the wrong row.
1470
- *
1471
- * Position is resolved at CLICK time, never cached. That is what makes it safe with no observer
1472
- * and no re-stamping: by the time a human clicks, the framework has long finished patching, so
1473
- * there is no window where a stale pairing could be consulted.
1474
- *
1475
- * `data-mono-item-key` still wins when present — the escape hatch for a wrapper that cannot emit
1476
- * exactly one element per entry (a sticky header of your own, a row that renders as a fragment).
1477
- */
1478
- /**
1479
- * The consumer's `slot="list"` wrapper, wherever the build keeps it.
1480
- *
1481
- * The shadow build never moves it — it stays a light child of the host and is PROJECTED — so
1482
- * that is the default. The light build moves it into the panel and overrides this with the node
1483
- * it captured, which is also findable while the panel is closed and the wrapper parked.
1484
- */
1485
- get _listSlotWrapper() {
1486
- return this._lateListSlot.find();
1487
- }
1488
- /**
1489
- * Re-decorate whenever the consumer re-renders their rows.
1490
- *
1491
- * `_syncListChrome()` runs from `updated()`, but a framework patches the wrapper on ITS next
1492
- * tick — so a row the search box just revealed would be born undecorated and stay that way until
1493
- * something else made mono render. This closes that gap.
1494
- *
1495
- * `childList` on the WRAPPER, which is exactly the churn {@link LateSlotWatcher} refuses on the
1496
- * host: there the consumer's row traffic is noise, here it IS the signal. No `subtree`, so
1497
- * mono's own attribute writes cannot re-trigger it.
1498
- */
1499
- _watchListChrome() {
1500
- if (typeof MutationObserver === "undefined") return;
1501
- const wrapper = this._listSlotWrapper;
1502
- if (!wrapper || wrapper === this._observedListWrapper) return;
1503
- this._listChromeObserver?.disconnect();
1504
- this._observedListWrapper = wrapper;
1505
- this._listChromeObserver = new MutationObserver(() => this._syncListChrome());
1506
- this._listChromeObserver.observe(wrapper, { childList: true });
1507
- }
1508
- _stopWatchingListChrome() {
1509
- this._listChromeObserver?.disconnect();
1510
- this._listChromeObserver = void 0;
1511
- this._observedListWrapper = void 0;
1512
- }
1513
- /**
1514
- * Decorate the lines the consumer rendered — the half of this feature that keeps the SLOT about
1515
- * markup and mono about behaviour.
1516
- *
1517
- * Your template says what a line LOOKS like; the state it is in is stamped here, as `data-*`
1518
- * attributes, so nothing in that template has to bind `e.selected` or `e.active` to say so.
1519
- *
1520
- * Where the tag input also injects a checkbox, this stops at the stamp: a select carries no
1521
- * checkbox and no select-all, so there is no chrome to put anywhere.
1522
- *
1523
- * Three rules keep this safe beside a framework:
1524
- * · children are ANNOTATED only, never moved, reordered or removed — every `insertBefore`
1525
- * anchor a `v-for` patches against stays valid;
1526
- * · `data-*` only, never `class` — `class` is the consumer's binding and would be clobbered;
1527
- * · nothing is stamped unless the lines and the published entries match 1:1.
1528
- */
1529
- _syncListChrome() {
1530
- if (!this._hasListSlotState) return;
1531
- const wrapper = this._listSlotWrapper;
1532
- if (!wrapper) return;
1533
- const children = Array.from(wrapper.children);
1534
- const entries = this._publishedEntries;
1535
- if (children.length !== entries.length) return;
1536
- for (let i = 0; i < children.length; i++) {
1537
- const node = children[i];
1538
- const entry = entries[i];
1539
- node.setAttribute("data-mono-type", entry.type);
1540
- node.setAttribute("data-mono-level", String(entry.level ?? 0));
1541
- node.setAttribute("data-mono-key", entry.key);
1542
- if (entry.type === "group") continue;
1543
- node.toggleAttribute("data-mono-selected", !!entry.selected);
1544
- node.toggleAttribute("data-mono-active", !!entry.active);
1545
- }
1546
- }
1547
- _entryFromNode(node) {
1548
- const keyed = node.closest?.("[data-mono-item-key]");
1549
- if (keyed) {
1550
- const key = keyed.getAttribute("data-mono-item-key");
1551
- return this._publishedEntries.find((e) => e.type === "row" && e.key === key);
1552
- }
1553
- const wrapper = node.closest?.("[slot=\"list\"]");
1554
- if (!wrapper) return void 0;
1555
- let row = node;
1556
- while (row && row.parentElement !== wrapper) row = row.parentElement;
1557
- if (!row) return void 0;
1558
- const children = Array.from(wrapper.children);
1559
- if (children.length !== this._publishedEntries.length) {
1560
- console.warn(`[mono-select] slot="list" rendered ${children.length} element(s) for ${this._publishedEntries.length} option(s), so a click cannot be paired with its row. Render exactly one element per entry, or put data-mono-item-key="<entry.key>" on each row.`);
1561
- return;
1562
- }
1563
- const entry = this._publishedEntries[children.indexOf(row)];
1564
- return entry?.type === "row" ? entry : void 0;
1565
- }
1566
- _emptyRow() {
1567
- const text = this.dataSource && this._ds.loading ? this.loadingText : this._query ? "No matches" : "No items";
1568
- return html`<div class=${this._cls("mono-select-item disabled", "itemDisabled")} mono-empty-row>${text}</div>`;
1569
- }
1570
- _renderItems() {
1571
- if (this._hasListSlotState) return html`
1572
- ${this._filteredItems.length ? nothing : this._emptyRow()}
1573
- ${this.renderListSlot()}
1574
- `;
1575
- if (this._grouped) {
1576
- const rows = this._groupRows();
1577
- if (!rows.some((r) => r.kind === "item")) return this._emptyRow();
1578
- return html`
1579
- ${rows.map((row) => row.kind === "group" ? html`<div
1580
- class=${`${this._cls("mono-select-group", "group")}${this.groupSticky ? " sticky" : ""}`}
1581
- mono-group
1582
- mono-level=${row.level}
1583
- ?mono-sticky=${this.groupSticky}
1584
- data-level=${row.level}
1585
- role="presentation"
1586
- >
1587
- ${row.label}
1588
- </div>` : this._renderOption(row.item, row.index))}
1589
- `;
1590
- }
1591
- const items = this._visibleItems.filter((item) => this._searchMatches(item));
1592
- if (!items.length) return this._emptyRow();
1593
- return html`${items.map((item, index) => this._renderOption(item, index))}`;
1594
- }
1595
- _renderLoadMore() {
1596
- if (!this._loadMoreMode) return nothing;
1597
- if (!this._normalizedItems.length || this._ds.atLastPage) return nothing;
1598
- if (this._ds.loadingMore) return html`
1599
- <div class=${`${this._cls("mono-select-load-more", "loadMore")} loading`} mono-load-more mono-loading>
1600
- Loading…
1601
- </div>
1602
- `;
1603
- if (this._loadMoreMode !== "button") return nothing;
1604
- return html`
1605
- <button
1606
- type="button"
1607
- class=${this._cls("mono-select-load-more", "loadMore")}
1608
- mono-load-more
1609
- @click=${(event) => {
1610
- event.preventDefault();
1611
- event.stopPropagation();
1612
- this._ds.loadMore();
1613
- }}
1614
- >
1615
- Load more
1616
- </button>
1617
- `;
1618
- }
1619
- _renderHelper() {
1620
- const base = this._cls("mono-select-message", "message");
1621
- if (this._validation.moved) return html`${this._renderHelperText()}${this._renderMovedMessage()}`;
1622
- if (this.validationMessage) {
1623
- const state = this._resolvedValidationState;
1624
- return html`
1625
- <div class=${`${base} ${state}`} mono-message=${state} role=${state === "invalid" ? "alert" : nothing}>
1626
- ${this.validationMessage}
1627
- </div>
1628
- `;
1629
- }
1630
- if (this.errorMessage) return html`
1631
- <div class=${`${base} invalid`} mono-message="invalid" role="alert">
1632
- ${this.errorMessage}
1633
- </div>
1634
- `;
1635
- if (this.successMessage) return html`
1636
- <div class=${`${base} valid`} mono-message="valid">
1637
- ${this.successMessage}
1638
- </div>
1639
- `;
1640
- return this._renderHelperText();
1641
- }
1642
- _renderHelperText() {
1643
- const base = this._cls("mono-select-message", "message");
1644
- if (this.helperText || this._hasHelperSlot || this._slotsAlwaysRender) return html`
1645
- <div
1646
- class=${`${base} helper`}
1647
- mono-message="helper"
1648
- ?mono-empty=${!this.helperText && !this._hasHelperSlot}
1649
- >
1650
- ${this._slotOutlet("helper", this.helperText)}
1651
- </div>
1652
- `;
1653
- return nothing;
1654
- }
1655
- /** Shared clear (×) / chevron action. `toggleArrow` lets the chevron close an
1656
- * already-open dropdown in the searchable field (the plain button toggles itself).
1657
- *
1658
- * ONE glyph, matching `<mono-tag-input>` and `<mono-dropdown-table>`: with a
1659
- * clearable value the corner shows `✕` alone — clearing is the useful action
1660
- * then, and the chevron would only say what the value already says. Without a
1661
- * value (or `clearable` off) the chevron is alone. `disabled` / `readonly`
1662
- * show NEITHER: the field refuses every gesture, so a chevron would promise an
1663
- * open it never delivers. The actions span still renders so the gutter — and
1664
- * the text beside it — stays put when the state toggles.
1665
- *
1666
- * THE VALUE WINS over the open state. On a searchable field the chevron is the
1667
- * only mouse way to CLOSE — the body OPENS but never closes there (a click in a
1668
- * text input is caret placement, not a toggle) — and it was once kept beside
1669
- * `✕` for exactly that reason. The rule now is the simpler one: while there is
1670
- * something to clear there is no chevron, open or closed; a searchable field
1671
- * with a value closes by picking, Escape or an outside click. `role="button"`
1672
- * on a span, not a `<button>`: the plain trigger is itself a button and buttons
1673
- * do not nest. `mousedown.preventDefault` keeps focus where it is (the search
1674
- * input); `_toggleOpen` stops propagation so the plain trigger does not toggle
1675
- * a second time. */
1676
- _renderActions() {
1677
- if (this._busy) return html`
1678
- <span class=${this._cls("mono-select-actions", "actions")} mono-actions>
1679
- <span mono-ds-spinner role="status" aria-label=${this.loadingText}>${spinnerIcon()}</span>
1680
- </span>
1681
- `;
1682
- if (this._failure && !this.disabled) return html`
1683
- <span class=${this._cls("mono-select-actions", "actions")} mono-actions>
1684
- <span
1685
- role="button"
1686
- tabindex="-1"
1687
- mono-ds-retry
1688
- aria-label=${this.retryLabel}
1689
- title=${this.retryLabel}
1690
- @mousedown=${(event) => event.preventDefault()}
1691
- @click=${this._retry}
1692
- >
1693
- ${refreshIcon()}
1694
- </span>
1695
- </span>
1696
- `;
1697
- const inert = this._inert;
1698
- const canClear = this.clearable && this._hasValue && !inert;
1699
- const showArrow = !inert && !canClear;
1700
- return html`
1701
- <span class=${this._cls("mono-select-actions", "actions")} mono-actions>
1702
- ${canClear ? html`
1703
- <span
1704
- role="button"
1705
- tabindex="-1"
1706
- class=${this._cls("mono-select-clear", "clear")}
1707
- mono-clear
1708
- aria-label="Clear select"
1709
- @mousedown=${(event) => event.preventDefault()}
1710
- @click=${this._clear}
1711
- >
1712
- ${this.renderIcon("close")}
1713
- </span>
1714
- ` : nothing}
1715
- ${showArrow ? html`
1716
- <span
1717
- role="button"
1718
- tabindex="-1"
1719
- class=${this._cls("mono-select-arrow", "arrow")}
1720
- mono-arrow
1721
- aria-label="Toggle options"
1722
- aria-expanded=${this._open ? "true" : "false"}
1723
- @mousedown=${(event) => event.preventDefault()}
1724
- @click=${this._toggleOpen}
1725
- >
1726
- ${this.renderIcon("chevron")}
1727
- </span>
1728
- ` : nothing}
1729
- </span>
1730
- `;
1731
- }
1732
- /** Plain trigger: a button showing the selected value (non-searchable). */
1733
- _renderButtonTrigger(ariaLabel, describedBy) {
1734
- return html`
1735
- <button
1736
- id=${this._selectId}
1737
- type="button"
1738
- class=${this._triggerClasses}
1739
- mono-trigger
1740
- ?disabled=${this.disabled}
1741
- aria-haspopup="listbox"
1742
- aria-expanded=${this._open ? "true" : "false"}
1743
- aria-controls=${this._listboxId}
1744
- aria-label=${ifDefined(ariaLabel)}
1745
- aria-describedby=${ifDefined(describedBy)}
1746
- @click=${this._toggleOpen}
1747
- @keydown=${this._handleTriggerKeydown}
1748
- >
1749
- ${this._renderValue()}
1750
- ${this._validation.renderIcon()}
1751
- ${this._renderActions()}
1752
- </button>
1753
- `;
1754
- }
1755
- /** Searchable trigger: the field itself is a search input (combobox). */
1756
- _renderSearchTrigger(ariaLabel, describedBy) {
1757
- return html`
1758
- <div class=${this._triggerClasses} mono-trigger @click=${this._handleFieldClick}>
1759
- <input
1760
- id=${this._selectId}
1761
- class=${this._cls("mono-select-search-field", "searchField")}
1762
- mono-search-field
1763
- type="text"
1764
- role="combobox"
1765
- autocomplete="off"
1766
- aria-haspopup="listbox"
1767
- aria-expanded=${this._open ? "true" : "false"}
1768
- aria-controls=${this._listboxId}
1769
- aria-label=${ifDefined(ariaLabel)}
1770
- aria-describedby=${ifDefined(describedBy)}
1771
- .value=${this._dsInert ? "" : this._searchActive ? this._query : this._displayText}
1772
- placeholder=${this._fieldPlaceholder}
1773
- ?disabled=${this.disabled}
1774
- ?readonly=${this.readonly || this._dsInert}
1775
- @input=${this._onSearchInput}
1776
- @focus=${this._handleSearchFocus}
1777
- @keydown=${this._handleSearchKeydown}
1778
- />
1779
- ${this._validation.renderIcon()}
1780
- ${this._renderActions()}
1781
- </div>
1782
- `;
1783
- }
1784
- /** Inline sizing applied to the root wrapper. */
1785
- _sizeStyle() {
1786
- return buildSizeStyle(this);
1787
- }
1788
- render() {
1789
- const describedBy = this.validationMessage || this.errorMessage || this.successMessage || this.helperText || this._hasHelperSlot ? this._messageId : void 0;
1790
- const ariaLabel = this.ariaLabelText || this.label || this.placeholder || void 0;
1791
- const state = this._resolvedValidationState;
1792
- return html`
1793
- <div
1794
- class=${this._wrapperClasses}
1795
- style=${styleMap(this._sizeStyle())}
1796
- mono-select
1797
- mono-size=${this.size === "md" ? nothing : this.size}
1798
- mono-color=${this.color === "primary" ? nothing : this.color}
1799
- mono-variant=${this.variant === "outlined" ? nothing : this.variant}
1800
- mono-label-position=${this.labelPosition === "top" ? nothing : this.labelPosition}
1801
- mono-validation-position=${this.validationPosition === "bottom" ? nothing : this.validationPosition}
1802
- mono-validation-state=${state === "default" ? nothing : state}
1803
- ?mono-open=${this._open}
1804
- ?mono-disabled=${this.disabled}
1805
- ?mono-readonly=${this.readonly}
1806
- ?mono-required=${this.required}
1807
- ?mono-clearable=${this.clearable}
1808
- ?mono-searchable=${this.searchable}
1809
- ?mono-ds-loading=${this._busy}
1810
- ?mono-ds-error=${!!this._failure}
1811
- aria-busy=${this._busy ? "true" : nothing}
1812
- >
1813
- ${this._renderLabel()}
1814
-
1815
- ${this.searchable ? this._renderSearchTrigger(ariaLabel, describedBy) : this._renderButtonTrigger(ariaLabel, describedBy)}
1816
-
1817
- <div
1818
- id=${this._listboxId}
1819
- role="listbox"
1820
- class=${this._cls("mono-select-dropdown", "dropdown")}
1821
- mono-dropdown
1822
- style=${styleMap(this._dropdownPanelStyle)}
1823
- >
1824
- <div
1825
- class=${this._cls("mono-select-dropdown-body", "dropdownBody")}
1826
- mono-dropdown-body
1827
- style=${styleMap(this._dropdownBodyStyle)}
1828
- @scroll=${this._handleDropdownScroll}
1829
- >
1830
- ${this._open ? this._renderItems() : nothing}
1831
- ${this._open ? this._renderLoadMore() : nothing}
1832
- </div>
1833
- </div>
1834
-
1835
- <div id=${this._messageId} class=${this.cssClass?.messageWrap ?? ""} mono-message-wrap>
1836
- ${this._renderHelper()}
1837
- </div>
1838
-
1839
- ${this._validation.renderFloat()}
1840
- </div>
1841
- `;
1842
- }
1843
- focus(options) {
1844
- if (this.searchable) this._searchFieldEl?.focus(options);
1845
- else this._triggerEl?.focus(options);
1846
- }
1847
- blur() {
1848
- if (this.searchable) this._searchFieldEl?.blur();
1849
- else this._triggerEl?.blur();
1850
- }
1851
- /** Whether the dropdown panel is currently open. */
1852
- get isOpen() {
1853
- return this._open;
1854
- }
1855
- open() {
1856
- if (this._inert) return;
1857
- this._open = true;
1858
- }
1859
- close() {
1860
- this._close();
1861
- }
1862
- toggle() {
1863
- if (this._inert) return;
1864
- if (this._open) this._close();
1865
- else this._open = true;
1866
- }
1867
- /** Coerce a possibly-stringy SSR value to a real boolean (matches
1868
- * `booleanStringConverter`: a bare / empty attribute means present → true). */
1869
- _toBoolean(value) {
1870
- if (typeof value === "boolean") return value;
1871
- if (value == null) return false;
1872
- const s = String(value).toLowerCase().trim();
1873
- return s === "" || s === "true";
1874
- }
1875
- /** Whether the slot regions render even when empty. Light: no (omit empty
1876
- * regions). Shadow: yes — the native `<slot>`s must exist to project DSD
1877
- * content + be scanned (hidden via `[mono-empty]` when empty). */
1878
- get _slotsAlwaysRender() {
1879
- return false;
1880
- }
1881
- _hasSlot(name) {
1882
- return name === "label" ? this._hasLabelSlotState : name === "helper" ? this._hasHelperSlotState : name === "prefix" ? this._hasPrefixSlotState : name === "suffix" ? this._hasSuffixSlotState : this._hasListSlotState;
1883
- }
1884
- _setSlotState(name, has) {
1885
- if (name === "label") this._hasLabelSlotState = has;
1886
- else if (name === "helper") this._hasHelperSlotState = has;
1887
- else if (name === "prefix") this._hasPrefixSlotState = has;
1888
- else if (name === "suffix") this._hasSuffixSlotState = has;
1889
- else this._hasListSlotState = has;
1890
- }
1891
- /**
1892
- * Slot outlet. Light build (default): a `data-mono-slot` placeholder the
1893
- * captured light-DOM nodes are re-parented into when present, else the prop
1894
- * `fallback`. Shadow build overrides this with a native `<slot name>` that
1895
- * carries the fallback as native slot content.
1896
- */
1897
- _slotOutlet(name, fallback = nothing) {
1898
- return this._hasSlot(name) ? html`<span data-mono-slot=${name}></span>` : html`${fallback}`;
1899
- }
1900
- /**
1901
- * Icon hook. Light build (default): the global `.mono-icon` / `i-mdi-*` UnoCSS
1902
- * icon. Shadow overrides with inline SVG (UnoCSS can't reach a shadow root).
1903
- */
1904
- renderIcon(name) {
1905
- return html`<span class=${`mono-icon ${name === "close" ? "i-mdi-close" : "i-mdi-chevron-down"}`} mono-icon aria-hidden="true"></span>`;
1906
- }
1907
- }
1908
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "size", void 0);
1909
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "color", void 0);
1910
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "variant", void 0);
1911
- __decorate([property({
1912
- type: String,
1913
- attribute: "label-position"
1914
- })], MonoSelectCoreClass.prototype, "labelPosition", void 0);
1915
- __decorate([property({
1916
- type: String,
1917
- attribute: "validation-position"
1918
- })], MonoSelectCoreClass.prototype, "validationPosition", void 0);
1919
- __decorate([property({ attribute: false })], MonoSelectCoreClass.prototype, "modelValue", void 0);
1920
- __decorate([property({ attribute: false })], MonoSelectCoreClass.prototype, "value", void 0);
1921
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "name", void 0);
1922
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "label", void 0);
1923
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "placeholder", void 0);
1924
- __decorate([property({
1925
- type: String,
1926
- attribute: "helper-text"
1927
- })], MonoSelectCoreClass.prototype, "helperText", void 0);
1928
- __decorate([property()], MonoSelectCoreClass.prototype, "tooltip", void 0);
1929
- __decorate([property({
1930
- type: String,
1931
- attribute: "validation-state"
1932
- })], MonoSelectCoreClass.prototype, "validationState", void 0);
1933
- __decorate([property({
1934
- type: String,
1935
- attribute: "validation-message"
1936
- })], MonoSelectCoreClass.prototype, "validationMessage", void 0);
1937
- __decorate([property({
1938
- type: String,
1939
- attribute: "error-message"
1940
- })], MonoSelectCoreClass.prototype, "errorMessage", void 0);
1941
- __decorate([property({
1942
- type: String,
1943
- attribute: "success-message"
1944
- })], MonoSelectCoreClass.prototype, "successMessage", void 0);
1945
- __decorate([property({
1946
- type: String,
1947
- attribute: "aria-label"
1948
- })], MonoSelectCoreClass.prototype, "ariaLabelText", void 0);
1949
- __decorate([property({
1950
- reflect: true,
1951
- converter: booleanStringConverter
1952
- })], MonoSelectCoreClass.prototype, "disabled", void 0);
1953
- __decorate([property({
1954
- reflect: true,
1955
- converter: booleanStringConverter
1956
- })], MonoSelectCoreClass.prototype, "readonly", void 0);
1957
- __decorate([property({
1958
- reflect: true,
1959
- converter: booleanStringConverter
1960
- })], MonoSelectCoreClass.prototype, "required", void 0);
1961
- __decorate([property({
1962
- reflect: true,
1963
- converter: booleanStringConverter
1964
- })], MonoSelectCoreClass.prototype, "clearable", void 0);
1965
- __decorate([property({
1966
- attribute: false,
1967
- hasChanged: arrayHasChanged
1968
- })], MonoSelectCoreClass.prototype, "items", void 0);
1969
- __decorate([property({ attribute: false })], MonoSelectCoreClass.prototype, "dataSource", void 0);
1970
- __decorate([property({
1971
- attribute: "immediate",
1972
- reflect: true,
1973
- converter: booleanStringConverter
1974
- })], MonoSelectCoreClass.prototype, "immediate", void 0);
1975
- __decorate([property({
1976
- attribute: "load-more",
1977
- reflect: true
1978
- })], MonoSelectCoreClass.prototype, "loadMore", void 0);
1979
- __decorate([property({
1980
- attribute: "page-size",
1981
- reflect: true,
1982
- type: Number
1983
- })], MonoSelectCoreClass.prototype, "pageSize", void 0);
1984
- __decorate([property({ attribute: false })], MonoSelectCoreClass.prototype, "dropdown", void 0);
1985
- __decorate([property({
1986
- attribute: "dropdown-height",
1987
- reflect: true
1988
- })], MonoSelectCoreClass.prototype, "dropdownHeight", void 0);
1989
- __decorate([property({
1990
- attribute: "dropdown-max-height",
1991
- reflect: true
1992
- })], MonoSelectCoreClass.prototype, "dropdownMaxHeight", void 0);
1993
- __decorate([property({ converter: booleanStringConverter })], MonoSelectCoreClass.prototype, "flip", void 0);
1994
- __decorate([property({ converter: booleanStringConverter })], MonoSelectCoreClass.prototype, "shift", void 0);
1995
- __decorate([property({
1996
- type: String,
1997
- attribute: "key-value",
1998
- reflect: true
1999
- })], MonoSelectCoreClass.prototype, "keyValue", void 0);
2000
- __decorate([property({ attribute: false })], MonoSelectCoreClass.prototype, "displayValue", void 0);
2001
- __decorate([property({
2002
- attribute: false,
2003
- hasChanged: arrayHasChanged
2004
- })], MonoSelectCoreClass.prototype, "displayGroup", void 0);
2005
- __decorate([property({
2006
- type: String,
2007
- attribute: "group-key",
2008
- reflect: true
2009
- })], MonoSelectCoreClass.prototype, "groupKey", void 0);
2010
- __decorate([property({
2011
- type: String,
2012
- attribute: "group-items",
2013
- reflect: true
2014
- })], MonoSelectCoreClass.prototype, "groupItems", void 0);
2015
- __decorate([property({
2016
- attribute: "group",
2017
- reflect: true,
2018
- converter: booleanStringConverter
2019
- })], MonoSelectCoreClass.prototype, "group", void 0);
2020
- __decorate([property({
2021
- attribute: "group-sticky",
2022
- reflect: true,
2023
- converter: booleanStringConverter
2024
- })], MonoSelectCoreClass.prototype, "groupSticky", void 0);
2025
- __decorate([property({
2026
- attribute: "searchable",
2027
- reflect: true,
2028
- converter: booleanStringConverter
2029
- })], MonoSelectCoreClass.prototype, "searchable", void 0);
2030
- __decorate([property({
2031
- attribute: "stay-open",
2032
- reflect: true,
2033
- converter: booleanStringConverter
2034
- })], MonoSelectCoreClass.prototype, "stayOpen", void 0);
2035
- __decorate([property({
2036
- attribute: "search-value",
2037
- hasChanged: arrayHasChanged
2038
- })], MonoSelectCoreClass.prototype, "searchValue", void 0);
2039
- __decorate([property({
2040
- type: String,
2041
- attribute: "search-operation"
2042
- })], MonoSelectCoreClass.prototype, "searchOperation", void 0);
2043
- __decorate([property({
2044
- attribute: "search-debounce",
2045
- type: Number
2046
- })], MonoSelectCoreClass.prototype, "searchDebounce", void 0);
2047
- __decorate([property({
2048
- type: String,
2049
- attribute: "search-placeholder"
2050
- })], MonoSelectCoreClass.prototype, "searchPlaceholder", void 0);
2051
- __decorate([property({
2052
- type: String,
2053
- attribute: "loading-text"
2054
- })], MonoSelectCoreClass.prototype, "loadingText", void 0);
2055
- __decorate([property({
2056
- type: String,
2057
- attribute: "retry-label"
2058
- })], MonoSelectCoreClass.prototype, "retryLabel", void 0);
2059
- __decorate([property({ converter: booleanStringConverter })], MonoSelectCoreClass.prototype, "loading", void 0);
2060
- __decorate([property({ attribute: "loading-error" })], MonoSelectCoreClass.prototype, "loadingError", void 0);
2061
- __decorate([property({
2062
- attribute: false,
2063
- hasChanged: rateLimitHasChanged
2064
- })], MonoSelectCoreClass.prototype, "cssClass", void 0);
2065
- __decorate([property({ attribute: false })], MonoSelectCoreClass.prototype, "cssClassName", void 0);
2066
- __decorate([state()], MonoSelectCoreClass.prototype, "_open", void 0);
2067
- __decorate([state()], MonoSelectCoreClass.prototype, "_query", void 0);
2068
- __decorate([state()], MonoSelectCoreClass.prototype, "_searchActive", void 0);
2069
- __decorate([state()], MonoSelectCoreClass.prototype, "_activeIndex", void 0);
2070
- __decorate([state()], MonoSelectCoreClass.prototype, "_scrollAutoMaxHeight", void 0);
2071
- __decorate([state()], MonoSelectCoreClass.prototype, "_hasLabelSlotState", void 0);
2072
- __decorate([state()], MonoSelectCoreClass.prototype, "_hasHelperSlotState", void 0);
2073
- __decorate([state()], MonoSelectCoreClass.prototype, "_hasPrefixSlotState", void 0);
2074
- __decorate([state()], MonoSelectCoreClass.prototype, "_hasSuffixSlotState", void 0);
2075
- __decorate([state()], MonoSelectCoreClass.prototype, "_hasListSlotState", void 0);
2076
- __decorate([query(".mono-select-search-field")], MonoSelectCoreClass.prototype, "_searchFieldEl", void 0);
2077
- __decorate([query(".mono-select-trigger")], MonoSelectCoreClass.prototype, "_triggerEl", void 0);
2078
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "width", void 0);
2079
- __decorate([property({ type: String })], MonoSelectCoreClass.prototype, "height", void 0);
2080
- __decorate([property({
2081
- type: String,
2082
- attribute: "min-width"
2083
- })], MonoSelectCoreClass.prototype, "minWidth", void 0);
2084
- __decorate([property({
2085
- type: String,
2086
- attribute: "max-width"
2087
- })], MonoSelectCoreClass.prototype, "maxWidth", void 0);
2088
- __decorate([property({
2089
- type: String,
2090
- attribute: "min-height"
2091
- })], MonoSelectCoreClass.prototype, "minHeight", void 0);
2092
- __decorate([property({
2093
- type: String,
2094
- attribute: "max-height"
2095
- })], MonoSelectCoreClass.prototype, "maxHeight", void 0);
2096
- return MonoSelectCoreClass;
2097
- };
2098
-
2099
- //#endregion
2100
8
  //#region src/components/select/mono-select.ts
2101
9
  let MonoSelect = class MonoSelect extends MonoSelectCore(LitElement) {
2102
10
  constructor(..._args) {
@@ -2222,46 +130,5 @@ let MonoSelect = class MonoSelect extends MonoSelectCore(LitElement) {
2222
130
  };
2223
131
  MonoSelect = __decorate([customElement("mono-select")], MonoSelect);
2224
132
 
2225
- //#endregion
2226
- //#region src/components/select/select-utils.ts
2227
- function validateSelectProps(props) {
2228
- const validSizes = [
2229
- "xs",
2230
- "sm",
2231
- "md",
2232
- "lg",
2233
- "xl",
2234
- "xxl"
2235
- ];
2236
- const validColors = [
2237
- "primary",
2238
- "secondary",
2239
- "success",
2240
- "danger",
2241
- "warning",
2242
- "info",
2243
- "teal",
2244
- "purple",
2245
- "neutral",
2246
- "dark"
2247
- ];
2248
- const validVariants = [
2249
- "outlined",
2250
- "filled",
2251
- "underlined"
2252
- ];
2253
- const validStates = [
2254
- "default",
2255
- "valid",
2256
- "invalid",
2257
- "warning"
2258
- ];
2259
- if (props.size && !validSizes.includes(props.size)) return false;
2260
- if (props.color && !validColors.includes(props.color)) return false;
2261
- if (props.variant && !validVariants.includes(props.variant)) return false;
2262
- if (props.validationState && !validStates.includes(props.validationState)) return false;
2263
- return true;
2264
- }
2265
-
2266
133
  //#endregion
2267
134
  export { MonoSelect, validateSelectProps };