@eifi1/ui-kit 0.22.0 → 0.23.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 (205) hide show
  1. package/README.md +42 -25
  2. package/dist/chart.d.ts +11 -8
  3. package/dist/components/amount-input.d.ts +28 -8
  4. package/dist/components/amount-input.js +3 -1
  5. package/dist/components/amount-input.js.map +1 -1
  6. package/dist/components/autocomplete.d.ts +21 -0
  7. package/dist/components/autocomplete.js +128 -103
  8. package/dist/components/autocomplete.js.map +1 -1
  9. package/dist/components/button-group.d.ts +11 -8
  10. package/dist/components/calculator.d.ts +11 -8
  11. package/dist/components/column-mapper.d.ts +182 -0
  12. package/dist/components/column-mapper.js +375 -0
  13. package/dist/components/column-mapper.js.map +1 -0
  14. package/dist/components/combobox-core.d.ts +39 -2
  15. package/dist/components/combobox-core.js +29 -6
  16. package/dist/components/combobox-core.js.map +1 -1
  17. package/dist/components/combobox.d.ts +80 -2
  18. package/dist/components/combobox.js +448 -311
  19. package/dist/components/combobox.js.map +1 -1
  20. package/dist/components/confirm-dialog.d.ts +11 -8
  21. package/dist/components/confirm-dialog.js +7 -8
  22. package/dist/components/confirm-dialog.js.map +1 -1
  23. package/dist/components/copy-button.d.ts +8 -5
  24. package/dist/components/country-select.d.ts +50 -7
  25. package/dist/components/country-select.js +65 -9
  26. package/dist/components/country-select.js.map +1 -1
  27. package/dist/components/danger-confirm.d.ts +11 -8
  28. package/dist/components/danger-confirm.js +13 -3
  29. package/dist/components/danger-confirm.js.map +1 -1
  30. package/dist/components/date-picker.d.ts +41 -0
  31. package/dist/components/date-picker.js +269 -8
  32. package/dist/components/date-picker.js.map +1 -1
  33. package/dist/components/entity-combobox.d.ts +35 -1
  34. package/dist/components/entity-combobox.js +148 -112
  35. package/dist/components/entity-combobox.js.map +1 -1
  36. package/dist/components/facing-pair.d.ts +11 -8
  37. package/dist/components/field-parts.d.ts +116 -6
  38. package/dist/components/field-parts.js +84 -0
  39. package/dist/components/field-parts.js.map +1 -1
  40. package/dist/components/field-strip.d.ts +105 -0
  41. package/dist/components/field-strip.js +46 -0
  42. package/dist/components/field-strip.js.map +1 -0
  43. package/dist/components/file-button.d.ts +11 -8
  44. package/dist/components/file-dropzone.d.ts +11 -8
  45. package/dist/components/form-actions.d.ts +10 -7
  46. package/dist/components/form-actions.js +8 -2
  47. package/dist/components/form-actions.js.map +1 -1
  48. package/dist/components/iban-input.d.ts +11 -8
  49. package/dist/components/icon-picker.d.ts +10 -1
  50. package/dist/components/icon-picker.js +12 -4
  51. package/dist/components/icon-picker.js.map +1 -1
  52. package/dist/components/language-select.d.ts +11 -8
  53. package/dist/components/money-field.d.ts +14 -8
  54. package/dist/components/money-field.js.map +1 -1
  55. package/dist/components/month-picker.js +2 -1
  56. package/dist/components/month-picker.js.map +1 -1
  57. package/dist/components/multi-entity-combobox.d.ts +26 -1
  58. package/dist/components/multi-entity-combobox.js +143 -102
  59. package/dist/components/multi-entity-combobox.js.map +1 -1
  60. package/dist/components/number-field.d.ts +11 -8
  61. package/dist/components/number-input.d.ts +11 -8
  62. package/dist/components/numpad-sheet.d.ts +11 -8
  63. package/dist/components/phone-input.d.ts +11 -8
  64. package/dist/components/reauth-dialog.d.ts +2 -2
  65. package/dist/components/reauth-dialog.js +6 -8
  66. package/dist/components/reauth-dialog.js.map +1 -1
  67. package/dist/components/series-chart.d.ts +11 -8
  68. package/dist/components/settings-fields.d.ts +11 -8
  69. package/dist/components/share-card.d.ts +10 -7
  70. package/dist/components/swatch-picker.d.ts +17 -1
  71. package/dist/components/swatch-picker.js +10 -4
  72. package/dist/components/swatch-picker.js.map +1 -1
  73. package/dist/components/text-link.d.ts +10 -7
  74. package/dist/components/tile-radio.d.ts +20 -0
  75. package/dist/components/tile-radio.js +25 -8
  76. package/dist/components/tile-radio.js.map +1 -1
  77. package/dist/components/time-input.d.ts +11 -8
  78. package/dist/components/toggle-group.js +2 -2
  79. package/dist/components/toggle-group.js.map +1 -1
  80. package/dist/components/trigger-aria.d.ts +3 -0
  81. package/dist/components/trigger-aria.js +3 -1
  82. package/dist/components/trigger-aria.js.map +1 -1
  83. package/dist/components/ui.d.ts +8 -5
  84. package/dist/components/ui.js +5 -11
  85. package/dist/components/ui.js.map +1 -1
  86. package/dist/feedback/feedback-attachment.d.ts +1 -1
  87. package/dist/feedback/feedback-attachment.js +156 -56
  88. package/dist/feedback/feedback-attachment.js.map +1 -1
  89. package/dist/feedback/feedback-dialog.d.ts +1 -1
  90. package/dist/feedback/feedback-dialog.js.map +1 -1
  91. package/dist/feedback/feedback-inbox.d.ts +3 -2
  92. package/dist/feedback/feedback-inbox.js.map +1 -1
  93. package/dist/feedback/feedback-thread.d.ts +68 -287
  94. package/dist/feedback/feedback-thread.js +8 -1
  95. package/dist/feedback/feedback-thread.js.map +1 -1
  96. package/dist/{kit-labels-R8mIAc9N.d.ts → feedback-BxeQVzwq.d.ts} +405 -22
  97. package/dist/{feedback-attachment-NhUm7Zga.d.ts → feedback-attachment-fGAzZPf0.d.ts} +112 -10
  98. package/dist/feedback.d.ts +66 -2
  99. package/dist/hooks/use-file-drop.d.ts +11 -8
  100. package/dist/i18n/defaults.d.ts +11 -8
  101. package/dist/i18n/defaults.js +3 -1
  102. package/dist/i18n/defaults.js.map +1 -1
  103. package/dist/i18n/german.d.ts +11 -8
  104. package/dist/i18n/german.js +36 -2
  105. package/dist/i18n/german.js.map +1 -1
  106. package/dist/i18n/kit-labels.d.ts +7 -4
  107. package/dist/i18n/kit-labels.js.map +1 -1
  108. package/dist/i18n/languages.d.ts +11 -8
  109. package/dist/i18n/locales/de-CH.d.ts +11 -8
  110. package/dist/i18n/locales/en.d.ts +11 -8
  111. package/dist/i18n/locales/en.js +7 -0
  112. package/dist/i18n/locales/en.js.map +1 -1
  113. package/dist/i18n/locales/es.d.ts +11 -8
  114. package/dist/i18n/locales/es.js +37 -2
  115. package/dist/i18n/locales/es.js.map +1 -1
  116. package/dist/i18n/locales/fr.d.ts +11 -8
  117. package/dist/i18n/locales/fr.js +36 -2
  118. package/dist/i18n/locales/fr.js.map +1 -1
  119. package/dist/i18n/locales/hu.d.ts +11 -8
  120. package/dist/i18n/locales/hu.js +38 -2
  121. package/dist/i18n/locales/hu.js.map +1 -1
  122. package/dist/i18n/locales/it.d.ts +11 -8
  123. package/dist/i18n/locales/it.js +37 -2
  124. package/dist/i18n/locales/it.js.map +1 -1
  125. package/dist/i18n/locales/zh.d.ts +11 -8
  126. package/dist/i18n/locales/zh.js +34 -2
  127. package/dist/i18n/locales/zh.js.map +1 -1
  128. package/dist/i18n/review.d.ts +11 -8
  129. package/dist/i18n/review.js +20 -1
  130. package/dist/i18n/review.js.map +1 -1
  131. package/dist/index.d.ts +7 -3
  132. package/dist/index.js +16 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/lib/column-mapping.d.ts +81 -0
  135. package/dist/lib/column-mapping.js +108 -0
  136. package/dist/lib/column-mapping.js.map +1 -0
  137. package/dist/lib/table-text.d.ts +109 -1
  138. package/dist/lib/table-text.js +122 -1
  139. package/dist/lib/table-text.js.map +1 -1
  140. package/dist/rhf/fields.d.ts +89 -11
  141. package/dist/rhf/fields.js +124 -0
  142. package/dist/rhf/fields.js.map +1 -1
  143. package/dist/rhf/form.d.ts +11 -8
  144. package/dist/rhf.d.ts +12 -9
  145. package/dist/rhf.js.map +1 -1
  146. package/dist/shell/app-shell.d.ts +10 -7
  147. package/dist/shell/top-bar-brand.d.ts +11 -8
  148. package/dist/shell/topbar-action-menu.d.ts +36 -3
  149. package/dist/shell/topbar-action-menu.js +74 -33
  150. package/dist/shell/topbar-action-menu.js.map +1 -1
  151. package/dist/shell.d.ts +10 -7
  152. package/dist/table-text.d.ts +1 -1
  153. package/dist/wizard/stepper-nav.d.ts +41 -10
  154. package/dist/wizard/stepper-nav.js +4 -0
  155. package/dist/wizard/stepper-nav.js.map +1 -1
  156. package/dist/wizard.d.ts +11 -8
  157. package/package.json +1 -1
  158. package/src/components/amount-input.tsx +20 -1
  159. package/src/components/autocomplete.tsx +172 -111
  160. package/src/components/column-mapper.tsx +666 -0
  161. package/src/components/combobox-core.tsx +82 -9
  162. package/src/components/combobox.tsx +614 -332
  163. package/src/components/confirm-dialog.tsx +12 -8
  164. package/src/components/country-select.tsx +135 -22
  165. package/src/components/danger-confirm.tsx +91 -12
  166. package/src/components/date-picker.tsx +423 -10
  167. package/src/components/entity-combobox.tsx +217 -126
  168. package/src/components/field-parts.tsx +224 -5
  169. package/src/components/field-strip.tsx +149 -0
  170. package/src/components/form-actions.tsx +32 -4
  171. package/src/components/icon-picker.tsx +23 -4
  172. package/src/components/money-field.tsx +3 -0
  173. package/src/components/month-picker.tsx +2 -1
  174. package/src/components/multi-entity-combobox.tsx +207 -120
  175. package/src/components/reauth-dialog.tsx +17 -18
  176. package/src/components/swatch-picker.tsx +29 -5
  177. package/src/components/tile-radio.tsx +74 -13
  178. package/src/components/toggle-group.tsx +2 -2
  179. package/src/components/trigger-aria.ts +5 -0
  180. package/src/components/ui.tsx +7 -33
  181. package/src/feedback/feedback-attachment.tsx +308 -61
  182. package/src/feedback/feedback-dialog.tsx +7 -3
  183. package/src/feedback/feedback-inbox.tsx +3 -2
  184. package/src/feedback/feedback-thread.tsx +47 -4
  185. package/src/i18n/defaults.ts +2 -0
  186. package/src/i18n/german.ts +42 -0
  187. package/src/i18n/kit-labels.tsx +4 -0
  188. package/src/i18n/locales/en.ts +27 -5
  189. package/src/i18n/locales/es.ts +41 -0
  190. package/src/i18n/locales/fr.ts +42 -0
  191. package/src/i18n/locales/hu.ts +37 -0
  192. package/src/i18n/locales/it.ts +41 -0
  193. package/src/i18n/locales/zh.ts +32 -0
  194. package/src/i18n/review.ts +19 -0
  195. package/src/index.ts +18 -0
  196. package/src/lib/column-mapping.ts +234 -0
  197. package/src/lib/table-text.ts +271 -0
  198. package/src/rhf/fields.tsx +254 -0
  199. package/src/rhf.ts +2 -1
  200. package/src/shell/topbar-action-menu.tsx +134 -38
  201. package/src/wizard/stepper-nav.tsx +35 -1
  202. package/dist/components/field-anatomy.d.ts +0 -95
  203. package/dist/components/field-anatomy.js +0 -84
  204. package/dist/components/field-anatomy.js.map +0 -1
  205. package/src/components/field-anatomy.tsx +0 -190
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Fragment, useId, useMemo, useRef, useState } from "react";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { Fragment as Fragment2, useId, useMemo, useRef, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import { ChevronDown, X } from "lucide-react";
5
+ import { ChevronDown, Plus, X } from "lucide-react";
6
6
  import { FIELD_BASE, FIELD_FLOATING_PAD, FIELD_INVALID, PHONE_QUERY } from "./ui.js";
7
7
  import { cn } from "../lib/cn.js";
8
8
  import { useDropdown } from "./dropdown.js";
@@ -13,7 +13,9 @@ import { PickerSheet, SHEET_ROW_CLASS } from "./picker-sheet.js";
13
13
  import {
14
14
  ComboboxFieldLabel,
15
15
  DISABLED_ROW_CLASS,
16
+ endHintRowProps,
16
17
  isOptionEnabled,
18
+ offersCreate,
17
19
  stepEnabled,
18
20
  SUGGESTION_LIST_CLASS,
19
21
  suggestionRowClass,
@@ -21,7 +23,17 @@ import {
21
23
  useComboboxFieldError
22
24
  } from "./combobox-core.js";
23
25
  import { DEFAULT_COMBOBOX_LABELS, useKitLabels } from "../i18n/kit-labels.js";
24
- import { EndHintRow, FieldCaption, LABEL_IN_ROW, StaticLabelRow, useFieldHint } from "./field-parts.js";
26
+ import {
27
+ EndHintRow,
28
+ FieldCaption,
29
+ LABEL_IN_ROW,
30
+ LockedReason,
31
+ StaticLabelRow,
32
+ useFieldHint,
33
+ useLockReason
34
+ } from "./field-parts.js";
35
+ import { hasMessage, mergeDescribedBy } from "./choice-parts.js";
36
+ import { useCommitReason } from "./write-lock.js";
25
37
  const LIST_CLASS = SUGGESTION_LIST_CLASS;
26
38
  function usePrimaryPressOnly() {
27
39
  const auxPress = useRef(false);
@@ -61,36 +73,43 @@ function SuggestionList({
61
73
  );
62
74
  }
63
75
  const rowClass = suggestionRowClass;
64
- function Combobox({
65
- value,
66
- onChange,
67
- options,
68
- label,
69
- id,
70
- placeholder,
71
- className,
72
- groupBy,
73
- maxSuggestions,
74
- searchPlaceholder,
75
- closeLabel,
76
- createLabel,
77
- invalid,
78
- error,
79
- hint,
80
- disabled,
81
- optionAdornment,
82
- autoFocus,
83
- onBlur,
84
- onSubmit,
85
- "aria-label": ariaLabel,
86
- // Off `rest` and onto the <input>, which is the combobox a reader meets: `Field`'s
87
- // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and
88
- // on the wrapper div the hint and required state described nothing.
89
- "aria-describedby": ariaDescribedBy,
90
- "aria-invalid": ariaInvalid,
91
- "aria-required": ariaRequired,
92
- ...rest
93
- }) {
76
+ function lockedKeyDown(e) {
77
+ if (e.key === "Enter") e.preventDefault();
78
+ }
79
+ const CREATE_ROW_CLASS = "flex items-center gap-2 font-medium text-[var(--brand)]";
80
+ function Combobox(props) {
81
+ const {
82
+ value,
83
+ onChange,
84
+ options,
85
+ label,
86
+ id,
87
+ placeholder,
88
+ className,
89
+ groupBy,
90
+ maxSuggestions,
91
+ searchPlaceholder,
92
+ closeLabel,
93
+ createLabel,
94
+ invalid,
95
+ error,
96
+ hint,
97
+ disabled,
98
+ disabledReason,
99
+ commit,
100
+ optionAdornment,
101
+ autoFocus,
102
+ onBlur,
103
+ onSubmit,
104
+ "aria-label": ariaLabel,
105
+ // Off `rest` and onto the <input>, which is the combobox a reader meets: `Field`'s
106
+ // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and
107
+ // on the wrapper div the hint and required state described nothing.
108
+ "aria-describedby": ariaDescribedBy,
109
+ "aria-invalid": ariaInvalid,
110
+ "aria-required": ariaRequired,
111
+ ...rest
112
+ } = props;
94
113
  const hintParts = useFieldHint(hint, ariaDescribedBy);
95
114
  const field = useComboboxFieldError(
96
115
  error,
@@ -105,6 +124,10 @@ function Combobox({
105
124
  const { open, setOpen, wrapperRef, panelRef } = useDropdown({ backCloses: !isPhone });
106
125
  const primaryOnly = usePrimaryPressOnly();
107
126
  const [active, setActive] = useState(-1);
127
+ const lock = useLockReason(commit, disabledReason);
128
+ const locked = lock.locked;
129
+ const inert = locked || Boolean(disabled);
130
+ if (locked && open) setOpen(false);
108
131
  const sheetInputRef = useRef(null);
109
132
  const fieldRef = useRef(null);
110
133
  const [typing, setTyping] = useState(false);
@@ -143,7 +166,8 @@ function Combobox({
143
166
  });
144
167
  return heads;
145
168
  }, [matches, groupBy]);
146
- const commit = (v) => {
169
+ const take = (v) => {
170
+ if (locked) return;
147
171
  onChange(v);
148
172
  setOpen(false);
149
173
  setActive(-1);
@@ -151,6 +175,7 @@ function Combobox({
151
175
  };
152
176
  const activeId = !isPhone && active >= 0 && active < matches.length ? optionId(active) : void 0;
153
177
  useActiveOptionScroll(activeId);
178
+ const withLock = (box) => locked ? /* @__PURE__ */ jsx(LockedReason, { lock, className: "block", children: box }) : box;
154
179
  const typed = value.trim();
155
180
  const createRow = createLabel && typed.length > 0 && !options.some((o) => o.toLowerCase() === typed.toLowerCase()) ? createLabel(typed) : null;
156
181
  return (
@@ -159,101 +184,117 @@ function Combobox({
159
184
  // which is the combobox a reader meets. Spread FIRST, so the field's ARIA and the
160
185
  // handlers carrying live #309 and dev#549 cannot be replaced from outside.
161
186
  /* @__PURE__ */ jsxs("div", { ...rest, ref: wrapperRef, className: cn("relative", className), children: [
162
- label !== void 0 && hintParts.labelHint === void 0 && /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: disabled ? "opacity-50" : void 0, children: label }),
163
- /* @__PURE__ */ jsx(EndHintRow, { hint: label === void 0 ? hintParts.labelHint : void 0, children: /* @__PURE__ */ jsxs("div", { ref: fieldRef, className: cn("relative", disabled && "cursor-not-allowed opacity-50"), children: [
164
- /* @__PURE__ */ jsx(
165
- "input",
166
- {
167
- id: fieldId,
168
- value,
169
- placeholder,
170
- "aria-label": ariaLabel,
171
- role: "combobox",
172
- "aria-expanded": open,
173
- "aria-controls": listboxId,
174
- "aria-activedescendant": activeId,
175
- "aria-autocomplete": "list",
176
- "aria-invalid": field.isInvalid || void 0,
177
- "aria-describedby": field.describedBy,
178
- "aria-required": ariaRequired,
179
- autoComplete: "off",
180
- disabled,
181
- autoFocus,
182
- onBlur,
183
- onMouseDown: primaryOnly.onMouseDown,
184
- onFocus: () => {
185
- if (primaryOnly.fromAuxButton()) return;
186
- setOpen(true);
187
- setTyping(false);
188
- if (isPhone) sheetInputRef.current?.focus();
189
- },
190
- onClick: () => {
191
- setOpen(true);
192
- setTyping(false);
193
- },
194
- inputMode: isPhone ? "none" : void 0,
195
- onChange: (e) => {
196
- onChange(e.target.value);
197
- setOpen(true);
198
- setActive(-1);
199
- setTyping(true);
200
- },
201
- onKeyDown: (e) => {
202
- if (e.key === "ArrowDown") {
203
- e.preventDefault();
187
+ label !== void 0 && hintParts.labelHint === void 0 && /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: inert ? "opacity-50" : void 0, children: label }),
188
+ /* @__PURE__ */ jsx(EndHintRow, { ...endHintRowProps("hint" in props, label !== void 0, hintParts.labelHint), children: withLock(
189
+ /* The chevron centers against this inner wrapper, which hugs the input.
190
+ The outer div can be taller than the input (as a grid item it
191
+ stretches to the row height, e.g. next to the editor's category cell
192
+ with its split button), which used to drag a top-1/2 chevron down to
193
+ the input's bottom edge (feedback #248).
194
+ Dimmed as a whole when disabled (or locked), the way EntityCombobox dims its
195
+ trigger. FIELD_BASE's grey alone left a disabled picker looking like a
196
+ filled-in one beside the pickers that do dim (the label is not the input's
197
+ `peer`, so it is dimmed by hand above). */
198
+ /* @__PURE__ */ jsxs("div", { ref: fieldRef, className: cn("relative", inert && "cursor-not-allowed opacity-50"), children: [
199
+ /* @__PURE__ */ jsx(
200
+ "input",
201
+ {
202
+ id: fieldId,
203
+ value,
204
+ placeholder,
205
+ "aria-label": ariaLabel,
206
+ role: "combobox",
207
+ "aria-expanded": open,
208
+ "aria-controls": listboxId,
209
+ "aria-activedescendant": activeId,
210
+ "aria-autocomplete": "list",
211
+ "aria-invalid": field.isInvalid || void 0,
212
+ "aria-describedby": mergeDescribedBy(field.describedBy, locked && lock.reasonId),
213
+ "aria-required": ariaRequired,
214
+ autoComplete: "off",
215
+ disabled: locked ? void 0 : disabled,
216
+ readOnly: locked || void 0,
217
+ "aria-disabled": locked || void 0,
218
+ autoFocus,
219
+ onBlur,
220
+ onMouseDown: primaryOnly.onMouseDown,
221
+ onFocus: () => {
222
+ if (primaryOnly.fromAuxButton() || locked) return;
204
223
  setOpen(true);
205
- setActive((i) => Math.min(i + 1, matches.length - 1));
206
- } else if (e.key === "ArrowUp") {
207
- e.preventDefault();
208
- setActive((i) => Math.max(i - 1, 0));
209
- } else if (e.key === "Enter") {
210
- if (open && active >= 0 && active < matches.length) {
224
+ setTyping(false);
225
+ if (isPhone) sheetInputRef.current?.focus();
226
+ },
227
+ onClick: () => {
228
+ if (locked) return;
229
+ setOpen(true);
230
+ setTyping(false);
231
+ },
232
+ inputMode: isPhone ? "none" : void 0,
233
+ onChange: (e) => {
234
+ if (locked) return;
235
+ onChange(e.target.value);
236
+ setOpen(true);
237
+ setActive(-1);
238
+ setTyping(true);
239
+ },
240
+ onKeyDown: (e) => {
241
+ if (locked) return lockedKeyDown(e);
242
+ if (e.key === "ArrowDown") {
243
+ e.preventDefault();
244
+ setOpen(true);
245
+ setActive((i) => Math.min(i + 1, matches.length - 1));
246
+ } else if (e.key === "ArrowUp") {
211
247
  e.preventDefault();
212
- commit(matches[active]);
213
- } else {
214
- if (onSubmit) e.preventDefault();
248
+ setActive((i) => Math.max(i - 1, 0));
249
+ } else if (e.key === "Enter") {
250
+ if (open && active >= 0 && active < matches.length) {
251
+ e.preventDefault();
252
+ take(matches[active]);
253
+ } else {
254
+ if (onSubmit) e.preventDefault();
255
+ setOpen(false);
256
+ onSubmit?.();
257
+ }
258
+ } else if (e.key === "Escape") {
259
+ setOpen(false);
260
+ setActive(-1);
261
+ } else if (e.key === "Tab") {
215
262
  setOpen(false);
216
- onSubmit?.();
263
+ setActive(-1);
217
264
  }
218
- } else if (e.key === "Escape") {
219
- setOpen(false);
220
- setActive(-1);
221
- } else if (e.key === "Tab") {
222
- setOpen(false);
223
- setActive(-1);
224
- }
225
- },
226
- className: cn(
227
- FIELD_BASE,
228
- label !== void 0 && FIELD_FLOATING_PAD,
229
- "pe-9",
230
- field.isInvalid && FIELD_INVALID
231
- )
232
- }
233
- ),
234
- /* @__PURE__ */ jsx(
235
- ChevronDown,
236
- {
237
- "aria-hidden": true,
238
- onMouseDown: (e) => {
239
- e.preventDefault();
240
- if (!disabled) setOpen((o) => !o);
241
- },
242
- className: cn(
243
- "absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--text-placeholder)]",
244
- // The box above does the dimming; dimming here too would halve it again.
245
- !disabled && "cursor-pointer"
246
- )
247
- }
248
- )
249
- ] }) }),
250
- label !== void 0 && hintParts.labelHint !== void 0 && /* @__PURE__ */ jsx(StaticLabelRow, { hint: hintParts.labelHint, children: /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: cn(LABEL_IN_ROW, disabled && "opacity-50"), children: label }) }),
265
+ },
266
+ className: cn(
267
+ FIELD_BASE,
268
+ label !== void 0 && FIELD_FLOATING_PAD,
269
+ "pe-9",
270
+ field.isInvalid && FIELD_INVALID
271
+ )
272
+ }
273
+ ),
274
+ /* @__PURE__ */ jsx(
275
+ ChevronDown,
276
+ {
277
+ "aria-hidden": true,
278
+ onMouseDown: (e) => {
279
+ e.preventDefault();
280
+ if (!inert) setOpen((o) => !o);
281
+ },
282
+ className: cn(
283
+ "absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--text-placeholder)]",
284
+ // The box above does the dimming; dimming here too would halve it again.
285
+ !inert && "cursor-pointer"
286
+ )
287
+ }
288
+ )
289
+ ] })
290
+ ) }),
291
+ label !== void 0 && hintParts.labelHint !== void 0 && /* @__PURE__ */ jsx(StaticLabelRow, { hint: hintParts.labelHint, children: /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: cn(LABEL_IN_ROW, inert && "opacity-50"), children: label }) }),
251
292
  /* @__PURE__ */ jsx(FieldCaption, { parts: hintParts }),
252
293
  field.errorEl,
253
294
  isPhone && /* @__PURE__ */ jsx(
254
295
  PickerSheet,
255
296
  {
256
- open: open && !disabled,
297
+ open: open && !inert,
257
298
  onClose: () => setOpen(false),
258
299
  title: label,
259
300
  query: value,
@@ -274,14 +315,14 @@ function Combobox({
274
315
  type: "button",
275
316
  role: "option",
276
317
  "aria-selected": false,
277
- onClick: () => commit(typed),
318
+ onClick: () => take(typed),
278
319
  className: cn(SHEET_ROW_CLASS, "font-medium text-[var(--brand)]"),
279
320
  children: createRow
280
321
  }
281
322
  ) }),
282
323
  matches.map((o, i) => {
283
324
  const group = groupHeadings?.get(i);
284
- return /* @__PURE__ */ jsxs(Fragment, { children: [
325
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
285
326
  group !== void 0 && /* @__PURE__ */ jsx(
286
327
  "li",
287
328
  {
@@ -296,7 +337,7 @@ function Combobox({
296
337
  type: "button",
297
338
  role: "option",
298
339
  "aria-selected": o === value,
299
- onClick: () => commit(o),
340
+ onClick: () => take(o),
300
341
  className: cn(
301
342
  SHEET_ROW_CLASS,
302
343
  optionAdornment && "flex items-center justify-between gap-2"
@@ -312,7 +353,7 @@ function Combobox({
312
353
  ] })
313
354
  }
314
355
  ),
315
- !isPhone && open && !disabled && (matches.length > 0 || createRow) && /* @__PURE__ */ jsxs(SuggestionList, { id: listboxId, anchorRef: fieldRef, panelRef, children: [
356
+ !isPhone && open && !inert && (matches.length > 0 || createRow) && /* @__PURE__ */ jsxs(SuggestionList, { id: listboxId, anchorRef: fieldRef, panelRef, children: [
316
357
  createRow && /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsx(
317
358
  "button",
318
359
  {
@@ -322,7 +363,7 @@ function Combobox({
322
363
  tabIndex: -1,
323
364
  onMouseDown: (e) => {
324
365
  e.preventDefault();
325
- commit(typed);
366
+ take(typed);
326
367
  },
327
368
  className: cn(rowClass(false), "font-medium text-[var(--brand)]"),
328
369
  children: createRow
@@ -330,7 +371,7 @@ function Combobox({
330
371
  ) }),
331
372
  matches.map((o, i) => {
332
373
  const group = groupHeadings?.get(i);
333
- return /* @__PURE__ */ jsxs(Fragment, { children: [
374
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
334
375
  group !== void 0 && /* @__PURE__ */ jsx(
335
376
  "li",
336
377
  {
@@ -349,7 +390,7 @@ function Combobox({
349
390
  tabIndex: -1,
350
391
  onMouseDown: (e) => {
351
392
  e.preventDefault();
352
- commit(o);
393
+ take(o);
353
394
  },
354
395
  onMouseEnter: () => setActive(i),
355
396
  className: cn(
@@ -372,34 +413,41 @@ function Combobox({
372
413
  ] })
373
414
  );
374
415
  }
375
- function InlineEntityCombobox({
376
- value: rawValue,
377
- onChange,
378
- clearValue = null,
379
- options,
380
- label,
381
- id,
382
- placeholder,
383
- className,
384
- disabled,
385
- autoFocus,
386
- searchPlaceholder,
387
- emptyLabel,
388
- closeLabel,
389
- clearable,
390
- clearLabel,
391
- invalid,
392
- error,
393
- hint,
394
- "aria-label": ariaLabel,
395
- // Off `rest` and onto the <input>, which is the combobox a reader meets: `Field`'s
396
- // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and
397
- // on the wrapper div the hint and required state described nothing.
398
- "aria-describedby": ariaDescribedBy,
399
- "aria-invalid": ariaInvalid,
400
- "aria-required": ariaRequired,
401
- ...rest
402
- }) {
416
+ function InlineEntityCombobox(props) {
417
+ const {
418
+ value: rawValue,
419
+ onChange,
420
+ clearValue = null,
421
+ options,
422
+ label,
423
+ id,
424
+ placeholder,
425
+ className,
426
+ disabled,
427
+ autoFocus,
428
+ searchPlaceholder,
429
+ emptyLabel,
430
+ closeLabel,
431
+ clearable,
432
+ clearLabel,
433
+ disabledReason,
434
+ commit,
435
+ onCreate,
436
+ createLabel,
437
+ createEmptyLabel,
438
+ createCommit,
439
+ invalid,
440
+ error,
441
+ hint,
442
+ "aria-label": ariaLabel,
443
+ // Off `rest` and onto the <input>, which is the combobox a reader meets: `Field`'s
444
+ // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and
445
+ // on the wrapper div the hint and required state described nothing.
446
+ "aria-describedby": ariaDescribedBy,
447
+ "aria-invalid": ariaInvalid,
448
+ "aria-required": ariaRequired,
449
+ ...rest
450
+ } = props;
403
451
  const value = rawValue == null || rawValue === clearValue ? null : rawValue;
404
452
  const hintParts = useFieldHint(hint, ariaDescribedBy);
405
453
  const field = useComboboxFieldError(
@@ -422,14 +470,24 @@ function InlineEntityCombobox({
422
470
  const labels = useKitLabels("combobox", DEFAULT_COMBOBOX_LABELS, {
423
471
  search: searchPlaceholder,
424
472
  noResults: emptyLabel,
425
- clear: clearLabel
473
+ clear: clearLabel,
474
+ create: createLabel
426
475
  });
476
+ const lock = useLockReason(commit, disabledReason);
477
+ const locked = lock.locked;
478
+ const inert = locked || Boolean(disabled);
479
+ if (locked && (open || text !== null)) {
480
+ setOpen(false);
481
+ setText(null);
482
+ }
483
+ const createReason = useCommitReason(createCommit, void 0);
484
+ const createLocked = hasMessage(createReason);
427
485
  const selected = useMemo(
428
486
  () => value == null ? null : options.find((o) => o.value === value) ?? null,
429
487
  [options, value]
430
488
  );
431
489
  const shown = text ?? selected?.label ?? "";
432
- const showClear = Boolean(clearable && value != null && !disabled);
490
+ const showClear = Boolean(clearable && value != null && !inert);
433
491
  const typedQuery = text !== null && text !== (selected?.label ?? "") ? text.trim().toLowerCase() : "";
434
492
  const query = isPhone ? sheetQuery.trim().toLowerCase() : typedQuery;
435
493
  const matches = useMemo(() => {
@@ -451,21 +509,37 @@ function InlineEntityCombobox({
451
509
  }
452
510
  return [...blocks.values()].flat();
453
511
  }, [options, query]);
454
- const activeId = !isPhone && isOptionEnabled(matches[active]) ? optionId(active) : void 0;
512
+ const createQuery = isPhone ? sheetQuery.trim() : text !== null && text !== (selected?.label ?? "") ? text.trim() : "";
513
+ const showCreate = offersCreate(
514
+ onCreate,
515
+ createQuery,
516
+ options.map((o) => o.label),
517
+ createEmptyLabel
518
+ );
519
+ const createText = createQuery ? labels.create(createQuery) : createEmptyLabel;
520
+ const rows = showCreate ? [...matches, { disabled: createLocked }] : matches;
521
+ const createIndex = matches.length;
522
+ const activeId = !isPhone && isOptionEnabled(rows[active]) ? optionId(active) : void 0;
455
523
  useActiveOptionScroll(activeId);
456
524
  const close = () => {
457
525
  setOpen(false);
458
526
  setActive(-1);
459
527
  setSheetQuery("");
460
528
  };
461
- const commit = (o) => {
462
- if (o.disabled) return;
529
+ const take = (o) => {
530
+ if (o.disabled || locked) return;
463
531
  if (o.value !== value) onChange(o.value);
464
532
  setText(null);
465
533
  close();
466
534
  };
535
+ const create = () => {
536
+ if (!onCreate || locked || createLocked) return;
537
+ onCreate(createQuery);
538
+ setText(null);
539
+ close();
540
+ };
467
541
  const reconcile = () => {
468
- if (text !== null) {
542
+ if (text !== null && !locked) {
469
543
  const q = text.trim();
470
544
  if (!q) {
471
545
  if (value != null) onChange(clearValue);
@@ -476,124 +550,145 @@ function InlineEntityCombobox({
476
550
  const ids = new Set(hits.map((h) => h.value));
477
551
  if (ids.size === 1 && hits[0].value !== value) onChange(hits[0].value);
478
552
  }
479
- setText(null);
480
553
  }
554
+ setText(null);
481
555
  close();
482
556
  };
557
+ const createRowContent = /* @__PURE__ */ jsxs(Fragment, { children: [
558
+ /* @__PURE__ */ jsx(Plus, { "aria-hidden": true, className: "size-4 shrink-0" }),
559
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
560
+ /* @__PURE__ */ jsx("span", { className: "block truncate", children: createText }),
561
+ createLocked && /* @__PURE__ */ jsx("span", { className: "block truncate text-xs font-normal text-[var(--text-placeholder)]", children: createReason })
562
+ ] })
563
+ ] });
564
+ const withLock = (box) => locked ? /* @__PURE__ */ jsx(LockedReason, { lock, className: "block", children: box }) : box;
483
565
  return (
484
566
  // `rest` dresses the outer box — a `data-tour` anchor, a test id. Not the NAME,
485
567
  // description, invalid or required state: those belong on the <input> below,
486
568
  // which is the combobox a reader meets. Spread FIRST, so the field's ARIA and the
487
569
  // handlers carrying live #309 and dev#549 cannot be replaced from outside.
488
570
  /* @__PURE__ */ jsxs("div", { ...rest, ref: wrapperRef, className: cn("relative", className), children: [
489
- label !== void 0 && hintParts.labelHint === void 0 && /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: disabled ? "opacity-50" : void 0, children: label }),
490
- /* @__PURE__ */ jsx(EndHintRow, { hint: label === void 0 ? hintParts.labelHint : void 0, children: /* @__PURE__ */ jsxs("div", { ref: fieldRef, className: cn("relative", disabled && "cursor-not-allowed opacity-50"), children: [
491
- /* @__PURE__ */ jsx(
492
- "input",
493
- {
494
- id: fieldId,
495
- value: shown,
496
- placeholder,
497
- "aria-label": ariaLabel,
498
- role: "combobox",
499
- "aria-expanded": open,
500
- "aria-controls": listboxId,
501
- "aria-activedescendant": activeId,
502
- "aria-autocomplete": "list",
503
- "aria-invalid": field.isInvalid || void 0,
504
- "aria-describedby": field.describedBy,
505
- "aria-required": ariaRequired,
506
- autoComplete: "off",
507
- disabled,
508
- autoFocus,
509
- inputMode: isPhone ? "none" : void 0,
510
- onMouseDown: primaryOnly.onMouseDown,
511
- onFocus: (e) => {
512
- if (primaryOnly.fromAuxButton()) return;
513
- setText(shown);
514
- e.currentTarget.select();
515
- setOpen(true);
516
- if (isPhone) sheetInputRef.current?.focus();
517
- },
518
- onBlur: () => {
519
- if (!isPhone) reconcile();
520
- },
521
- onChange: (e) => {
522
- setText(e.target.value);
523
- setOpen(true);
524
- setActive(-1);
525
- },
526
- onKeyDown: (e) => {
527
- if (e.key === "ArrowDown") {
528
- e.preventDefault();
571
+ label !== void 0 && hintParts.labelHint === void 0 && /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: inert ? "opacity-50" : void 0, children: label }),
572
+ /* @__PURE__ */ jsx(EndHintRow, { ...endHintRowProps("hint" in props, label !== void 0, hintParts.labelHint), children: withLock(
573
+ /* Inner wrapper for chevron centering — same reasoning as Combobox above.
574
+ It is also what the portalled list anchors to.
575
+ Dimmed as a whole when disabled (or locked), the way EntityCombobox dims its
576
+ trigger. FIELD_BASE's grey alone left a disabled picker looking like a
577
+ filled-in one beside the pickers that do dim (the label is not the input's
578
+ `peer`, so it is dimmed by hand above). */
579
+ /* @__PURE__ */ jsxs("div", { ref: fieldRef, className: cn("relative", inert && "cursor-not-allowed opacity-50"), children: [
580
+ /* @__PURE__ */ jsx(
581
+ "input",
582
+ {
583
+ id: fieldId,
584
+ value: shown,
585
+ placeholder,
586
+ "aria-label": ariaLabel,
587
+ role: "combobox",
588
+ "aria-expanded": open,
589
+ "aria-controls": listboxId,
590
+ "aria-activedescendant": activeId,
591
+ "aria-autocomplete": "list",
592
+ "aria-invalid": field.isInvalid || void 0,
593
+ "aria-describedby": mergeDescribedBy(field.describedBy, locked && lock.reasonId),
594
+ "aria-required": ariaRequired,
595
+ autoComplete: "off",
596
+ disabled: locked ? void 0 : disabled,
597
+ readOnly: locked || void 0,
598
+ "aria-disabled": locked || void 0,
599
+ autoFocus,
600
+ inputMode: isPhone ? "none" : void 0,
601
+ onMouseDown: primaryOnly.onMouseDown,
602
+ onFocus: (e) => {
603
+ if (primaryOnly.fromAuxButton() || locked) return;
604
+ setText(shown);
605
+ e.currentTarget.select();
529
606
  setOpen(true);
530
- setActive((i) => stepEnabled(matches, i, 1));
531
- } else if (e.key === "ArrowUp") {
532
- e.preventDefault();
533
- setActive((i) => i < 0 ? stepEnabled(matches, -1, 1) : stepEnabled(matches, i, -1));
534
- } else if (e.key === "Enter") {
535
- if (open && isOptionEnabled(matches[active])) {
607
+ if (isPhone) sheetInputRef.current?.focus();
608
+ },
609
+ onBlur: () => {
610
+ if (!isPhone) reconcile();
611
+ },
612
+ onChange: (e) => {
613
+ if (locked) return;
614
+ setText(e.target.value);
615
+ setOpen(true);
616
+ setActive(-1);
617
+ },
618
+ onKeyDown: (e) => {
619
+ if (locked) return lockedKeyDown(e);
620
+ if (e.key === "ArrowDown") {
621
+ e.preventDefault();
622
+ setOpen(true);
623
+ setActive((i) => stepEnabled(rows, i, 1));
624
+ } else if (e.key === "ArrowUp") {
536
625
  e.preventDefault();
537
- commit(matches[active]);
538
- } else {
626
+ setActive((i) => i < 0 ? stepEnabled(rows, -1, 1) : stepEnabled(rows, i, -1));
627
+ } else if (e.key === "Enter") {
628
+ if (open && isOptionEnabled(rows[active])) {
629
+ e.preventDefault();
630
+ if (active === createIndex && showCreate) create();
631
+ else take(matches[active]);
632
+ } else {
633
+ reconcile();
634
+ }
635
+ } else if (e.key === "Escape") {
636
+ setText(null);
637
+ close();
638
+ } else if (e.key === "Tab") {
539
639
  reconcile();
540
640
  }
541
- } else if (e.key === "Escape") {
641
+ },
642
+ className: cn(
643
+ FIELD_BASE,
644
+ label !== void 0 && FIELD_FLOATING_PAD,
645
+ "pe-9",
646
+ field.isInvalid && FIELD_INVALID
647
+ )
648
+ }
649
+ ),
650
+ showClear ? /* @__PURE__ */ jsx(
651
+ "button",
652
+ {
653
+ type: "button",
654
+ tabIndex: -1,
655
+ "aria-label": labels.clear,
656
+ onMouseDown: (e) => e.preventDefault(),
657
+ onClick: () => {
542
658
  setText(null);
543
659
  close();
544
- } else if (e.key === "Tab") {
545
- reconcile();
546
- }
547
- },
548
- className: cn(
549
- FIELD_BASE,
550
- label !== void 0 && FIELD_FLOATING_PAD,
551
- "pe-9",
552
- field.isInvalid && FIELD_INVALID
553
- )
554
- }
555
- ),
556
- showClear ? /* @__PURE__ */ jsx(
557
- "button",
558
- {
559
- type: "button",
560
- tabIndex: -1,
561
- "aria-label": labels.clear,
562
- onMouseDown: (e) => e.preventDefault(),
563
- onClick: () => {
564
- setText(null);
565
- close();
566
- onChange(clearValue);
567
- },
568
- className: cn(
569
- "absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-[var(--text-placeholder)]",
570
- "hover:text-[var(--text-secondary)]"
571
- ),
572
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": true, className: "size-4" })
573
- }
574
- ) : /* @__PURE__ */ jsx(
575
- ChevronDown,
576
- {
577
- "aria-hidden": true,
578
- onMouseDown: (e) => {
579
- e.preventDefault();
580
- if (!disabled) setOpen((o) => !o);
581
- },
582
- className: cn(
583
- "absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--text-placeholder)]",
584
- // The box above does the dimming; dimming here too would halve it again.
585
- !disabled && "cursor-pointer"
586
- )
587
- }
588
- )
589
- ] }) }),
590
- label !== void 0 && hintParts.labelHint !== void 0 && /* @__PURE__ */ jsx(StaticLabelRow, { hint: hintParts.labelHint, children: /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: cn(LABEL_IN_ROW, disabled && "opacity-50"), children: label }) }),
660
+ if (!locked) onChange(clearValue);
661
+ },
662
+ className: cn(
663
+ "absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-[var(--text-placeholder)]",
664
+ "hover:text-[var(--text-secondary)]"
665
+ ),
666
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": true, className: "size-4" })
667
+ }
668
+ ) : /* @__PURE__ */ jsx(
669
+ ChevronDown,
670
+ {
671
+ "aria-hidden": true,
672
+ onMouseDown: (e) => {
673
+ e.preventDefault();
674
+ if (!inert) setOpen((o) => !o);
675
+ },
676
+ className: cn(
677
+ "absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--text-placeholder)]",
678
+ // The box above does the dimming; dimming here too would halve it again.
679
+ !inert && "cursor-pointer"
680
+ )
681
+ }
682
+ )
683
+ ] })
684
+ ) }),
685
+ label !== void 0 && hintParts.labelHint !== void 0 && /* @__PURE__ */ jsx(StaticLabelRow, { hint: hintParts.labelHint, children: /* @__PURE__ */ jsx(ComboboxFieldLabel, { htmlFor: fieldId, className: cn(LABEL_IN_ROW, inert && "opacity-50"), children: label }) }),
591
686
  /* @__PURE__ */ jsx(FieldCaption, { parts: hintParts }),
592
687
  field.errorEl,
593
688
  isPhone && /* @__PURE__ */ jsx(
594
689
  PickerSheet,
595
690
  {
596
- open: open && !disabled,
691
+ open: open && !inert,
597
692
  onClose: reconcile,
598
693
  title: label,
599
694
  query: sheetQuery,
@@ -609,7 +704,7 @@ function InlineEntityCombobox({
609
704
  // Keyed by group AND value, like combobox-core.tsx: an option may
610
705
  // deliberately appear twice (see `reconcile`), and a bare value key
611
706
  // would collide.
612
- /* @__PURE__ */ jsxs(Fragment, { children: [
707
+ /* @__PURE__ */ jsxs(Fragment2, { children: [
613
708
  o.group && o.group !== matches[i - 1]?.group && /* @__PURE__ */ jsx(
614
709
  "li",
615
710
  {
@@ -625,7 +720,7 @@ function InlineEntityCombobox({
625
720
  role: "option",
626
721
  "aria-selected": o.value === value,
627
722
  "aria-disabled": o.disabled || void 0,
628
- onClick: () => commit(o),
723
+ onClick: () => take(o),
629
724
  className: cn(
630
725
  SHEET_ROW_CLASS,
631
726
  o.value === value && "font-medium",
@@ -636,58 +731,100 @@ function InlineEntityCombobox({
636
731
  ) })
637
732
  ] }, `${o.group ?? ""}|${String(o.value)}`)
638
733
  )),
639
- matches.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-4 py-3 text-sm text-[var(--text-muted)]", children: labels.noResults })
734
+ showCreate && /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsx(
735
+ "button",
736
+ {
737
+ type: "button",
738
+ role: "option",
739
+ "aria-selected": false,
740
+ "aria-disabled": createLocked || void 0,
741
+ onClick: create,
742
+ className: cn(
743
+ SHEET_ROW_CLASS,
744
+ CREATE_ROW_CLASS,
745
+ createLocked && DISABLED_ROW_CLASS
746
+ ),
747
+ children: createRowContent
748
+ }
749
+ ) }),
750
+ matches.length === 0 && !showCreate && /* @__PURE__ */ jsx("li", { className: "px-4 py-3 text-sm text-[var(--text-muted)]", children: labels.noResults })
640
751
  ] })
641
752
  }
642
753
  ),
643
- !isPhone && open && !disabled && matches.length > 0 && /* @__PURE__ */ jsx(SuggestionList, { id: listboxId, anchorRef: fieldRef, panelRef, children: matches.map((o, i) => (
644
- // A group heading is emitted at each group boundary rather than repeating
645
- // the group on every row (feedback #136). `matches` is group-contiguous,
646
- // so comparing with the previous row is enough. Fragment key sits here;
647
- // the heading and the option carry their own list semantics — keyed by
648
- // group AND value, because an option may deliberately appear twice (see
649
- // `reconcile`).
650
- /* @__PURE__ */ jsxs(Fragment, { children: [
651
- o.group && o.group !== matches[i - 1]?.group && /* @__PURE__ */ jsx(
652
- "li",
653
- {
654
- role: "presentation",
655
- className: "px-3 pb-0.5 pt-2 text-[11px] font-semibold uppercase tracking-wide text-[var(--text-muted)] first:pt-1",
656
- children: o.group
657
- }
658
- ),
659
- /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsxs(
660
- "button",
661
- {
662
- type: "button",
663
- id: optionId(i),
664
- role: "option",
665
- "aria-selected": o.value === value,
666
- "aria-disabled": o.disabled || void 0,
667
- tabIndex: -1,
668
- onMouseDown: (e) => {
669
- e.preventDefault();
670
- commit(o);
671
- },
672
- onMouseEnter: () => {
673
- if (!o.disabled) setActive(i);
674
- },
675
- className: cn(
676
- rowClass(i === active && !o.disabled),
677
- o.disabled && DISABLED_ROW_CLASS,
678
- // Indented under its heading, so the hierarchy is readable at a
679
- // glance instead of inferred from grey trailing text.
680
- o.group && "ps-6",
681
- o.value === value && "font-medium"
682
- ),
683
- children: [
684
- o.label,
685
- o.sublabel && /* @__PURE__ */ jsx("span", { className: "ms-2 text-xs text-[var(--text-placeholder)]", children: o.sublabel })
686
- ]
687
- }
688
- ) })
689
- ] }, `${o.group ?? ""}|${String(o.value)}`)
690
- )) })
754
+ !isPhone && open && !inert && (matches.length > 0 || showCreate) && /* @__PURE__ */ jsxs(SuggestionList, { id: listboxId, anchorRef: fieldRef, panelRef, children: [
755
+ matches.map((o, i) => (
756
+ // A group heading is emitted at each group boundary rather than repeating
757
+ // the group on every row (feedback #136). `matches` is group-contiguous,
758
+ // so comparing with the previous row is enough. Fragment key sits here;
759
+ // the heading and the option carry their own list semantics — keyed by
760
+ // group AND value, because an option may deliberately appear twice (see
761
+ // `reconcile`).
762
+ /* @__PURE__ */ jsxs(Fragment2, { children: [
763
+ o.group && o.group !== matches[i - 1]?.group && /* @__PURE__ */ jsx(
764
+ "li",
765
+ {
766
+ role: "presentation",
767
+ className: "px-3 pb-0.5 pt-2 text-[11px] font-semibold uppercase tracking-wide text-[var(--text-muted)] first:pt-1",
768
+ children: o.group
769
+ }
770
+ ),
771
+ /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsxs(
772
+ "button",
773
+ {
774
+ type: "button",
775
+ id: optionId(i),
776
+ role: "option",
777
+ "aria-selected": o.value === value,
778
+ "aria-disabled": o.disabled || void 0,
779
+ tabIndex: -1,
780
+ onMouseDown: (e) => {
781
+ e.preventDefault();
782
+ take(o);
783
+ },
784
+ onMouseEnter: () => {
785
+ if (!o.disabled) setActive(i);
786
+ },
787
+ className: cn(
788
+ rowClass(i === active && !o.disabled),
789
+ o.disabled && DISABLED_ROW_CLASS,
790
+ // Indented under its heading, so the hierarchy is readable at a
791
+ // glance instead of inferred from grey trailing text.
792
+ o.group && "ps-6",
793
+ o.value === value && "font-medium"
794
+ ),
795
+ children: [
796
+ o.label,
797
+ o.sublabel && /* @__PURE__ */ jsx("span", { className: "ms-2 text-xs text-[var(--text-placeholder)]", children: o.sublabel })
798
+ ]
799
+ }
800
+ ) })
801
+ ] }, `${o.group ?? ""}|${String(o.value)}`)
802
+ )),
803
+ showCreate && /* @__PURE__ */ jsx("li", { role: "presentation", children: /* @__PURE__ */ jsx(
804
+ "button",
805
+ {
806
+ type: "button",
807
+ id: optionId(createIndex),
808
+ role: "option",
809
+ "aria-selected": false,
810
+ "aria-disabled": createLocked || void 0,
811
+ tabIndex: -1,
812
+ onMouseDown: (e) => {
813
+ e.preventDefault();
814
+ create();
815
+ },
816
+ onMouseEnter: () => {
817
+ if (!createLocked) setActive(createIndex);
818
+ },
819
+ className: cn(
820
+ rowClass(active === createIndex && !createLocked),
821
+ CREATE_ROW_CLASS,
822
+ createLocked && DISABLED_ROW_CLASS
823
+ ),
824
+ children: createRowContent
825
+ }
826
+ ) })
827
+ ] })
691
828
  ] })
692
829
  );
693
830
  }