@k2b/ui 0.40.0 → 0.42.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 (135) hide show
  1. package/dist/base.css +22 -0
  2. package/dist/browser/actions/Dropdown.js +220 -110
  3. package/dist/browser/actions/Dropdown.js.map +1 -1
  4. package/dist/browser/actions/index.js.map +1 -1
  5. package/dist/browser/actions/menu-search.js +166 -0
  6. package/dist/browser/actions/menu-search.js.map +1 -0
  7. package/dist/browser/actions/menu-sheet.js +107 -13
  8. package/dist/browser/actions/menu-sheet.js.map +1 -1
  9. package/dist/browser/chat/ChatComposer.js +759 -570
  10. package/dist/browser/chat/ChatComposer.js.map +1 -1
  11. package/dist/browser/content/CalendarWeekView.js +13 -1
  12. package/dist/browser/content/CalendarWeekView.js.map +1 -1
  13. package/dist/browser/inputs/ChoiceGroups.js +30 -18
  14. package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
  15. package/dist/browser/inputs/IconChoices.js +121 -0
  16. package/dist/browser/inputs/IconChoices.js.map +1 -0
  17. package/dist/browser/inputs/Select.js +32 -19
  18. package/dist/browser/inputs/Select.js.map +1 -1
  19. package/dist/browser/inputs/SpecialInputs.js +54 -25
  20. package/dist/browser/inputs/SpecialInputs.js.map +1 -1
  21. package/dist/browser/inputs/choice.js +4 -1
  22. package/dist/browser/inputs/choice.js.map +1 -1
  23. package/dist/browser/inputs/dictation/Dictation.js +546 -0
  24. package/dist/browser/inputs/dictation/Dictation.js.map +1 -0
  25. package/dist/browser/inputs/dictation/create-dictation.js +324 -0
  26. package/dist/browser/inputs/dictation/create-dictation.js.map +1 -0
  27. package/dist/browser/inputs/dictation/dictation-messages.js +115 -0
  28. package/dist/browser/inputs/dictation/dictation-messages.js.map +1 -0
  29. package/dist/browser/inputs/dictation/text-dictation.js +152 -0
  30. package/dist/browser/inputs/dictation/text-dictation.js.map +1 -0
  31. package/dist/browser/inputs/dictation/types.js +2 -0
  32. package/dist/browser/inputs/dictation/types.js.map +1 -0
  33. package/dist/browser/inputs/icon-messages.js +29 -2
  34. package/dist/browser/inputs/icon-messages.js.map +1 -1
  35. package/dist/browser/inputs/icon-texts.js +172 -0
  36. package/dist/browser/inputs/icon-texts.js.map +1 -0
  37. package/dist/browser/inputs/index.js +3 -0
  38. package/dist/browser/inputs/index.js.map +1 -1
  39. package/dist/browser/intl/messages.js +6 -0
  40. package/dist/browser/intl/messages.js.map +1 -1
  41. package/dist/browser/layout/AnchoredPopover.js +1 -1
  42. package/dist/browser/layout/AnchoredPopover.js.map +1 -1
  43. package/dist/browser/layout/AppWorkspace.js +118 -93
  44. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  45. package/dist/browser/layout/SettingsModal.js +10 -5
  46. package/dist/browser/layout/SettingsModal.js.map +1 -1
  47. package/dist/browser/text-editor/TextEditor.js +74 -25
  48. package/dist/browser/text-editor/TextEditor.js.map +1 -1
  49. package/dist/browser/text-editor/dictation.js +166 -0
  50. package/dist/browser/text-editor/dictation.js.map +1 -0
  51. package/dist/browser/text-editor/format.js +11 -7
  52. package/dist/browser/text-editor/format.js.map +1 -1
  53. package/dist/browser/text-editor/table.js +39 -11
  54. package/dist/browser/text-editor/table.js.map +1 -1
  55. package/dist/browser/text-editor.js +2 -1
  56. package/dist/browser/text-editor.js.map +1 -1
  57. package/dist/ssr/actions/Dropdown.js +118 -40
  58. package/dist/ssr/actions/Dropdown.js.map +1 -1
  59. package/dist/ssr/actions/index.js.map +1 -1
  60. package/dist/ssr/actions/menu-search.js +113 -0
  61. package/dist/ssr/actions/menu-search.js.map +1 -0
  62. package/dist/ssr/actions/menu-sheet.js +82 -13
  63. package/dist/ssr/actions/menu-sheet.js.map +1 -1
  64. package/dist/ssr/chat/ChatComposer.js +271 -99
  65. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  66. package/dist/ssr/content/CalendarWeekView.js +13 -1
  67. package/dist/ssr/content/CalendarWeekView.js.map +1 -1
  68. package/dist/ssr/inputs/ChoiceGroups.js +12 -3
  69. package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
  70. package/dist/ssr/inputs/IconChoices.js +59 -0
  71. package/dist/ssr/inputs/IconChoices.js.map +1 -0
  72. package/dist/ssr/inputs/Select.js +8 -2
  73. package/dist/ssr/inputs/Select.js.map +1 -1
  74. package/dist/ssr/inputs/SpecialInputs.js +54 -25
  75. package/dist/ssr/inputs/SpecialInputs.js.map +1 -1
  76. package/dist/ssr/inputs/choice.js +4 -1
  77. package/dist/ssr/inputs/choice.js.map +1 -1
  78. package/dist/ssr/inputs/dictation/Dictation.js +386 -0
  79. package/dist/ssr/inputs/dictation/Dictation.js.map +1 -0
  80. package/dist/ssr/inputs/dictation/create-dictation.js +324 -0
  81. package/dist/ssr/inputs/dictation/create-dictation.js.map +1 -0
  82. package/dist/ssr/inputs/dictation/dictation-messages.js +115 -0
  83. package/dist/ssr/inputs/dictation/dictation-messages.js.map +1 -0
  84. package/dist/ssr/inputs/dictation/text-dictation.js +152 -0
  85. package/dist/ssr/inputs/dictation/text-dictation.js.map +1 -0
  86. package/dist/ssr/inputs/dictation/types.js +2 -0
  87. package/dist/ssr/inputs/dictation/types.js.map +1 -0
  88. package/dist/ssr/inputs/icon-messages.js +29 -2
  89. package/dist/ssr/inputs/icon-messages.js.map +1 -1
  90. package/dist/ssr/inputs/icon-texts.js +172 -0
  91. package/dist/ssr/inputs/icon-texts.js.map +1 -0
  92. package/dist/ssr/inputs/index.js +3 -0
  93. package/dist/ssr/inputs/index.js.map +1 -1
  94. package/dist/ssr/intl/messages.js +6 -0
  95. package/dist/ssr/intl/messages.js.map +1 -1
  96. package/dist/ssr/layout/AnchoredPopover.js +1 -1
  97. package/dist/ssr/layout/AnchoredPopover.js.map +1 -1
  98. package/dist/ssr/layout/AppWorkspace.js +40 -20
  99. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  100. package/dist/ssr/layout/SettingsModal.js +10 -5
  101. package/dist/ssr/layout/SettingsModal.js.map +1 -1
  102. package/dist/ssr/text-editor/TextEditor.js +74 -25
  103. package/dist/ssr/text-editor/TextEditor.js.map +1 -1
  104. package/dist/ssr/text-editor/dictation.js +166 -0
  105. package/dist/ssr/text-editor/dictation.js.map +1 -0
  106. package/dist/ssr/text-editor/format.js +11 -7
  107. package/dist/ssr/text-editor/format.js.map +1 -1
  108. package/dist/ssr/text-editor/table.js +39 -11
  109. package/dist/ssr/text-editor/table.js.map +1 -1
  110. package/dist/ssr/text-editor.js +2 -1
  111. package/dist/ssr/text-editor.js.map +1 -1
  112. package/dist/styles.css +1 -1
  113. package/dist/types/actions/Dropdown.d.ts +34 -3
  114. package/dist/types/actions/index.d.ts +1 -1
  115. package/dist/types/actions/menu-search.d.ts +27 -0
  116. package/dist/types/actions/menu-sheet.d.ts +5 -1
  117. package/dist/types/chat/ChatComposer.d.ts +9 -0
  118. package/dist/types/inputs/IconChoices.d.ts +19 -0
  119. package/dist/types/inputs/SpecialInputs.d.ts +8 -0
  120. package/dist/types/inputs/dictation/Dictation.d.ts +26 -0
  121. package/dist/types/inputs/dictation/create-dictation.d.ts +77 -0
  122. package/dist/types/inputs/dictation/dictation-messages.d.ts +54 -0
  123. package/dist/types/inputs/dictation/text-dictation.d.ts +36 -0
  124. package/dist/types/inputs/dictation/types.d.ts +74 -0
  125. package/dist/types/inputs/icon-messages.d.ts +14 -0
  126. package/dist/types/inputs/icon-texts.d.ts +13 -0
  127. package/dist/types/inputs/index.d.ts +7 -0
  128. package/dist/types/intl/messages.d.ts +3 -0
  129. package/dist/types/layout/AnchoredPopover.d.ts +5 -0
  130. package/dist/types/layout/AppWorkspace.d.ts +6 -1
  131. package/dist/types/text-editor/TextEditor.d.ts +10 -1
  132. package/dist/types/text-editor/dictation.d.ts +20 -0
  133. package/dist/types/text-editor/table.d.ts +2 -0
  134. package/dist/types/text-editor.d.ts +2 -1
  135. package/package.json +3 -1
package/dist/base.css CHANGED
@@ -447,6 +447,28 @@
447
447
  margin: 0;
448
448
  }
449
449
 
450
+ /* Not the native box: it ignores border-radius, so it could not match the
451
+ @k2b/ui checkbox. Radio buttons stay native; they are round anyway. */
452
+ input[type="checkbox"] {
453
+ box-sizing: border-box;
454
+ appearance: none;
455
+ border: 1px solid var(--k2b-border-strong);
456
+ border-radius: 0.25rem;
457
+ background: var(--k2b-surface);
458
+ cursor: pointer;
459
+ }
460
+
461
+ input[type="checkbox"]:checked {
462
+ border-color: var(--k2b-action-solid);
463
+ /* Two gradient strokes, not an image: a host's img-src policy may refuse a
464
+ data: URL, and an input has no reliable pseudo-element in every engine. */
465
+ background:
466
+ linear-gradient(45deg, transparent 27%, #fff 29.5%, #fff 49.2%, transparent 51.7%) 23.08% 0 / 22% 100% no-repeat,
467
+ linear-gradient(135deg, transparent 41.1%, #fff 43.6%, #fff 60.6%, transparent 63.1%) 68.97% 0 / 42% 100% no-repeat,
468
+ var(--k2b-action-solid);
469
+ forced-color-adjust: none;
470
+ }
471
+
450
472
  input[type="range"] {
451
473
  width: 100%;
452
474
  }
@@ -19,20 +19,22 @@ var _tmpl$ = /*#__PURE__*/_$template(`<span class=k2b-dropdown__copy><span></spa
19
19
  _tmpl$2 = /*#__PURE__*/_$template(`<i aria-hidden=true>`),
20
20
  _tmpl$3 = /*#__PURE__*/_$template(`<img class=k2b-dropdown__image alt>`),
21
21
  _tmpl$4 = /*#__PURE__*/_$template(`<small>`),
22
- _tmpl$5 = /*#__PURE__*/_$template(`<button type=button role=menuitem tabindex=-1>`),
23
- _tmpl$6 = /*#__PURE__*/_$template(`<a role=menuitem tabindex=-1>`),
22
+ _tmpl$5 = /*#__PURE__*/_$template(`<button type=button tabindex=-1>`),
23
+ _tmpl$6 = /*#__PURE__*/_$template(`<a tabindex=-1>`),
24
24
  _tmpl$7 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__pair><!$><!/><!$><!/>`),
25
- _tmpl$8 = /*#__PURE__*/_$template(`<i class="ti ti-check">`),
26
- _tmpl$9 = /*#__PURE__*/_$template(`<span class=k2b-dropdown__checkbox aria-hidden=true>`),
27
- _tmpl$0 = /*#__PURE__*/_$template(`<i class="ti ti-check k2b-dropdown__check"aria-hidden=true>`),
28
- _tmpl$1 = /*#__PURE__*/_$template(`<span class=k2b-dropdown__color aria-hidden=true>`),
29
- _tmpl$10 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__row>`),
30
- _tmpl$11 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__section role=group><!$><!/><!$><!/>`),
31
- _tmpl$12 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__label>`),
32
- _tmpl$13 = /*#__PURE__*/_$template(`<div class="k2b-dropdown__label k2b-dropdown__label--action"><span></span><!$><!/>`),
33
- _tmpl$14 = /*#__PURE__*/_$template(`<button>`),
34
- _tmpl$15 = /*#__PURE__*/_$template(`<span><!$><!/><div popover=auto role=menu>`);
35
- import { createContext, createEffect, createSignal, createUniqueId, For, mergeProps, onCleanup, onMount, Show, splitProps, untrack, useContext } from "solid-js";
25
+ _tmpl$8 = /*#__PURE__*/_$template(`<button type=button role=menuitem tabindex=-1>`),
26
+ _tmpl$9 = /*#__PURE__*/_$template(`<i class="ti ti-check">`),
27
+ _tmpl$0 = /*#__PURE__*/_$template(`<span class=k2b-dropdown__checkbox aria-hidden=true>`),
28
+ _tmpl$1 = /*#__PURE__*/_$template(`<i class="ti ti-check k2b-dropdown__check"aria-hidden=true>`),
29
+ _tmpl$10 = /*#__PURE__*/_$template(`<span class=k2b-dropdown__color aria-hidden=true>`),
30
+ _tmpl$11 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__row>`),
31
+ _tmpl$12 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__section role=group><!$><!/><!$><!/>`),
32
+ _tmpl$13 = /*#__PURE__*/_$template(`<div class=k2b-dropdown__label>`),
33
+ _tmpl$14 = /*#__PURE__*/_$template(`<div class="k2b-dropdown__label k2b-dropdown__label--action"><span></span><!$><!/>`),
34
+ _tmpl$15 = /*#__PURE__*/_$template(`<button>`),
35
+ _tmpl$16 = /*#__PURE__*/_$template(`<p class=k2b-dropdown__note aria-hidden=true>`),
36
+ _tmpl$17 = /*#__PURE__*/_$template(`<span><!$><!/><div popover=auto>`);
37
+ import { createContext, createEffect, createSignal, createUniqueId, mergeProps, onCleanup, onMount, Show, splitProps, untrack, useContext } from "solid-js";
36
38
  import { Dynamic } from "solid-js/web";
37
39
  import { Tooltip, useLabelTooltip } from "../feedback/Tooltip";
38
40
  import { returnFocus, ringOnReturn } from "../internal/focus-return";
@@ -41,6 +43,7 @@ import { useLocale } from "../intl/locale";
41
43
  import { useUiMessages } from "../intl/messages";
42
44
  import { Button, iconTitle } from "./Button";
43
45
  import { focusMenuRow, focusRowSegment } from "./menu-rows";
46
+ import { MenuSearch } from "./menu-search";
44
47
  import { openMenuSheet, prefersMenuSheet } from "./menu-sheet";
45
48
 
46
49
  // Resolved outside JSX: a conditional attribute compiles to a lazy memo, which would be created inside the click handler.
@@ -57,9 +60,11 @@ const externalOf = item => "external" in item ? item.external : undefined;
57
60
 
58
61
  /** Declarative radio or checkbox row for compact action menus. */
59
62
 
63
+ /** A search field above the entries, for a menu long enough that finding an entry takes reading. */
64
+
60
65
  const MenuContext = createContext();
61
66
  const DropdownContext = createContext();
62
- const actionableItems = menu => menu ? Array.from(menu.querySelectorAll(["[role='menuitem']:not([aria-disabled='true'])", "[role='menuitemcheckbox']:not([aria-disabled='true'])", "[role='menuitemradio']:not([aria-disabled='true'])"].join(", "))) : [];
67
+ const actionableItems = menu => menu ? Array.from(menu.querySelectorAll(["[role='menuitem']:not([aria-disabled='true'])", "[role='menuitemcheckbox']:not([aria-disabled='true'])", "[role='menuitemradio']:not([aria-disabled='true'])", "[role='option']:not([aria-disabled='true'])"].join(", "))) : [];
63
68
  const focusMenuItem = (menu, index) => {
64
69
  const items = actionableItems(menu).filter(item => !item.classList.contains("k2b-dropdown__secondary"));
65
70
  if (items.length === 0) return;
@@ -121,6 +126,7 @@ const itemContent = props => [_$createComponent(Show, {
121
126
  export function DropdownItem(props) {
122
127
  const menu = useContext(MenuContext);
123
128
  const danger = () => props.danger || props.variant === "danger";
129
+ const role = () => menu?.role ?? "menuitem";
124
130
  const content = itemContent(props);
125
131
  return _$createComponent(Show, {
126
132
  get when() {
@@ -132,28 +138,36 @@ export function DropdownItem(props) {
132
138
  _el$8.$$click = event => {
133
139
  event.preventDefault();
134
140
  event.stopPropagation();
141
+ // Read before closing: a searchable menu clears its query as it closes, and the row then shows another entry.
142
+ const select = props.onSelect;
135
143
  if (props.closeOnSelect !== false) menu?.close();
136
- props.onSelect?.();
144
+ select?.();
137
145
  };
138
146
  _$insert(_el$8, content);
139
147
  _$effect(_p$ => {
140
- var _v$ = props.ariaLabel,
141
- _v$2 = `k2b-dropdown__item ${props.class ?? ""}`,
142
- _v$3 = danger() ? "true" : undefined,
143
- _v$4 = props.disabled,
144
- _v$5 = props.disabled ? "true" : undefined;
145
- _v$ !== _p$.e && _$setAttribute(_el$8, "aria-label", _p$.e = _v$);
146
- _v$2 !== _p$.t && _$className(_el$8, _p$.t = _v$2);
147
- _v$3 !== _p$.a && _$setAttribute(_el$8, "data-danger", _p$.a = _v$3);
148
- _v$4 !== _p$.o && _$setProperty(_el$8, "disabled", _p$.o = _v$4);
149
- _v$5 !== _p$.i && _$setAttribute(_el$8, "aria-disabled", _p$.i = _v$5);
148
+ var _v$ = role(),
149
+ _v$2 = props.ariaLabel,
150
+ _v$3 = `k2b-dropdown__item ${props.class ?? ""}`,
151
+ _v$4 = danger() ? "true" : undefined,
152
+ _v$5 = props.variant === "accent" ? "true" : undefined,
153
+ _v$6 = props.disabled,
154
+ _v$7 = props.disabled ? "true" : undefined;
155
+ _v$ !== _p$.e && _$setAttribute(_el$8, "role", _p$.e = _v$);
156
+ _v$2 !== _p$.t && _$setAttribute(_el$8, "aria-label", _p$.t = _v$2);
157
+ _v$3 !== _p$.a && _$className(_el$8, _p$.a = _v$3);
158
+ _v$4 !== _p$.o && _$setAttribute(_el$8, "data-danger", _p$.o = _v$4);
159
+ _v$5 !== _p$.i && _$setAttribute(_el$8, "data-accent", _p$.i = _v$5);
160
+ _v$6 !== _p$.n && _$setProperty(_el$8, "disabled", _p$.n = _v$6);
161
+ _v$7 !== _p$.s && _$setAttribute(_el$8, "aria-disabled", _p$.s = _v$7);
150
162
  return _p$;
151
163
  }, {
152
164
  e: undefined,
153
165
  t: undefined,
154
166
  a: undefined,
155
167
  o: undefined,
156
- i: undefined
168
+ i: undefined,
169
+ n: undefined,
170
+ s: undefined
157
171
  });
158
172
  _$runHydrationEvents();
159
173
  return _el$8;
@@ -163,28 +177,33 @@ export function DropdownItem(props) {
163
177
  var _el$9 = _$getNextElement(_tmpl$6);
164
178
  _el$9.$$click = event => {
165
179
  // With `onSelect`, a plain click runs it in place of the link; a modified click keeps its browser meaning.
166
- if (props.onSelect && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
180
+ const select = props.onSelect;
181
+ if (select && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
167
182
  event.preventDefault();
168
183
  menu?.close();
169
- props.onSelect();
184
+ select();
170
185
  return;
171
186
  }
172
187
  menu?.close(false);
173
188
  };
174
189
  _$insert(_el$9, content);
175
190
  _$effect(_p$ => {
176
- var _v$6 = href(),
177
- _v$7 = props.external ? "_blank" : undefined,
178
- _v$8 = props.external ? "noopener noreferrer" : undefined,
179
- _v$9 = props.ariaLabel,
180
- _v$0 = `k2b-dropdown__item ${props.class ?? ""}`,
181
- _v$1 = danger() ? "true" : undefined;
182
- _v$6 !== _p$.e && _$setAttribute(_el$9, "href", _p$.e = _v$6);
183
- _v$7 !== _p$.t && _$setAttribute(_el$9, "target", _p$.t = _v$7);
184
- _v$8 !== _p$.a && _$setAttribute(_el$9, "rel", _p$.a = _v$8);
185
- _v$9 !== _p$.o && _$setAttribute(_el$9, "aria-label", _p$.o = _v$9);
186
- _v$0 !== _p$.i && _$className(_el$9, _p$.i = _v$0);
187
- _v$1 !== _p$.n && _$setAttribute(_el$9, "data-danger", _p$.n = _v$1);
191
+ var _v$8 = href(),
192
+ _v$9 = props.external ? "_blank" : undefined,
193
+ _v$0 = props.external ? "noopener noreferrer" : undefined,
194
+ _v$1 = role(),
195
+ _v$10 = props.ariaLabel,
196
+ _v$11 = `k2b-dropdown__item ${props.class ?? ""}`,
197
+ _v$12 = danger() ? "true" : undefined,
198
+ _v$13 = props.variant === "accent" ? "true" : undefined;
199
+ _v$8 !== _p$.e && _$setAttribute(_el$9, "href", _p$.e = _v$8);
200
+ _v$9 !== _p$.t && _$setAttribute(_el$9, "target", _p$.t = _v$9);
201
+ _v$0 !== _p$.a && _$setAttribute(_el$9, "rel", _p$.a = _v$0);
202
+ _v$1 !== _p$.o && _$setAttribute(_el$9, "role", _p$.o = _v$1);
203
+ _v$10 !== _p$.i && _$setAttribute(_el$9, "aria-label", _p$.i = _v$10);
204
+ _v$11 !== _p$.n && _$className(_el$9, _p$.n = _v$11);
205
+ _v$12 !== _p$.s && _$setAttribute(_el$9, "data-danger", _p$.s = _v$12);
206
+ _v$13 !== _p$.h && _$setAttribute(_el$9, "data-accent", _p$.h = _v$13);
188
207
  return _p$;
189
208
  }, {
190
209
  e: undefined,
@@ -192,7 +211,9 @@ export function DropdownItem(props) {
192
211
  a: undefined,
193
212
  o: undefined,
194
213
  i: undefined,
195
- n: undefined
214
+ n: undefined,
215
+ s: undefined,
216
+ h: undefined
196
217
  });
197
218
  _$runHydrationEvents();
198
219
  return _el$9;
@@ -200,9 +221,11 @@ export function DropdownItem(props) {
200
221
  });
201
222
  }
202
223
  function DropdownChoiceItem(props) {
224
+ const menu = useContext(MenuContext);
225
+ // A searchable menu's field keeps the focus, so a second button in a row could not be reached; it is left out.
203
226
  return _$createComponent(Show, {
204
227
  get when() {
205
- return props.secondary;
228
+ return _$memo(() => menu?.role !== "option")() && props.secondary;
206
229
  },
207
230
  get fallback() {
208
231
  return _$createComponent(DropdownChoiceRow, props);
@@ -223,7 +246,7 @@ function DropdownChoiceItem(props) {
223
246
  }
224
247
  function DropdownSecondaryButton(props) {
225
248
  return (() => {
226
- var _el$13 = _$getNextElement(_tmpl$5);
249
+ var _el$13 = _$getNextElement(_tmpl$8);
227
250
  _el$13.$$click = event => {
228
251
  event.preventDefault();
229
252
  event.stopPropagation();
@@ -231,10 +254,10 @@ function DropdownSecondaryButton(props) {
231
254
  };
232
255
  _$insert(_el$13, () => props.label);
233
256
  _$effect(_p$ => {
234
- var _v$10 = props.class,
235
- _v$11 = props.ariaLabel;
236
- _v$10 !== _p$.e && _$className(_el$13, _p$.e = _v$10);
237
- _v$11 !== _p$.t && _$setAttribute(_el$13, "aria-label", _p$.t = _v$11);
257
+ var _v$14 = props.class,
258
+ _v$15 = props.ariaLabel;
259
+ _v$14 !== _p$.e && _$className(_el$13, _p$.e = _v$14);
260
+ _v$15 !== _p$.t && _$setAttribute(_el$13, "aria-label", _p$.t = _v$15);
238
261
  return _p$;
239
262
  }, {
240
263
  e: undefined,
@@ -262,7 +285,7 @@ function DropdownChoiceRow(props) {
262
285
  return !props.disabled && props.href ? undefined : "button";
263
286
  },
264
287
  get role() {
265
- return props.choice === "checkbox" ? "menuitemcheckbox" : "menuitemradio";
288
+ return _$memo(() => menu?.role === "option")() ? "option" : props.choice === "checkbox" ? "menuitemcheckbox" : "menuitemradio";
266
289
  },
267
290
  get ["aria-label"]() {
268
291
  return props.ariaLabel;
@@ -290,8 +313,9 @@ function DropdownChoiceRow(props) {
290
313
  }
291
314
  event.preventDefault();
292
315
  event.stopPropagation();
316
+ const action = props.action;
293
317
  if (props.closeOnSelect !== false) menu?.close();
294
- props.action();
318
+ action();
295
319
  },
296
320
  onKeyDown: event => {
297
321
  // A link answers only Enter; as a menu choice it also answers Space, like the button form.
@@ -305,13 +329,13 @@ function DropdownChoiceRow(props) {
305
329
  return props.choice === "checkbox";
306
330
  },
307
331
  get children() {
308
- var _el$14 = _$getNextElement(_tmpl$9);
332
+ var _el$14 = _$getNextElement(_tmpl$0);
309
333
  _$insert(_el$14, _$createComponent(Show, {
310
334
  get when() {
311
335
  return checked();
312
336
  },
313
337
  get children() {
314
- return _$getNextElement(_tmpl$8);
338
+ return _$getNextElement(_tmpl$9);
315
339
  }
316
340
  }));
317
341
  return _el$14;
@@ -342,7 +366,7 @@ function DropdownChoiceRow(props) {
342
366
  return props.color;
343
367
  },
344
368
  children: color => (() => {
345
- var _el$23 = _$getNextElement(_tmpl$1);
369
+ var _el$23 = _$getNextElement(_tmpl$10);
346
370
  _$effect(_$p => _$setStyleProperty(_el$23, "--k2b-dropdown-color", color()));
347
371
  return _el$23;
348
372
  })()
@@ -368,7 +392,7 @@ function DropdownChoiceRow(props) {
368
392
  return props.choice === "radio";
369
393
  },
370
394
  get children() {
371
- var _el$20 = _$getNextElement(_tmpl$0);
395
+ var _el$20 = _$getNextElement(_tmpl$1);
372
396
  _$effect(() => _$setAttribute(_el$20, "data-hidden", checked() ? undefined : "true"));
373
397
  return _el$20;
374
398
  }
@@ -428,7 +452,8 @@ export function DropdownItems(props) {
428
452
  return _$createComponent(MenuContext.Provider, {
429
453
  get value() {
430
454
  return {
431
- close: props.close
455
+ close: props.close,
456
+ role: props.role ?? "menuitem"
432
457
  };
433
458
  },
434
459
  get children() {
@@ -447,7 +472,7 @@ export function DropdownItems(props) {
447
472
  return renderItem(current);
448
473
  },
449
474
  get children() {
450
- var _el$25 = _$getNextElement(_tmpl$11),
475
+ var _el$25 = _$getNextElement(_tmpl$12),
451
476
  _el$27 = _el$25.firstChild,
452
477
  [_el$28, _co$5] = _$getNextMarker(_el$27.nextSibling),
453
478
  _el$29 = _el$28.nextSibling,
@@ -458,17 +483,17 @@ export function DropdownItems(props) {
458
483
  },
459
484
  children: label => _$createComponent(Show, {
460
485
  get when() {
461
- return current().labelAction;
486
+ return _$memo(() => props.role !== "option")() && current().labelAction;
462
487
  },
463
488
  get fallback() {
464
489
  return (() => {
465
- var _el$31 = _$getNextElement(_tmpl$12);
490
+ var _el$31 = _$getNextElement(_tmpl$13);
466
491
  _$insert(_el$31, label);
467
492
  return _el$31;
468
493
  })();
469
494
  },
470
495
  children: action => (() => {
471
- var _el$32 = _$getNextElement(_tmpl$13),
496
+ var _el$32 = _$getNextElement(_tmpl$14),
472
497
  _el$33 = _el$32.firstChild,
473
498
  _el$34 = _el$33.nextSibling,
474
499
  [_el$35, _co$7] = _$getNextMarker(_el$34.nextSibling);
@@ -494,7 +519,7 @@ export function DropdownItems(props) {
494
519
  });
495
520
  },
496
521
  get children() {
497
- var _el$26 = _$getNextElement(_tmpl$10);
522
+ var _el$26 = _$getNextElement(_tmpl$11);
498
523
  _$insert(_el$26, _$createComponent(KeyedFor, {
499
524
  get each() {
500
525
  return current().items;
@@ -506,10 +531,10 @@ export function DropdownItems(props) {
506
531
  }
507
532
  }), _el$30, _co$6);
508
533
  _$effect(_p$ => {
509
- var _v$12 = index() > 0 ? "true" : undefined,
510
- _v$13 = current().sectionLabel ?? (current().layout === "row" ? undefined : messages().actions);
511
- _v$12 !== _p$.e && _$setAttribute(_el$25, "data-divided", _p$.e = _v$12);
512
- _v$13 !== _p$.t && _$setAttribute(_el$25, "aria-label", _p$.t = _v$13);
534
+ var _v$16 = index() > 0 ? "true" : undefined,
535
+ _v$17 = current().sectionLabel ?? (current().layout === "row" ? undefined : messages().actions);
536
+ _v$16 !== _p$.e && _$setAttribute(_el$25, "data-divided", _p$.e = _v$16);
537
+ _v$17 !== _p$.t && _$setAttribute(_el$25, "aria-label", _p$.t = _v$17);
513
538
  return _p$;
514
539
  }, {
515
540
  e: undefined,
@@ -549,7 +574,9 @@ function DropdownTrigger(props) {
549
574
  get ["aria-expanded"]() {
550
575
  return context.open();
551
576
  },
552
- "aria-haspopup": "menu",
577
+ get ["aria-haspopup"]() {
578
+ return context.popup();
579
+ },
553
580
  get ["aria-label"]() {
554
581
  return local.label;
555
582
  },
@@ -593,7 +620,7 @@ function DropdownTrigger(props) {
593
620
  }));
594
621
  }
595
622
  return [(() => {
596
- var _el$36 = _$getNextElement(_tmpl$14);
623
+ var _el$36 = _$getNextElement(_tmpl$15);
597
624
  _$use(register, _el$36);
598
625
  _$spread(_el$36, _$mergeProps(rest, {
599
626
  get ["aria-controls"]() {
@@ -602,7 +629,9 @@ function DropdownTrigger(props) {
602
629
  get ["aria-expanded"]() {
603
630
  return context.open();
604
631
  },
605
- "aria-haspopup": "menu",
632
+ get ["aria-haspopup"]() {
633
+ return context.popup();
634
+ },
606
635
  get ["aria-label"]() {
607
636
  return local.label;
608
637
  },
@@ -664,6 +693,8 @@ function DropdownRoot(props) {
664
693
  const [internalOpen, setInternalOpen] = createSignal(false);
665
694
  const [entriesShown, setEntriesShown] = createSignal(!props.lazy);
666
695
  let triggerRef;
696
+ const [query, setQuery] = createSignal("");
697
+ let searchApi;
667
698
  /** Set while the menu is open from `openAt`: it appears beside `anchor`, and focus returns to `origin`. */
668
699
  let anchor;
669
700
  let origin;
@@ -712,6 +743,7 @@ function DropdownRoot(props) {
712
743
  // Before hiding, so the popover does not return focus on its own.
713
744
  if (restoreFocus) returnFocus(originElement() ?? triggerRef, triggerRing);
714
745
  if (menuRef?.matches(":popover-open")) menuRef.hidePopover();
746
+ setQuery("");
715
747
  // Like `open`, expose the closed state now: the toggle event can arrive after the next key press, and a trigger
716
748
  // that still reads as open would ignore Enter (Escape, then Enter at once, must reopen).
717
749
  setInternalOpen(false);
@@ -728,7 +760,8 @@ function DropdownRoot(props) {
728
760
  attachViewportListeners();
729
761
  place();
730
762
  queueMicrotask(() => {
731
- if (focus) focusMenuItem(menuRef, focus === "first" ? 0 : -1);
763
+ if (!focus) return;
764
+ if (searchApi) searchApi.focus(focus);else focusMenuItem(menuRef, focus === "first" ? 0 : -1);
732
765
  });
733
766
  };
734
767
  const requestOpen = (next, focus = "first") => {
@@ -755,6 +788,9 @@ function DropdownRoot(props) {
755
788
  label: props.label ?? messages().dropdownMenu,
756
789
  locale: locale(),
757
790
  top: props.sheetTop,
791
+ search: props.search,
792
+ icons: props.icons,
793
+ note: props.note,
758
794
  signal: current.signal
759
795
  }).finally(() => {
760
796
  sheet = undefined;
@@ -772,7 +808,8 @@ function DropdownRoot(props) {
772
808
  };
773
809
  const handleMenuKeyDown = event => {
774
810
  const items = actionableItems(menuRef);
775
- if (focusMenuRow(items, event.key) || focusRowSegment(items, event.key)) {
811
+ // A searchable menu's field keeps the focus and moves the marked entry itself.
812
+ if (!props.search && (focusMenuRow(items, event.key) || focusRowSegment(items, event.key))) {
776
813
  event.preventDefault();
777
814
  } else if (event.key === "Escape") {
778
815
  event.preventDefault();
@@ -782,6 +819,25 @@ function DropdownRoot(props) {
782
819
  requestOpen(false);
783
820
  }
784
821
  };
822
+ const triggerKey = event => {
823
+ if (!["Enter", " ", "ArrowDown", "ArrowUp"].includes(event.key)) return;
824
+ // Ctrl+Enter and the like keep their browser meaning on an adopted link, such as opening it in a new tab.
825
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
826
+ event.preventDefault();
827
+ event.stopPropagation();
828
+ if (isOpen()) {
829
+ if (searchApi) searchApi.focus(event.key === "ArrowUp" ? "last" : "first");else focusMenuItem(menuRef, event.key === "ArrowUp" ? -1 : 0);
830
+ return;
831
+ }
832
+ anchor = origin = undefined;
833
+ keyFocus = event.key === "ArrowUp" ? "last" : "first";
834
+ requestOpen(true, keyFocus);
835
+ keyFocus = false;
836
+ };
837
+ const toggle = () => {
838
+ if (!isOpen()) anchor = origin = undefined;
839
+ requestOpen(!isOpen());
840
+ };
785
841
  const context = {
786
842
  disabled: () => Boolean(props.disabled),
787
843
  menuId,
@@ -789,23 +845,9 @@ function DropdownRoot(props) {
789
845
  registerTrigger: element => {
790
846
  triggerRef = element;
791
847
  },
792
- toggle: () => {
793
- if (!isOpen()) anchor = origin = undefined;
794
- requestOpen(!isOpen());
795
- },
796
- triggerKeyDown: event => {
797
- if (!["Enter", " ", "ArrowDown", "ArrowUp"].includes(event.key)) return;
798
- event.preventDefault();
799
- event.stopPropagation();
800
- if (isOpen()) {
801
- focusMenuItem(menuRef, event.key === "ArrowUp" ? -1 : 0);
802
- return;
803
- }
804
- anchor = origin = undefined;
805
- keyFocus = event.key === "ArrowUp" ? "last" : "first";
806
- requestOpen(true, keyFocus);
807
- keyFocus = false;
808
- }
848
+ toggle,
849
+ popup: () => props.search ? "dialog" : "menu",
850
+ triggerKeyDown: event => triggerKey(event)
809
851
  };
810
852
  createEffect(() => {
811
853
  const controlled = props.open;
@@ -832,16 +874,33 @@ function DropdownRoot(props) {
832
874
  mounted = true;
833
875
  if (props.open) open(false);
834
876
  onCleanup(detachViewportListeners);
877
+ const adopted = props.attach?.();
878
+ if (!adopted) return;
879
+ triggerRef = adopted;
880
+ adopted.setAttribute("aria-controls", menuId);
881
+ const onClick = event => {
882
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
883
+ event.preventDefault();
884
+ event.stopPropagation();
885
+ toggle();
886
+ };
887
+ adopted.addEventListener("click", onClick);
888
+ adopted.addEventListener("keydown", triggerKey);
889
+ createEffect(() => adopted.setAttribute("aria-expanded", String(isOpen())));
890
+ onCleanup(() => {
891
+ adopted.removeEventListener("click", onClick);
892
+ adopted.removeEventListener("keydown", triggerKey);
893
+ });
835
894
  });
836
895
  onCleanup(() => sheet?.abort());
837
896
  return _$createComponent(DropdownContext.Provider, {
838
897
  value: context,
839
898
  get children() {
840
- var _el$37 = _$getNextElement(_tmpl$15),
841
- _el$39 = _el$37.firstChild,
842
- [_el$40, _co$8] = _$getNextMarker(_el$39.nextSibling),
843
- _el$38 = _el$40.nextSibling;
844
- _$insert(_el$37, () => props.children, _el$40, _co$8);
899
+ var _el$37 = _$getNextElement(_tmpl$17),
900
+ _el$40 = _el$37.firstChild,
901
+ [_el$41, _co$8] = _$getNextMarker(_el$40.nextSibling),
902
+ _el$38 = _el$41.nextSibling;
903
+ _$insert(_el$37, () => props.children, _el$41, _co$8);
845
904
  _el$38.addEventListener("toggle", event => {
846
905
  const nextOpen = event.newState === "open";
847
906
  const wasOpen = event.oldState === "open";
@@ -853,6 +912,7 @@ function DropdownRoot(props) {
853
912
  const focused = document.activeElement;
854
913
  const target = wasOpen && !nextOpen ? originElement() : undefined;
855
914
  if (target?.isConnected && (!focused || focused === document.body || menuRef?.contains(focused))) returnFocus(target, triggerRing);
915
+ if (wasOpen && !nextOpen) setQuery("");
856
916
  if (wasOpen && !nextOpen) props.onClose?.();
857
917
  if (!nextOpen && props.open === true) queueMicrotask(() => props.open === true && open(false));
858
918
  });
@@ -865,31 +925,77 @@ function DropdownRoot(props) {
865
925
  return entriesShown();
866
926
  },
867
927
  get children() {
868
- return _$createComponent(DropdownItems, {
869
- get items() {
870
- return props.items;
928
+ return [_$createComponent(Show, {
929
+ get when() {
930
+ return props.search;
871
931
  },
872
- close: close
873
- });
932
+ get fallback() {
933
+ return _$createComponent(DropdownItems, {
934
+ get items() {
935
+ return props.items;
936
+ },
937
+ close: close
938
+ });
939
+ },
940
+ children: search => _$createComponent(MenuSearch, {
941
+ Items: DropdownItems,
942
+ get items() {
943
+ return props.items;
944
+ },
945
+ get search() {
946
+ return search();
947
+ },
948
+ get label() {
949
+ return props.label ?? messages().dropdownMenu;
950
+ },
951
+ query: query,
952
+ setQuery: setQuery,
953
+ close: close,
954
+ api: api => {
955
+ searchApi = api;
956
+ onCleanup(() => {
957
+ if (searchApi === api) searchApi = undefined;
958
+ });
959
+ }
960
+ })
961
+ }), _$createComponent(Show, {
962
+ get when() {
963
+ return props.note;
964
+ },
965
+ get children() {
966
+ var _el$39 = _$getNextElement(_tmpl$16);
967
+ _$setAttribute(_el$39, "id", `${menuId}-note`);
968
+ _$insert(_el$39, () => props.note);
969
+ return _el$39;
970
+ }
971
+ })];
874
972
  }
875
973
  }));
876
974
  _$effect(_p$ => {
877
- var _v$14 = `k2b-dropdown ${props.class ?? ""}`,
878
- _v$15 = props.label ?? messages().dropdownMenu,
879
- _v$16 = `k2b-dropdown__menu ${props.menuClass ?? ""}`,
880
- _v$17 = props.width ? {
975
+ var _v$18 = `k2b-dropdown ${props.class ?? ""}`,
976
+ _v$19 = props.search ? "dialog" : "menu",
977
+ _v$20 = props.label ?? messages().dropdownMenu,
978
+ _v$21 = props.note ? `${menuId}-note` : undefined,
979
+ _v$22 = `k2b-dropdown__menu ${props.menuClass ?? ""}`,
980
+ _v$23 = props.width ? {
881
981
  "--k2b-dropdown-width": props.width
882
982
  } : undefined,
883
- _v$18 = props.width ? "fixed" : undefined,
884
- _v$19 = props.variant ?? "default",
885
- _v$20 = position();
886
- _v$14 !== _p$.e && _$className(_el$37, _p$.e = _v$14);
887
- _v$15 !== _p$.t && _$setAttribute(_el$38, "aria-label", _p$.t = _v$15);
888
- _v$16 !== _p$.a && _$className(_el$38, _p$.a = _v$16);
889
- _p$.o = _$style(_el$38, _v$17, _p$.o);
890
- _v$18 !== _p$.i && _$setAttribute(_el$38, "data-width", _p$.i = _v$18);
891
- _v$19 !== _p$.n && _$setAttribute(_el$38, "data-variant", _p$.n = _v$19);
892
- _v$20 !== _p$.s && _$setAttribute(_el$38, "data-position", _p$.s = _v$20);
983
+ _v$24 = props.width ? "fixed" : undefined,
984
+ _v$25 = props.variant ?? "default",
985
+ _v$26 = props.icons === "tile" ? "tile" : undefined,
986
+ _v$27 = props.search ? "" : undefined,
987
+ _v$28 = position();
988
+ _v$18 !== _p$.e && _$className(_el$37, _p$.e = _v$18);
989
+ _v$19 !== _p$.t && _$setAttribute(_el$38, "role", _p$.t = _v$19);
990
+ _v$20 !== _p$.a && _$setAttribute(_el$38, "aria-label", _p$.a = _v$20);
991
+ _v$21 !== _p$.o && _$setAttribute(_el$38, "aria-describedby", _p$.o = _v$21);
992
+ _v$22 !== _p$.i && _$className(_el$38, _p$.i = _v$22);
993
+ _p$.n = _$style(_el$38, _v$23, _p$.n);
994
+ _v$24 !== _p$.s && _$setAttribute(_el$38, "data-width", _p$.s = _v$24);
995
+ _v$25 !== _p$.h && _$setAttribute(_el$38, "data-variant", _p$.h = _v$25);
996
+ _v$26 !== _p$.r && _$setAttribute(_el$38, "data-icons", _p$.r = _v$26);
997
+ _v$27 !== _p$.d && _$setAttribute(_el$38, "data-search", _p$.d = _v$27);
998
+ _v$28 !== _p$.l && _$setAttribute(_el$38, "data-position", _p$.l = _v$28);
893
999
  return _p$;
894
1000
  }, {
895
1001
  e: undefined,
@@ -898,7 +1004,11 @@ function DropdownRoot(props) {
898
1004
  o: undefined,
899
1005
  i: undefined,
900
1006
  n: undefined,
901
- s: undefined
1007
+ s: undefined,
1008
+ h: undefined,
1009
+ r: undefined,
1010
+ d: undefined,
1011
+ l: undefined
902
1012
  });
903
1013
  _$runHydrationEvents();
904
1014
  return _el$37;