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
@@ -1,5 +1,12 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
+ function fillTemplate(template, values) {
4
+ return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
5
+ const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
6
+ return replacement === void 0 ? match : String(replacement);
7
+ });
8
+ }
9
+
3
10
  function announce(message, options = {}) {
4
11
  const text = message.trim();
5
12
  if (text.length === 0) return;
@@ -9,11 +16,12 @@ function announce(message, options = {}) {
9
16
  })
10
17
  );
11
18
  }
12
- function fillTemplate(template, values) {
13
- return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
14
- const replacement = values[name];
15
- return replacement === void 0 ? match : String(replacement);
16
- });
19
+
20
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
21
+ if (element.getAttribute(name) === value) return false;
22
+ if (value === null) element.removeAttribute(name);
23
+ else element.setAttribute(name, value);
24
+ return true;
17
25
  }
18
26
 
19
27
  function sharedRegistry(key, create) {
@@ -21,11 +29,15 @@ function sharedRegistry(key, create) {
21
29
  const scope = globalThis;
22
30
  const existing = scope[symbol];
23
31
  if (existing !== void 0) return existing;
24
- const registry = create();
25
- Object.defineProperty(globalThis, symbol, { value: registry });
26
- return registry;
32
+ const registry2 = create();
33
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
34
+ return registry2;
27
35
  }
28
36
 
37
+ var registry = sharedRegistry(
38
+ "stimeo-ui.attribute-lease.registry.v1",
39
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
40
+ );
29
41
  function isAttributeValue(value) {
30
42
  return value === null || typeof value === "string";
31
43
  }
@@ -42,24 +54,46 @@ function readLeaseRecord(element, record) {
42
54
  return void 0;
43
55
  }
44
56
  }
45
- var AttributeLease = class _AttributeLease {
46
- static #registry = sharedRegistry(
47
- "stimeo-ui.attribute-lease.registry.v1",
48
- () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
49
- );
57
+ function attributeStack(element, attribute) {
58
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
59
+ registry.stacks.set(element, byAttribute);
60
+ const known = byAttribute.get(attribute);
61
+ if (known) return known;
62
+ const found = [];
63
+ for (const record of registry.names.get(attribute) ?? []) {
64
+ const read = readLeaseRecord(element, record);
65
+ if (read) found.push({ ...read, record });
66
+ }
67
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
68
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
69
+ lease: null,
70
+ record,
71
+ written,
72
+ original,
73
+ linked: linked && index > 0
74
+ }));
75
+ byAttribute.set(attribute, stack);
76
+ return stack;
77
+ }
78
+ function findHold(stack, lease, record) {
79
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
80
+ }
81
+ var AttributeLease = class {
50
82
  #attribute;
51
83
  #record;
52
84
  #base;
85
+ #preserveAuthor;
53
86
  #held = /* @__PURE__ */ new Set();
54
87
  constructor(attribute, owner, options = {}) {
55
88
  this.#attribute = attribute;
56
89
  this.#record = `data-${owner}-${attribute}-lease`;
57
90
  this.#base = options.base === true;
58
- const names = _AttributeLease.#registry.names;
91
+ this.#preserveAuthor = options.preserveAuthor === true;
92
+ const names = registry.names;
59
93
  names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
60
94
  }
61
95
  write(element, value) {
62
- const stack = this.#stack(element);
96
+ const stack = attributeStack(element, this.#attribute);
63
97
  const hold = this.#take(stack) ?? this.#acquire(element, stack);
64
98
  this.#held.add(element);
65
99
  const shows = !this.#covered(element, stack, hold);
@@ -69,8 +103,8 @@ var AttributeLease = class _AttributeLease {
69
103
  }
70
104
  return(element) {
71
105
  const held = this.#held.delete(element);
72
- const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
73
- const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
106
+ const known = registry.stacks.get(element)?.has(this.#attribute);
107
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
74
108
  const hold = this.#take(stack);
75
109
  if (!hold) return false;
76
110
  const carries = element.getAttribute(this.#attribute) === hold.written;
@@ -88,31 +122,8 @@ var AttributeLease = class _AttributeLease {
88
122
  returnAll() {
89
123
  for (const element of Array.from(this.#held)) this.return(element);
90
124
  }
91
- #stack(element) {
92
- const stacks = _AttributeLease.#registry.stacks;
93
- const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
94
- stacks.set(element, byAttribute);
95
- const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
96
- byAttribute.set(this.#attribute, stack);
97
- return stack;
98
- }
99
- #seed(element) {
100
- const found = [];
101
- for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
102
- const read = readLeaseRecord(element, record);
103
- if (read) found.push({ ...read, record });
104
- }
105
- const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
106
- return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
107
- lease: null,
108
- record,
109
- written,
110
- original,
111
- linked: linked && index > 0
112
- }));
113
- }
114
125
  #take(stack) {
115
- const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
126
+ const hold = findHold(stack, this, this.#record);
116
127
  if (hold) hold.lease = this;
117
128
  return hold;
118
129
  }
@@ -121,7 +132,7 @@ var AttributeLease = class _AttributeLease {
121
132
  const top = stack.at(-1);
122
133
  const bottom = stack[0];
123
134
  const over = top?.written === current ? top : void 0;
124
- const from = this.#base ? bottom : over;
135
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
125
136
  const hold = {
126
137
  lease: this,
127
138
  record: this.#record,
@@ -151,15 +162,11 @@ var AttributeLease = class _AttributeLease {
151
162
  );
152
163
  });
153
164
  for (const [name, recorded] of records) {
154
- if (element.getAttribute(name) === recorded) continue;
155
- if (recorded === null) element.removeAttribute(name);
156
- else element.setAttribute(name, recorded);
165
+ setAttributeIfChanged(element, name, recorded);
157
166
  }
158
167
  }
159
168
  #reflect(element, value) {
160
- if (element.getAttribute(this.#attribute) === value) return;
161
- if (value === null) element.removeAttribute(this.#attribute);
162
- else element.setAttribute(this.#attribute, value);
169
+ setAttributeIfChanged(element, this.#attribute, value);
163
170
  }
164
171
  };
165
172
 
@@ -169,63 +176,182 @@ var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
169
176
  "deleteCompositionText",
170
177
  "deleteByComposition"
171
178
  ]);
179
+ function isCompositionInputType(inputType) {
180
+ return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
181
+ }
182
+ var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
183
+ var textOf = (element) => element.value;
184
+ var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
172
185
  var CompositionTracker = class {
173
186
  #observedTargets = /* @__PURE__ */ new Set();
174
- #activeTargets = /* @__PURE__ */ new Set();
187
+ #composition = null;
175
188
  #onStart;
176
189
  #onEnd;
190
+ #onAbandon;
177
191
  #confirmedTarget = null;
192
+ #keyComposing = false;
193
+ #connected = false;
194
+ #abandoned = null;
178
195
  constructor(options = {}) {
179
196
  this.#onStart = options.onStart;
180
197
  this.#onEnd = options.onEnd;
198
+ this.#onAbandon = options.onAbandon;
181
199
  }
182
200
  observe(target) {
201
+ this.#connected = true;
183
202
  if (this.#observedTargets.has(target)) return;
184
203
  target.addEventListener("compositionstart", this.#handleStart);
185
204
  target.addEventListener("compositionend", this.#handleEnd);
186
- target.addEventListener("keydown", this.#handleKeydown);
205
+ target.addEventListener("keydown", this.#handleKeydown, true);
206
+ target.addEventListener("beforeinput", this.#handleBeforeInput, true);
207
+ target.addEventListener("input", this.#handleInput, true);
208
+ target.addEventListener("blur", this.#handleBlur, true);
209
+ target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
187
210
  this.#observedTargets.add(target);
188
211
  }
189
212
  unobserve(target) {
190
213
  if (!this.#observedTargets.delete(target)) return;
191
- target.removeEventListener("compositionstart", this.#handleStart);
192
- target.removeEventListener("compositionend", this.#handleEnd);
193
- target.removeEventListener("keydown", this.#handleKeydown);
194
- this.#activeTargets.delete(target);
214
+ this.#release(target);
215
+ if (this.#composition?.target === target) this.#letGo();
195
216
  if (this.#confirmedTarget === target) this.#confirmedTarget = null;
196
217
  }
197
218
  disconnect() {
198
- for (const target of this.#observedTargets) {
199
- target.removeEventListener("compositionstart", this.#handleStart);
200
- target.removeEventListener("compositionend", this.#handleEnd);
201
- target.removeEventListener("keydown", this.#handleKeydown);
202
- }
219
+ this.#connected = false;
220
+ for (const target of this.#observedTargets) this.#release(target);
203
221
  this.#observedTargets.clear();
204
- this.#activeTargets.clear();
222
+ this.#letGo();
205
223
  this.#confirmedTarget = null;
224
+ this.#keyComposing = false;
225
+ this.#abandoned = null;
206
226
  }
207
227
  isComposing(event) {
208
- return this.#activeTargets.size > 0 || event?.isComposing === true;
228
+ this.#settle();
229
+ return event?.isComposing === true || this.#composition !== null;
230
+ }
231
+ composingElement() {
232
+ this.#settle();
233
+ return this.#composition?.element ?? null;
234
+ }
235
+ owns(event) {
236
+ const element = this.composingElement();
237
+ return event.isComposing === true || element !== null && element === event.target;
209
238
  }
210
239
  consumesConfirmedInput(event) {
211
240
  const confirmed = this.#confirmedTarget;
212
241
  this.#confirmedTarget = null;
213
242
  if (confirmed === null || confirmed !== event.target) return false;
214
243
  const inputType = event.inputType;
215
- return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
244
+ return !inputType || isCompositionInputType(inputType);
245
+ }
246
+ #release(target) {
247
+ target.removeEventListener("compositionstart", this.#handleStart);
248
+ target.removeEventListener("compositionend", this.#handleEnd);
249
+ target.removeEventListener("keydown", this.#handleKeydown, true);
250
+ target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
251
+ target.removeEventListener("input", this.#handleInput, true);
252
+ target.removeEventListener("blur", this.#handleBlur, true);
253
+ target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
254
+ }
255
+ #letGo() {
256
+ const composition = this.#composition;
257
+ if (composition === null) return;
258
+ if (!holdsFocus(composition.element)) {
259
+ this.#composition = null;
260
+ return;
261
+ }
262
+ queueMicrotask(() => {
263
+ if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
264
+ return;
265
+ }
266
+ this.#composition = null;
267
+ if (!this.#connected) return;
268
+ this.#abandoned = { element: composition.element, inPlace: true };
269
+ this.#runAbandon();
270
+ });
271
+ }
272
+ #settle() {
273
+ const composition = this.#composition;
274
+ if (composition === null) return;
275
+ const { target, element, text } = composition;
276
+ if (target instanceof Node && element instanceof Node && !target.contains(element)) {
277
+ this.#composition = null;
278
+ } else if (textOf(element) !== text) {
279
+ this.#abandon(false);
280
+ }
281
+ }
282
+ #abandon(inPlace) {
283
+ this.#owe(inPlace);
284
+ this.#composition = null;
285
+ queueMicrotask(this.#runAbandon);
216
286
  }
287
+ #owe(inPlace) {
288
+ const composition = this.#composition;
289
+ if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
290
+ }
291
+ #runAbandon = () => {
292
+ const abandoned = this.#abandoned;
293
+ if (abandoned === null) return;
294
+ this.#abandoned = null;
295
+ this.#onAbandon?.(abandoned.element, abandoned.inPlace);
296
+ };
217
297
  #handleStart = (event) => {
218
298
  this.#confirmedTarget = null;
219
- if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
299
+ this.#settle();
300
+ this.#owe(true);
301
+ const target = event.currentTarget;
302
+ const element = event.target;
303
+ this.#composition = { target, element, text: textOf(element) };
304
+ this.#runAbandon();
220
305
  this.#onStart?.(event);
221
306
  };
222
307
  #handleEnd = (event) => {
223
- if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
308
+ this.#runAbandon();
309
+ if (this.#composition?.target === event.currentTarget) this.#composition = null;
224
310
  this.#confirmedTarget = event.target;
225
311
  this.#onEnd?.(event);
226
312
  };
227
- #handleKeydown = () => {
313
+ #handleKeydown = (event) => {
314
+ this.#settle();
315
+ this.#runAbandon();
228
316
  this.#confirmedTarget = null;
317
+ this.#keyComposing = event.isComposing === true;
318
+ };
319
+ #handleBeforeInput = (event) => {
320
+ const keyComposing = this.#keyComposing;
321
+ this.#keyComposing = false;
322
+ this.#settle();
323
+ const { isComposing, inputType } = event;
324
+ if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
325
+ this.#abandon(true);
326
+ }
327
+ this.#runAbandon();
328
+ };
329
+ #handleInput = (event) => {
330
+ const composition = this.#composition;
331
+ const { isTrusted, isComposing, inputType } = event;
332
+ if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
333
+ composition.text = textOf(composition.element);
334
+ } else {
335
+ this.#settle();
336
+ }
337
+ this.#runAbandon();
338
+ };
339
+ #handleBlur = (event) => {
340
+ this.#settle();
341
+ this.#runAbandon();
342
+ const composition = this.#composition;
343
+ if (composition === null || composition.target === composition.element) return;
344
+ const { element } = composition;
345
+ if (!(element instanceof Node) || event.target !== element) return;
346
+ const root = element.getRootNode();
347
+ if (root.activeElement !== element) this.#composition = null;
348
+ };
349
+ #handleBeforeMorphAttribute = (event) => {
350
+ this.#settle();
351
+ const composition = this.#composition;
352
+ if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
353
+ const detail = event.detail;
354
+ if (detail?.attributeName === "value") event.preventDefault();
229
355
  };
230
356
  };
231
357
 
@@ -256,6 +382,9 @@ var MicrotaskCoalescer = class {
256
382
  this.#queued = false;
257
383
  this.#generation += 1;
258
384
  }
385
+ get pending() {
386
+ return this.#queued;
387
+ }
259
388
  schedule() {
260
389
  if (!this.#active || this.#queued) return;
261
390
  this.#queued = true;
@@ -286,6 +415,9 @@ var MorphRenderWatcher = class {
286
415
  schedule() {
287
416
  this.#pass.schedule();
288
417
  }
418
+ get pending() {
419
+ return this.#pass.pending;
420
+ }
289
421
  disconnect() {
290
422
  this.#listeners.dispose();
291
423
  this.#pass.cancel();
@@ -393,36 +525,57 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
393
525
  );
394
526
  #repaint = new MorphRenderWatcher(() => this.#reconcile(true));
395
527
  #composition = new CompositionTracker({
396
- onEnd: (event) => this.#commitFrom(event.currentTarget)
528
+ onStart: (event) => {
529
+ if (event.target === this.#acceptedOwed && this.#field === this.#boundField) {
530
+ this.#reconcileAccepted();
531
+ }
532
+ },
533
+ onEnd: (event) => this.#commitFrom(event.currentTarget),
534
+ onAbandon: () => {
535
+ const first = this.#field === this.#boundField;
536
+ if (this.#reconcileHeld && first) this.#recount(true, this.#owedField);
537
+ else if (first && !this.#repaint.pending) this.#reconcileAccepted();
538
+ else {
539
+ if (!this.#reconcileHeld) this.#acceptedOwed = this.#boundField;
540
+ this.#repaint.schedule();
541
+ }
542
+ }
397
543
  });
544
+ #acceptedOwed = null;
545
+ #reconcileHeld = false;
546
+ #accepted = /* @__PURE__ */ new WeakMap();
398
547
  #announceId = null;
399
548
  #boundField = null;
400
- #lastLength = null;
549
+ #fieldListeners = new ListenerSet();
401
550
  #lastDetail = null;
402
551
  #reflectedOver = null;
403
552
  #onInput = (event) => {
404
553
  const field = this.#boundField;
405
554
  if (!field || event.currentTarget !== field || this.#field !== field) return;
406
555
  if (this.#composition.consumesConfirmedInput(event)) return;
407
- if (this.#composition.isComposing(event)) return;
556
+ const composing = this.#composition.composingElement() === field;
557
+ if (composing || event.isComposing === true) return;
408
558
  this.#commit(field);
409
559
  };
410
560
  connect() {
561
+ this.#observeFields();
411
562
  this.#repaint.observe(this.element);
412
563
  this.#reconcile(false);
413
564
  }
414
565
  disconnect() {
415
566
  this.#repaint.disconnect();
416
- this.#cancelAnnouncement();
417
567
  this.#bindField(null);
568
+ this.#composition.disconnect();
418
569
  this.#clearStateHooks();
419
- this.#lastLength = null;
420
570
  this.#lastDetail = null;
421
571
  }
422
572
  inputTargetConnected() {
573
+ this.#observeFields();
423
574
  this.#repaint.schedule();
424
575
  }
425
- inputTargetDisconnected() {
576
+ inputTargetDisconnected(element) {
577
+ this.#composition.unobserve(element);
578
+ if (element === this.#boundField) this.#composition.observe(element);
426
579
  this.#repaint.schedule();
427
580
  }
428
581
  outputTargetConnected() {
@@ -440,25 +593,33 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
440
593
  modeValueChanged() {
441
594
  this.#repaint.schedule();
442
595
  }
443
- announceTextValueChanged() {
444
- this.#repaint.schedule();
445
- }
446
596
  #reconcile(report) {
447
- const previous = this.#lastDetail;
448
- const owedField = this.#announceId === null ? null : this.#boundField;
449
- this.#cancelAnnouncement();
597
+ const owedField = this.#owedField;
450
598
  this.#bindField(this.#field);
451
- if (this.#composition.isComposing()) return;
599
+ this.#recount(report, owedField);
600
+ }
601
+ get #owedField() {
602
+ return this.#announceId === null ? null : this.#boundField;
603
+ }
604
+ #recount(report, owedField) {
605
+ const owed = this.#acceptedOwed;
606
+ this.#acceptedOwed = null;
607
+ const previous = this.#lastDetail;
452
608
  const field = this.#boundField;
609
+ if (field !== null && this.#composition.composingElement() === field) {
610
+ this.#reconcileHeld = this.#accepted.get(field)?.declared !== this.#declared;
611
+ this.#showAgain(field);
612
+ if (owed === field) this.#reconcileAccepted();
613
+ return;
614
+ }
615
+ this.#reconcileHeld = false;
453
616
  if (!field) {
454
617
  this.#renderEmpty();
455
- this.#lastLength = null;
456
618
  this.#lastDetail = null;
457
619
  return;
458
620
  }
459
621
  const reading = this.#render(field);
460
622
  const detail = this.#detail(reading);
461
- this.#lastLength = reading.length;
462
623
  this.#lastDetail = detail;
463
624
  if (owedField === field) this.#scheduleAnnouncement(reading);
464
625
  if (report && previous && this.#detailsDiffer(previous, detail)) {
@@ -471,20 +632,22 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
471
632
  this.#commit(field);
472
633
  }
473
634
  #commit(field) {
635
+ const held = this.#reconcileHeld;
636
+ this.#reconcileHeld = false;
637
+ const previousLength = this.#accepted.get(field)?.length ?? null;
474
638
  const reading = this.#render(field);
475
- const previousLength = this.#lastLength;
476
639
  const previousDetail = this.#lastDetail;
477
640
  const detail = this.#detail(reading);
478
- this.#lastLength = reading.length;
479
641
  this.#lastDetail = detail;
480
642
  if (previousLength === reading.length) {
481
643
  if (previousDetail && this.#detailsDiffer(previousDetail, detail)) {
482
644
  this.dispatch("reconcile", { detail });
483
645
  }
646
+ if (held && this.#owedField === field) this.#scheduleAnnouncement(reading);
484
647
  return;
485
648
  }
486
649
  this.dispatch("change", { detail });
487
- this.#scheduleAnnouncement(reading);
650
+ if (this.#boundField === field && this.#field === field) this.#scheduleAnnouncement(reading);
488
651
  }
489
652
  #detail(reading) {
490
653
  return {
@@ -508,26 +671,56 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
508
671
  this.#reflectInvalid(field, over);
509
672
  const text = this.#format(length, remaining, max);
510
673
  this.#writeOutput(text);
674
+ this.#accepted.set(field, {
675
+ text,
676
+ over,
677
+ near,
678
+ length,
679
+ detail: { length, remaining, over },
680
+ declared: this.#declared
681
+ });
511
682
  return { length, remaining, over, max, text };
512
683
  }
684
+ #showAgain(field) {
685
+ const shown = this.#accepted.get(field);
686
+ if (shown === void 0) return;
687
+ this.#lastDetail ??= shown.detail;
688
+ this.#toggle("data-over-limit", shown.over);
689
+ this.#toggle("data-near-limit", shown.near);
690
+ this.#reflectInvalid(field, shown.over);
691
+ this.#writeOutput(shown.text);
692
+ }
693
+ #reconcileAccepted() {
694
+ const previous = this.#lastDetail;
695
+ const field = this.#boundField;
696
+ const accepted = field && this.#accepted.get(field)?.detail;
697
+ if (!previous || !accepted || !this.#detailsDiffer(previous, accepted)) return;
698
+ this.#lastDetail = accepted;
699
+ this.dispatch("reconcile", { detail: accepted });
700
+ }
513
701
  #renderEmpty() {
514
702
  this.#reflectedOver = null;
515
703
  this.#clearStateHooks();
516
704
  this.#writeOutput("");
517
705
  }
706
+ #observeFields() {
707
+ for (const input of this.inputTargets) this.#composition.observe(input);
708
+ }
518
709
  #bindField(field) {
519
710
  if (field === this.#boundField) return;
520
711
  const previous = this.#boundField;
521
712
  if (previous) {
522
- previous.removeEventListener("input", this.#onInput);
523
- this.#composition.unobserve(previous);
713
+ this.#fieldListeners.dispose();
714
+ if (previous !== this.element && !this.inputTargets.includes(previous)) {
715
+ this.#composition.unobserve(previous);
716
+ }
524
717
  this.#ariaInvalid.return(previous);
525
718
  }
719
+ this.#cancelAnnouncement();
526
720
  this.#boundField = field;
527
- this.#lastLength = null;
528
721
  this.#reflectedOver = null;
529
722
  if (!field) return;
530
- field.addEventListener("input", this.#onInput);
723
+ this.#fieldListeners.add(field, "input", this.#onInput);
531
724
  this.#composition.observe(field);
532
725
  }
533
726
  #reflectInvalid(field, over) {
@@ -549,16 +742,16 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
549
742
  }
550
743
  #scheduleAnnouncement(reading) {
551
744
  this.#cancelAnnouncement();
552
- const message = fillTemplate(this.announceTextValue, {
745
+ const message = () => fillTemplate(this.announceTextValue, {
553
746
  count: reading.text,
554
747
  length: reading.length,
555
748
  remaining: reading.remaining ?? "",
556
749
  max: reading.max,
557
750
  over: String(reading.over)
558
751
  });
559
- if (message.trim().length === 0) return;
752
+ if (message().trim().length === 0) return;
560
753
  this.#announceId = this.#timeouts.set(() => {
561
- announce(message);
754
+ announce(message());
562
755
  this.#announceId = null;
563
756
  }, _CharacterCounterController.#announceDelay);
564
757
  }
@@ -571,11 +764,7 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
571
764
  this.outputTarget.textContent = text;
572
765
  }
573
766
  #toggle(name, on) {
574
- if (on) {
575
- if (this.element.getAttribute(name) !== "true") this.element.setAttribute(name, "true");
576
- } else if (this.element.hasAttribute(name)) {
577
- this.element.removeAttribute(name);
578
- }
767
+ setAttributeIfChanged(this.element, name, on ? "true" : null);
579
768
  }
580
769
  #clearStateHooks() {
581
770
  this.#toggle("data-over-limit", false);
@@ -588,6 +777,13 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
588
777
  if (this.modeValue === "used" || this.modeValue === "both") return this.modeValue;
589
778
  return "remaining";
590
779
  }
780
+ get #declared() {
781
+ return [
782
+ this.#normalizeCount(this.#safeMax),
783
+ this.#normalizeCount(this.#safeWarnAt),
784
+ this.#mode
785
+ ].join();
786
+ }
591
787
  get #field() {
592
788
  if (this.hasInputTarget) return this.inputTarget;
593
789
  const element = this.element;