@kerfjs/ui 5.0.0-beta.16 → 5.0.0-beta.18

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 (218) hide show
  1. package/README.md +111 -81
  2. package/ai/component-catalog-extension.schema.json +79 -0
  3. package/ai/component-catalog.json +499 -0
  4. package/ai/component-catalog.schema.json +15 -0
  5. package/ai/public-api-signatures-v1.md +86 -32
  6. package/ai/skill.md +60 -51
  7. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  8. package/dist/app-tab.d.ts +1 -1
  9. package/dist/app-tab.js +1 -1
  10. package/dist/catalog.d.ts +12 -3
  11. package/dist/catalog.js +283 -90
  12. package/dist/catalog.js.map +1 -1
  13. package/dist/chunk-43FUQH35.js +83 -0
  14. package/dist/chunk-43FUQH35.js.map +1 -0
  15. package/dist/{chunk-YGN7OMLT.js → chunk-4TRDV6RL.js} +2 -2
  16. package/dist/chunk-4TRDV6RL.js.map +1 -0
  17. package/dist/chunk-6ETZKKAE.js +38 -0
  18. package/dist/chunk-6ETZKKAE.js.map +1 -0
  19. package/dist/chunk-6WTETUZ6.js +21 -0
  20. package/dist/chunk-6WTETUZ6.js.map +1 -0
  21. package/dist/chunk-7L6EHUMV.js +31 -0
  22. package/dist/chunk-7L6EHUMV.js.map +1 -0
  23. package/dist/chunk-BBRXX6PF.js +108 -0
  24. package/dist/chunk-BBRXX6PF.js.map +1 -0
  25. package/dist/chunk-DPDBVKNA.js +113 -0
  26. package/dist/chunk-DPDBVKNA.js.map +1 -0
  27. package/dist/chunk-ELSJX4W7.js +68 -0
  28. package/dist/chunk-ELSJX4W7.js.map +1 -0
  29. package/dist/{chunk-LCOBDOCC.js → chunk-ENVTNLYC.js} +153 -67
  30. package/dist/chunk-ENVTNLYC.js.map +1 -0
  31. package/dist/{chunk-AHQI52QO.js → chunk-FEY65TBV.js} +6 -2
  32. package/dist/chunk-FEY65TBV.js.map +1 -0
  33. package/dist/chunk-JTHUXDN2.js +58 -0
  34. package/dist/chunk-JTHUXDN2.js.map +1 -0
  35. package/dist/chunk-LPOMRC4C.js +25 -0
  36. package/dist/chunk-LPOMRC4C.js.map +1 -0
  37. package/dist/chunk-NENBUYX5.js +79 -0
  38. package/dist/chunk-NENBUYX5.js.map +1 -0
  39. package/dist/{chunk-XXMOARTH.js → chunk-NVWXKU37.js} +57 -16
  40. package/dist/chunk-NVWXKU37.js.map +1 -0
  41. package/dist/chunk-PIDTEQ3U.js +105 -0
  42. package/dist/chunk-PIDTEQ3U.js.map +1 -0
  43. package/dist/chunk-PX3YSS5C.js +32 -0
  44. package/dist/chunk-PX3YSS5C.js.map +1 -0
  45. package/dist/chunk-PZQVSDLH.js +61 -0
  46. package/dist/chunk-PZQVSDLH.js.map +1 -0
  47. package/dist/chunk-QIRXYKCE.js +113 -0
  48. package/dist/chunk-QIRXYKCE.js.map +1 -0
  49. package/dist/chunk-QO55FN2Y.js +20 -0
  50. package/dist/chunk-QO55FN2Y.js.map +1 -0
  51. package/dist/chunk-QQFJMRIB.js +45 -0
  52. package/dist/chunk-QQFJMRIB.js.map +1 -0
  53. package/dist/chunk-S3HKPI7I.js +218 -0
  54. package/dist/chunk-S3HKPI7I.js.map +1 -0
  55. package/dist/chunk-S3PCZ7NV.js +38 -0
  56. package/dist/chunk-S3PCZ7NV.js.map +1 -0
  57. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  58. package/dist/chunk-SRSJO5QE.js.map +1 -0
  59. package/dist/chunk-U5IFGQCD.js +117 -0
  60. package/dist/chunk-U5IFGQCD.js.map +1 -0
  61. package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
  62. package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
  63. package/dist/chunk-ZK2G3VR5.js +35 -0
  64. package/dist/chunk-ZK2G3VR5.js.map +1 -0
  65. package/dist/chunk-ZVI5YST2.js +41 -0
  66. package/dist/chunk-ZVI5YST2.js.map +1 -0
  67. package/dist/collapsible-panel.d.ts +2 -2
  68. package/dist/collapsible-panel.js +47 -6
  69. package/dist/collapsible-panel.js.map +1 -1
  70. package/dist/device-class.js +33 -5
  71. package/dist/device-class.js.map +1 -1
  72. package/dist/disclosure-arrow.js +1 -1
  73. package/dist/empty-state.d.ts +1 -1
  74. package/dist/empty-state.js +1 -1
  75. package/dist/floating-toolbar.d.ts +1 -1
  76. package/dist/floating-toolbar.js +1 -1
  77. package/dist/index.js +25 -25
  78. package/dist/list-action-row.d.ts +1 -1
  79. package/dist/list-action-row.js +1 -1
  80. package/dist/list-header.d.ts +1 -1
  81. package/dist/list-header.js +1 -1
  82. package/dist/list-inset-control.d.ts +1 -1
  83. package/dist/list-inset-control.js +1 -1
  84. package/dist/list-inset-text.d.ts +1 -1
  85. package/dist/list-inset-text.js +1 -1
  86. package/dist/list-item.d.ts +1 -1
  87. package/dist/list-item.js +1 -1
  88. package/dist/loading-spinner.js +1 -1
  89. package/dist/nav-stack.d.ts +1 -1
  90. package/dist/nav-stack.js +1 -1
  91. package/dist/panel-header.d.ts +1 -1
  92. package/dist/panel-header.js +1 -1
  93. package/dist/resizable-region.d.ts +1 -1
  94. package/dist/resizable-region.js +1 -1
  95. package/dist/segmented-control.js +1 -1
  96. package/dist/select.d.ts +1 -1
  97. package/dist/select.js +1 -1
  98. package/dist/skeleton.d.ts +1 -1
  99. package/dist/skeleton.js +1 -1
  100. package/dist/split-view.d.ts +1 -1
  101. package/dist/split-view.js +70 -10
  102. package/dist/split-view.js.map +1 -1
  103. package/dist/state-banner.d.ts +1 -1
  104. package/dist/state-banner.js +1 -1
  105. package/dist/styles/catalog.css +48 -1
  106. package/dist/styles/toolbar-text.css +54 -2
  107. package/dist/styles/webawesome.css +131 -0
  108. package/dist/tab-bar.d.ts +1 -1
  109. package/dist/tab-bar.js +1 -1
  110. package/dist/tab-scaffold.d.ts +1 -1
  111. package/dist/tab-scaffold.js +43 -8
  112. package/dist/tab-scaffold.js.map +1 -1
  113. package/dist/token-search-field.js +1 -1
  114. package/dist/toolbar-control-group.d.ts +1 -1
  115. package/dist/toolbar-control-group.js +1 -1
  116. package/dist/toolbar-text.d.ts +17 -1
  117. package/dist/toolbar-text.js +1 -1
  118. package/dist/toolbar.d.ts +1 -1
  119. package/dist/toolbar.js +1 -1
  120. package/dist/value-table.d.ts +2 -2
  121. package/dist/value-table.js +1 -1
  122. package/dist/wire-catalog.d.ts +31 -2
  123. package/dist/wire-catalog.js +203 -8
  124. package/dist/wire-catalog.js.map +1 -1
  125. package/dist/wire-nav-stack.js +22 -7
  126. package/dist/wire-nav-stack.js.map +1 -1
  127. package/dist/wire-resizable-regions.d.ts +1 -1
  128. package/dist/wire-resizable-regions.js +1 -1
  129. package/dist/wire-sidebar.js +52 -41
  130. package/dist/wire-sidebar.js.map +1 -1
  131. package/dist/wire-tab-bars.js +1 -1
  132. package/dist/wire-tab-scaffold.js +9 -4
  133. package/dist/wire-tab-scaffold.js.map +1 -1
  134. package/dist/wire-token-search-fields.d.ts +1 -1
  135. package/dist/wire-token-search-fields.js +1 -1
  136. package/dist/workbench.d.ts +1 -1
  137. package/dist/workbench.js +56 -9
  138. package/dist/workbench.js.map +1 -1
  139. package/docs/app-layouts.md +7 -7
  140. package/docs/catalog.md +110 -25
  141. package/docs/collapsible-panel.md +38 -11
  142. package/docs/component-contract.md +30 -2
  143. package/docs/component-selection.md +76 -65
  144. package/docs/design/templates.md +1 -1
  145. package/docs/design-philosophy.md +5 -5
  146. package/docs/device-class.md +8 -8
  147. package/docs/examples/command-palette-adapter.tsx +67 -17
  148. package/docs/examples/component-catalog-extension.json +45 -0
  149. package/docs/layout.md +19 -19
  150. package/docs/nav-stack.md +15 -6
  151. package/docs/recipes.md +4 -4
  152. package/docs/split-view.md +3 -3
  153. package/docs/tab-scaffold.md +16 -7
  154. package/docs/ux-demo.md +2 -2
  155. package/docs/webawesome-theme.md +73 -8
  156. package/docs/workbench.md +18 -5
  157. package/llms.txt +36 -15
  158. package/package.json +6 -4
  159. package/ux-demo/recipes/app-shell.tsx +206 -10
  160. package/ux-demo/recipes/collapsible-sidebar.tsx +167 -44
  161. package/ux-demo/recipes/compact-toolbar.tsx +114 -12
  162. package/ux-demo/recipes/composer-form.tsx +106 -18
  163. package/ux-demo/recipes/list-detail-dialog.tsx +144 -8
  164. package/ux-demo/recipes/list-workspace-states.tsx +165 -8
  165. package/ux-demo/recipes/loaders.ts +5 -2
  166. package/ux-demo/recipes/loading-inspector.tsx +124 -41
  167. package/ux-demo/recipes/mount-recipe.ts +27 -6
  168. package/ux-demo/recipes/navigation-sidebar.tsx +120 -9
  169. package/ux-demo/recipes/navigation-stack.tsx +61 -19
  170. package/ux-demo/recipes/types.ts +3 -1
  171. package/ux-demo/recipes/workspace-header.tsx +57 -2
  172. package/dist/chunk-2Z5TWHKQ.js +0 -34
  173. package/dist/chunk-2Z5TWHKQ.js.map +0 -1
  174. package/dist/chunk-37T3VNIZ.js +0 -19
  175. package/dist/chunk-37T3VNIZ.js.map +0 -1
  176. package/dist/chunk-55DATV3A.js +0 -10
  177. package/dist/chunk-55DATV3A.js.map +0 -1
  178. package/dist/chunk-5ATCDSQS.js +0 -42
  179. package/dist/chunk-5ATCDSQS.js.map +0 -1
  180. package/dist/chunk-5E2HVJ33.js +0 -10
  181. package/dist/chunk-5E2HVJ33.js.map +0 -1
  182. package/dist/chunk-7BR3DO2A.js.map +0 -1
  183. package/dist/chunk-7JB7VPRI.js +0 -34
  184. package/dist/chunk-7JB7VPRI.js.map +0 -1
  185. package/dist/chunk-7LDCTU7D.js +0 -134
  186. package/dist/chunk-7LDCTU7D.js.map +0 -1
  187. package/dist/chunk-AHQI52QO.js.map +0 -1
  188. package/dist/chunk-AQNKJRFH.js +0 -79
  189. package/dist/chunk-AQNKJRFH.js.map +0 -1
  190. package/dist/chunk-AVZHJN6C.js +0 -14
  191. package/dist/chunk-AVZHJN6C.js.map +0 -1
  192. package/dist/chunk-B24C4A6T.js +0 -22
  193. package/dist/chunk-B24C4A6T.js.map +0 -1
  194. package/dist/chunk-BLK3UEEI.js +0 -23
  195. package/dist/chunk-BLK3UEEI.js.map +0 -1
  196. package/dist/chunk-CH4SQ6H5.js +0 -39
  197. package/dist/chunk-CH4SQ6H5.js.map +0 -1
  198. package/dist/chunk-DAFFDO33.js +0 -14
  199. package/dist/chunk-DAFFDO33.js.map +0 -1
  200. package/dist/chunk-I7INRJO4.js +0 -17
  201. package/dist/chunk-I7INRJO4.js.map +0 -1
  202. package/dist/chunk-L4OKFDJY.js +0 -24
  203. package/dist/chunk-L4OKFDJY.js.map +0 -1
  204. package/dist/chunk-LCOBDOCC.js.map +0 -1
  205. package/dist/chunk-M5JRI6MN.js +0 -27
  206. package/dist/chunk-M5JRI6MN.js.map +0 -1
  207. package/dist/chunk-RNPGFQ33.js +0 -10
  208. package/dist/chunk-RNPGFQ33.js.map +0 -1
  209. package/dist/chunk-RUNWEPR4.js +0 -10
  210. package/dist/chunk-RUNWEPR4.js.map +0 -1
  211. package/dist/chunk-VPCTCUED.js +0 -10
  212. package/dist/chunk-VPCTCUED.js.map +0 -1
  213. package/dist/chunk-WVPYVDXY.js +0 -15
  214. package/dist/chunk-WVPYVDXY.js.map +0 -1
  215. package/dist/chunk-XE5ZLPCC.js +0 -20
  216. package/dist/chunk-XE5ZLPCC.js.map +0 -1
  217. package/dist/chunk-XXMOARTH.js.map +0 -1
  218. package/dist/chunk-YGN7OMLT.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { placeTokenSearchCaret, readTokenSearchField } from './chunk-7LDCTU7D.js';
1
+ import { placeTokenSearchCaret, readTokenSearchField } from './chunk-S3HKPI7I.js';
2
2
  import { delegate, signal } from 'kerfjs';
3
3
 
4
4
  function editorFromEvent(root, event) {
@@ -20,15 +20,45 @@ function caretQueryOffset(editor) {
20
20
  return readTokenSearchField(prefix).query.length;
21
21
  }
22
22
  function replacementEditor(root, id) {
23
- return [...root.querySelectorAll("[data-token-search-editor]")].find((editor) => editor.dataset.tokenSearchEditor === id);
23
+ return [
24
+ ...root.querySelectorAll("[data-token-search-editor]")
25
+ ].find((editor) => editor.dataset.tokenSearchEditor === id);
24
26
  }
25
27
  function editorIsEmpty(editor) {
26
28
  const value = readTokenSearchField(editor);
27
29
  return value.query.length === 0 && value.tokens.length === 0;
28
30
  }
31
+ function selectionCoversEditor(editor) {
32
+ const selected = editor.ownerDocument.getSelection().getRangeAt(0);
33
+ if (selected.collapsed || !editor.contains(selected.commonAncestorContainer))
34
+ return false;
35
+ const contents = editor.ownerDocument.createRange();
36
+ contents.selectNodeContents(editor);
37
+ const RangeCtor = editor.ownerDocument.defaultView.Range;
38
+ return selected.compareBoundaryPoints(RangeCtor.START_TO_START, contents) <= 0 && selected.compareBoundaryPoints(RangeCtor.END_TO_END, contents) >= 0;
39
+ }
29
40
  var TOKEN_SELECTOR = '[data-component="token-search-token"]';
30
41
  var TEXT_SELECTOR = "[data-token-search-text]";
31
42
  var stripZwsp = (text) => text.replaceAll("\u200B", "");
43
+ function normalizeEmptiedEditor(editor, selectedAll = false) {
44
+ const lineBreaks = editor.querySelectorAll("br");
45
+ if (lineBreaks.length === 0 && !selectedAll) return;
46
+ for (const lineBreak of lineBreaks) lineBreak.remove();
47
+ if (!selectedAll && editor.querySelector(TOKEN_SELECTOR)) return;
48
+ if (!selectedAll && stripZwsp(editor.textContent ?? "") !== "") return;
49
+ const doc = editor.ownerDocument;
50
+ const span = doc.createElement("span");
51
+ span.setAttribute("data-token-search-text", "");
52
+ span.setAttribute("data-empty", "true");
53
+ editor.replaceChildren(span);
54
+ if (doc.activeElement !== editor) return;
55
+ const selection = doc.getSelection();
56
+ const range = doc.createRange();
57
+ range.setStart(span, 0);
58
+ range.collapse(true);
59
+ selection.removeAllRanges();
60
+ selection.addRange(range);
61
+ }
32
62
  function collapsedCaret(editor) {
33
63
  const selection = editor.ownerDocument.getSelection();
34
64
  if (!selection || selection.rangeCount === 0) return;
@@ -38,16 +68,19 @@ function collapsedCaret(editor) {
38
68
  }
39
69
  function topBlock(editor, node) {
40
70
  let current = node;
41
- while (current.parentNode && current.parentNode !== editor) current = current.parentNode;
71
+ while (current.parentNode && current.parentNode !== editor)
72
+ current = current.parentNode;
42
73
  return current.parentNode === editor ? current : null;
43
74
  }
44
75
  function isBlankText(node) {
45
- if (node.nodeType === Node.TEXT_NODE) return stripZwsp(node.data) === "";
76
+ if (node.nodeType === Node.TEXT_NODE)
77
+ return stripZwsp(node.data) === "";
46
78
  return node instanceof Element && node.matches(TEXT_SELECTOR) && stripZwsp(node.textContent ?? "") === "";
47
79
  }
48
80
  function meaningfulSibling(block, direction) {
49
81
  let sibling = direction === "backward" ? block.previousSibling : block.nextSibling;
50
- while (sibling && isBlankText(sibling)) sibling = direction === "backward" ? sibling.previousSibling : sibling.nextSibling;
82
+ while (sibling && isBlankText(sibling))
83
+ sibling = direction === "backward" ? sibling.previousSibling : sibling.nextSibling;
51
84
  return sibling;
52
85
  }
53
86
  function caretSideNode(editor, range, direction) {
@@ -84,7 +117,12 @@ function placeCaretAfterToken(editor, chip) {
84
117
  selection.addRange(range);
85
118
  editor.focus({ preventScroll: true });
86
119
  }
87
- function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, keyboard = false } = {}) {
120
+ function wireTokenSearchFields(root, {
121
+ onSubmit,
122
+ onEdit,
123
+ collapsible = true,
124
+ keyboard = false
125
+ } = {}) {
88
126
  const managed = collapsible !== false;
89
127
  const config = typeof collapsible === "object" ? collapsible : {};
90
128
  const expandOnActivate = managed && (config.expandOnActivate ?? true);
@@ -125,7 +163,8 @@ function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, key
125
163
  `[data-component="token-search-field"][data-token-search-id="${CSS.escape(id)}"] ${selector}`
126
164
  );
127
165
  if (!target) return;
128
- if (target.matches("[data-token-search-editor]")) placeTokenSearchCaret(target);
166
+ if (target.matches("[data-token-search-editor]"))
167
+ placeTokenSearchCaret(target);
129
168
  else target.focus();
130
169
  });
131
170
  };
@@ -141,33 +180,47 @@ function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, key
141
180
  const onBeforeInput = (event) => {
142
181
  const inputEvent = event;
143
182
  const editor = editorFromEvent(root, event);
144
- if (!editor || !inputEvent.inputType.startsWith("delete") || editor.ownerDocument.activeElement !== editor) return;
145
- const field = editor.closest('[data-component="token-search-field"]');
183
+ if (!editor || !inputEvent.inputType.startsWith("delete") || editor.ownerDocument.activeElement !== editor)
184
+ return;
185
+ const field = editor.closest(
186
+ '[data-component="token-search-field"]'
187
+ );
146
188
  const id = field?.dataset.tokenSearchId;
147
189
  const offset = caretQueryOffset(editor);
148
- if (!id || offset === void 0 || field?.dataset.disabled === "true") return;
190
+ if (!id || offset === void 0 || field?.dataset.disabled === "true")
191
+ return;
149
192
  pending.set(editor, {
150
193
  id,
151
194
  offset,
152
- tokenCount: editor.querySelectorAll('[data-component="token-search-token"]').length
195
+ tokenCount: editor.querySelectorAll(
196
+ '[data-component="token-search-token"]'
197
+ ).length,
198
+ selectedAll: selectionCoversEditor(editor)
153
199
  });
154
200
  };
155
201
  const onInput = (event) => {
156
202
  const editor = editorFromEvent(root, event);
157
203
  if (!editor) return;
204
+ const deletion = pending.get(editor);
205
+ if (event.inputType?.startsWith("delete"))
206
+ normalizeEmptiedEditor(editor, deletion?.selectedAll);
158
207
  if (onEdit) {
159
- const field = editor.closest('[data-component="token-search-field"]');
208
+ const field = editor.closest(
209
+ '[data-component="token-search-field"]'
210
+ );
160
211
  const id = field?.dataset.tokenSearchId;
161
- if (id && field?.dataset.disabled !== "true") onEdit({ id, editor, event });
212
+ if (id && field?.dataset.disabled !== "true")
213
+ onEdit({ id, editor, event });
162
214
  }
163
- const deletion = pending.get(editor);
164
215
  pending.delete(editor);
165
- if (!deletion || editor.querySelectorAll('[data-component="token-search-token"]').length >= deletion.tokenCount) return;
216
+ if (!deletion || editor.querySelectorAll('[data-component="token-search-token"]').length >= deletion.tokenCount)
217
+ return;
166
218
  editor.ownerDocument.defaultView.requestAnimationFrame(() => {
167
219
  const replacement = replacementEditor(root, deletion.id);
168
220
  if (!replacement) return;
169
221
  const active = replacement.ownerDocument.activeElement;
170
- if (active !== editor && active !== replacement && active !== replacement.ownerDocument.body) return;
222
+ if (active !== editor && active !== replacement && active !== replacement.ownerDocument.body)
223
+ return;
171
224
  placeTokenSearchCaret(replacement, deletion.offset);
172
225
  });
173
226
  };
@@ -178,71 +231,104 @@ function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, key
178
231
  () => root.removeEventListener("input", onInput, true)
179
232
  ];
180
233
  disposers.push(
181
- delegate(root, "keydown", "[data-token-search-editor]", (event, element) => {
182
- const keyboardEvent = event;
183
- const editor = element;
184
- if (keyboardEvent.isComposing) return;
185
- const field = editor.closest('[data-component="token-search-field"]');
186
- const id = field?.dataset.tokenSearchId;
187
- if (!id || field?.dataset.disabled === "true") return;
188
- if (keyboardEvent.key === "Enter") {
189
- keyboardEvent.preventDefault();
190
- onSubmit?.({ id, editor });
191
- return;
192
- }
193
- if (removeAdjacentToken && (keyboardEvent.key === "Backspace" || keyboardEvent.key === "Delete")) {
194
- const direction = keyboardEvent.key === "Backspace" ? "backward" : "forward";
195
- const chip = adjacentToken(editor, direction);
196
- if (chip) {
234
+ delegate(
235
+ root,
236
+ "keydown",
237
+ "[data-token-search-editor]",
238
+ (event, element) => {
239
+ const keyboardEvent = event;
240
+ const editor = element;
241
+ if (keyboardEvent.isComposing) return;
242
+ const field = editor.closest(
243
+ '[data-component="token-search-field"]'
244
+ );
245
+ const id = field?.dataset.tokenSearchId;
246
+ if (!id || field?.dataset.disabled === "true") return;
247
+ if (keyboardEvent.key === "Enter") {
197
248
  keyboardEvent.preventDefault();
198
- onRemoveToken?.({ id, value: chip.dataset.tokenValue ?? "", editor, direction });
249
+ onSubmit?.({ id, editor });
199
250
  return;
200
251
  }
201
- }
202
- if (moveCaretPastToken && keyboardEvent.key === "ArrowRight") {
203
- const chip = adjacentToken(editor, "forward");
204
- if (chip) {
252
+ if (removeAdjacentToken && (keyboardEvent.key === "Backspace" || keyboardEvent.key === "Delete")) {
253
+ const direction = keyboardEvent.key === "Backspace" ? "backward" : "forward";
254
+ const chip = adjacentToken(editor, direction);
255
+ if (chip) {
256
+ keyboardEvent.preventDefault();
257
+ onRemoveToken?.({
258
+ id,
259
+ value: chip.dataset.tokenValue ?? "",
260
+ editor,
261
+ direction
262
+ });
263
+ return;
264
+ }
265
+ }
266
+ if (moveCaretPastToken && keyboardEvent.key === "ArrowRight") {
267
+ const chip = adjacentToken(editor, "forward");
268
+ if (chip) {
269
+ keyboardEvent.preventDefault();
270
+ placeCaretAfterToken(editor, chip);
271
+ return;
272
+ }
273
+ }
274
+ if (collapseOnEscape && keyboardEvent.key === "Escape" && field?.dataset.collapsible === "true" && editorIsEmpty(editor)) {
205
275
  keyboardEvent.preventDefault();
206
- placeCaretAfterToken(editor, chip);
207
- return;
276
+ closeField(id);
208
277
  }
209
278
  }
210
- if (collapseOnEscape && keyboardEvent.key === "Escape" && field?.dataset.collapsible === "true" && editorIsEmpty(editor)) {
211
- keyboardEvent.preventDefault();
212
- closeField(id);
213
- }
214
- })
279
+ )
215
280
  );
216
281
  if (expandOnActivate) {
217
282
  disposers.push(
218
- delegate(root, "click", ".kui-token-search__expand", (_event, element) => {
219
- const field = element.closest('[data-component="token-search-field"]');
220
- const id = field?.dataset.tokenSearchId;
221
- if (id && field?.dataset.disabled !== "true") openField(id);
222
- })
283
+ delegate(
284
+ root,
285
+ "click",
286
+ ".kui-token-search__expand",
287
+ (_event, element) => {
288
+ const field = element.closest(
289
+ '[data-component="token-search-field"]'
290
+ );
291
+ const id = field?.dataset.tokenSearchId;
292
+ if (id && field?.dataset.disabled !== "true") openField(id);
293
+ }
294
+ )
223
295
  );
224
296
  }
225
297
  if (collapseOnEmptyBlur) {
226
298
  disposers.push(
227
- delegate(root, "mousedown", '[data-component="token-search-field"][data-collapsible="true"] button', (event) => {
228
- event.preventDefault();
229
- })
299
+ delegate(
300
+ root,
301
+ "mousedown",
302
+ '[data-component="token-search-field"][data-collapsible="true"] button',
303
+ (event) => {
304
+ event.preventDefault();
305
+ }
306
+ )
230
307
  );
231
308
  disposers.push(
232
- delegate(root, "focusout", "[data-token-search-editor]", (event, element) => {
233
- const editor = element;
234
- const field = editor.closest('[data-component="token-search-field"]');
235
- const id = field?.dataset.tokenSearchId;
236
- if (!id || field?.dataset.collapsible !== "true" || field.dataset.disabled === "true") return;
237
- const next = event.relatedTarget;
238
- if (next instanceof Node && field.contains(next)) return;
239
- if (isExemptTarget(next instanceof Element ? next : null)) return;
240
- if (!editorIsEmpty(editor)) return;
241
- view().queueMicrotask(() => {
242
- const active = field.ownerDocument.activeElement;
243
- if (!field.contains(active) && !isExemptTarget(active)) setExpanded(id, false);
244
- });
245
- })
309
+ delegate(
310
+ root,
311
+ "focusout",
312
+ "[data-token-search-editor]",
313
+ (event, element) => {
314
+ const editor = element;
315
+ const field = editor.closest(
316
+ '[data-component="token-search-field"]'
317
+ );
318
+ const id = field?.dataset.tokenSearchId;
319
+ if (!id || field?.dataset.collapsible !== "true" || field.dataset.disabled === "true")
320
+ return;
321
+ const next = event.relatedTarget;
322
+ if (next instanceof Node && field.contains(next)) return;
323
+ if (isExemptTarget(next instanceof Element ? next : null)) return;
324
+ if (!editorIsEmpty(editor)) return;
325
+ view().queueMicrotask(() => {
326
+ const active = field.ownerDocument.activeElement;
327
+ if (!field.contains(active) && !isExemptTarget(active))
328
+ setExpanded(id, false);
329
+ });
330
+ }
331
+ )
246
332
  );
247
333
  }
248
334
  const dispose = () => {
@@ -262,4 +348,4 @@ function wireTokenSearchFields(root, { onSubmit, onEdit, collapsible = true, key
262
348
 
263
349
  export { wireTokenSearchFields };
264
350
 
265
- //# sourceMappingURL=chunk-LCOBDOCC.js.map
351
+ //# sourceMappingURL=chunk-ENVTNLYC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wire-token-search-fields.ts"],"names":["block"],"mappings":"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","file":"chunk-ENVTNLYC.js","sourcesContent":["import { delegate, type Signal, signal } from 'kerfjs';\n\nimport {\n placeTokenSearchCaret,\n readTokenSearchField,\n} from './token-search-field.js';\n\nexport interface TokenSearchSubmit {\n id: string;\n editor: HTMLElement;\n}\n\n/** Reported to {@link WireTokenSearchFieldsOptions.onEdit} on every editor input. */\nexport interface TokenSearchEdit extends TokenSearchSubmit {\n /**\n * The `InputEvent` that mutated the editor — read `event.inputType` / `event.data`\n * to gate commit behavior (e.g. only parse a chip on whitespace-terminated input)\n * without keeping a separate `input` listener.\n */\n event: InputEvent;\n}\n\n/** Reported when adjacent-token keyboard deletion asks the app to drop a chip. */\nexport interface TokenSearchTokenRemoval {\n id: string;\n /** The `data-token-value` of the token the app should remove from its state. */\n value: string;\n editor: HTMLElement;\n /** `'backward'` = the token before the caret (Backspace); `'forward'` = after (Delete). */\n direction: 'backward' | 'forward';\n}\n\n/**\n * Opt-in keyboard behavior for the atomic token chips. Off unless `keyboard` is\n * set; each piece defaults on once opted in. The helper never mutates app state:\n * a removal is reported through {@link TokenSearchKeyboardOptions.onRemoveToken}\n * for the caller to apply, while caret movement past a chip is a pure ephemeral\n * mechanic the helper performs itself.\n */\nexport interface TokenSearchKeyboardOptions {\n /**\n * From a collapsed caret with no selection, Backspace removes the token\n * immediately before it and Delete the token immediately after — reported via\n * `onRemoveToken` — instead of deleting a character. Default: true.\n */\n removeAdjacentToken?: boolean;\n /**\n * ArrowRight moves the caret past a trailing atomic token so text typed next\n * lands after the chip. Default: true.\n */\n moveCaretPastToken?: boolean;\n /** Apply the reported removal to your controlled state, then re-render. */\n onRemoveToken?: (removal: TokenSearchTokenRemoval) => void;\n}\n\n/**\n * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on\n * by default; disable a specific one to own it in the app. Provide `signals` to\n * drive app-owned `expanded` signals per field id instead of helper-created ones.\n */\nexport interface TokenSearchCollapsibleOptions {\n /** Expand the field and focus its editor when the iconic trigger is activated. Default: true. */\n expandOnActivate?: boolean;\n /** Collapse the field when focus leaves it while it is empty. Default: true. */\n collapseOnEmptyBlur?: boolean;\n /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */\n collapseOnEscape?: boolean;\n /** Focus the editor on expand and the trigger on Escape-collapse. Default: true. */\n manageFocus?: boolean;\n /**\n * Keep an empty field expanded when focus moves to a caller-owned surface\n * rendered outside the field — a suggestions dropdown, date picker, or help\n * popover shown beside it. Return true for any focus target that must NOT\n * trigger collapse-on-empty-blur. An element carrying `data-token-search-keep-open`\n * (or any node inside one) is always exempt, so this predicate is only needed\n * for surfaces you cannot mark declaratively.\n */\n keepOpenOn?: (target: Node | null) => boolean;\n /** App-owned `expanded` signals keyed by field id; adopted instead of helper-created. */\n signals?: Readonly<Record<string, Signal<boolean>>>;\n}\n\nexport interface WireTokenSearchFieldsOptions {\n onSubmit?: (submission: TokenSearchSubmit) => void;\n /** Fired on every editor `input`, after the browser mutates it, so a caller can drop its own `input` listener. */\n onEdit?: (edit: TokenSearchEdit) => void;\n /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */\n collapsible?: boolean | TokenSearchCollapsibleOptions;\n /** Opt-in atomic-chip keyboard behavior (off by default). `true` = on with defaults. */\n keyboard?: boolean | TokenSearchKeyboardOptions;\n}\n\n/**\n * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to\n * tear down. When collapsible behavior is managed, it also exposes the transient\n * `expanded` state per field id so the app can read it in render, hand in its own\n * signal, or drive it imperatively.\n */\nexport interface TokenSearchFieldsHandle {\n (): void;\n dispose(): void;\n /** The managed `expanded` signal for a field id (adopted or helper-created); undefined when unmanaged. */\n expanded(id: string): Signal<boolean> | undefined;\n /** Expand the field (and, when focus is managed, focus its editor). */\n open(id: string): void;\n /** Collapse the field (and, when focus is managed, restore focus to its trigger). */\n close(id: string): void;\n}\n\ninterface PendingTokenDeletion {\n id: string;\n offset: number;\n tokenCount: number;\n selectedAll: boolean;\n}\n\nfunction editorFromEvent(\n root: HTMLElement,\n event: Event,\n): HTMLElement | undefined {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const editor = target.closest<HTMLElement>('[data-token-search-editor]');\n return editor && root.contains(editor) ? editor : undefined;\n}\n\nfunction caretQueryOffset(editor: HTMLElement): number | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const selectionRange = selection.getRangeAt(0);\n if (!editor.contains(selectionRange.startContainer)) return;\n const prefixRange = editor.ownerDocument.createRange();\n prefixRange.selectNodeContents(editor);\n prefixRange.setEnd(selectionRange.startContainer, selectionRange.startOffset);\n const prefix = editor.ownerDocument.createElement('div');\n prefix.append(prefixRange.cloneContents());\n return readTokenSearchField(prefix).query.length;\n}\n\nfunction replacementEditor(\n root: HTMLElement,\n id: string,\n): HTMLElement | undefined {\n return [\n ...root.querySelectorAll<HTMLElement>('[data-token-search-editor]'),\n ].find((editor) => editor.dataset.tokenSearchEditor === id);\n}\n\nfunction editorIsEmpty(editor: HTMLElement): boolean {\n const value = readTokenSearchField(editor);\n return value.query.length === 0 && value.tokens.length === 0;\n}\n\n/** Whether the active selection covers every child of the editing host. */\nfunction selectionCoversEditor(editor: HTMLElement): boolean {\n // onBeforeInput calls this only after caretQueryOffset verified a live range.\n const selected = editor.ownerDocument.getSelection()!.getRangeAt(0);\n if (selected.collapsed || !editor.contains(selected.commonAncestorContainer))\n return false;\n const contents = editor.ownerDocument.createRange();\n contents.selectNodeContents(editor);\n const RangeCtor = editor.ownerDocument.defaultView!.Range;\n return (\n selected.compareBoundaryPoints(RangeCtor.START_TO_START, contents) <= 0 &&\n selected.compareBoundaryPoints(RangeCtor.END_TO_END, contents) >= 0\n );\n}\n\nconst TOKEN_SELECTOR = '[data-component=\"token-search-token\"]';\nconst TEXT_SELECTOR = '[data-token-search-text]';\nconst stripZwsp = (text: string): string => text.replaceAll('​', '');\n\n/**\n * A `contenteditable` host emptied by select-all + Delete (or Backspace) leaves a\n * bogus `<br>` in every engine: it renders as a stray newline and `readTokenSearchField`\n * reads it back as a space. Strip those line breaks after a delete, and when the field\n * is otherwise empty (no chips, no visible text) restore the canonical empty text span\n * and place the caret in it so typing resumes cleanly. Called only after the browser has\n * applied the delete, so an ordinary partial delete is never touched.\n */\nfunction normalizeEmptiedEditor(\n editor: HTMLElement,\n selectedAll = false,\n): void {\n const lineBreaks = editor.querySelectorAll('br');\n if (lineBreaks.length === 0 && !selectedAll) return;\n for (const lineBreak of lineBreaks) lineBreak.remove();\n if (!selectedAll && editor.querySelector(TOKEN_SELECTOR)) return;\n if (!selectedAll && stripZwsp(editor.textContent ?? '') !== '') return;\n const doc = editor.ownerDocument;\n const span = doc.createElement('span');\n span.setAttribute('data-token-search-text', '');\n span.setAttribute('data-empty', 'true');\n editor.replaceChildren(span);\n if (doc.activeElement !== editor) return;\n // The editor is the focused, connected editing host, so a selection exists.\n const selection = doc.getSelection()!;\n const range = doc.createRange();\n range.setStart(span, 0);\n range.collapse(true);\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\n/** The collapsed, in-editor caret range, or undefined when there is a selection or none. */\nfunction collapsedCaret(editor: HTMLElement): Range | undefined {\n const selection = editor.ownerDocument.getSelection();\n if (!selection || selection.rangeCount === 0) return;\n const range = selection.getRangeAt(0);\n if (!range.collapsed || !editor.contains(range.startContainer)) return;\n return range;\n}\n\n/** The direct child of `editor` that contains `node` (or `node` itself), else null. */\nfunction topBlock(editor: HTMLElement, node: Node): Node | null {\n let current: Node = node;\n while (current.parentNode && current.parentNode !== editor)\n current = current.parentNode;\n return current.parentNode === editor ? current : null;\n}\n\n/** A text node or text span holding no visible character (only zero-width spacers). */\nfunction isBlankText(node: Node): boolean {\n if (node.nodeType === Node.TEXT_NODE)\n return stripZwsp((node as CharacterData).data) === '';\n return (\n node instanceof Element &&\n node.matches(TEXT_SELECTOR) &&\n stripZwsp(node.textContent ?? '') === ''\n );\n}\n\n/** The nearest sibling block on `direction`, skipping blank text spacers. */\nfunction meaningfulSibling(\n block: Node,\n direction: 'backward' | 'forward',\n): Node | null {\n let sibling =\n direction === 'backward' ? block.previousSibling : block.nextSibling;\n while (sibling && isBlankText(sibling))\n sibling =\n direction === 'backward' ? sibling.previousSibling : sibling.nextSibling;\n return sibling;\n}\n\n/** The node immediately on `direction` of the caret, only when no visible character separates them. */\nfunction caretSideNode(\n editor: HTMLElement,\n range: Range,\n direction: 'backward' | 'forward',\n): Node | null {\n const { startContainer: container, startOffset: offset } = range;\n if (container.nodeType === Node.TEXT_NODE) {\n const text = (container as CharacterData).data;\n const side =\n direction === 'backward' ? text.slice(0, offset) : text.slice(offset);\n if (stripZwsp(side).length > 0) return null;\n // An in-editor text node always has a top block (itself or its wrapping span).\n return meaningfulSibling(topBlock(editor, container)!, direction);\n }\n const child = container.childNodes[\n direction === 'backward' ? offset - 1 : offset\n ] as Node | undefined;\n if (child) {\n // `child` is a descendant of the in-editor caret container, so it has a top block.\n const block = topBlock(editor, child)!;\n return isBlankText(child) ? meaningfulSibling(block, direction) : block;\n }\n const block = topBlock(editor, container);\n return block ? meaningfulSibling(block, direction) : null;\n}\n\n/** The atomic token chip adjacent to a collapsed caret on `direction`, if any. */\nfunction adjacentToken(\n editor: HTMLElement,\n direction: 'backward' | 'forward',\n): HTMLElement | undefined {\n const range = collapsedCaret(editor);\n if (!range) return;\n const node = caretSideNode(editor, range, direction);\n if (!node) return;\n const base = node instanceof Element ? node : node.parentElement;\n const chip = base?.closest<HTMLElement>(TOKEN_SELECTOR) ?? null;\n return chip && editor.contains(chip) ? chip : undefined;\n}\n\n/** Move the caret to just after an atomic token chip so typed text lands after it.\n * Only reached after {@link collapsedCaret} already found a live selection, and the\n * chip is a direct block of the editor. */\nfunction placeCaretAfterToken(editor: HTMLElement, chip: HTMLElement): void {\n const selection = editor.ownerDocument.getSelection()!;\n const range = editor.ownerDocument.createRange();\n range.setStartAfter(topBlock(editor, chip)!);\n range.collapse(true);\n selection.removeAllRanges();\n selection.addRange(range);\n editor.focus({ preventScroll: true });\n}\n\n/**\n * Wire every TokenSearchField under `root`: submit on Enter, preserve the caret across\n * controlled token deletion, and (by default) manage the collapsible field's transient\n * expand/collapse/focus. Returns a {@link TokenSearchFieldsHandle} — a disposer that also\n * exposes the managed `expanded` state per field id.\n */\nexport function wireTokenSearchFields(\n root: HTMLElement,\n {\n onSubmit,\n onEdit,\n collapsible = true,\n keyboard = false,\n }: WireTokenSearchFieldsOptions = {},\n): TokenSearchFieldsHandle {\n const managed = collapsible !== false;\n const config = typeof collapsible === 'object' ? collapsible : {};\n const expandOnActivate = managed && (config.expandOnActivate ?? true);\n const collapseOnEmptyBlur = managed && (config.collapseOnEmptyBlur ?? true);\n const collapseOnEscape = managed && (config.collapseOnEscape ?? true);\n const manageFocus = managed && (config.manageFocus ?? true);\n const keepOpenOn = config.keepOpenOn;\n const keyboardOn = keyboard !== false;\n const keyboardConfig = typeof keyboard === 'object' ? keyboard : {};\n const removeAdjacentToken =\n keyboardOn && (keyboardConfig.removeAdjacentToken ?? true);\n const moveCaretPastToken =\n keyboardOn && (keyboardConfig.moveCaretPastToken ?? true);\n const onRemoveToken = keyboardConfig.onRemoveToken;\n const adopted = config.signals ?? {};\n const created = new Map<string, Signal<boolean>>();\n\n /** True when focus moving to `element` should keep an empty field expanded. */\n const isExemptTarget = (element: Element | null): boolean => {\n if (!element) return false;\n if (element.closest('[data-token-search-keep-open]')) return true;\n return keepOpenOn ? keepOpenOn(element) : false;\n };\n\n const signalFor = (id: string): Signal<boolean> => {\n const own = adopted[id];\n if (own) return own;\n let field = created.get(id);\n if (!field) {\n field = signal(false);\n created.set(id, field);\n }\n return field;\n };\n const setExpanded = (id: string, next: boolean) => {\n const state = signalFor(id);\n if (state.value !== next) state.value = next;\n };\n const view = () => root.ownerDocument.defaultView!;\n const focusAfterRender = (id: string, selector: string) => {\n view().requestAnimationFrame(() => {\n const target = root.querySelector<HTMLElement>(\n `[data-component=\"token-search-field\"][data-token-search-id=\"${CSS.escape(id)}\"] ${selector}`,\n );\n if (!target) return;\n if (target.matches('[data-token-search-editor]'))\n placeTokenSearchCaret(target);\n else target.focus();\n });\n };\n const openField = (id: string) => {\n setExpanded(id, true);\n if (manageFocus) focusAfterRender(id, '[data-token-search-editor]');\n };\n const closeField = (id: string) => {\n setExpanded(id, false);\n if (manageFocus) focusAfterRender(id, '.kui-token-search__expand');\n };\n\n const pending = new WeakMap<HTMLElement, PendingTokenDeletion>();\n const onBeforeInput = (event: Event) => {\n const inputEvent = event as InputEvent;\n const editor = editorFromEvent(root, event);\n if (\n !editor ||\n !inputEvent.inputType.startsWith('delete') ||\n editor.ownerDocument.activeElement !== editor\n )\n return;\n const field = editor.closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n );\n const id = field?.dataset.tokenSearchId;\n const offset = caretQueryOffset(editor);\n if (!id || offset === undefined || field?.dataset.disabled === 'true')\n return;\n pending.set(editor, {\n id,\n offset,\n tokenCount: editor.querySelectorAll(\n '[data-component=\"token-search-token\"]',\n ).length,\n selectedAll: selectionCoversEditor(editor),\n });\n };\n const onInput = (event: Event) => {\n const editor = editorFromEvent(root, event);\n if (!editor) return;\n const deletion = pending.get(editor);\n if ((event as InputEvent).inputType?.startsWith('delete'))\n normalizeEmptiedEditor(editor, deletion?.selectedAll);\n if (onEdit) {\n const field = editor.closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n );\n const id = field?.dataset.tokenSearchId;\n if (id && field?.dataset.disabled !== 'true')\n onEdit({ id, editor, event: event as InputEvent });\n }\n pending.delete(editor);\n if (\n !deletion ||\n editor.querySelectorAll('[data-component=\"token-search-token\"]').length >=\n deletion.tokenCount\n )\n return;\n editor.ownerDocument.defaultView!.requestAnimationFrame(() => {\n const replacement = replacementEditor(root, deletion.id);\n if (!replacement) return;\n const active = replacement.ownerDocument.activeElement;\n if (\n active !== editor &&\n active !== replacement &&\n active !== replacement.ownerDocument.body\n )\n return;\n placeTokenSearchCaret(replacement, deletion.offset);\n });\n };\n root.addEventListener('beforeinput', onBeforeInput, true);\n root.addEventListener('input', onInput, true);\n\n const disposers: Array<() => void> = [\n () => root.removeEventListener('beforeinput', onBeforeInput, true),\n () => root.removeEventListener('input', onInput, true),\n ];\n\n disposers.push(\n delegate(\n root,\n 'keydown',\n '[data-token-search-editor]',\n (event, element) => {\n const keyboardEvent = event as KeyboardEvent;\n const editor = element as HTMLElement;\n if (keyboardEvent.isComposing) return;\n const field = editor.closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n );\n const id = field?.dataset.tokenSearchId;\n if (!id || field?.dataset.disabled === 'true') return;\n if (keyboardEvent.key === 'Enter') {\n keyboardEvent.preventDefault();\n onSubmit?.({ id, editor });\n return;\n }\n if (\n removeAdjacentToken &&\n (keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete')\n ) {\n const direction =\n keyboardEvent.key === 'Backspace' ? 'backward' : 'forward';\n const chip = adjacentToken(editor, direction);\n if (chip) {\n keyboardEvent.preventDefault();\n onRemoveToken?.({\n id,\n value: chip.dataset.tokenValue ?? '',\n editor,\n direction,\n });\n return;\n }\n }\n if (moveCaretPastToken && keyboardEvent.key === 'ArrowRight') {\n const chip = adjacentToken(editor, 'forward');\n if (chip) {\n keyboardEvent.preventDefault();\n placeCaretAfterToken(editor, chip);\n return;\n }\n }\n if (\n collapseOnEscape &&\n keyboardEvent.key === 'Escape' &&\n field?.dataset.collapsible === 'true' &&\n editorIsEmpty(editor)\n ) {\n keyboardEvent.preventDefault();\n closeField(id);\n }\n },\n ),\n );\n\n if (expandOnActivate) {\n disposers.push(\n delegate(\n root,\n 'click',\n '.kui-token-search__expand',\n (_event, element) => {\n // The selector guarantees a collapsible field ancestor; only skip a disabled one.\n const field = (element as HTMLElement).closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n );\n const id = field?.dataset.tokenSearchId;\n if (id && field?.dataset.disabled !== 'true') openField(id);\n },\n ),\n );\n }\n\n if (collapseOnEmptyBlur) {\n // Pressing an in-field control (clear, trailing, token, or the trigger) must not\n // blur the editor — otherwise the transient blur would collapse the field before\n // the control's own handler runs. Keeping focus also keeps the click firing.\n disposers.push(\n delegate(\n root,\n 'mousedown',\n '[data-component=\"token-search-field\"][data-collapsible=\"true\"] button',\n (event) => {\n event.preventDefault();\n },\n ),\n );\n disposers.push(\n delegate(\n root,\n 'focusout',\n '[data-token-search-editor]',\n (event, element) => {\n const editor = element as HTMLElement;\n const field = editor.closest<HTMLElement>(\n '[data-component=\"token-search-field\"]',\n );\n const id = field?.dataset.tokenSearchId;\n if (\n !id ||\n field?.dataset.collapsible !== 'true' ||\n field.dataset.disabled === 'true'\n )\n return;\n const next = (event as FocusEvent).relatedTarget;\n if (next instanceof Node && field.contains(next)) return;\n if (isExemptTarget(next instanceof Element ? next : null)) return;\n if (!editorIsEmpty(editor)) return;\n view().queueMicrotask(() => {\n const active = field.ownerDocument.activeElement;\n if (!field.contains(active) && !isExemptTarget(active))\n setExpanded(id, false);\n });\n },\n ),\n );\n }\n\n const dispose = () => {\n for (const stop of disposers.splice(0)) stop();\n };\n const handle = (() => dispose()) as TokenSearchFieldsHandle;\n handle.dispose = dispose;\n handle.expanded = (id) => (managed ? signalFor(id) : undefined);\n handle.open = (id) => {\n if (managed) openField(id);\n };\n handle.close = (id) => {\n if (managed) closeField(id);\n };\n return handle;\n}\n"]}
@@ -1,11 +1,15 @@
1
1
  import { jsx } from 'kerfjs/jsx-runtime';
2
2
 
3
3
  // src/list-inset-text.tsx
4
- function ListInsetText({ children, horizontalOnly = false, className = "" }) {
4
+ function ListInsetText({
5
+ children,
6
+ horizontalOnly = false,
7
+ className = ""
8
+ }) {
5
9
  const cls = `kui-list-inset-text${horizontalOnly ? " kui-list-inset-text--horizontal" : ""} ${className}`.trim();
6
10
  return /* @__PURE__ */ jsx("div", { class: cls, "data-component": "list-inset-text", children });
7
11
  }
8
12
 
9
13
  export { ListInsetText };
10
14
 
11
- //# sourceMappingURL=chunk-AHQI52QO.js.map
15
+ //# sourceMappingURL=chunk-FEY65TBV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/list-inset-text.tsx"],"names":[],"mappings":";;;AAuBO,SAAS,aAAA,CAAc;AAAA,EAC5B,QAAA;AAAA,EACA,cAAA,GAAiB,KAAA;AAAA,EACjB,SAAA,GAAY;AACd,CAAA,EAAuB;AACrB,EAAA,MAAM,GAAA,GACJ,sBAAsB,cAAA,GAAiB,kCAAA,GAAqC,EAAE,CAAA,CAAA,EAAI,SAAS,GAAG,IAAA,EAAK;AACrG,EAAA,2BACG,KAAA,EAAA,EAAI,KAAA,EAAO,GAAA,EAAK,gBAAA,EAAe,mBAC7B,QAAA,EACH,CAAA;AAEJ","file":"chunk-FEY65TBV.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nexport interface ListInsetTextProps {\n /** Text (or inline content) that carries no margin, border, or padding of its own. */\n children: SafeHtml | SafeHtml[] | string;\n /**\n * Keep only the horizontal geometry (inline margin, left/right border, and\n * left/right padding) and drop the vertical margin, border, and padding. Use it\n * when the text edge must still align with bordered items but the line should not\n * add its own vertical box space — tight text layout inside a content region.\n */\n horizontalOnly?: boolean;\n className?: string;\n}\n\n/**\n * Gives bare text the content-item geometry — an 8px inline margin, a 1px\n * transparent border, and 8px padding — so a plain string lines up with\n * bordered `.kui-content` items (its text edge lands at the same 17px inset).\n * Use it for text elements that have no margin, border, or padding of their own.\n * Pass `horizontalOnly` to keep the horizontal inset but drop the vertical box\n * space for tight text layout.\n */\nexport function ListInsetText({\n children,\n horizontalOnly = false,\n className = '',\n}: ListInsetTextProps) {\n const cls =\n `kui-list-inset-text${horizontalOnly ? ' kui-list-inset-text--horizontal' : ''} ${className}`.trim();\n return (\n <div class={cls} data-component=\"list-inset-text\">\n {children}\n </div>\n );\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { ToolbarText } from './chunk-S3PCZ7NV.js';
2
+ import { ToolbarControlGroup } from './chunk-ZK2G3VR5.js';
3
+ import { Toolbar } from './chunk-7L6EHUMV.js';
4
+ import { Skeleton } from './chunk-PZQVSDLH.js';
5
+ import { jsxs, jsx, Fragment } from 'kerfjs/jsx-runtime';
6
+
7
+ function PanelHeader({
8
+ title,
9
+ titleId,
10
+ summary,
11
+ summaryId,
12
+ icon,
13
+ iconClassName = "",
14
+ actions,
15
+ headingLevel,
16
+ placeholder = false
17
+ }) {
18
+ const identity = /* @__PURE__ */ jsxs(Fragment, { children: [
19
+ icon && /* @__PURE__ */ jsx(
20
+ ToolbarControlGroup,
21
+ {
22
+ single: true,
23
+ className: `kui-panel-header__icon ${iconClassName}`.trim(),
24
+ children: icon
25
+ }
26
+ ),
27
+ /* @__PURE__ */ jsx(
28
+ ToolbarText,
29
+ {
30
+ text: title,
31
+ size: "xlarge",
32
+ id: titleId,
33
+ className: "kui-panel-header__title",
34
+ headingLevel,
35
+ placeholder
36
+ }
37
+ )
38
+ ] });
39
+ return /* @__PURE__ */ jsxs(
40
+ "div",
41
+ {
42
+ class: "kui-panel-header",
43
+ "data-component": "panel-header",
44
+ "data-has-icon": String(Boolean(icon)),
45
+ "data-has-actions": String(Boolean(actions)),
46
+ "data-has-summary": String(Boolean(summary)),
47
+ "data-placeholder": placeholder ? "true" : void 0,
48
+ children: [
49
+ /* @__PURE__ */ jsx(Toolbar, { leading: identity, trailing: actions, divider: false }),
50
+ summary && /* @__PURE__ */ jsx("p", { class: "kui-panel-header__summary", id: summaryId, children: placeholder ? /* @__PURE__ */ jsx(Skeleton, { width: "18em" }) : summary })
51
+ ]
52
+ }
53
+ );
54
+ }
55
+
56
+ export { PanelHeader };
57
+
58
+ //# sourceMappingURL=chunk-JTHUXDN2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/panel-header.tsx"],"names":[],"mappings":";;;;;;AAsCO,SAAS,WAAA,CAAY;AAAA,EAC1B,KAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,IAAA;AAAA,EACA,aAAA,GAAgB,EAAA;AAAA,EAChB,OAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA,GAAc;AAChB,CAAA,EAAqB;AACnB,EAAA,MAAM,2BACJ,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,IAAA,IAAA,oBACC,GAAA;AAAA,MAAC,mBAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAM,IAAA;AAAA,QACN,SAAA,EAAW,CAAA,uBAAA,EAA0B,aAAa,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,QAEzD,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,oBAEF,GAAA;AAAA,MAAC,WAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,KAAA;AAAA,QACN,IAAA,EAAK,QAAA;AAAA,QACL,EAAA,EAAI,OAAA;AAAA,QACJ,SAAA,EAAU,yBAAA;AAAA,QACV,YAAA;AAAA,QACA;AAAA;AAAA;AACF,GAAA,EACF,CAAA;AAGF,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,kBAAA;AAAA,MACN,gBAAA,EAAe,cAAA;AAAA,MACf,eAAA,EAAe,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,MACnC,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,MACzC,kBAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,MACzC,kBAAA,EAAkB,cAAc,MAAA,GAAS,MAAA;AAAA,MAEzC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,WAAQ,OAAA,EAAS,QAAA,EAAU,QAAA,EAAU,OAAA,EAAS,SAAS,KAAA,EAAO,CAAA;AAAA,QAC9D,OAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAE,KAAA,EAAM,2BAAA,EAA4B,EAAA,EAAI,SAAA,EACtC,QAAA,EAAA,WAAA,mBAAc,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,MAAA,EAAO,IAAK,OAAA,EAC7C;AAAA;AAAA;AAAA,GAEJ;AAEJ","file":"chunk-JTHUXDN2.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { Skeleton } from './skeleton.js';\nimport { Toolbar } from './toolbar.js';\nimport { ToolbarControlGroup } from './toolbar-control-group.js';\nimport { type HeadingLevel, ToolbarText } from './toolbar-text.js';\n\nexport interface PanelHeaderProps {\n title: string;\n titleId: string;\n summary?: string;\n summaryId?: string;\n icon?: SafeHtml;\n iconClassName?: string;\n actions?: SafeHtml;\n /**\n * Expose the title as a heading landmark (`role=\"heading\"` + `aria-level`). Set it\n * for a PAGE or view heading so screen-reader heading navigation works and the view\n * has a primary heading; omit it (the default) for a dialog title, which is instead\n * referenced via `aria-labelledby={titleId}` and needs no heading landmark.\n */\n headingLevel?: HeadingLevel;\n /** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */\n placeholder?: boolean;\n}\n\n/**\n * The heading of a panel, dialog, or page: a plain `Toolbar` whose leading zone\n * holds an optional icon control group and the title as extra-large `ToolbarText`,\n * whose trailing zone holds the app's action controls, and with an optional\n * subtitle on its own row, left-aligned with the title.\n *\n * PanelHeader overrides no Toolbar styles — it is just a Toolbar with an xl title.\n * The only styling it adds is the icon group's fill/border color and the subtitle.\n * When no icon is provided, the icon group is omitted entirely. The `actions` slot\n * is passed straight into the toolbar's trailing zone; the app supplies whatever\n * trailing controls it needs (typically a `ToolbarControlGroup`).\n */\nexport function PanelHeader({\n title,\n titleId,\n summary,\n summaryId,\n icon,\n iconClassName = '',\n actions,\n headingLevel,\n placeholder = false,\n}: PanelHeaderProps) {\n const identity = (\n <>\n {icon && (\n <ToolbarControlGroup\n single\n className={`kui-panel-header__icon ${iconClassName}`.trim()}\n >\n {icon}\n </ToolbarControlGroup>\n )}\n <ToolbarText\n text={title}\n size=\"xlarge\"\n id={titleId}\n className=\"kui-panel-header__title\"\n headingLevel={headingLevel}\n placeholder={placeholder}\n />\n </>\n );\n\n return (\n <div\n class=\"kui-panel-header\"\n data-component=\"panel-header\"\n data-has-icon={String(Boolean(icon))}\n data-has-actions={String(Boolean(actions))}\n data-has-summary={String(Boolean(summary))}\n data-placeholder={placeholder ? 'true' : undefined}\n >\n <Toolbar leading={identity} trailing={actions} divider={false} />\n {summary && (\n <p class=\"kui-panel-header__summary\" id={summaryId}>\n {placeholder ? <Skeleton width=\"18em\" /> : summary}\n </p>\n )}\n </div>\n );\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { jsx } from 'kerfjs/jsx-runtime';
2
+
3
+ // src/floating-toolbar.tsx
4
+ function FloatingToolbar({
5
+ children,
6
+ label,
7
+ position = "bottom-end",
8
+ className = ""
9
+ }) {
10
+ return /* @__PURE__ */ jsx(
11
+ "div",
12
+ {
13
+ class: `kui-floating-toolbar ${className}`.trim(),
14
+ "data-component": "floating-toolbar",
15
+ "data-position": position,
16
+ role: "toolbar",
17
+ "aria-label": label,
18
+ children
19
+ }
20
+ );
21
+ }
22
+
23
+ export { FloatingToolbar };
24
+
25
+ //# sourceMappingURL=chunk-LPOMRC4C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/floating-toolbar.tsx"],"names":[],"mappings":";;;AA4BO,SAAS,eAAA,CAAgB;AAAA,EAC9B,QAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA,GAAW,YAAA;AAAA,EACX,SAAA,GAAY;AACd,CAAA,EAAyB;AACvB,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,qBAAA,EAAwB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MAChD,gBAAA,EAAe,kBAAA;AAAA,MACf,eAAA,EAAe,QAAA;AAAA,MACf,IAAA,EAAK,SAAA;AAAA,MACL,YAAA,EAAY,KAAA;AAAA,MAEX;AAAA;AAAA,GACH;AAEJ","file":"chunk-LPOMRC4C.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\n/** Where a {@link FloatingToolbar} floats within its positioned container. */\nexport type FloatingToolbarPosition =\n 'bottom' | 'bottom-start' | 'bottom-end' | 'top' | 'top-start' | 'top-end';\n\nexport interface FloatingToolbarProps {\n /** Toolbar contents — normally one or more `ToolbarControlGroup`s. */\n children: SafeHtml | SafeHtml[];\n /** Accessible name for the toolbar (required — it exposes `role=\"toolbar\"`). */\n label: string;\n /**\n * Corner or edge it floats to inside its nearest positioned ancestor.\n * Default: `'bottom-end'`.\n */\n position?: FloatingToolbarPosition;\n className?: string;\n}\n\n/**\n * A toolbar that floats above the main content of its nearest positioned\n * ancestor — a transparent, forced-dark cluster of controls (e.g. a drawer\n * restore button) that sits over the content but NOT over dialogs or overlays\n * (it is not in the top layer). It is inset from the container edges by\n * `--kui-floating-toolbar-inset` (default `--kui-space-m`, i.e. 8px more than a\n * top toolbar's own inset); override that token to move it. The app owns the\n * controls and their behavior — wire them with `delegate()` as usual.\n */\nexport function FloatingToolbar({\n children,\n label,\n position = 'bottom-end',\n className = '',\n}: FloatingToolbarProps) {\n return (\n <div\n class={`kui-floating-toolbar ${className}`.trim()}\n data-component=\"floating-toolbar\"\n data-position={position}\n role=\"toolbar\"\n aria-label={label}\n >\n {children}\n </div>\n );\n}\n"]}
@@ -0,0 +1,79 @@
1
+ import { jsxs, jsx, Fragment } from 'kerfjs/jsx-runtime';
2
+
3
+ // src/resizable-region.tsx
4
+ var clampRegionSize = (size, min, max) => Math.min(max, Math.max(min, Math.round(size)));
5
+ var resizeRegionFromPointer = (startSize, delta, edge) => startSize + delta * (edge === "start" ? -1 : 1);
6
+ function ResizeGrip({ axis }) {
7
+ return /* @__PURE__ */ jsx(
8
+ "svg",
9
+ {
10
+ "aria-hidden": "true",
11
+ viewBox: "0 0 24 24",
12
+ fill: "none",
13
+ stroke: "currentColor",
14
+ "stroke-width": "2",
15
+ "stroke-linecap": "round",
16
+ children: axis === "horizontal" ? /* @__PURE__ */ jsxs(Fragment, { children: [
17
+ /* @__PURE__ */ jsx("path", { d: "M9 6v12" }),
18
+ /* @__PURE__ */ jsx("path", { d: "M15 6v12" })
19
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
20
+ /* @__PURE__ */ jsx("path", { d: "M6 9h12" }),
21
+ /* @__PURE__ */ jsx("path", { d: "M6 15h12" })
22
+ ] })
23
+ }
24
+ );
25
+ }
26
+ function ResizableRegion({
27
+ id,
28
+ label,
29
+ size,
30
+ min,
31
+ max,
32
+ axis = "horizontal",
33
+ edge = "end",
34
+ collapsed = false,
35
+ transitioning = false,
36
+ handleIcon,
37
+ children
38
+ }) {
39
+ const expandedSize = clampRegionSize(size, min, max);
40
+ const resolved = collapsed ? 0 : expandedSize;
41
+ const orientation = axis === "horizontal" ? "vertical" : "horizontal";
42
+ return /* @__PURE__ */ jsxs(
43
+ "section",
44
+ {
45
+ class: "kui-resizable-region",
46
+ "data-component": "resizable-region",
47
+ "data-region-id": id,
48
+ "data-axis": axis,
49
+ "data-edge": edge,
50
+ "data-collapsed": String(collapsed),
51
+ "data-transitioning": String(transitioning),
52
+ style: `--kui-resizable-region-size:${resolved}px;--kui-resizable-region-expanded-size:${expandedSize}px`,
53
+ "aria-label": label,
54
+ children: [
55
+ /* @__PURE__ */ jsx("div", { class: "kui-resizable-region__content", children }),
56
+ /* @__PURE__ */ jsx(
57
+ "div",
58
+ {
59
+ class: "kui-resizable-region__handle",
60
+ role: "separator",
61
+ tabindex: "0",
62
+ "aria-label": `Resize ${label}`,
63
+ "aria-orientation": orientation,
64
+ "aria-valuemin": collapsed ? 0 : min,
65
+ "aria-valuemax": max,
66
+ "aria-valuenow": resolved,
67
+ "data-kui-resize-handle": true,
68
+ "data-region-id": id,
69
+ children: /* @__PURE__ */ jsx("span", { class: "kui-resizable-region__handle-icon", "aria-hidden": "true", children: handleIcon ?? /* @__PURE__ */ jsx(ResizeGrip, { axis }) })
70
+ }
71
+ )
72
+ ]
73
+ }
74
+ );
75
+ }
76
+
77
+ export { ResizableRegion, clampRegionSize, resizeRegionFromPointer };
78
+
79
+ //# sourceMappingURL=chunk-NENBUYX5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/resizable-region.tsx"],"names":[],"mappings":"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","file":"chunk-NENBUYX5.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nexport type ResizableRegionAxis = 'horizontal' | 'vertical';\nexport type ResizableRegionEdge = 'start' | 'end';\n\nexport interface ResizableRegionProps {\n id: string;\n label: string;\n size: number;\n min: number;\n max: number;\n axis?: ResizableRegionAxis;\n edge?: ResizableRegionEdge;\n collapsed?: boolean;\n transitioning?: boolean;\n /** Decorative dormant content for the separator handle. Must not contain interactive descendants. */\n handleIcon?: SafeHtml;\n children: SafeHtml | SafeHtml[];\n}\n\nexport const clampRegionSize = (size: number, min: number, max: number) =>\n Math.min(max, Math.max(min, Math.round(size)));\nexport const resizeRegionFromPointer = (\n startSize: number,\n delta: number,\n edge: ResizableRegionEdge,\n) => startSize + delta * (edge === 'start' ? -1 : 1);\n\nfunction ResizeGrip({ axis }: { axis: ResizableRegionAxis }) {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n >\n {axis === 'horizontal' ? (\n <>\n <path d=\"M9 6v12\"></path>\n <path d=\"M15 6v12\"></path>\n </>\n ) : (\n <>\n <path d=\"M6 9h12\"></path>\n <path d=\"M6 15h12\"></path>\n </>\n )}\n </svg>\n );\n}\n\nexport function ResizableRegion({\n id,\n label,\n size,\n min,\n max,\n axis = 'horizontal',\n edge = 'end',\n collapsed = false,\n transitioning = false,\n handleIcon,\n children,\n}: ResizableRegionProps) {\n const expandedSize = clampRegionSize(size, min, max);\n const resolved = collapsed ? 0 : expandedSize;\n const orientation = axis === 'horizontal' ? 'vertical' : 'horizontal';\n return (\n <section\n class=\"kui-resizable-region\"\n data-component=\"resizable-region\"\n data-region-id={id}\n data-axis={axis}\n data-edge={edge}\n data-collapsed={String(collapsed)}\n data-transitioning={String(transitioning)}\n style={`--kui-resizable-region-size:${resolved}px;--kui-resizable-region-expanded-size:${expandedSize}px`}\n aria-label={label}\n >\n <div class=\"kui-resizable-region__content\">{children}</div>\n <div\n class=\"kui-resizable-region__handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-label={`Resize ${label}`}\n aria-orientation={orientation}\n aria-valuemin={collapsed ? 0 : min}\n aria-valuemax={max}\n aria-valuenow={resolved}\n data-kui-resize-handle\n data-region-id={id}\n >\n <span class=\"kui-resizable-region__handle-icon\" aria-hidden=\"true\">\n {handleIcon ?? <ResizeGrip axis={axis} />}\n </span>\n </div>\n </section>\n );\n}\n"]}