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