stimeo-ui 0.17.0 → 0.18.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 (297) hide show
  1. package/CHANGELOG.md +106 -1
  2. package/README.md +19 -10
  3. package/dist/cable/index.d.ts +9 -8
  4. package/dist/cable/index.js +22 -14
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.js +14 -6
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.js +145 -118
  10. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  11. package/dist/controllers/announcer_controller.d.ts +5 -3
  12. package/dist/controllers/announcer_controller.js +113 -17
  13. package/dist/controllers/announcer_controller.js.map +1 -1
  14. package/dist/controllers/auto_submit_controller.js +185 -60
  15. package/dist/controllers/auto_submit_controller.js.map +1 -1
  16. package/dist/controllers/avatar_controller.js +55 -43
  17. package/dist/controllers/avatar_controller.js.map +1 -1
  18. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  19. package/dist/controllers/bulk_select_controller.d.ts +3 -3
  20. package/dist/controllers/bulk_select_controller.js +64 -50
  21. package/dist/controllers/bulk_select_controller.js.map +1 -1
  22. package/dist/controllers/calendar_controller.d.ts +15 -12
  23. package/dist/controllers/calendar_controller.js +55 -53
  24. package/dist/controllers/calendar_controller.js.map +1 -1
  25. package/dist/controllers/carousel_controller.d.ts +9 -5
  26. package/dist/controllers/carousel_controller.js +124 -86
  27. package/dist/controllers/carousel_controller.js.map +1 -1
  28. package/dist/controllers/character_counter_controller.d.ts +27 -6
  29. package/dist/controllers/character_counter_controller.js +291 -95
  30. package/dist/controllers/character_counter_controller.js.map +1 -1
  31. package/dist/controllers/checkbox_controller.js +6 -0
  32. package/dist/controllers/checkbox_controller.js.map +1 -1
  33. package/dist/controllers/clipboard_controller.js +118 -27
  34. package/dist/controllers/clipboard_controller.js.map +1 -1
  35. package/dist/controllers/collapsible_controller.d.ts +1 -1
  36. package/dist/controllers/collapsible_controller.js +52 -49
  37. package/dist/controllers/collapsible_controller.js.map +1 -1
  38. package/dist/controllers/color_picker_controller.js +29 -7
  39. package/dist/controllers/color_picker_controller.js.map +1 -1
  40. package/dist/controllers/combobox_controller.d.ts +1 -1
  41. package/dist/controllers/combobox_controller.js +210 -71
  42. package/dist/controllers/combobox_controller.js.map +1 -1
  43. package/dist/controllers/command_palette_controller.d.ts +9 -9
  44. package/dist/controllers/command_palette_controller.js +289 -140
  45. package/dist/controllers/command_palette_controller.js.map +1 -1
  46. package/dist/controllers/conditional_fields_controller.js +145 -58
  47. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  48. package/dist/controllers/confirm_controller.js +145 -118
  49. package/dist/controllers/confirm_controller.js.map +1 -1
  50. package/dist/controllers/context_menu_controller.d.ts +49 -14
  51. package/dist/controllers/context_menu_controller.js +138 -73
  52. package/dist/controllers/context_menu_controller.js.map +1 -1
  53. package/dist/controllers/count_up_controller.js.map +1 -1
  54. package/dist/controllers/countdown_controller.d.ts +2 -2
  55. package/dist/controllers/countdown_controller.js +109 -21
  56. package/dist/controllers/countdown_controller.js.map +1 -1
  57. package/dist/controllers/currency_input_controller.d.ts +75 -11
  58. package/dist/controllers/currency_input_controller.js +446 -72
  59. package/dist/controllers/currency_input_controller.js.map +1 -1
  60. package/dist/controllers/data_grid_controller.d.ts +10 -8
  61. package/dist/controllers/data_grid_controller.js +134 -28
  62. package/dist/controllers/data_grid_controller.js.map +1 -1
  63. package/dist/controllers/date_range_picker_controller.d.ts +12 -9
  64. package/dist/controllers/date_range_picker_controller.js +93 -72
  65. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  66. package/dist/controllers/dialog_controller.d.ts +4 -5
  67. package/dist/controllers/dialog_controller.js +145 -118
  68. package/dist/controllers/dialog_controller.js.map +1 -1
  69. package/dist/controllers/direct_upload_controller.d.ts +1 -1
  70. package/dist/controllers/direct_upload_controller.js +24 -14
  71. package/dist/controllers/direct_upload_controller.js.map +1 -1
  72. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  73. package/dist/controllers/dirty_form_controller.js +68 -31
  74. package/dist/controllers/dirty_form_controller.js.map +1 -1
  75. package/dist/controllers/dismissible_controller.js +64 -46
  76. package/dist/controllers/dismissible_controller.js.map +1 -1
  77. package/dist/controllers/drawer_controller.js +145 -118
  78. package/dist/controllers/drawer_controller.js.map +1 -1
  79. package/dist/controllers/dropdown_controller.js +76 -63
  80. package/dist/controllers/dropdown_controller.js.map +1 -1
  81. package/dist/controllers/editable_controller.js +199 -62
  82. package/dist/controllers/editable_controller.js.map +1 -1
  83. package/dist/controllers/empty_state_controller.js +13 -6
  84. package/dist/controllers/empty_state_controller.js.map +1 -1
  85. package/dist/controllers/file_dropzone_controller.d.ts +3 -0
  86. package/dist/controllers/file_dropzone_controller.js +55 -48
  87. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  88. package/dist/controllers/filter_controller.js +9 -2
  89. package/dist/controllers/filter_controller.js.map +1 -1
  90. package/dist/controllers/flash_controller.d.ts +4 -4
  91. package/dist/controllers/flash_controller.js +66 -33
  92. package/dist/controllers/flash_controller.js.map +1 -1
  93. package/dist/controllers/focus_controller.d.ts +3 -3
  94. package/dist/controllers/focus_controller.js +39 -14
  95. package/dist/controllers/focus_controller.js.map +1 -1
  96. package/dist/controllers/form_field_controller.js +52 -43
  97. package/dist/controllers/form_field_controller.js.map +1 -1
  98. package/dist/controllers/form_validation_controller.d.ts +3 -3
  99. package/dist/controllers/form_validation_controller.js +18 -11
  100. package/dist/controllers/form_validation_controller.js.map +1 -1
  101. package/dist/controllers/frame_loading_controller.d.ts +13 -12
  102. package/dist/controllers/frame_loading_controller.js +55 -48
  103. package/dist/controllers/frame_loading_controller.js.map +1 -1
  104. package/dist/controllers/highlight_controller.d.ts +5 -4
  105. package/dist/controllers/highlight_controller.js.map +1 -1
  106. package/dist/controllers/hover_card_controller.d.ts +4 -1
  107. package/dist/controllers/hover_card_controller.js +67 -61
  108. package/dist/controllers/hover_card_controller.js.map +1 -1
  109. package/dist/controllers/idle_controller.d.ts +6 -5
  110. package/dist/controllers/idle_controller.js +10 -6
  111. package/dist/controllers/idle_controller.js.map +1 -1
  112. package/dist/controllers/input_mask_controller.d.ts +30 -13
  113. package/dist/controllers/input_mask_controller.js +411 -24
  114. package/dist/controllers/input_mask_controller.js.map +1 -1
  115. package/dist/controllers/intersection_controller.js +3 -0
  116. package/dist/controllers/intersection_controller.js.map +1 -1
  117. package/dist/controllers/lazy_frame_controller.d.ts +3 -2
  118. package/dist/controllers/lazy_frame_controller.js +15 -3
  119. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  120. package/dist/controllers/listbox_controller.d.ts +10 -7
  121. package/dist/controllers/listbox_controller.js +260 -79
  122. package/dist/controllers/listbox_controller.js.map +1 -1
  123. package/dist/controllers/local_time_controller.d.ts +8 -6
  124. package/dist/controllers/local_time_controller.js +48 -13
  125. package/dist/controllers/local_time_controller.js.map +1 -1
  126. package/dist/controllers/masonry_controller.js +11 -4
  127. package/dist/controllers/masonry_controller.js.map +1 -1
  128. package/dist/controllers/menu_controller.d.ts +24 -5
  129. package/dist/controllers/menu_controller.js +138 -82
  130. package/dist/controllers/menu_controller.js.map +1 -1
  131. package/dist/controllers/menubar_controller.d.ts +49 -7
  132. package/dist/controllers/menubar_controller.js +65 -19
  133. package/dist/controllers/menubar_controller.js.map +1 -1
  134. package/dist/controllers/meter_controller.d.ts +13 -20
  135. package/dist/controllers/meter_controller.js +270 -24
  136. package/dist/controllers/meter_controller.js.map +1 -1
  137. package/dist/controllers/multi_select_controller.d.ts +17 -15
  138. package/dist/controllers/multi_select_controller.js +251 -94
  139. package/dist/controllers/multi_select_controller.js.map +1 -1
  140. package/dist/controllers/navigation_menu_controller.d.ts +19 -5
  141. package/dist/controllers/navigation_menu_controller.js +67 -19
  142. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  143. package/dist/controllers/nested_form_controller.js +98 -64
  144. package/dist/controllers/nested_form_controller.js.map +1 -1
  145. package/dist/controllers/network_status_controller.js +70 -52
  146. package/dist/controllers/network_status_controller.js.map +1 -1
  147. package/dist/controllers/number_input_controller.d.ts +24 -6
  148. package/dist/controllers/number_input_controller.js +344 -94
  149. package/dist/controllers/number_input_controller.js.map +1 -1
  150. package/dist/controllers/optimistic_controller.d.ts +8 -5
  151. package/dist/controllers/optimistic_controller.js +29 -11
  152. package/dist/controllers/optimistic_controller.js.map +1 -1
  153. package/dist/controllers/otp_controller.d.ts +35 -4
  154. package/dist/controllers/otp_controller.js +502 -104
  155. package/dist/controllers/otp_controller.js.map +1 -1
  156. package/dist/controllers/overflow_indicator_controller.js +6 -0
  157. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  158. package/dist/controllers/overflow_menu_controller.d.ts +10 -9
  159. package/dist/controllers/overflow_menu_controller.js +136 -21
  160. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  161. package/dist/controllers/pagination_controller.d.ts +3 -2
  162. package/dist/controllers/pagination_controller.js +47 -14
  163. package/dist/controllers/pagination_controller.js.map +1 -1
  164. package/dist/controllers/password_reveal_controller.d.ts +5 -4
  165. package/dist/controllers/password_reveal_controller.js +52 -49
  166. package/dist/controllers/password_reveal_controller.js.map +1 -1
  167. package/dist/controllers/password_strength_controller.d.ts +1 -1
  168. package/dist/controllers/password_strength_controller.js +13 -6
  169. package/dist/controllers/password_strength_controller.js.map +1 -1
  170. package/dist/controllers/persist_controller.js +48 -128
  171. package/dist/controllers/persist_controller.js.map +1 -1
  172. package/dist/controllers/pointer_drag_controller.d.ts +19 -8
  173. package/dist/controllers/pointer_drag_controller.js +249 -23
  174. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  175. package/dist/controllers/popover_controller.d.ts +3 -3
  176. package/dist/controllers/popover_controller.js +82 -65
  177. package/dist/controllers/popover_controller.js.map +1 -1
  178. package/dist/controllers/portal_controller.js +6 -0
  179. package/dist/controllers/portal_controller.js.map +1 -1
  180. package/dist/controllers/preview_guard_controller.js +14 -3
  181. package/dist/controllers/preview_guard_controller.js.map +1 -1
  182. package/dist/controllers/progress_controller.d.ts +9 -19
  183. package/dist/controllers/progress_controller.js +271 -27
  184. package/dist/controllers/progress_controller.js.map +1 -1
  185. package/dist/controllers/radio_group_controller.d.ts +11 -8
  186. package/dist/controllers/radio_group_controller.js +167 -80
  187. package/dist/controllers/radio_group_controller.js.map +1 -1
  188. package/dist/controllers/range_slider_controller.d.ts +4 -3
  189. package/dist/controllers/range_slider_controller.js +27 -7
  190. package/dist/controllers/range_slider_controller.js.map +1 -1
  191. package/dist/controllers/rating_controller.js +93 -57
  192. package/dist/controllers/rating_controller.js.map +1 -1
  193. package/dist/controllers/read_more_controller.js +10 -6
  194. package/dist/controllers/read_more_controller.js.map +1 -1
  195. package/dist/controllers/reading_progress_controller.js +22 -5
  196. package/dist/controllers/reading_progress_controller.js.map +1 -1
  197. package/dist/controllers/relative_time_controller.d.ts +4 -4
  198. package/dist/controllers/relative_time_controller.js +75 -11
  199. package/dist/controllers/relative_time_controller.js.map +1 -1
  200. package/dist/controllers/reset_on_restore_controller.d.ts +3 -3
  201. package/dist/controllers/reset_on_restore_controller.js +6 -0
  202. package/dist/controllers/reset_on_restore_controller.js.map +1 -1
  203. package/dist/controllers/resizable_controller.d.ts +3 -0
  204. package/dist/controllers/resizable_controller.js +55 -22
  205. package/dist/controllers/resizable_controller.js.map +1 -1
  206. package/dist/controllers/roving_controller.d.ts +3 -3
  207. package/dist/controllers/roving_controller.js +10 -4
  208. package/dist/controllers/roving_controller.js.map +1 -1
  209. package/dist/controllers/scroll_area_controller.js +129 -88
  210. package/dist/controllers/scroll_area_controller.js.map +1 -1
  211. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  212. package/dist/controllers/scroll_restore_controller.js +14 -2
  213. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  214. package/dist/controllers/scroll_visibility_controller.js +93 -60
  215. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  216. package/dist/controllers/scrollspy_controller.js +11 -5
  217. package/dist/controllers/scrollspy_controller.js.map +1 -1
  218. package/dist/controllers/separator_controller.d.ts +1 -1
  219. package/dist/controllers/separator_controller.js +59 -43
  220. package/dist/controllers/separator_controller.js.map +1 -1
  221. package/dist/controllers/sidebar_controller.d.ts +1 -1
  222. package/dist/controllers/sidebar_controller.js +150 -122
  223. package/dist/controllers/sidebar_controller.js.map +1 -1
  224. package/dist/controllers/skeleton_controller.js +55 -48
  225. package/dist/controllers/skeleton_controller.js.map +1 -1
  226. package/dist/controllers/slider_controller.d.ts +2 -1
  227. package/dist/controllers/slider_controller.js +27 -6
  228. package/dist/controllers/slider_controller.js.map +1 -1
  229. package/dist/controllers/smart_sticky_header_controller.d.ts +1 -1
  230. package/dist/controllers/smart_sticky_header_controller.js +13 -5
  231. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  232. package/dist/controllers/sortable_controller.d.ts +4 -4
  233. package/dist/controllers/sortable_controller.js +27 -14
  234. package/dist/controllers/sortable_controller.js.map +1 -1
  235. package/dist/controllers/spinner_controller.js +55 -48
  236. package/dist/controllers/spinner_controller.js.map +1 -1
  237. package/dist/controllers/step_indicator_controller.d.ts +4 -4
  238. package/dist/controllers/step_indicator_controller.js +6 -0
  239. package/dist/controllers/step_indicator_controller.js.map +1 -1
  240. package/dist/controllers/stepper_controller.d.ts +2 -3
  241. package/dist/controllers/stepper_controller.js +6 -0
  242. package/dist/controllers/stepper_controller.js.map +1 -1
  243. package/dist/controllers/stick_to_bottom_controller.d.ts +3 -1
  244. package/dist/controllers/stick_to_bottom_controller.js +19 -8
  245. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  246. package/dist/controllers/sticky_observer_controller.js +49 -43
  247. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  248. package/dist/controllers/submit_once_controller.d.ts +2 -1
  249. package/dist/controllers/submit_once_controller.js +145 -85
  250. package/dist/controllers/submit_once_controller.js.map +1 -1
  251. package/dist/controllers/switch_controller.d.ts +7 -4
  252. package/dist/controllers/switch_controller.js +172 -28
  253. package/dist/controllers/switch_controller.js.map +1 -1
  254. package/dist/controllers/tabs_controller.d.ts +7 -4
  255. package/dist/controllers/tabs_controller.js +6 -1
  256. package/dist/controllers/tabs_controller.js.map +1 -1
  257. package/dist/controllers/tags_input_controller.d.ts +4 -3
  258. package/dist/controllers/tags_input_controller.js +157 -28
  259. package/dist/controllers/tags_input_controller.js.map +1 -1
  260. package/dist/controllers/textarea_autosize_controller.d.ts +1 -1
  261. package/dist/controllers/textarea_autosize_controller.js +40 -19
  262. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  263. package/dist/controllers/theme_controller.d.ts +3 -2
  264. package/dist/controllers/theme_controller.js +10 -4
  265. package/dist/controllers/theme_controller.js.map +1 -1
  266. package/dist/controllers/time_picker_controller.d.ts +38 -5
  267. package/dist/controllers/time_picker_controller.js +192 -54
  268. package/dist/controllers/time_picker_controller.js.map +1 -1
  269. package/dist/controllers/toast_controller.d.ts +4 -5
  270. package/dist/controllers/toast_controller.js +58 -27
  271. package/dist/controllers/toast_controller.js.map +1 -1
  272. package/dist/controllers/toggle_group_controller.d.ts +2 -3
  273. package/dist/controllers/toggle_group_controller.js +158 -56
  274. package/dist/controllers/toggle_group_controller.js.map +1 -1
  275. package/dist/controllers/toolbar_controller.d.ts +8 -6
  276. package/dist/controllers/toolbar_controller.js +150 -21
  277. package/dist/controllers/toolbar_controller.js.map +1 -1
  278. package/dist/controllers/tooltip_controller.d.ts +4 -1
  279. package/dist/controllers/tooltip_controller.js.map +1 -1
  280. package/dist/controllers/transition_controller.js.map +1 -1
  281. package/dist/controllers/tree_view_controller.d.ts +1 -1
  282. package/dist/controllers/tree_view_controller.js +134 -25
  283. package/dist/controllers/tree_view_controller.js.map +1 -1
  284. package/dist/index.d.ts +2 -0
  285. package/dist/index.js +2451 -1312
  286. package/dist/index.js.map +1 -1
  287. package/dist/inspector/cli.d.ts +15 -11
  288. package/dist/inspector/cli.js +16 -3
  289. package/dist/inspector/cli.js.map +1 -1
  290. package/dist/inspector/cli_bin.js +53 -8
  291. package/dist/inspector/cli_bin.js.map +1 -1
  292. package/dist/inspector/examples.json +13 -13
  293. package/dist/inspector/manifest.json +19 -5
  294. package/dist/positioning/index.d.ts +7 -7
  295. package/dist/positioning/index.js +6 -0
  296. package/dist/positioning/index.js.map +1 -1
  297. package/package.json +10 -9
@@ -5,9 +5,9 @@ function sharedRegistry(key, create) {
5
5
  const scope = globalThis;
6
6
  const existing = scope[symbol];
7
7
  if (existing !== void 0) return existing;
8
- const registry = create();
9
- Object.defineProperty(globalThis, symbol, { value: registry });
10
- return registry;
8
+ const registry2 = create();
9
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
10
+ return registry2;
11
11
  }
12
12
 
13
13
  var ids = sharedRegistry("stimeo-ui.aria-ids.registry.v1", () => ({ counter: 0 }));
@@ -39,7 +39,21 @@ function isReservedArrowChord(event, allow = []) {
39
39
  if (!event.key.startsWith("Arrow")) return false;
40
40
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
41
41
  }
42
+ function hasModifierChord(event) {
43
+ return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
44
+ }
45
+
46
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
47
+ if (element.getAttribute(name) === value) return false;
48
+ if (value === null) element.removeAttribute(name);
49
+ else element.setAttribute(name, value);
50
+ return true;
51
+ }
42
52
 
53
+ var registry = sharedRegistry(
54
+ "stimeo-ui.attribute-lease.registry.v1",
55
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
56
+ );
43
57
  function isAttributeValue(value) {
44
58
  return value === null || typeof value === "string";
45
59
  }
@@ -56,24 +70,46 @@ function readLeaseRecord(element, record) {
56
70
  return void 0;
57
71
  }
58
72
  }
59
- var AttributeLease = class _AttributeLease {
60
- static #registry = sharedRegistry(
61
- "stimeo-ui.attribute-lease.registry.v1",
62
- () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
63
- );
73
+ function attributeStack(element, attribute) {
74
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
75
+ registry.stacks.set(element, byAttribute);
76
+ const known = byAttribute.get(attribute);
77
+ if (known) return known;
78
+ const found = [];
79
+ for (const record of registry.names.get(attribute) ?? []) {
80
+ const read = readLeaseRecord(element, record);
81
+ if (read) found.push({ ...read, record });
82
+ }
83
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
84
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
85
+ lease: null,
86
+ record,
87
+ written,
88
+ original,
89
+ linked: linked && index > 0
90
+ }));
91
+ byAttribute.set(attribute, stack);
92
+ return stack;
93
+ }
94
+ function findHold(stack, lease, record) {
95
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
96
+ }
97
+ var AttributeLease = class {
64
98
  #attribute;
65
99
  #record;
66
100
  #base;
101
+ #preserveAuthor;
67
102
  #held = /* @__PURE__ */ new Set();
68
103
  constructor(attribute, owner, options = {}) {
69
104
  this.#attribute = attribute;
70
105
  this.#record = `data-${owner}-${attribute}-lease`;
71
106
  this.#base = options.base === true;
72
- const names = _AttributeLease.#registry.names;
107
+ this.#preserveAuthor = options.preserveAuthor === true;
108
+ const names = registry.names;
73
109
  names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
74
110
  }
75
111
  write(element, value) {
76
- const stack = this.#stack(element);
112
+ const stack = attributeStack(element, this.#attribute);
77
113
  const hold = this.#take(stack) ?? this.#acquire(element, stack);
78
114
  this.#held.add(element);
79
115
  const shows = !this.#covered(element, stack, hold);
@@ -83,8 +119,8 @@ var AttributeLease = class _AttributeLease {
83
119
  }
84
120
  return(element) {
85
121
  const held = this.#held.delete(element);
86
- const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
87
- const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
122
+ const known = registry.stacks.get(element)?.has(this.#attribute);
123
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
88
124
  const hold = this.#take(stack);
89
125
  if (!hold) return false;
90
126
  const carries = element.getAttribute(this.#attribute) === hold.written;
@@ -102,31 +138,8 @@ var AttributeLease = class _AttributeLease {
102
138
  returnAll() {
103
139
  for (const element of Array.from(this.#held)) this.return(element);
104
140
  }
105
- #stack(element) {
106
- const stacks = _AttributeLease.#registry.stacks;
107
- const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
108
- stacks.set(element, byAttribute);
109
- const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
110
- byAttribute.set(this.#attribute, stack);
111
- return stack;
112
- }
113
- #seed(element) {
114
- const found = [];
115
- for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
116
- const read = readLeaseRecord(element, record);
117
- if (read) found.push({ ...read, record });
118
- }
119
- const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
120
- return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
121
- lease: null,
122
- record,
123
- written,
124
- original,
125
- linked: linked && index > 0
126
- }));
127
- }
128
141
  #take(stack) {
129
- const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
142
+ const hold = findHold(stack, this, this.#record);
130
143
  if (hold) hold.lease = this;
131
144
  return hold;
132
145
  }
@@ -135,7 +148,7 @@ var AttributeLease = class _AttributeLease {
135
148
  const top = stack.at(-1);
136
149
  const bottom = stack[0];
137
150
  const over = top?.written === current ? top : void 0;
138
- const from = this.#base ? bottom : over;
151
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
139
152
  const hold = {
140
153
  lease: this,
141
154
  record: this.#record,
@@ -165,15 +178,11 @@ var AttributeLease = class _AttributeLease {
165
178
  );
166
179
  });
167
180
  for (const [name, recorded] of records) {
168
- if (element.getAttribute(name) === recorded) continue;
169
- if (recorded === null) element.removeAttribute(name);
170
- else element.setAttribute(name, recorded);
181
+ setAttributeIfChanged(element, name, recorded);
171
182
  }
172
183
  }
173
184
  #reflect(element, value) {
174
- if (element.getAttribute(this.#attribute) === value) return;
175
- if (value === null) element.removeAttribute(this.#attribute);
176
- else element.setAttribute(this.#attribute, value);
185
+ setAttributeIfChanged(element, this.#attribute, value);
177
186
  }
178
187
  };
179
188
 
@@ -183,63 +192,182 @@ var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
183
192
  "deleteCompositionText",
184
193
  "deleteByComposition"
185
194
  ]);
195
+ function isCompositionInputType(inputType) {
196
+ return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
197
+ }
198
+ var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
199
+ var textOf = (element) => element.value;
200
+ var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
186
201
  var CompositionTracker = class {
187
202
  #observedTargets = /* @__PURE__ */ new Set();
188
- #activeTargets = /* @__PURE__ */ new Set();
203
+ #composition = null;
189
204
  #onStart;
190
205
  #onEnd;
206
+ #onAbandon;
191
207
  #confirmedTarget = null;
208
+ #keyComposing = false;
209
+ #connected = false;
210
+ #abandoned = null;
192
211
  constructor(options = {}) {
193
212
  this.#onStart = options.onStart;
194
213
  this.#onEnd = options.onEnd;
214
+ this.#onAbandon = options.onAbandon;
195
215
  }
196
216
  observe(target) {
217
+ this.#connected = true;
197
218
  if (this.#observedTargets.has(target)) return;
198
219
  target.addEventListener("compositionstart", this.#handleStart);
199
220
  target.addEventListener("compositionend", this.#handleEnd);
200
- target.addEventListener("keydown", this.#handleKeydown);
221
+ target.addEventListener("keydown", this.#handleKeydown, true);
222
+ target.addEventListener("beforeinput", this.#handleBeforeInput, true);
223
+ target.addEventListener("input", this.#handleInput, true);
224
+ target.addEventListener("blur", this.#handleBlur, true);
225
+ target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
201
226
  this.#observedTargets.add(target);
202
227
  }
203
228
  unobserve(target) {
204
229
  if (!this.#observedTargets.delete(target)) return;
205
- target.removeEventListener("compositionstart", this.#handleStart);
206
- target.removeEventListener("compositionend", this.#handleEnd);
207
- target.removeEventListener("keydown", this.#handleKeydown);
208
- this.#activeTargets.delete(target);
230
+ this.#release(target);
231
+ if (this.#composition?.target === target) this.#letGo();
209
232
  if (this.#confirmedTarget === target) this.#confirmedTarget = null;
210
233
  }
211
234
  disconnect() {
212
- for (const target of this.#observedTargets) {
213
- target.removeEventListener("compositionstart", this.#handleStart);
214
- target.removeEventListener("compositionend", this.#handleEnd);
215
- target.removeEventListener("keydown", this.#handleKeydown);
216
- }
235
+ this.#connected = false;
236
+ for (const target of this.#observedTargets) this.#release(target);
217
237
  this.#observedTargets.clear();
218
- this.#activeTargets.clear();
238
+ this.#letGo();
219
239
  this.#confirmedTarget = null;
240
+ this.#keyComposing = false;
241
+ this.#abandoned = null;
220
242
  }
221
243
  isComposing(event) {
222
- return this.#activeTargets.size > 0 || event?.isComposing === true;
244
+ this.#settle();
245
+ return event?.isComposing === true || this.#composition !== null;
246
+ }
247
+ composingElement() {
248
+ this.#settle();
249
+ return this.#composition?.element ?? null;
250
+ }
251
+ owns(event) {
252
+ const element = this.composingElement();
253
+ return event.isComposing === true || element !== null && element === event.target;
223
254
  }
224
255
  consumesConfirmedInput(event) {
225
256
  const confirmed = this.#confirmedTarget;
226
257
  this.#confirmedTarget = null;
227
258
  if (confirmed === null || confirmed !== event.target) return false;
228
259
  const inputType = event.inputType;
229
- return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
260
+ return !inputType || isCompositionInputType(inputType);
261
+ }
262
+ #release(target) {
263
+ target.removeEventListener("compositionstart", this.#handleStart);
264
+ target.removeEventListener("compositionend", this.#handleEnd);
265
+ target.removeEventListener("keydown", this.#handleKeydown, true);
266
+ target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
267
+ target.removeEventListener("input", this.#handleInput, true);
268
+ target.removeEventListener("blur", this.#handleBlur, true);
269
+ target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
270
+ }
271
+ #letGo() {
272
+ const composition = this.#composition;
273
+ if (composition === null) return;
274
+ if (!holdsFocus(composition.element)) {
275
+ this.#composition = null;
276
+ return;
277
+ }
278
+ queueMicrotask(() => {
279
+ if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
280
+ return;
281
+ }
282
+ this.#composition = null;
283
+ if (!this.#connected) return;
284
+ this.#abandoned = { element: composition.element, inPlace: true };
285
+ this.#runAbandon();
286
+ });
287
+ }
288
+ #settle() {
289
+ const composition = this.#composition;
290
+ if (composition === null) return;
291
+ const { target, element, text } = composition;
292
+ if (target instanceof Node && element instanceof Node && !target.contains(element)) {
293
+ this.#composition = null;
294
+ } else if (textOf(element) !== text) {
295
+ this.#abandon(false);
296
+ }
297
+ }
298
+ #abandon(inPlace) {
299
+ this.#owe(inPlace);
300
+ this.#composition = null;
301
+ queueMicrotask(this.#runAbandon);
230
302
  }
303
+ #owe(inPlace) {
304
+ const composition = this.#composition;
305
+ if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
306
+ }
307
+ #runAbandon = () => {
308
+ const abandoned = this.#abandoned;
309
+ if (abandoned === null) return;
310
+ this.#abandoned = null;
311
+ this.#onAbandon?.(abandoned.element, abandoned.inPlace);
312
+ };
231
313
  #handleStart = (event) => {
232
314
  this.#confirmedTarget = null;
233
- if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
315
+ this.#settle();
316
+ this.#owe(true);
317
+ const target = event.currentTarget;
318
+ const element = event.target;
319
+ this.#composition = { target, element, text: textOf(element) };
320
+ this.#runAbandon();
234
321
  this.#onStart?.(event);
235
322
  };
236
323
  #handleEnd = (event) => {
237
- if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
324
+ this.#runAbandon();
325
+ if (this.#composition?.target === event.currentTarget) this.#composition = null;
238
326
  this.#confirmedTarget = event.target;
239
327
  this.#onEnd?.(event);
240
328
  };
241
- #handleKeydown = () => {
329
+ #handleKeydown = (event) => {
330
+ this.#settle();
331
+ this.#runAbandon();
242
332
  this.#confirmedTarget = null;
333
+ this.#keyComposing = event.isComposing === true;
334
+ };
335
+ #handleBeforeInput = (event) => {
336
+ const keyComposing = this.#keyComposing;
337
+ this.#keyComposing = false;
338
+ this.#settle();
339
+ const { isComposing, inputType } = event;
340
+ if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
341
+ this.#abandon(true);
342
+ }
343
+ this.#runAbandon();
344
+ };
345
+ #handleInput = (event) => {
346
+ const composition = this.#composition;
347
+ const { isTrusted, isComposing, inputType } = event;
348
+ if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
349
+ composition.text = textOf(composition.element);
350
+ } else {
351
+ this.#settle();
352
+ }
353
+ this.#runAbandon();
354
+ };
355
+ #handleBlur = (event) => {
356
+ this.#settle();
357
+ this.#runAbandon();
358
+ const composition = this.#composition;
359
+ if (composition === null || composition.target === composition.element) return;
360
+ const { element } = composition;
361
+ if (!(element instanceof Node) || event.target !== element) return;
362
+ const root = element.getRootNode();
363
+ if (root.activeElement !== element) this.#composition = null;
364
+ };
365
+ #handleBeforeMorphAttribute = (event) => {
366
+ this.#settle();
367
+ const composition = this.#composition;
368
+ if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
369
+ const detail = event.detail;
370
+ if (detail?.attributeName === "value") event.preventDefault();
243
371
  };
244
372
  };
245
373
 
@@ -314,6 +442,75 @@ function toHalfWidth(text) {
314
442
  return out;
315
443
  }
316
444
 
445
+ var TimerRegistry = class {
446
+ ids = /* @__PURE__ */ new Set();
447
+ clear(id) {
448
+ if (this.ids.delete(id)) {
449
+ this.cancel(id);
450
+ }
451
+ }
452
+ clearAll() {
453
+ for (const id of this.ids) {
454
+ this.cancel(id);
455
+ }
456
+ this.ids.clear();
457
+ }
458
+ get size() {
459
+ return this.ids.size;
460
+ }
461
+ };
462
+ var SafeTimeout = class extends TimerRegistry {
463
+ set(callback, delay) {
464
+ const id = this.schedule(() => {
465
+ this.ids.delete(id);
466
+ callback();
467
+ }, delay);
468
+ this.ids.add(id);
469
+ return id;
470
+ }
471
+ schedule(callback, delay) {
472
+ return window.setTimeout(callback, delay);
473
+ }
474
+ cancel(id) {
475
+ window.clearTimeout(id);
476
+ }
477
+ };
478
+
479
+ var KeyedTimers = class {
480
+ #timers = new SafeTimeout();
481
+ #pending = /* @__PURE__ */ new Map();
482
+ set(key, callback, delay) {
483
+ this.clear(key);
484
+ const id = this.#timers.set(() => {
485
+ this.#pending.delete(key);
486
+ callback();
487
+ }, delay);
488
+ this.#pending.set(key, id);
489
+ }
490
+ clear(key) {
491
+ this.#timers.clear(this.#pending.get(key) ?? -1);
492
+ this.#pending.delete(key);
493
+ }
494
+ clearAll() {
495
+ this.#timers.clearAll();
496
+ this.#pending.clear();
497
+ }
498
+ has(key) {
499
+ return this.#pending.has(key);
500
+ }
501
+ };
502
+
503
+ var ListenerSet = class {
504
+ #abort = new AbortController();
505
+ add(target, type, handler, options) {
506
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
507
+ }
508
+ dispose() {
509
+ this.#abort.abort();
510
+ this.#abort = new AbortController();
511
+ }
512
+ };
513
+
317
514
  var MicrotaskCoalescer = class {
318
515
  #run;
319
516
  #queued = false;
@@ -330,6 +527,9 @@ var MicrotaskCoalescer = class {
330
527
  this.#queued = false;
331
528
  this.#generation += 1;
332
529
  }
530
+ get pending() {
531
+ return this.#queued;
532
+ }
333
533
  schedule() {
334
534
  if (!this.#active || this.#queued) return;
335
535
  this.#queued = true;
@@ -342,6 +542,34 @@ var MicrotaskCoalescer = class {
342
542
  }
343
543
  };
344
544
 
545
+ var MorphRenderWatcher = class {
546
+ #listeners = new ListenerSet();
547
+ #pass;
548
+ #element = null;
549
+ #onMorph = () => this.#pass.schedule();
550
+ constructor(run) {
551
+ this.#pass = new MicrotaskCoalescer(run);
552
+ }
553
+ observe(element) {
554
+ if (this.#element === element) return;
555
+ this.disconnect();
556
+ this.#element = element;
557
+ this.#pass.activate();
558
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
559
+ }
560
+ schedule() {
561
+ this.#pass.schedule();
562
+ }
563
+ get pending() {
564
+ return this.#pass.pending;
565
+ }
566
+ disconnect() {
567
+ this.#listeners.dispose();
568
+ this.#pass.cancel();
569
+ this.#element = null;
570
+ }
571
+ };
572
+
345
573
  var MoveCounter = class {
346
574
  #sequence = 0;
347
575
  record() {
@@ -376,9 +604,6 @@ function describedByWith(described, errorId, earlier) {
376
604
  }
377
605
  var DEFAULT_PATTERN = "[0-9]";
378
606
  var DEFAULT_MATCHER = new RegExp(`^(?:${DEFAULT_PATTERN})$`);
379
- function hasModifier(event) {
380
- return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
381
- }
382
607
  function statesDiffer(left, right) {
383
608
  return left.value !== right.value || left.state !== right.state;
384
609
  }
@@ -395,9 +620,15 @@ var OtpController = class extends Controller {
395
620
  #published = null;
396
621
  #connected = false;
397
622
  #committed = /* @__PURE__ */ new WeakMap();
623
+ #retained = /* @__PURE__ */ new WeakMap();
624
+ #inputBaselines = /* @__PURE__ */ new Map();
625
+ #inputExpiry = new KeyedTimers();
626
+ #endBaselines = /* @__PURE__ */ new Map();
627
+ #away = /* @__PURE__ */ new Set();
398
628
  #composing = null;
399
629
  #dropHeld = false;
400
- #reconcile = new MicrotaskCoalescer(() => this.#reconcileFields());
630
+ #judgedBy = null;
631
+ #reconcile = new MorphRenderWatcher(() => this.#reconcileFields());
401
632
  #errorHidden = new AttributeLease("hidden", this.identifier);
402
633
  #state = new AttributeLease("data-state", this.identifier);
403
634
  #formReset = new FormResetWatcher(
@@ -410,36 +641,68 @@ var OtpController = class extends Controller {
410
641
  #composition = new CompositionTracker({
411
642
  onStart: (event) => {
412
643
  const field = this.#fieldFrom(event);
413
- if (field) this.#composing = { field, before: field.value };
644
+ this.#clearInputBaseline();
645
+ if (field) this.#endBaselines.delete(field);
646
+ if (field && this.#composition.composingElement() === field) {
647
+ this.#composing = { field, before: field.value };
648
+ }
414
649
  },
415
650
  onEnd: (event) => {
416
- const composing = this.#composing;
417
- this.#composing = null;
418
651
  const input = this.#fieldFrom(event);
652
+ if (!input) return;
653
+ const baseline = this.#inputBaselines.get(input) ?? this.#endBaselines.get(input)?.baseline;
654
+ this.#clearEdits(input);
655
+ const composing = this.#composing;
656
+ if (!this.#composition.isComposing()) this.#composing = null;
419
657
  const data = event.data ?? "";
420
- const text = input && data.length <= input.value.length ? input.value : data;
421
- const commit = input !== null && (composing?.field !== input || text !== composing.before);
422
- if (this.#dropHeld) {
423
- if (input && commit) input.value = this.#committed.get(input) ?? "";
658
+ const text = data.length <= input.value.length ? input.value : data;
659
+ const commit = composing?.field !== input || text !== composing.before;
660
+ if (commit) this.#accept(input, text, baseline?.interrupted ? baseline : void 0);
661
+ else {
662
+ this.#restoreRetained(input);
663
+ if (this.#dropHeld) this.#reconcileFields();
664
+ }
665
+ },
666
+ onAbandon: (element, inPlace) => {
667
+ this.#composing = null;
668
+ if (inPlace && element instanceof HTMLInputElement && this.fieldTargets.includes(element)) {
669
+ const baseline = this.#inputBaselines.get(element) ?? {
670
+ field: element,
671
+ before: this.#committed.get(element) ?? "",
672
+ dropHeld: this.#dropHeld,
673
+ interrupted: true
674
+ };
675
+ baseline.interrupted = true;
676
+ this.#settleResidual(element);
677
+ this.#endBaselines.set(element, { text: element.value, baseline });
424
678
  this.#reconcileFields();
679
+ } else if (this.#dropHeld) {
680
+ this.#reconcile.schedule();
425
681
  }
426
- if (input && commit) this.#accept(input, text);
427
682
  }
428
683
  });
429
684
  connect() {
430
685
  this.#connected = true;
431
686
  for (const field of this.fieldTargets) this.#bind(field);
432
687
  this.#formReset.observe();
433
- this.#reconcile.activate();
688
+ this.#reconcile.observe(this.element);
434
689
  this.#adopt();
435
690
  this.#sync();
691
+ this.#judgedBy ??= this.#patternSource;
692
+ if (this.#judgedBy !== this.#patternSource) {
693
+ if (this.#composition.isComposing()) this.#dropHeld = true;
694
+ else if (this.#dropRejected()) this.#reconcile.schedule();
695
+ }
436
696
  }
437
697
  disconnect() {
438
698
  this.#connected = false;
439
699
  for (const field of this.fieldTargets) this.#unbind(field);
440
700
  this.#composition.disconnect();
701
+ this.#clearInputBaseline();
702
+ for (const field of this.#endBaselines.keys())
703
+ if (!this.#away.has(field)) this.#endBaselines.delete(field);
441
704
  this.#formReset.disconnect();
442
- this.#reconcile.cancel();
705
+ this.#reconcile.disconnect();
443
706
  this.#dropHeld = false;
444
707
  this.#clearError();
445
708
  this.#state.return(this.element);
@@ -451,7 +714,7 @@ var OtpController = class extends Controller {
451
714
  }
452
715
  fieldTargetDisconnected(element) {
453
716
  this.#unbind(element);
454
- this.#returnLeases(element);
717
+ if (!this.fieldTargets.includes(element)) this.#returnLeases(element);
455
718
  this.#reconcile.schedule();
456
719
  }
457
720
  valueTargetConnected() {
@@ -472,18 +735,28 @@ var OtpController = class extends Controller {
472
735
  this.#patternSource = compiled ? this.patternValue : DEFAULT_PATTERN;
473
736
  this.#pattern = compiled ?? DEFAULT_MATCHER;
474
737
  if (!this.#connected) return;
475
- if (this.#composing !== null) {
738
+ if (this.#composition.isComposing()) {
476
739
  this.#dropHeld = true;
477
740
  return;
478
741
  }
479
742
  if (this.#dropRejected()) this.#reconcile.schedule();
480
743
  }
481
- #dropRejected() {
744
+ #dropRejected(composing = null) {
745
+ if (composing === null) this.#judgedBy = this.#patternSource;
482
746
  let dropped = false;
483
747
  for (const field of this.fieldTargets) {
484
- if (field.value === "" || !this.#isWritable(field)) continue;
485
- if (this.#pattern.test(field.value)) continue;
486
- this.#writeField(field, "");
748
+ if (field === composing) {
749
+ const committed = this.#committed.get(field) ?? "";
750
+ if (committed === "" || !this.#isWritable(field) || this.#pattern.test(committed)) continue;
751
+ this.#committed.set(field, "");
752
+ dropped = true;
753
+ continue;
754
+ }
755
+ const value = this.#settledText(field);
756
+ if (value === "" || !this.#isWritable(field)) continue;
757
+ if (this.#pattern.test(value)) continue;
758
+ if (this.#retained.has(field)) this.#holdText(field, "");
759
+ else this.#writeField(field, "");
487
760
  dropped = true;
488
761
  }
489
762
  return dropped;
@@ -491,20 +764,24 @@ var OtpController = class extends Controller {
491
764
  onInput(event) {
492
765
  const input = this.#fieldFrom(event);
493
766
  if (!input) return;
767
+ const baseline = this.#inputBaselines.get(input);
768
+ this.#clearEdits(input);
494
769
  if (this.#composition.consumesConfirmedInput(event)) return;
495
- if (this.#composition.isComposing(event)) return;
496
- this.#accept(input);
770
+ if (this.#composition.owns(event)) return;
771
+ this.#accept(input, input.value, baseline?.interrupted ? baseline : void 0);
497
772
  }
498
773
  onKeydown(event) {
774
+ this.#clearInputBaseline();
499
775
  if (isReservedArrowChord(event)) return;
500
776
  const input = this.#fieldFrom(event);
501
777
  if (!input) return;
502
- if (this.#composition.isComposing(event)) return;
778
+ if (this.#composition.owns(event)) return;
779
+ if (!this.#prepareEdit(input)) return;
503
780
  const index = this.fieldTargets.indexOf(input);
504
781
  const fields = this.fieldTargets;
505
782
  switch (logicalArrowKey(event.key, this.element)) {
506
783
  case "Backspace":
507
- if (hasModifier(event)) break;
784
+ if (hasModifierChord(event)) break;
508
785
  if (input.value && this.#isWritable(input)) {
509
786
  event.preventDefault();
510
787
  this.#writeField(input, "");
@@ -538,7 +815,7 @@ var OtpController = class extends Controller {
538
815
  break;
539
816
  }
540
817
  case "Home": {
541
- if (hasModifier(event)) break;
818
+ if (hasModifierChord(event)) break;
542
819
  const first = fields.find((field) => this.#isFocusable(field));
543
820
  if (first) {
544
821
  event.preventDefault();
@@ -547,7 +824,7 @@ var OtpController = class extends Controller {
547
824
  break;
548
825
  }
549
826
  case "End": {
550
- if (hasModifier(event)) break;
827
+ if (hasModifierChord(event)) break;
551
828
  const last = fields.slice().reverse().find((field) => this.#isFocusable(field));
552
829
  if (last) {
553
830
  event.preventDefault();
@@ -561,7 +838,8 @@ var OtpController = class extends Controller {
561
838
  const input = this.#fieldFrom(event);
562
839
  if (!input) return;
563
840
  event.preventDefault();
564
- this.#distribute(input, toHalfWidth(event.clipboardData?.getData("text") ?? ""));
841
+ if (this.#prepareEdit(input))
842
+ this.#distribute(input, toHalfWidth(event.clipboardData?.getData("text") ?? ""), false);
565
843
  }
566
844
  onPointerDown(event) {
567
845
  const input = this.#fieldFrom(event);
@@ -579,6 +857,31 @@ var OtpController = class extends Controller {
579
857
  this.fieldTargets.find((field) => this.#isWritable(field))?.focus();
580
858
  this.#syncAndDispatch();
581
859
  }
860
+ #onBeforeInput = (event) => {
861
+ const field = event.currentTarget;
862
+ this.#clearEdits(field);
863
+ if (event.isComposing || !event.inputType || isCompositionInputType(event.inputType)) return;
864
+ this.#inputBaselines.set(field, {
865
+ field,
866
+ before: this.#committed.get(field) ?? "",
867
+ dropHeld: this.#dropHeld,
868
+ interrupted: false
869
+ });
870
+ this.#inputExpiry.set(field, () => this.#clearInputBaseline(field), 0);
871
+ };
872
+ #clearInputBaseline(field) {
873
+ if (field) {
874
+ this.#inputBaselines.delete(field);
875
+ this.#inputExpiry.clear(field);
876
+ } else {
877
+ this.#inputBaselines.clear();
878
+ this.#inputExpiry.clearAll();
879
+ }
880
+ }
881
+ #clearEdits(field) {
882
+ this.#clearInputBaseline(field);
883
+ this.#endBaselines.delete(field);
884
+ }
582
885
  #onFieldFocus = (event) => {
583
886
  const input = event.currentTarget;
584
887
  if (input) {
@@ -591,12 +894,24 @@ var OtpController = class extends Controller {
591
894
  }
592
895
  #bind(field) {
593
896
  field.addEventListener("focus", this.#onFieldFocus);
897
+ field.addEventListener("beforeinput", this.#onBeforeInput, true);
898
+ if (this.#away.delete(field) && document.activeElement !== field)
899
+ this.#endBaselines.delete(field);
594
900
  this.#composition.observe(field);
595
901
  }
596
902
  #unbind(field) {
597
903
  field.removeEventListener("focus", this.#onFieldFocus);
904
+ field.removeEventListener("beforeinput", this.#onBeforeInput, true);
905
+ this.#clearInputBaseline(field);
906
+ if (document.activeElement === field) this.#holdAway(field);
907
+ else this.#endBaselines.delete(field);
598
908
  this.#composition.unobserve(field);
599
- if (this.#composing?.field === field) this.#composing = null;
909
+ }
910
+ #holdAway(field) {
911
+ this.#away.add(field);
912
+ queueMicrotask(() => {
913
+ if (this.#away.delete(field)) this.#endBaselines.delete(field);
914
+ });
600
915
  }
601
916
  #adopt() {
602
917
  for (const field of this.fieldTargets) this.#adoptField(field);
@@ -620,36 +935,49 @@ var OtpController = class extends Controller {
620
935
  }
621
936
  }
622
937
  #adoptField(field) {
623
- this.#committed.set(field, field.value);
624
- this.#markFilled(field, field.value);
938
+ if (this.#composition.composingElement() === field) return;
939
+ const value = this.#settledText(field);
940
+ this.#committed.set(field, value);
941
+ this.#markFilled(field, value);
625
942
  }
626
943
  #reconcileFields() {
627
- if (this.#dropHeld && this.#composing === null) {
944
+ const composing = this.#composition.composingElement();
945
+ if (this.#dropHeld && composing === null) {
628
946
  this.#dropHeld = false;
629
947
  this.#dropRejected();
630
948
  }
949
+ for (const field of this.fieldTargets) {
950
+ if (field === composing) this.#markFilled(field, this.#publishedText(field));
951
+ else this.#adoptField(field);
952
+ }
631
953
  const previous = this.#published;
632
954
  const current = this.#sync();
633
- if (previous && !statesDiffer(previous, current)) return;
634
- this.#moves.record();
955
+ if (previous && !statesDiffer(previous, current)) return true;
956
+ const token = this.#moves.record();
635
957
  this.dispatch("reconcile", { detail: { value: current.value } });
958
+ return this.#moves.isLatest(token);
636
959
  }
637
- #accept(input, text = input.value) {
960
+ #accept(input, text = input.value, baseline) {
961
+ if (!this.#prepareEdit(input, true)) return;
962
+ const before = baseline ? this.fieldTargets.map(
963
+ (field) => field === input ? baseline.dropHeld && !this.#pattern.test(baseline.before) ? "" : baseline.before : this.#publishedText(field)
964
+ ).join("") : void 0;
638
965
  const raw = toHalfWidth(text);
639
966
  if (raw === "") {
640
967
  this.#writeField(input, "");
641
968
  this.#clearError();
642
- this.#syncAndDispatch();
969
+ this.#syncAndDispatch(before);
643
970
  return;
644
971
  }
645
- this.#distribute(input, raw);
972
+ this.#distribute(input, raw, true, before);
646
973
  }
647
- #distribute(from, text) {
974
+ #distribute(from, text, spare = true, before) {
648
975
  const accepted = Array.from(text).filter((char) => this.#pattern.test(char));
649
976
  let reached = false;
977
+ const composing = spare ? this.#composition.composingElement() : null;
650
978
  const slots = this.fieldTargets.filter((field) => {
651
979
  reached ||= field === from;
652
- return reached && this.#isWritable(field);
980
+ return reached && field !== composing && this.#isWritable(field);
653
981
  });
654
982
  const filled = Math.min(accepted.length, slots.length);
655
983
  if (filled === 0) {
@@ -664,14 +992,76 @@ var OtpController = class extends Controller {
664
992
  if (field && char) this.#writeField(field, char);
665
993
  }
666
994
  const last = slots[filled - 1];
667
- if (last) (this.#writableAfter(last) ?? last).focus();
995
+ if (last && composing === null) (this.#writableAfter(last) ?? last).focus();
668
996
  this.#clearError();
669
- this.#syncAndDispatch();
997
+ this.#syncAndDispatch(before);
998
+ }
999
+ #prepareEdit(input, edit = false) {
1000
+ if (!this.#dropHeld) return true;
1001
+ if (edit) this.#holdText(input, this.#committed.get(input) ?? "");
1002
+ const composing = this.#composition.composingElement();
1003
+ if (composing !== null) this.#dropRejected(composing);
1004
+ return this.#reconcileFields() && this.#connected && this.fieldTargets.includes(input) && this.#composition.composingElement() === composing;
1005
+ }
1006
+ #settleResidual(field) {
1007
+ const raw = toHalfWidth(field.value);
1008
+ const previous = this.#committed.get(field) ?? "";
1009
+ const accepts = (text) => Array.from(text).length === 1 && this.#pattern.test(text);
1010
+ const value = raw === "" || accepts(raw) ? raw : accepts(previous) ? previous : "";
1011
+ if (this.#isWritable(field) && this.#composition.composingElement() !== field) {
1012
+ const { selectionStart, selectionEnd } = field;
1013
+ this.#writeField(field, value);
1014
+ if (selectionStart !== null && selectionEnd !== null)
1015
+ field.setSelectionRange(selectionStart, selectionEnd);
1016
+ } else this.#holdText(field, value);
1017
+ }
1018
+ #holdText(field, value) {
1019
+ const retained = { text: field.value, value };
1020
+ this.#retained.set(field, retained);
1021
+ field.setAttribute(this.#retainedMarker, JSON.stringify(retained));
1022
+ this.#committed.set(field, value);
1023
+ }
1024
+ #settledText(field) {
1025
+ const ending = this.#endBaselines.get(field);
1026
+ if (ending && ending.text !== field.value) this.#clearEdits(field);
1027
+ const retained = this.#retained.get(field) ?? this.#readRetained(field);
1028
+ if (retained?.text === field.value) {
1029
+ this.#retained.set(field, retained);
1030
+ const marker = JSON.stringify(retained);
1031
+ if (field.getAttribute(this.#retainedMarker) !== marker)
1032
+ field.setAttribute(this.#retainedMarker, marker);
1033
+ return retained.value;
1034
+ }
1035
+ this.#retained.delete(field);
1036
+ field.removeAttribute(this.#retainedMarker);
1037
+ return field.value;
1038
+ }
1039
+ get #retainedMarker() {
1040
+ return `data-${this.identifier}-interrupted`;
1041
+ }
1042
+ #readRetained(field) {
1043
+ try {
1044
+ const record = JSON.parse(field.getAttribute(this.#retainedMarker) ?? "null");
1045
+ if (typeof record === "object" && record !== null && "text" in record && "value" in record && typeof record.text === "string" && typeof record.value === "string") {
1046
+ return { text: record.text, value: record.value };
1047
+ }
1048
+ } catch {
1049
+ return null;
1050
+ }
1051
+ return null;
1052
+ }
1053
+ #restoreRetained(field) {
1054
+ const retained = this.#retained.get(field);
1055
+ if (retained?.text === field.value && this.#isWritable(field))
1056
+ this.#writeField(field, retained.value);
670
1057
  }
671
1058
  #restore(field) {
672
1059
  this.#writeField(field, this.#committed.get(field) ?? "");
673
1060
  }
674
1061
  #writeField(field, value) {
1062
+ this.#retained.delete(field);
1063
+ this.#endBaselines.delete(field);
1064
+ field.removeAttribute(this.#retainedMarker);
675
1065
  field.value = value;
676
1066
  this.#committed.set(field, value);
677
1067
  this.#markFilled(field, value);
@@ -714,7 +1104,7 @@ var OtpController = class extends Controller {
714
1104
  return fields.length > 0 && fields.every((field) => this.#publishedText(field).length > 0);
715
1105
  }
716
1106
  #publishedText(field) {
717
- return field === this.#composing?.field ? this.#committed.get(field) ?? "" : field.value;
1107
+ return field === this.#composition.composingElement() ? this.#committed.get(field) ?? "" : this.#settledText(field);
718
1108
  }
719
1109
  #sync() {
720
1110
  const combined = this.#combinedValue();
@@ -731,11 +1121,19 @@ var OtpController = class extends Controller {
731
1121
  if (combined.length === 0) return "empty";
732
1122
  return this.#isComplete() ? "complete" : "partial";
733
1123
  }
734
- #syncAndDispatch() {
1124
+ #syncAndDispatch(before) {
735
1125
  const previous = this.#published;
736
- const { value: combined } = this.#sync();
737
- if (previous?.value === combined) return;
1126
+ const current = this.#sync();
1127
+ const combined = current.value;
1128
+ if ((before ?? previous?.value) === combined) {
1129
+ if (previous && statesDiffer(previous, current)) {
1130
+ this.#moves.record();
1131
+ this.dispatch("reconcile", { detail: { value: combined } });
1132
+ }
1133
+ return;
1134
+ }
738
1135
  const token = this.#moves.record();
1136
+ this.#endBaselines.clear();
739
1137
  this.dispatch("change", { detail: { value: combined } });
740
1138
  if (!this.#moves.isLatest(token)) return;
741
1139
  if (this.#isComplete()) {