@k2b/ui 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/browser/actions/Dropdown.js +132 -116
  2. package/dist/browser/actions/Dropdown.js.map +1 -1
  3. package/dist/browser/actions/Tabs.js +18 -15
  4. package/dist/browser/actions/Tabs.js.map +1 -1
  5. package/dist/browser/chat/ChatComposer.js +58 -25
  6. package/dist/browser/chat/ChatComposer.js.map +1 -1
  7. package/dist/browser/content/MapView.js +128 -13
  8. package/dist/browser/content/MapView.js.map +1 -1
  9. package/dist/browser/content/MapViewFrame.js +1 -1
  10. package/dist/browser/content/MapViewFrame.js.map +1 -1
  11. package/dist/browser/content/MessageRow.js +164 -152
  12. package/dist/browser/content/MessageRow.js.map +1 -1
  13. package/dist/browser/content/index.js +1 -0
  14. package/dist/browser/content/index.js.map +1 -1
  15. package/dist/browser/content/map-view-glyphs.js +23 -0
  16. package/dist/browser/content/map-view-glyphs.js.map +1 -0
  17. package/dist/browser/content/map-view-live.js +45 -22
  18. package/dist/browser/content/map-view-live.js.map +1 -1
  19. package/dist/browser/content/map-view-snapshot.js +191 -0
  20. package/dist/browser/content/map-view-snapshot.js.map +1 -0
  21. package/dist/browser/content/map-view-types.js.map +1 -1
  22. package/dist/browser/feedback/prompts.js +23 -9
  23. package/dist/browser/feedback/prompts.js.map +1 -1
  24. package/dist/browser/inputs/Combobox.js +164 -82
  25. package/dist/browser/inputs/Combobox.js.map +1 -1
  26. package/dist/browser/inputs/RichSelect.js +86 -67
  27. package/dist/browser/inputs/RichSelect.js.map +1 -1
  28. package/dist/browser/inputs/TextInput.js +1 -1
  29. package/dist/browser/inputs/TextInput.js.map +1 -1
  30. package/dist/browser/internal/keyed-for.js +47 -0
  31. package/dist/browser/internal/keyed-for.js.map +1 -0
  32. package/dist/browser/layout/AppWorkspace.js +73 -70
  33. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  34. package/dist/browser/layout/ScrollArea.js +4 -1
  35. package/dist/browser/layout/ScrollArea.js.map +1 -1
  36. package/dist/browser/text-editor/engine.js +13 -10
  37. package/dist/browser/text-editor/engine.js.map +1 -1
  38. package/dist/ssr/actions/Dropdown.js +101 -85
  39. package/dist/ssr/actions/Dropdown.js.map +1 -1
  40. package/dist/ssr/actions/Tabs.js +12 -9
  41. package/dist/ssr/actions/Tabs.js.map +1 -1
  42. package/dist/ssr/chat/ChatComposer.js +41 -15
  43. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  44. package/dist/ssr/content/MapView.js +128 -13
  45. package/dist/ssr/content/MapView.js.map +1 -1
  46. package/dist/ssr/content/MapViewFrame.js +1 -1
  47. package/dist/ssr/content/MapViewFrame.js.map +1 -1
  48. package/dist/ssr/content/MessageRow.js +9 -2
  49. package/dist/ssr/content/MessageRow.js.map +1 -1
  50. package/dist/ssr/content/index.js +1 -0
  51. package/dist/ssr/content/index.js.map +1 -1
  52. package/dist/ssr/content/map-view-glyphs.js +23 -0
  53. package/dist/ssr/content/map-view-glyphs.js.map +1 -0
  54. package/dist/ssr/content/map-view-snapshot.js +191 -0
  55. package/dist/ssr/content/map-view-snapshot.js.map +1 -0
  56. package/dist/ssr/content/map-view-types.js.map +1 -1
  57. package/dist/ssr/feedback/prompts.js +22 -8
  58. package/dist/ssr/feedback/prompts.js.map +1 -1
  59. package/dist/ssr/inputs/Combobox.js +79 -28
  60. package/dist/ssr/inputs/Combobox.js.map +1 -1
  61. package/dist/ssr/inputs/RichSelect.js +47 -28
  62. package/dist/ssr/inputs/RichSelect.js.map +1 -1
  63. package/dist/ssr/inputs/TextInput.js.map +1 -1
  64. package/dist/ssr/internal/keyed-for.js +47 -0
  65. package/dist/ssr/internal/keyed-for.js.map +1 -0
  66. package/dist/ssr/layout/AppWorkspace.js +1 -1
  67. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  68. package/dist/ssr/layout/ScrollArea.js +4 -1
  69. package/dist/ssr/layout/ScrollArea.js.map +1 -1
  70. package/dist/ssr/text-editor/engine.js +13 -10
  71. package/dist/ssr/text-editor/engine.js.map +1 -1
  72. package/dist/styles.css +1 -1
  73. package/dist/types/content/MapViewFrame.d.ts +1 -1
  74. package/dist/types/content/MessageRow.d.ts +1 -1
  75. package/dist/types/content/index.d.ts +2 -1
  76. package/dist/types/content/map-view-glyphs.d.ts +5 -0
  77. package/dist/types/content/map-view-live.d.ts +1 -1
  78. package/dist/types/content/map-view-snapshot.d.ts +13 -0
  79. package/dist/types/content/map-view-types.d.ts +15 -0
  80. package/dist/types/feedback/prompts.d.ts +5 -0
  81. package/dist/types/inputs/Combobox.d.ts +12 -0
  82. package/dist/types/inputs/TextInput.d.ts +2 -1
  83. package/dist/types/internal/keyed-for.d.ts +13 -0
  84. package/dist/types/layout/AppWorkspace.d.ts +4 -1
  85. package/dist/types/layout/ScrollArea.d.ts +2 -0
  86. package/dist/types/text-editor/engine.d.ts +1 -1
  87. package/package.json +4 -2
@@ -1,6 +1,7 @@
1
1
  import { template as _$template } from "solid-js/web";
2
2
  import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
3
  import { className as _$className } from "solid-js/web";
4
+ import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
4
5
  import { addEventListener as _$addEventListener } from "solid-js/web";
5
6
  import { effect as _$effect } from "solid-js/web";
6
7
  import { runHydrationEvents as _$runHydrationEvents } from "solid-js/web";
@@ -14,14 +15,17 @@ import { mergeProps as _$mergeProps } from "solid-js/web";
14
15
  import { memo as _$memo } from "solid-js/web";
15
16
  import { use as _$use } from "solid-js/web";
16
17
  var _tmpl$ = /*#__PURE__*/_$template(`<i class="ti ti-loader-2 k2b-spin"aria-hidden=true>`),
17
- _tmpl$2 = /*#__PURE__*/_$template(`<div class=k2b-combobox><div class=k2b-combobox__input><i class="ti ti-search"aria-hidden=true></i><input type=text role=combobox aria-autocomplete=list><!$><!/></div><div popover=manual class=k2b-choice-popover><div class=k2b-choice-options role=listbox><!$><!/><!$><!/>`),
18
+ _tmpl$2 = /*#__PURE__*/_$template(`<div class=k2b-combobox><div class=k2b-combobox__input><i class="ti ti-search"aria-hidden=true></i><input type=text role=combobox autocomplete=off aria-autocomplete=list><!$><!/></div><div popover=manual class=k2b-choice-popover><div class=k2b-choice-options role=listbox><!$><!/><!$><!/><!$><!/></div><!$><!/>`),
18
19
  _tmpl$3 = /*#__PURE__*/_$template(`<i class="ti ti-chevron-down"aria-hidden=true>`),
19
- _tmpl$4 = /*#__PURE__*/_$template(`<div class=k2b-choice-status data-tone=danger><span></span><button type=button>`),
20
- _tmpl$5 = /*#__PURE__*/_$template(`<div class=k2b-choice-status>`),
21
- _tmpl$6 = /*#__PURE__*/_$template(`<button type=button class=k2b-choice-option role=option><!$><!/><span><strong></strong><!$><!/>`),
22
- _tmpl$7 = /*#__PURE__*/_$template(`<i aria-hidden=true>`),
23
- _tmpl$8 = /*#__PURE__*/_$template(`<small>`);
24
- import { createEffect, createSignal, For, Show } from "solid-js";
20
+ _tmpl$4 = /*#__PURE__*/_$template(`<div class=k2b-choice-status>`),
21
+ _tmpl$5 = /*#__PURE__*/_$template(`<div class=k2b-choice-status data-tone=danger><span></span><button type=button>`),
22
+ _tmpl$6 = /*#__PURE__*/_$template(`<div class=k2b-choice-loading role=status><span class=k2b-sr-only></span><!$><!/>`),
23
+ _tmpl$7 = /*#__PURE__*/_$template(`<div class=k2b-choice-loading__row aria-hidden=true><span class=k2b-skeleton></span><span class=k2b-skeleton>`),
24
+ _tmpl$8 = /*#__PURE__*/_$template(`<button type=button class=k2b-choice-option role=option><!$><!/><span><strong></strong><!$><!/>`),
25
+ _tmpl$9 = /*#__PURE__*/_$template(`<i aria-hidden=true>`),
26
+ _tmpl$0 = /*#__PURE__*/_$template(`<small>`),
27
+ _tmpl$1 = /*#__PURE__*/_$template(`<div class=k2b-choice-footer>`);
28
+ import { createEffect, createMemo, createSignal, For, on, Show } from "solid-js";
25
29
  import { createFieldMeta, Field, fieldControlAria } from "../internal/field";
26
30
  import { useUiMessages } from "../intl/messages";
27
31
  import { createChoiceLoader, createChoicePopover, nextEnabledChoiceIndex } from "./choice";
@@ -53,8 +57,11 @@ export function Combobox(props) {
53
57
  // 150ms keeps typing responsive without hammering the loader.
54
58
  () => props.debounceMs ?? 150);
55
59
  const popover = createChoicePopover(() => Boolean(props.disabled || props.readOnly));
56
- const options = loader.options;
60
+ // A host notice or an error replaces the results; hidden results are never selectable.
61
+ const options = () => props.status || loader.error() ? [] : loader.options();
62
+ const optionsById = createMemo(() => new Map(options().map(option => [option.value, option])));
57
63
  const focusedOption = () => options()[focusedIndex()];
64
+ const searchable = value => value.trim().length >= (props.minQueryLength ?? 0);
58
65
  const focus = index => {
59
66
  setFocusedIndex(index);
60
67
  optionRefs[index]?.scrollIntoView({
@@ -64,9 +71,9 @@ export function Combobox(props) {
64
71
  /** `eager` loads the currently controlled query on open; typing supplies its
65
72
  * own query, so opening from `onInput` skips the duplicate request. */
66
73
  const open = (eager = true) => {
67
- if (props.disabled || props.readOnly || popover.open()) return;
74
+ if (props.disabled || props.readOnly || popover.open() || !searchable(query())) return;
68
75
  popover.show();
69
- if (eager) loader.load(query(), true);
76
+ if (eager && !props.status) loader.load(query(), true);
70
77
  focus(-1);
71
78
  };
72
79
  const reset = () => {
@@ -76,8 +83,14 @@ export function Combobox(props) {
76
83
  };
77
84
  const close = () => {
78
85
  popover.hide();
79
- reset();
86
+ if (!props.freeText) return reset();
87
+ loader.cancel();
88
+ focus(-1);
80
89
  };
90
+ createEffect(on(options, list => {
91
+ const index = props.autoHighlight;
92
+ if (index !== undefined && focusedIndex() < 0 && popover.open() && list.length > 0) setFocusedIndex(Math.min(index, list.length - 1));
93
+ }));
81
94
  createEffect(() => {
82
95
  if (props.readOnly) {
83
96
  popover.hide();
@@ -94,7 +107,10 @@ export function Combobox(props) {
94
107
  description: option.description,
95
108
  icon: option.icon
96
109
  });
97
- reset();
110
+ if (props.freeText) {
111
+ loader.cancel();
112
+ focus(-1);
113
+ } else reset();
98
114
  inputRef?.focus();
99
115
  };
100
116
  const onKeyDown = event => {
@@ -106,7 +122,7 @@ export function Combobox(props) {
106
122
  // While the results are open, Enter belongs to them: it never submits an enclosing form, even with no option active.
107
123
  event.preventDefault();
108
124
  const option = options()[focusedIndex()];
109
- if (option) select(option);
125
+ if (option) select(option);else if (props.freeText) close();
110
126
  } else if ((event.key === "Escape" || event.key === "Tab") && popover.open()) {
111
127
  // Escape is only ours while the list is open; otherwise it has to reach an
112
128
  // enclosing dialog or drawer.
@@ -146,7 +162,11 @@ export function Combobox(props) {
146
162
  _el$0 = _el$9.firstChild,
147
163
  [_el$1, _co$2] = _$getNextMarker(_el$0.nextSibling),
148
164
  _el$10 = _el$1.nextSibling,
149
- [_el$11, _co$3] = _$getNextMarker(_el$10.nextSibling);
165
+ [_el$11, _co$3] = _$getNextMarker(_el$10.nextSibling),
166
+ _el$12 = _el$11.nextSibling,
167
+ [_el$13, _co$4] = _$getNextMarker(_el$12.nextSibling),
168
+ _el$14 = _el$9.nextSibling,
169
+ [_el$15, _co$5] = _$getNextMarker(_el$14.nextSibling);
150
170
  var _ref$ = popover.setTrigger;
151
171
  typeof _ref$ === "function" ? _$use(_ref$, _el$2) : popover.setTrigger = _el$2;
152
172
  _$use(element => {
@@ -185,10 +205,16 @@ export function Combobox(props) {
185
205
  event.currentTarget.value = query();
186
206
  return;
187
207
  }
188
- setQuery(event.currentTarget.value);
189
- open(false);
190
- loader.load(event.currentTarget.value);
208
+ const value = event.currentTarget.value;
209
+ setQuery(value);
191
210
  focus(-1);
211
+ if (!searchable(value)) {
212
+ popover.hide();
213
+ loader.cancel();
214
+ return;
215
+ }
216
+ open(false);
217
+ if (!props.status) loader.load(value);
192
218
  },
193
219
  "onKeyDown": onKeyDown
194
220
  }), false, false);
@@ -208,83 +234,139 @@ export function Combobox(props) {
208
234
  _$setAttribute(_el$9, "id", listboxId);
209
235
  _$insert(_el$9, _$createComponent(Show, {
210
236
  get when() {
211
- return loader.error();
237
+ return props.status;
238
+ },
239
+ children: status => (() => {
240
+ var _el$17 = _$getNextElement(_tmpl$4);
241
+ _$insert(_el$17, status);
242
+ return _el$17;
243
+ })()
244
+ }), _el$1, _co$2);
245
+ _$insert(_el$9, _$createComponent(Show, {
246
+ get when() {
247
+ return _$memo(() => !!!props.status)() && loader.error();
212
248
  },
213
249
  children: message => (() => {
214
- var _el$13 = _$getNextElement(_tmpl$4),
215
- _el$14 = _el$13.firstChild,
216
- _el$15 = _el$14.nextSibling;
217
- _$insert(_el$14, message);
218
- _$addEventListener(_el$15, "click", loader.retry, true);
219
- _$insert(_el$15, () => messages().retry);
250
+ var _el$18 = _$getNextElement(_tmpl$5),
251
+ _el$19 = _el$18.firstChild,
252
+ _el$20 = _el$19.nextSibling;
253
+ _$insert(_el$19, message);
254
+ _$addEventListener(_el$20, "click", loader.retry, true);
255
+ _$insert(_el$20, () => messages().retry);
220
256
  _$runHydrationEvents();
221
- return _el$13;
257
+ return _el$18;
222
258
  })()
223
- }), _el$1, _co$2);
259
+ }), _el$11, _co$3);
224
260
  _$insert(_el$9, _$createComponent(For, {
225
261
  get each() {
226
- return _$memo(() => !!loader.error())() ? [] : options();
262
+ return options().map(option => option.value);
227
263
  },
228
264
  get fallback() {
229
- return (() => {
230
- var _el$16 = _$getNextElement(_tmpl$5);
231
- _$insert(_el$16, (() => {
232
- var _c$ = _$memo(() => query().length >= 2);
233
- return () => _c$() ? selectMessages().noResultsFound : selectMessages().typeToSearch;
234
- })());
235
- return _el$16;
236
- })();
237
- },
238
- children: (option, index) => (() => {
239
- var _el$17 = _$getNextElement(_tmpl$6),
240
- _el$22 = _el$17.firstChild,
241
- [_el$23, _co$5] = _$getNextMarker(_el$22.nextSibling),
242
- _el$18 = _el$23.nextSibling,
243
- _el$19 = _el$18.firstChild,
244
- _el$20 = _el$19.nextSibling,
245
- [_el$21, _co$4] = _$getNextMarker(_el$20.nextSibling);
246
- _el$17.$$click = () => select(option);
247
- _el$17.$$pointerdown = event => event.preventDefault();
248
- _el$17.$$pointermove = () => focus(index());
249
- _$use(element => optionRefs[index()] = element, _el$17);
250
- _$insert(_el$17, _$createComponent(Show, {
265
+ return _$createComponent(Show, {
251
266
  get when() {
252
- return option.icon;
267
+ return _$memo(() => !!!props.status)() && !loader.error();
253
268
  },
254
- children: icon => (() => {
255
- var _el$24 = _$getNextElement(_tmpl$7);
256
- _$effect(() => _$className(_el$24, iconClass(icon())));
257
- return _el$24;
258
- })()
259
- }), _el$23, _co$5);
260
- _$insert(_el$19, () => option.label);
261
- _$insert(_el$18, _$createComponent(Show, {
262
- get when() {
263
- return option.description;
264
- },
265
- children: description => (() => {
266
- var _el$25 = _$getNextElement(_tmpl$8);
267
- _$insert(_el$25, description);
268
- return _el$25;
269
- })()
270
- }), _el$21, _co$4);
271
- _$effect(_p$ => {
272
- var _v$ = `${listboxId}-${index()}`,
273
- _v$2 = index() === focusedIndex(),
274
- _v$3 = index() === focusedIndex() ? "true" : undefined;
275
- _v$ !== _p$.e && _$setAttribute(_el$17, "id", _p$.e = _v$);
276
- _v$2 !== _p$.t && _$setAttribute(_el$17, "aria-selected", _p$.t = _v$2);
277
- _v$3 !== _p$.a && _$setAttribute(_el$17, "data-focused", _p$.a = _v$3);
278
- return _p$;
279
- }, {
280
- e: undefined,
281
- t: undefined,
282
- a: undefined
269
+ get children() {
270
+ return _$createComponent(Show, {
271
+ get when() {
272
+ return loader.loading();
273
+ },
274
+ get fallback() {
275
+ return (() => {
276
+ var _el$25 = _$getNextElement(_tmpl$4);
277
+ _$insert(_el$25, (() => {
278
+ var _c$ = _$memo(() => query().length >= 2);
279
+ return () => _c$() ? props.empty ?? selectMessages().noResultsFound : selectMessages().typeToSearch;
280
+ })());
281
+ return _el$25;
282
+ })();
283
+ },
284
+ get children() {
285
+ var _el$21 = _$getNextElement(_tmpl$6),
286
+ _el$22 = _el$21.firstChild,
287
+ _el$23 = _el$22.nextSibling,
288
+ [_el$24, _co$6] = _$getNextMarker(_el$23.nextSibling);
289
+ _$insert(_el$22, () => messages().loading);
290
+ _$insert(_el$21, _$createComponent(For, {
291
+ each: [62, 48, 55],
292
+ children: width => (() => {
293
+ var _el$26 = _$getNextElement(_tmpl$7),
294
+ _el$27 = _el$26.firstChild,
295
+ _el$28 = _el$27.nextSibling;
296
+ _$setStyleProperty(_el$28, "width", `${width}%`);
297
+ return _el$26;
298
+ })()
299
+ }), _el$24, _co$6);
300
+ return _el$21;
301
+ }
302
+ });
303
+ }
283
304
  });
284
- _$runHydrationEvents();
285
- return _el$17;
305
+ },
306
+ children: (id, index) => {
307
+ const first = optionsById().get(id);
308
+ const option = () => optionsById().get(id) ?? first;
309
+ return (() => {
310
+ var _el$29 = _$getNextElement(_tmpl$8),
311
+ _el$34 = _el$29.firstChild,
312
+ [_el$35, _co$8] = _$getNextMarker(_el$34.nextSibling),
313
+ _el$30 = _el$35.nextSibling,
314
+ _el$31 = _el$30.firstChild,
315
+ _el$32 = _el$31.nextSibling,
316
+ [_el$33, _co$7] = _$getNextMarker(_el$32.nextSibling);
317
+ _el$29.$$click = () => select(option());
318
+ _el$29.$$pointerdown = event => event.preventDefault();
319
+ _el$29.$$pointermove = () => focus(index());
320
+ _$use(element => optionRefs[index()] = element, _el$29);
321
+ _$insert(_el$29, _$createComponent(Show, {
322
+ get when() {
323
+ return option().icon;
324
+ },
325
+ children: icon => (() => {
326
+ var _el$36 = _$getNextElement(_tmpl$9);
327
+ _$effect(() => _$className(_el$36, iconClass(icon())));
328
+ return _el$36;
329
+ })()
330
+ }), _el$35, _co$8);
331
+ _$insert(_el$31, () => option().label);
332
+ _$insert(_el$30, _$createComponent(Show, {
333
+ get when() {
334
+ return option().description;
335
+ },
336
+ children: description => (() => {
337
+ var _el$37 = _$getNextElement(_tmpl$0);
338
+ _$insert(_el$37, description);
339
+ return _el$37;
340
+ })()
341
+ }), _el$33, _co$7);
342
+ _$effect(_p$ => {
343
+ var _v$ = `${listboxId}-${index()}`,
344
+ _v$2 = index() === focusedIndex(),
345
+ _v$3 = index() === focusedIndex() ? "true" : undefined;
346
+ _v$ !== _p$.e && _$setAttribute(_el$29, "id", _p$.e = _v$);
347
+ _v$2 !== _p$.t && _$setAttribute(_el$29, "aria-selected", _p$.t = _v$2);
348
+ _v$3 !== _p$.a && _$setAttribute(_el$29, "data-focused", _p$.a = _v$3);
349
+ return _p$;
350
+ }, {
351
+ e: undefined,
352
+ t: undefined,
353
+ a: undefined
354
+ });
355
+ _$runHydrationEvents();
356
+ return _el$29;
357
+ })();
358
+ }
359
+ }), _el$13, _co$4);
360
+ _$insert(_el$8, _$createComponent(Show, {
361
+ get when() {
362
+ return props.footer;
363
+ },
364
+ children: footer => (() => {
365
+ var _el$38 = _$getNextElement(_tmpl$1);
366
+ _$insert(_el$38, footer);
367
+ return _el$38;
286
368
  })()
287
- }), _el$11, _co$3);
369
+ }), _el$15, _co$5);
288
370
  _$effect(() => _$setAttribute(_el$, "data-invalid", error() ? "true" : undefined));
289
371
  _$runHydrationEvents();
290
372
  return _el$;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["inputs/Combobox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, type JSX, Show } from \"solid-js\";\nimport { createFieldMeta, Field, fieldControlAria } from \"../internal/field\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type ChoiceOption, createChoiceLoader, createChoicePopover, nextEnabledChoiceIndex } from \"./choice\";\nimport type { FieldProps, MaybeAccessor } from \"./field-contract\";\nimport { resolveMaybeAccessor } from \"./field-contract\";\nimport { useSelectMessages } from \"./select-messages\";\n\nexport type ComboboxOption = { id: string; label: string; description?: string; icon?: string };\nexport type ComboboxProps = FieldProps & {\n readOnly?: boolean;\n query?: MaybeAccessor<string>;\n onQueryChange?: (query: string) => void;\n placeholder?: string;\n fetchData: (query: string, signal: AbortSignal) => Promise<ComboboxOption[]>;\n onSelect: (option: ComboboxOption) => void;\n debounceMs?: number;\n};\n\nconst iconClass = (icon: string) => (icon.split(/\\s+/).includes(\"ti\") ? icon : `ti ${icon}`);\n\nexport function Combobox(props: ComboboxProps): JSX.Element {\n const messages = useUiMessages();\n const selectMessages = useSelectMessages();\n const meta = createFieldMeta(props.id);\n const listboxId = `${meta.controlId}-listbox`;\n const [localQuery, setLocalQuery] = createSignal(\"\");\n const query = () => resolveMaybeAccessor(props.query) ?? localQuery();\n const setQuery = (value: string) => {\n setLocalQuery(value);\n props.onQueryChange?.(value);\n };\n const error = () => resolveMaybeAccessor(props.error);\n const [focusedIndex, setFocusedIndex] = createSignal(-1);\n let inputRef: HTMLInputElement | undefined;\n let optionRefs: HTMLButtonElement[] = [];\n const loader = createChoiceLoader(\n () => async (value, signal) => (await props.fetchData(value, signal)).map((option) => ({ ...option, value: option.id })),\n // Cloud debounces at 200ms and then pads the response with a 200ms minimum\n // load time to stop the spinner flickering. The spinner here sits in the\n // chevron slot and stale results stay visible, so there is nothing to pad —\n // 150ms keeps typing responsive without hammering the loader.\n () => props.debounceMs ?? 150,\n );\n const popover = createChoicePopover(() => Boolean(props.disabled || props.readOnly));\n const options = loader.options;\n const focusedOption = () => options()[focusedIndex()];\n const focus = (index: number) => {\n setFocusedIndex(index);\n optionRefs[index]?.scrollIntoView({ block: \"nearest\" });\n };\n /** `eager` loads the currently controlled query on open; typing supplies its\n * own query, so opening from `onInput` skips the duplicate request. */\n const open = (eager = true) => {\n if (props.disabled || props.readOnly || popover.open()) return;\n popover.show();\n if (eager) loader.load(query(), true);\n focus(-1);\n };\n const reset = () => {\n loader.cancel();\n setQuery(\"\");\n focus(-1);\n };\n const close = () => {\n popover.hide();\n reset();\n };\n createEffect(() => {\n if (props.readOnly) {\n popover.hide();\n loader.cancel();\n setFocusedIndex(-1);\n }\n });\n const select = (option: ChoiceOption<string>) => {\n if (props.disabled || props.readOnly) return;\n popover.hide();\n props.onSelect({ id: option.value, label: option.label, description: option.description, icon: option.icon });\n reset();\n inputRef?.focus();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (props.disabled || props.readOnly) return;\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n if (!popover.open()) open();\n else focus(nextEnabledChoiceIndex(options(), focusedIndex(), event.key === \"ArrowDown\" ? 1 : -1));\n } else if (event.key === \"Enter\" && popover.open()) {\n // While the results are open, Enter belongs to them: it never submits an enclosing form, even with no option active.\n event.preventDefault();\n const option = options()[focusedIndex()];\n if (option) select(option);\n } else if ((event.key === \"Escape\" || event.key === \"Tab\") && popover.open()) {\n // Escape is only ours while the list is open; otherwise it has to reach an\n // enclosing dialog or drawer.\n if (event.key === \"Escape\") event.preventDefault();\n close();\n }\n };\n\n return (\n <Field\n class={props.class}\n label={props.label}\n description={props.description}\n error={error()}\n meta={meta}\n required={props.required}\n disabled={props.disabled}\n >\n <div class=\"k2b-combobox\" data-invalid={error() ? \"true\" : undefined}>\n <div ref={popover.setTrigger} class=\"k2b-combobox__input\">\n <i class=\"ti ti-search\" aria-hidden=\"true\" />\n {/* A click, typing, or an arrow key opens the list. Focus alone (Tab, a dialog's initial focus) does not, so Escape still closes the enclosing dialog. */}\n <input\n ref={(element) => {\n inputRef = element;\n }}\n id={meta.controlId}\n type=\"text\"\n value={query()}\n placeholder={props.placeholder ?? messages().search}\n disabled={props.disabled}\n readOnly={props.readOnly}\n aria-readonly={props.readOnly ? \"true\" : undefined}\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded={popover.open()}\n aria-controls={listboxId}\n aria-activedescendant={focusedOption() ? `${listboxId}-${focusedIndex()}` : undefined}\n {...fieldControlAria(meta, props)}\n onClick={() => open()}\n onInput={(event) => {\n if (props.disabled || props.readOnly) {\n event.currentTarget.value = query();\n return;\n }\n setQuery(event.currentTarget.value);\n open(false);\n loader.load(event.currentTarget.value);\n focus(-1);\n }}\n onKeyDown={onKeyDown}\n />\n <Show when={loader.loading()} fallback={<i class=\"ti ti-chevron-down\" aria-hidden=\"true\" />}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n </div>\n <div ref={popover.setPopover} popover=\"manual\" class=\"k2b-choice-popover\">\n <div class=\"k2b-choice-options\" role=\"listbox\" id={listboxId}>\n <Show when={loader.error()}>\n {(message) => (\n <div class=\"k2b-choice-status\" data-tone=\"danger\">\n <span>{message()}</span>\n <button type=\"button\" onClick={loader.retry}>\n {messages().retry}\n </button>\n </div>\n )}\n </Show>\n <For\n each={loader.error() ? [] : options()}\n fallback={\n <div class=\"k2b-choice-status\">{query().length >= 2 ? selectMessages().noResultsFound : selectMessages().typeToSearch}</div>\n }\n >\n {(option, index) => (\n <button\n ref={(element) => (optionRefs[index()] = element)}\n type=\"button\"\n id={`${listboxId}-${index()}`}\n class=\"k2b-choice-option\"\n role=\"option\"\n aria-selected={index() === focusedIndex()}\n data-focused={index() === focusedIndex() ? \"true\" : undefined}\n onPointerMove={() => focus(index())}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => select(option)}\n >\n <Show when={option.icon}>{(icon) => <i class={iconClass(icon())} aria-hidden=\"true\" />}</Show>\n <span>\n <strong>{option.label}</strong>\n <Show when={option.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n </button>\n )}\n </For>\n </div>\n </div>\n </div>\n </Field>\n );\n}\n\nexport default Combobox;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["inputs/Combobox.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, For, type JSX, on, Show } from \"solid-js\";\nimport { createFieldMeta, Field, fieldControlAria } from \"../internal/field\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type ChoiceOption, createChoiceLoader, createChoicePopover, nextEnabledChoiceIndex } from \"./choice\";\nimport type { FieldProps, MaybeAccessor } from \"./field-contract\";\nimport { resolveMaybeAccessor } from \"./field-contract\";\nimport { useSelectMessages } from \"./select-messages\";\n\nexport type ComboboxOption = { id: string; label: string; description?: string; icon?: string };\nexport type ComboboxProps = FieldProps & {\n readOnly?: boolean;\n query?: MaybeAccessor<string>;\n onQueryChange?: (query: string) => void;\n placeholder?: string;\n fetchData: (query: string, signal: AbortSignal) => Promise<ComboboxOption[]>;\n onSelect: (option: ComboboxOption) => void;\n debounceMs?: number;\n /** The query is the field's own value: picking or closing keeps the typed text, and Enter without an active option only closes the list. */\n freeText?: boolean;\n /** Below this many characters nothing loads and the list stays closed. */\n minQueryLength?: number;\n /** This result (by position) is active as soon as results arrive, so Enter takes it. */\n autoHighlight?: number;\n /** Replaces the results with a host-owned notice, such as an exhausted quota; nothing loads while it is set. */\n status?: JSX.Element;\n /** Shown instead of the default text when a search finds nothing. */\n empty?: JSX.Element;\n /** One line under the results, such as their source. */\n footer?: JSX.Element;\n};\n\nconst iconClass = (icon: string) => (icon.split(/\\s+/).includes(\"ti\") ? icon : `ti ${icon}`);\n\nexport function Combobox(props: ComboboxProps): JSX.Element {\n const messages = useUiMessages();\n const selectMessages = useSelectMessages();\n const meta = createFieldMeta(props.id);\n const listboxId = `${meta.controlId}-listbox`;\n const [localQuery, setLocalQuery] = createSignal(\"\");\n const query = () => resolveMaybeAccessor(props.query) ?? localQuery();\n const setQuery = (value: string) => {\n setLocalQuery(value);\n props.onQueryChange?.(value);\n };\n const error = () => resolveMaybeAccessor(props.error);\n const [focusedIndex, setFocusedIndex] = createSignal(-1);\n let inputRef: HTMLInputElement | undefined;\n let optionRefs: HTMLButtonElement[] = [];\n const loader = createChoiceLoader(\n () => async (value, signal) => (await props.fetchData(value, signal)).map((option) => ({ ...option, value: option.id })),\n // Cloud debounces at 200ms and then pads the response with a 200ms minimum\n // load time to stop the spinner flickering. The spinner here sits in the\n // chevron slot and stale results stay visible, so there is nothing to pad —\n // 150ms keeps typing responsive without hammering the loader.\n () => props.debounceMs ?? 150,\n );\n const popover = createChoicePopover(() => Boolean(props.disabled || props.readOnly));\n // A host notice or an error replaces the results; hidden results are never selectable.\n const options = () => (props.status || loader.error() ? [] : loader.options());\n const optionsById = createMemo(() => new Map(options().map((option) => [option.value, option])));\n const focusedOption = () => options()[focusedIndex()];\n const searchable = (value: string) => value.trim().length >= (props.minQueryLength ?? 0);\n const focus = (index: number) => {\n setFocusedIndex(index);\n optionRefs[index]?.scrollIntoView({ block: \"nearest\" });\n };\n /** `eager` loads the currently controlled query on open; typing supplies its\n * own query, so opening from `onInput` skips the duplicate request. */\n const open = (eager = true) => {\n if (props.disabled || props.readOnly || popover.open() || !searchable(query())) return;\n popover.show();\n if (eager && !props.status) loader.load(query(), true);\n focus(-1);\n };\n const reset = () => {\n loader.cancel();\n setQuery(\"\");\n focus(-1);\n };\n const close = () => {\n popover.hide();\n if (!props.freeText) return reset();\n loader.cancel();\n focus(-1);\n };\n createEffect(\n on(options, (list) => {\n const index = props.autoHighlight;\n if (index !== undefined && focusedIndex() < 0 && popover.open() && list.length > 0) setFocusedIndex(Math.min(index, list.length - 1));\n }),\n );\n createEffect(() => {\n if (props.readOnly) {\n popover.hide();\n loader.cancel();\n setFocusedIndex(-1);\n }\n });\n const select = (option: ChoiceOption<string>) => {\n if (props.disabled || props.readOnly) return;\n popover.hide();\n props.onSelect({ id: option.value, label: option.label, description: option.description, icon: option.icon });\n if (props.freeText) {\n loader.cancel();\n focus(-1);\n } else reset();\n inputRef?.focus();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (props.disabled || props.readOnly) return;\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n if (!popover.open()) open();\n else focus(nextEnabledChoiceIndex(options(), focusedIndex(), event.key === \"ArrowDown\" ? 1 : -1));\n } else if (event.key === \"Enter\" && popover.open()) {\n // While the results are open, Enter belongs to them: it never submits an enclosing form, even with no option active.\n event.preventDefault();\n const option = options()[focusedIndex()];\n if (option) select(option);\n else if (props.freeText) close();\n } else if ((event.key === \"Escape\" || event.key === \"Tab\") && popover.open()) {\n // Escape is only ours while the list is open; otherwise it has to reach an\n // enclosing dialog or drawer.\n if (event.key === \"Escape\") event.preventDefault();\n close();\n }\n };\n\n return (\n <Field\n class={props.class}\n label={props.label}\n description={props.description}\n error={error()}\n meta={meta}\n required={props.required}\n disabled={props.disabled}\n >\n <div class=\"k2b-combobox\" data-invalid={error() ? \"true\" : undefined}>\n <div ref={popover.setTrigger} class=\"k2b-combobox__input\">\n <i class=\"ti ti-search\" aria-hidden=\"true\" />\n {/* A click, typing, or an arrow key opens the list. Focus alone (Tab, a dialog's initial focus) does not, so Escape still closes the enclosing dialog. */}\n <input\n ref={(element) => {\n inputRef = element;\n }}\n id={meta.controlId}\n type=\"text\"\n value={query()}\n placeholder={props.placeholder ?? messages().search}\n disabled={props.disabled}\n readOnly={props.readOnly}\n aria-readonly={props.readOnly ? \"true\" : undefined}\n role=\"combobox\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n aria-expanded={popover.open()}\n aria-controls={listboxId}\n aria-activedescendant={focusedOption() ? `${listboxId}-${focusedIndex()}` : undefined}\n {...fieldControlAria(meta, props)}\n onClick={() => open()}\n onInput={(event) => {\n if (props.disabled || props.readOnly) {\n event.currentTarget.value = query();\n return;\n }\n const value = event.currentTarget.value;\n setQuery(value);\n focus(-1);\n if (!searchable(value)) {\n popover.hide();\n loader.cancel();\n return;\n }\n open(false);\n if (!props.status) loader.load(value);\n }}\n onKeyDown={onKeyDown}\n />\n <Show when={loader.loading()} fallback={<i class=\"ti ti-chevron-down\" aria-hidden=\"true\" />}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n </div>\n <div ref={popover.setPopover} popover=\"manual\" class=\"k2b-choice-popover\">\n <div class=\"k2b-choice-options\" role=\"listbox\" id={listboxId}>\n <Show when={props.status}>{(status) => <div class=\"k2b-choice-status\">{status()}</div>}</Show>\n <Show when={!props.status && loader.error()}>\n {(message) => (\n <div class=\"k2b-choice-status\" data-tone=\"danger\">\n <span>{message()}</span>\n <button type=\"button\" onClick={loader.retry}>\n {messages().retry}\n </button>\n </div>\n )}\n </Show>\n {/* Keyed by option ID, not object: each fetch returns new objects, and a row replaced between press and release\n would swallow the click. */}\n <For\n each={options().map((option) => option.value)}\n fallback={\n <Show when={!props.status && !loader.error()}>\n <Show\n when={loader.loading()}\n fallback={\n <div class=\"k2b-choice-status\">\n {query().length >= 2 ? (props.empty ?? selectMessages().noResultsFound) : selectMessages().typeToSearch}\n </div>\n }\n >\n {/* The first search has nothing to keep visible yet: rows of the final size wait in its place. */}\n <div class=\"k2b-choice-loading\" role=\"status\">\n <span class=\"k2b-sr-only\">{messages().loading}</span>\n <For each={[62, 48, 55]}>\n {(width) => (\n <div class=\"k2b-choice-loading__row\" aria-hidden=\"true\">\n <span class=\"k2b-skeleton\" />\n <span class=\"k2b-skeleton\" style={{ width: `${width}%` }} />\n </div>\n )}\n </For>\n </div>\n </Show>\n </Show>\n }\n >\n {(id, index) => {\n const first = optionsById().get(id)!;\n const option = () => optionsById().get(id) ?? first;\n return (\n <button\n ref={(element) => (optionRefs[index()] = element)}\n type=\"button\"\n id={`${listboxId}-${index()}`}\n class=\"k2b-choice-option\"\n role=\"option\"\n aria-selected={index() === focusedIndex()}\n data-focused={index() === focusedIndex() ? \"true\" : undefined}\n onPointerMove={() => focus(index())}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => select(option())}\n >\n <Show when={option().icon}>{(icon) => <i class={iconClass(icon())} aria-hidden=\"true\" />}</Show>\n <span>\n <strong>{option().label}</strong>\n <Show when={option().description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n </button>\n );\n }}\n </For>\n </div>\n <Show when={props.footer}>{(footer) => <div class=\"k2b-choice-footer\">{footer()}</div>}</Show>\n </div>\n </div>\n </Field>\n );\n}\n\nexport default Combobox;\n"],"mappings":"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","ignoreList":[]}
@@ -71,6 +71,7 @@ import { useRichSelectMessages } from "./rich-select-messages";
71
71
  export const RICH_SELECT_SEARCH_FROM = 8;
72
72
  const PHONE_QUERY = "(max-width: 47.999rem)";
73
73
  const PLACEHOLDER_ROWS = 4;
74
+ const rowKey = row => row.kind === "heading" ? `heading:${row.id}` : `item:${row.item.value}`;
74
75
  const isPhone = () => typeof matchMedia === "function" && matchMedia(PHONE_QUERY).matches;
75
76
  function RichSelectVisual(props) {
76
77
  return _$createComponent(Switch, {
@@ -445,6 +446,9 @@ export function RichSelect(props) {
445
446
  });
446
447
  return result;
447
448
  });
449
+ // Rows are keyed by heading and value, not by object: a host that rebuilds equal items must not replace the option
450
+ // under a press, which would swallow its click.
451
+ const rowsByKey = createMemo(() => new Map(rows().map(row => [rowKey(row), row])));
448
452
  const selected = createMemo(() => {
449
453
  const current = value();
450
454
  return current === null ? undefined : props.items.find(item => item.value === current);
@@ -879,7 +883,7 @@ export function RichSelect(props) {
879
883
  get children() {
880
884
  return _$createComponent(For, {
881
885
  get each() {
882
- return rows();
886
+ return rows().map(rowKey);
883
887
  },
884
888
  get fallback() {
885
889
  return (() => {
@@ -888,80 +892,95 @@ export function RichSelect(props) {
888
892
  return _el$77;
889
893
  })();
890
894
  },
891
- children: row => row.kind === "heading" ? (() => {
892
- var _el$78 = _$getNextElement(_tmpl$26);
893
- _$insert(_el$78, () => row.label);
894
- return _el$78;
895
- })() : // biome-ignore lint/a11y/useKeyWithClickEvents: the trigger or search field handles the keys through aria-activedescendant.
896
- (() => {
897
- var _el$79 = _$getNextElement(_tmpl$27),
898
- _el$81 = _el$79.firstChild,
899
- [_el$82, _co$13] = _$getNextMarker(_el$81.nextSibling),
900
- _el$83 = _el$82.nextSibling,
901
- [_el$84, _co$14] = _$getNextMarker(_el$83.nextSibling),
902
- _el$80 = _el$84.nextSibling;
903
- _el$79.$$click = () => choose(row.item);
904
- _el$79.$$pointermove = () => {
905
- if (active() !== row.index) setActive(row.index);
895
+ children: key => {
896
+ const first = rowsByKey().get(key);
897
+ if (first.kind === "heading") {
898
+ const label = () => {
899
+ const current = rowsByKey().get(key);
900
+ return current?.kind === "heading" ? current.label : first.label;
901
+ };
902
+ return (() => {
903
+ var _el$78 = _$getNextElement(_tmpl$26);
904
+ _$insert(_el$78, label);
905
+ return _el$78;
906
+ })();
907
+ }
908
+ const entry = () => {
909
+ const current = rowsByKey().get(key);
910
+ return current?.kind === "item" ? current : first;
906
911
  };
907
- _$insert(_el$79, _$createComponent(Show, {
908
- get when() {
909
- return anyVisual();
910
- },
911
- get children() {
912
- return _$createComponent(Show, {
912
+ return (// biome-ignore lint/a11y/useKeyWithClickEvents: the trigger or search field handles the keys through aria-activedescendant.
913
+ (() => {
914
+ var _el$79 = _$getNextElement(_tmpl$27),
915
+ _el$81 = _el$79.firstChild,
916
+ [_el$82, _co$13] = _$getNextMarker(_el$81.nextSibling),
917
+ _el$83 = _el$82.nextSibling,
918
+ [_el$84, _co$14] = _$getNextMarker(_el$83.nextSibling),
919
+ _el$80 = _el$84.nextSibling;
920
+ _el$79.$$click = () => choose(entry().item);
921
+ _el$79.$$pointermove = () => {
922
+ if (active() !== entry().index) setActive(entry().index);
923
+ };
924
+ _$insert(_el$79, _$createComponent(Show, {
913
925
  get when() {
914
- return hasVisual(row.item);
915
- },
916
- get fallback() {
917
- return _$getNextElement(_tmpl$);
926
+ return anyVisual();
918
927
  },
919
928
  get children() {
920
- return _$createComponent(RichSelectVisual, {
921
- get item() {
922
- return row.item;
929
+ return _$createComponent(Show, {
930
+ get when() {
931
+ return hasVisual(entry().item);
932
+ },
933
+ get fallback() {
934
+ return _$getNextElement(_tmpl$);
935
+ },
936
+ get children() {
937
+ return _$createComponent(RichSelectVisual, {
938
+ get item() {
939
+ return entry().item;
940
+ }
941
+ });
923
942
  }
924
943
  });
925
944
  }
945
+ }), _el$82, _co$13);
946
+ _$insert(_el$79, _$createComponent(RichSelectRowText, {
947
+ get item() {
948
+ return entry().item;
949
+ },
950
+ get labelId() {
951
+ return `${optionId(entry().index)}-label`;
952
+ },
953
+ get lineId() {
954
+ return `${optionId(entry().index)}-line`;
955
+ }
956
+ }), _el$84, _co$14);
957
+ _$effect(_p$ => {
958
+ var _v$25 = optionId(entry().index),
959
+ _v$26 = entry().item.value === value(),
960
+ _v$27 = entry().item.disabledReason ? "true" : undefined,
961
+ _v$28 = `${optionId(entry().index)}-label`,
962
+ _v$29 = [entry().item.disabledReason || entry().item.description ? `${optionId(entry().index)}-line` : "", withPreview() && entry().index === active() ? previewId : ""].filter(Boolean).join(" ") || undefined,
963
+ _v$30 = entry().index === active() ? "true" : undefined;
964
+ _v$25 !== _p$.e && _$setAttribute(_el$79, "id", _p$.e = _v$25);
965
+ _v$26 !== _p$.t && _$setAttribute(_el$79, "aria-selected", _p$.t = _v$26);
966
+ _v$27 !== _p$.a && _$setAttribute(_el$79, "aria-disabled", _p$.a = _v$27);
967
+ _v$28 !== _p$.o && _$setAttribute(_el$79, "aria-labelledby", _p$.o = _v$28);
968
+ _v$29 !== _p$.i && _$setAttribute(_el$79, "aria-describedby", _p$.i = _v$29);
969
+ _v$30 !== _p$.n && _$setAttribute(_el$79, "data-active", _p$.n = _v$30);
970
+ return _p$;
971
+ }, {
972
+ e: undefined,
973
+ t: undefined,
974
+ a: undefined,
975
+ o: undefined,
976
+ i: undefined,
977
+ n: undefined
926
978
  });
927
- }
928
- }), _el$82, _co$13);
929
- _$insert(_el$79, _$createComponent(RichSelectRowText, {
930
- get item() {
931
- return row.item;
932
- },
933
- get labelId() {
934
- return `${optionId(row.index)}-label`;
935
- },
936
- get lineId() {
937
- return `${optionId(row.index)}-line`;
938
- }
939
- }), _el$84, _co$14);
940
- _$effect(_p$ => {
941
- var _v$25 = optionId(row.index),
942
- _v$26 = row.item.value === value(),
943
- _v$27 = row.item.disabledReason ? "true" : undefined,
944
- _v$28 = `${optionId(row.index)}-label`,
945
- _v$29 = [row.item.disabledReason || row.item.description ? `${optionId(row.index)}-line` : "", withPreview() && row.index === active() ? previewId : ""].filter(Boolean).join(" ") || undefined,
946
- _v$30 = row.index === active() ? "true" : undefined;
947
- _v$25 !== _p$.e && _$setAttribute(_el$79, "id", _p$.e = _v$25);
948
- _v$26 !== _p$.t && _$setAttribute(_el$79, "aria-selected", _p$.t = _v$26);
949
- _v$27 !== _p$.a && _$setAttribute(_el$79, "aria-disabled", _p$.a = _v$27);
950
- _v$28 !== _p$.o && _$setAttribute(_el$79, "aria-labelledby", _p$.o = _v$28);
951
- _v$29 !== _p$.i && _$setAttribute(_el$79, "aria-describedby", _p$.i = _v$29);
952
- _v$30 !== _p$.n && _$setAttribute(_el$79, "data-active", _p$.n = _v$30);
953
- return _p$;
954
- }, {
955
- e: undefined,
956
- t: undefined,
957
- a: undefined,
958
- o: undefined,
959
- i: undefined,
960
- n: undefined
961
- });
962
- _$runHydrationEvents();
963
- return _el$79;
964
- })()
979
+ _$runHydrationEvents();
980
+ return _el$79;
981
+ })()
982
+ );
983
+ }
965
984
  });
966
985
  }
967
986
  }), _el$60, _co$8);