stimeo-ui 0.16.0 → 0.17.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 (328) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +8 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +363 -436
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +4 -2
  7. package/dist/controllers/accordion_controller.js +166 -55
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  10. package/dist/controllers/alert_dialog_controller.js +862 -221
  11. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  12. package/dist/controllers/announcer_controller.d.ts +35 -5
  13. package/dist/controllers/announcer_controller.js +135 -198
  14. package/dist/controllers/announcer_controller.js.map +1 -1
  15. package/dist/controllers/aspect_ratio_controller.js +0 -10
  16. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  17. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  18. package/dist/controllers/auto_submit_controller.js +271 -127
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +171 -105
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.js +0 -130
  23. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  24. package/dist/controllers/bulk_select_controller.d.ts +18 -0
  25. package/dist/controllers/bulk_select_controller.js +262 -52
  26. package/dist/controllers/bulk_select_controller.js.map +1 -1
  27. package/dist/controllers/calendar_controller.d.ts +24 -5
  28. package/dist/controllers/calendar_controller.js +175 -209
  29. package/dist/controllers/calendar_controller.js.map +1 -1
  30. package/dist/controllers/carousel_controller.d.ts +34 -7
  31. package/dist/controllers/carousel_controller.js +461 -399
  32. package/dist/controllers/carousel_controller.js.map +1 -1
  33. package/dist/controllers/character_counter_controller.d.ts +17 -1
  34. package/dist/controllers/character_counter_controller.js +246 -156
  35. package/dist/controllers/character_counter_controller.js.map +1 -1
  36. package/dist/controllers/checkbox_controller.d.ts +7 -2
  37. package/dist/controllers/checkbox_controller.js +32 -74
  38. package/dist/controllers/checkbox_controller.js.map +1 -1
  39. package/dist/controllers/clipboard_controller.d.ts +16 -5
  40. package/dist/controllers/clipboard_controller.js +168 -147
  41. package/dist/controllers/clipboard_controller.js.map +1 -1
  42. package/dist/controllers/collapsible_controller.d.ts +16 -4
  43. package/dist/controllers/collapsible_controller.js +301 -120
  44. package/dist/controllers/collapsible_controller.js.map +1 -1
  45. package/dist/controllers/color_picker_controller.d.ts +18 -6
  46. package/dist/controllers/color_picker_controller.js +149 -139
  47. package/dist/controllers/color_picker_controller.js.map +1 -1
  48. package/dist/controllers/combobox_controller.d.ts +23 -5
  49. package/dist/controllers/combobox_controller.js +360 -133
  50. package/dist/controllers/combobox_controller.js.map +1 -1
  51. package/dist/controllers/command_palette_controller.d.ts +45 -14
  52. package/dist/controllers/command_palette_controller.js +970 -358
  53. package/dist/controllers/command_palette_controller.js.map +1 -1
  54. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  55. package/dist/controllers/conditional_fields_controller.js +166 -124
  56. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  57. package/dist/controllers/confirm_controller.d.ts +49 -12
  58. package/dist/controllers/confirm_controller.js +939 -248
  59. package/dist/controllers/confirm_controller.js.map +1 -1
  60. package/dist/controllers/context_menu_controller.d.ts +31 -4
  61. package/dist/controllers/context_menu_controller.js +256 -128
  62. package/dist/controllers/context_menu_controller.js.map +1 -1
  63. package/dist/controllers/count_up_controller.d.ts +13 -1
  64. package/dist/controllers/count_up_controller.js +73 -21
  65. package/dist/controllers/count_up_controller.js.map +1 -1
  66. package/dist/controllers/countdown_controller.d.ts +45 -1
  67. package/dist/controllers/countdown_controller.js +183 -148
  68. package/dist/controllers/countdown_controller.js.map +1 -1
  69. package/dist/controllers/currency_input_controller.d.ts +21 -2
  70. package/dist/controllers/currency_input_controller.js +134 -158
  71. package/dist/controllers/currency_input_controller.js.map +1 -1
  72. package/dist/controllers/data_grid_controller.d.ts +15 -4
  73. package/dist/controllers/data_grid_controller.js +114 -186
  74. package/dist/controllers/data_grid_controller.js.map +1 -1
  75. package/dist/controllers/date_range_picker_controller.d.ts +30 -5
  76. package/dist/controllers/date_range_picker_controller.js +288 -206
  77. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  78. package/dist/controllers/dialog_controller.d.ts +13 -1
  79. package/dist/controllers/dialog_controller.js +858 -223
  80. package/dist/controllers/dialog_controller.js.map +1 -1
  81. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  82. package/dist/controllers/direct_upload_controller.js +73 -153
  83. package/dist/controllers/direct_upload_controller.js.map +1 -1
  84. package/dist/controllers/dirty_form_controller.js +14 -48
  85. package/dist/controllers/dirty_form_controller.js.map +1 -1
  86. package/dist/controllers/dismissible_controller.d.ts +19 -0
  87. package/dist/controllers/dismissible_controller.js +175 -21
  88. package/dist/controllers/dismissible_controller.js.map +1 -1
  89. package/dist/controllers/drawer_controller.d.ts +39 -11
  90. package/dist/controllers/drawer_controller.js +933 -341
  91. package/dist/controllers/drawer_controller.js.map +1 -1
  92. package/dist/controllers/dropdown_controller.d.ts +31 -1
  93. package/dist/controllers/dropdown_controller.js +312 -82
  94. package/dist/controllers/dropdown_controller.js.map +1 -1
  95. package/dist/controllers/editable_controller.d.ts +12 -1
  96. package/dist/controllers/editable_controller.js +158 -68
  97. package/dist/controllers/editable_controller.js.map +1 -1
  98. package/dist/controllers/empty_state_controller.js +67 -53
  99. package/dist/controllers/empty_state_controller.js.map +1 -1
  100. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  101. package/dist/controllers/file_dropzone_controller.js +311 -207
  102. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  103. package/dist/controllers/filter_controller.js +67 -39
  104. package/dist/controllers/filter_controller.js.map +1 -1
  105. package/dist/controllers/flash_controller.d.ts +31 -8
  106. package/dist/controllers/flash_controller.js +172 -314
  107. package/dist/controllers/flash_controller.js.map +1 -1
  108. package/dist/controllers/focus_controller.d.ts +12 -3
  109. package/dist/controllers/focus_controller.js +654 -240
  110. package/dist/controllers/focus_controller.js.map +1 -1
  111. package/dist/controllers/form_field_controller.js +147 -132
  112. package/dist/controllers/form_field_controller.js.map +1 -1
  113. package/dist/controllers/form_validation_controller.js +13 -86
  114. package/dist/controllers/form_validation_controller.js.map +1 -1
  115. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  116. package/dist/controllers/frame_loading_controller.js +264 -213
  117. package/dist/controllers/frame_loading_controller.js.map +1 -1
  118. package/dist/controllers/highlight_controller.d.ts +22 -2
  119. package/dist/controllers/highlight_controller.js +107 -75
  120. package/dist/controllers/highlight_controller.js.map +1 -1
  121. package/dist/controllers/hover_card_controller.d.ts +56 -3
  122. package/dist/controllers/hover_card_controller.js +309 -118
  123. package/dist/controllers/hover_card_controller.js.map +1 -1
  124. package/dist/controllers/idle_controller.d.ts +42 -1
  125. package/dist/controllers/idle_controller.js +267 -101
  126. package/dist/controllers/idle_controller.js.map +1 -1
  127. package/dist/controllers/input_mask_controller.js +71 -72
  128. package/dist/controllers/input_mask_controller.js.map +1 -1
  129. package/dist/controllers/intersection_controller.d.ts +32 -13
  130. package/dist/controllers/intersection_controller.js +147 -106
  131. package/dist/controllers/intersection_controller.js.map +1 -1
  132. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  133. package/dist/controllers/lazy_frame_controller.js +9 -65
  134. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  135. package/dist/controllers/listbox_controller.d.ts +34 -2
  136. package/dist/controllers/listbox_controller.js +306 -204
  137. package/dist/controllers/listbox_controller.js.map +1 -1
  138. package/dist/controllers/local_time_controller.d.ts +1 -1
  139. package/dist/controllers/local_time_controller.js +54 -64
  140. package/dist/controllers/local_time_controller.js.map +1 -1
  141. package/dist/controllers/masonry_controller.d.ts +11 -3
  142. package/dist/controllers/masonry_controller.js +70 -93
  143. package/dist/controllers/masonry_controller.js.map +1 -1
  144. package/dist/controllers/menu_controller.d.ts +32 -7
  145. package/dist/controllers/menu_controller.js +214 -156
  146. package/dist/controllers/menu_controller.js.map +1 -1
  147. package/dist/controllers/menubar_controller.d.ts +4 -4
  148. package/dist/controllers/menubar_controller.js +170 -329
  149. package/dist/controllers/menubar_controller.js.map +1 -1
  150. package/dist/controllers/meter_controller.d.ts +20 -0
  151. package/dist/controllers/meter_controller.js +151 -67
  152. package/dist/controllers/meter_controller.js.map +1 -1
  153. package/dist/controllers/multi_select_controller.d.ts +32 -4
  154. package/dist/controllers/multi_select_controller.js +593 -358
  155. package/dist/controllers/multi_select_controller.js.map +1 -1
  156. package/dist/controllers/navigation_menu_controller.d.ts +8 -1
  157. package/dist/controllers/navigation_menu_controller.js +124 -232
  158. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  159. package/dist/controllers/nested_form_controller.d.ts +10 -0
  160. package/dist/controllers/nested_form_controller.js +352 -182
  161. package/dist/controllers/nested_form_controller.js.map +1 -1
  162. package/dist/controllers/network_status_controller.d.ts +23 -0
  163. package/dist/controllers/network_status_controller.js +242 -54
  164. package/dist/controllers/network_status_controller.js.map +1 -1
  165. package/dist/controllers/number_input_controller.d.ts +26 -2
  166. package/dist/controllers/number_input_controller.js +320 -263
  167. package/dist/controllers/number_input_controller.js.map +1 -1
  168. package/dist/controllers/optimistic_controller.js +16 -76
  169. package/dist/controllers/optimistic_controller.js.map +1 -1
  170. package/dist/controllers/otp_controller.d.ts +20 -4
  171. package/dist/controllers/otp_controller.js +198 -193
  172. package/dist/controllers/otp_controller.js.map +1 -1
  173. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  174. package/dist/controllers/overflow_indicator_controller.js +116 -113
  175. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  176. package/dist/controllers/overflow_menu_controller.d.ts +48 -11
  177. package/dist/controllers/overflow_menu_controller.js +343 -283
  178. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  179. package/dist/controllers/pagination_controller.d.ts +35 -6
  180. package/dist/controllers/pagination_controller.js +295 -119
  181. package/dist/controllers/pagination_controller.js.map +1 -1
  182. package/dist/controllers/password_reveal_controller.d.ts +45 -8
  183. package/dist/controllers/password_reveal_controller.js +389 -121
  184. package/dist/controllers/password_reveal_controller.js.map +1 -1
  185. package/dist/controllers/password_strength_controller.d.ts +14 -5
  186. package/dist/controllers/password_strength_controller.js +99 -153
  187. package/dist/controllers/password_strength_controller.js.map +1 -1
  188. package/dist/controllers/persist_controller.d.ts +11 -4
  189. package/dist/controllers/persist_controller.js +237 -103
  190. package/dist/controllers/persist_controller.js.map +1 -1
  191. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  192. package/dist/controllers/pointer_drag_controller.js +185 -165
  193. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  194. package/dist/controllers/popover_controller.d.ts +38 -2
  195. package/dist/controllers/popover_controller.js +236 -97
  196. package/dist/controllers/popover_controller.js.map +1 -1
  197. package/dist/controllers/portal_controller.d.ts +44 -4
  198. package/dist/controllers/portal_controller.js +209 -76
  199. package/dist/controllers/portal_controller.js.map +1 -1
  200. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  201. package/dist/controllers/preview_guard_controller.js +232 -116
  202. package/dist/controllers/preview_guard_controller.js.map +1 -1
  203. package/dist/controllers/progress_controller.d.ts +11 -0
  204. package/dist/controllers/progress_controller.js +125 -56
  205. package/dist/controllers/progress_controller.js.map +1 -1
  206. package/dist/controllers/radio_group_controller.d.ts +14 -5
  207. package/dist/controllers/radio_group_controller.js +117 -137
  208. package/dist/controllers/radio_group_controller.js.map +1 -1
  209. package/dist/controllers/range_slider_controller.d.ts +43 -3
  210. package/dist/controllers/range_slider_controller.js +192 -105
  211. package/dist/controllers/range_slider_controller.js.map +1 -1
  212. package/dist/controllers/rating_controller.d.ts +20 -4
  213. package/dist/controllers/rating_controller.js +418 -207
  214. package/dist/controllers/rating_controller.js.map +1 -1
  215. package/dist/controllers/read_more_controller.d.ts +2 -0
  216. package/dist/controllers/read_more_controller.js +134 -63
  217. package/dist/controllers/read_more_controller.js.map +1 -1
  218. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  219. package/dist/controllers/reading_progress_controller.js +147 -135
  220. package/dist/controllers/reading_progress_controller.js.map +1 -1
  221. package/dist/controllers/relative_time_controller.d.ts +18 -4
  222. package/dist/controllers/relative_time_controller.js +145 -110
  223. package/dist/controllers/relative_time_controller.js.map +1 -1
  224. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  225. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  226. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  227. package/dist/controllers/resizable_controller.d.ts +28 -2
  228. package/dist/controllers/resizable_controller.js +255 -139
  229. package/dist/controllers/resizable_controller.js.map +1 -1
  230. package/dist/controllers/roving_controller.js +39 -84
  231. package/dist/controllers/roving_controller.js.map +1 -1
  232. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  233. package/dist/controllers/scroll_area_controller.js +428 -225
  234. package/dist/controllers/scroll_area_controller.js.map +1 -1
  235. package/dist/controllers/scroll_restore_controller.js +0 -53
  236. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  237. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  238. package/dist/controllers/scroll_visibility_controller.js +395 -145
  239. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  240. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  241. package/dist/controllers/scrollspy_controller.js +181 -242
  242. package/dist/controllers/scrollspy_controller.js.map +1 -1
  243. package/dist/controllers/separator_controller.d.ts +19 -4
  244. package/dist/controllers/separator_controller.js +267 -140
  245. package/dist/controllers/separator_controller.js.map +1 -1
  246. package/dist/controllers/sidebar_controller.d.ts +64 -7
  247. package/dist/controllers/sidebar_controller.js +1023 -386
  248. package/dist/controllers/sidebar_controller.js.map +1 -1
  249. package/dist/controllers/skeleton_controller.d.ts +23 -0
  250. package/dist/controllers/skeleton_controller.js +248 -97
  251. package/dist/controllers/skeleton_controller.js.map +1 -1
  252. package/dist/controllers/slider_controller.d.ts +27 -1
  253. package/dist/controllers/slider_controller.js +159 -79
  254. package/dist/controllers/slider_controller.js.map +1 -1
  255. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  256. package/dist/controllers/smart_sticky_header_controller.js +162 -63
  257. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  258. package/dist/controllers/sortable_controller.js +0 -112
  259. package/dist/controllers/sortable_controller.js.map +1 -1
  260. package/dist/controllers/spinner_controller.d.ts +39 -4
  261. package/dist/controllers/spinner_controller.js +277 -169
  262. package/dist/controllers/spinner_controller.js.map +1 -1
  263. package/dist/controllers/step_indicator_controller.d.ts +5 -0
  264. package/dist/controllers/step_indicator_controller.js +99 -46
  265. package/dist/controllers/step_indicator_controller.js.map +1 -1
  266. package/dist/controllers/stepper_controller.d.ts +12 -3
  267. package/dist/controllers/stepper_controller.js +114 -73
  268. package/dist/controllers/stepper_controller.js.map +1 -1
  269. package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
  270. package/dist/controllers/stick_to_bottom_controller.js +262 -138
  271. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  272. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  273. package/dist/controllers/sticky_observer_controller.js +214 -49
  274. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  275. package/dist/controllers/submit_once_controller.d.ts +23 -8
  276. package/dist/controllers/submit_once_controller.js +294 -205
  277. package/dist/controllers/submit_once_controller.js.map +1 -1
  278. package/dist/controllers/switch_controller.d.ts +12 -1
  279. package/dist/controllers/switch_controller.js +33 -60
  280. package/dist/controllers/switch_controller.js.map +1 -1
  281. package/dist/controllers/tabs_controller.d.ts +2 -2
  282. package/dist/controllers/tabs_controller.js +35 -29
  283. package/dist/controllers/tabs_controller.js.map +1 -1
  284. package/dist/controllers/tags_input_controller.d.ts +12 -0
  285. package/dist/controllers/tags_input_controller.js +136 -145
  286. package/dist/controllers/tags_input_controller.js.map +1 -1
  287. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  288. package/dist/controllers/textarea_autosize_controller.js +71 -58
  289. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  290. package/dist/controllers/theme_controller.d.ts +21 -9
  291. package/dist/controllers/theme_controller.js +130 -112
  292. package/dist/controllers/theme_controller.js.map +1 -1
  293. package/dist/controllers/time_picker_controller.d.ts +18 -1
  294. package/dist/controllers/time_picker_controller.js +146 -80
  295. package/dist/controllers/time_picker_controller.js.map +1 -1
  296. package/dist/controllers/toast_controller.d.ts +36 -11
  297. package/dist/controllers/toast_controller.js +186 -297
  298. package/dist/controllers/toast_controller.js.map +1 -1
  299. package/dist/controllers/toggle_group_controller.d.ts +12 -3
  300. package/dist/controllers/toggle_group_controller.js +95 -136
  301. package/dist/controllers/toggle_group_controller.js.map +1 -1
  302. package/dist/controllers/toolbar_controller.js +68 -98
  303. package/dist/controllers/toolbar_controller.js.map +1 -1
  304. package/dist/controllers/tooltip_controller.d.ts +45 -3
  305. package/dist/controllers/tooltip_controller.js +158 -124
  306. package/dist/controllers/tooltip_controller.js.map +1 -1
  307. package/dist/controllers/transition_controller.d.ts +22 -4
  308. package/dist/controllers/transition_controller.js +123 -119
  309. package/dist/controllers/transition_controller.js.map +1 -1
  310. package/dist/controllers/tree_view_controller.d.ts +21 -11
  311. package/dist/controllers/tree_view_controller.js +150 -345
  312. package/dist/controllers/tree_view_controller.js.map +1 -1
  313. package/dist/index.d.ts +8 -5
  314. package/dist/index.js +6370 -9346
  315. package/dist/index.js.map +1 -1
  316. package/dist/inspector/cli.d.ts +23 -3
  317. package/dist/inspector/cli.js +92 -37
  318. package/dist/inspector/cli.js.map +1 -1
  319. package/dist/inspector/cli_bin.js +92 -56
  320. package/dist/inspector/cli_bin.js.map +1 -1
  321. package/dist/inspector/examples.json +8 -8
  322. package/dist/inspector/manifest.json +787 -100
  323. package/dist/positioning/index.d.ts +8 -0
  324. package/dist/positioning/index.js +148 -47
  325. package/dist/positioning/index.js.map +1 -1
  326. package/package.json +6 -6
  327. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  328. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -25,11 +25,20 @@ import { Controller } from '@hotwired/stimulus';
25
25
  * `prev`/`next` must be real `<button>` elements: the boundary state is applied
26
26
  * through the native `disabled` property, which a `<div>` or `<a>` does not honor.
27
27
  *
28
- * `change` dispatches `{ page: number, total: number, previous: number }`, and
29
- * `reconcile` dispatches the same `{ page: number, total: number, previous: number }`
28
+ * `change` dispatches `{ page: number, total: number, previous: number, reason: StateReason }`;
29
+ * `reconcile` dispatches `{ page: number, total: number, previous: number }`
30
30
  * — `previous` is the page shown before — when a change the page made moves the
31
31
  * current page.
32
32
  *
33
+ * User `change` reports compare the resulting state with the last published
34
+ * state. A pending page write handled in the same script joins that confirmation;
35
+ * a browser-delivered listener may settle it first as `reconcile`. Confirming
36
+ * the last published state reports nothing.
37
+ *
38
+ * A synchronous subscriber that confirms another state replaces reports still
39
+ * pending for the outer confirmation. Reading state or confirming it unchanged
40
+ * does not replace them. An event already being dispatched cannot be recalled.
41
+ *
33
42
  * @remarks
34
43
  * Behavior only — each control is in the natural Tab order (no roving). When a
35
44
  * boundary disables the button that currently has focus, focus is moved first so
@@ -52,7 +61,7 @@ import { Controller } from '@hotwired/stimulus';
52
61
  * Once connected, a batch that moves the current page away from the one shown
53
62
  * dispatches `stimeo--pagination:reconcile` once; the initial render reports
54
63
  * nothing.
55
- * - Every navigation dispatches `stimeo--pagination:change`, whose `detail.total`
64
+ * - A navigation that moves the published page dispatches `stimeo--pagination:change`, whose `detail.total`
56
65
  * is the same clamped total the boundary state is derived from.
57
66
  *
58
67
  * The boundary `disabled` is **owned**: the controller marks what it disabled
@@ -76,11 +85,21 @@ declare class PaginationController extends Controller<HTMLElement> {
76
85
  default: number;
77
86
  };
78
87
  };
88
+ static valueConstraints: {
89
+ page: {
90
+ readonly finite: true;
91
+ };
92
+ total: {
93
+ readonly finite: true;
94
+ };
95
+ };
79
96
  static actions: readonly ["next", "prev", "select"];
80
97
  static events: readonly ["change", "reconcile"];
81
98
  readonly pageTargets: HTMLElement[];
82
99
  readonly prevTarget: HTMLButtonElement;
100
+ readonly prevTargets: HTMLButtonElement[];
83
101
  readonly nextTarget: HTMLButtonElement;
102
+ readonly nextTargets: HTMLButtonElement[];
84
103
  readonly hasPrevTarget: boolean;
85
104
  readonly hasNextTarget: boolean;
86
105
  pageValue: number;
@@ -97,14 +116,24 @@ declare class PaginationController extends Controller<HTMLElement> {
97
116
  pageTargetConnected(): void;
98
117
  /** Syncs a `prev` button appended/replaced at runtime. */
99
118
  prevTargetConnected(): void;
119
+ /**
120
+ * Gives a `prev` that is no longer a target back the boundary `disabled` this
121
+ * controller set, and brings the one that stays to the current boundary state.
122
+ */
123
+ prevTargetDisconnected(button: HTMLButtonElement): void;
100
124
  /** Syncs a `next` button appended/replaced at runtime. */
101
125
  nextTargetConnected(): void;
126
+ /**
127
+ * Gives a `next` that is no longer a target back the boundary `disabled` this
128
+ * controller set, and brings the one that stays to the current boundary state.
129
+ */
130
+ nextTargetDisconnected(button: HTMLButtonElement): void;
102
131
  /** Makes the clicked page button (its `data-page`) current. */
103
- select(event: Event): void;
132
+ select(source: Event | HTMLElement): void;
104
133
  /** Steps to the previous page. */
105
- prev(): void;
134
+ prev(event?: Event): void;
106
135
  /** Steps to the next page. */
107
- next(): void;
136
+ next(event?: Event): void;
108
137
  }
109
138
 
110
139
  export { PaginationController };
@@ -1,8 +1,27 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/pagination_controller.ts
3
+ var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
4
+ var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
5
+ function stateReasonFor(event) {
6
+ if (!event) return "api";
7
+ if (FOCUS_EVENTS.has(event.type)) return "focus";
8
+ if (POINTER_EVENTS.has(event.type)) return "pointer";
9
+ return "user";
10
+ }
11
+
12
+ function actionSource(source) {
13
+ if (source instanceof HTMLElement) {
14
+ return { event: null, host: source, origin: source, reason: "api" };
15
+ }
16
+ const event = source ?? null;
17
+ return {
18
+ event,
19
+ host: event?.currentTarget instanceof Element ? event.currentTarget : null,
20
+ origin: event?.target instanceof Element ? event.target : null,
21
+ reason: stateReasonFor(event)
22
+ };
23
+ }
4
24
 
5
- // src/utils/focus_candidate.ts
6
25
  function inheritsFieldsetDisabled(control) {
7
26
  let fieldset = control.closest("fieldset[disabled]");
8
27
  while (fieldset) {
@@ -12,35 +31,52 @@ function inheritsFieldsetDisabled(control) {
12
31
  }
13
32
  return false;
14
33
  }
34
+ function unslotted(element) {
35
+ return element.assignedSlot === null && element.parentElement?.shadowRoot != null;
36
+ }
37
+ function flatTreeParent(element) {
38
+ if (unslotted(element)) return null;
39
+ const parent = element.assignedSlot ?? element.parentNode;
40
+ if (parent instanceof ShadowRoot) return parent.host;
41
+ return parent instanceof Element ? parent : null;
42
+ }
15
43
  function canTakeFocus(element) {
16
- if (element.closest("[hidden], [inert]")) return false;
44
+ for (let node = element; node; node = flatTreeParent(node)) {
45
+ if (unslotted(node) || node.matches("[hidden], [inert]")) return false;
46
+ }
17
47
  if (element instanceof HTMLInputElement && element.type === "hidden") return false;
18
48
  if (!("disabled" in element)) return true;
19
49
  if (element.disabled) return false;
20
50
  return !inheritsFieldsetDisabled(element);
21
51
  }
22
52
 
23
- // src/utils/microtask_coalescer.ts
53
+ var ListenerSet = class {
54
+ #abort = new AbortController();
55
+ add(target, type, handler, options) {
56
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
57
+ }
58
+ dispose() {
59
+ this.#abort.abort();
60
+ this.#abort = new AbortController();
61
+ }
62
+ };
63
+
24
64
  var MicrotaskCoalescer = class {
25
65
  #run;
26
66
  #queued = false;
27
67
  #active = false;
28
68
  #generation = 0;
29
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
30
69
  constructor(run) {
31
70
  this.#run = run;
32
71
  }
33
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
34
72
  activate() {
35
73
  this.#active = true;
36
74
  }
37
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
38
75
  cancel() {
39
76
  this.#active = false;
40
77
  this.#queued = false;
41
78
  this.#generation += 1;
42
79
  }
43
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
44
80
  schedule() {
45
81
  if (!this.#active || this.#queued) return;
46
82
  this.#queued = true;
@@ -53,171 +89,304 @@ var MicrotaskCoalescer = class {
53
89
  }
54
90
  };
55
91
 
56
- // src/utils/before_cache_reset.ts
57
- var BeforeCacheReset = class _BeforeCacheReset {
58
- /** Every subscribed instance, iterated by the one shared document listener. */
59
- static #subscribers = /* @__PURE__ */ new Set();
60
- /** The shared listener; installed while at least one instance is subscribed. */
61
- static #onBeforeCache = () => {
62
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
63
- };
64
- #rewind;
65
- /** @param rewind - the pass that returns this controller's state to its initial form. */
66
- constructor(rewind) {
67
- this.#rewind = rewind;
92
+ var MorphRenderWatcher = class {
93
+ #listeners = new ListenerSet();
94
+ #pass;
95
+ #element = null;
96
+ #onMorph = () => this.#pass.schedule();
97
+ constructor(run) {
98
+ this.#pass = new MicrotaskCoalescer(run);
68
99
  }
69
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
70
- activate() {
71
- const first = _BeforeCacheReset.#subscribers.size === 0;
72
- _BeforeCacheReset.#subscribers.add(this);
73
- if (first) {
74
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
75
- }
100
+ observe(element) {
101
+ if (this.#element === element) return;
102
+ this.disconnect();
103
+ this.#element = element;
104
+ this.#pass.activate();
105
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
76
106
  }
77
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
78
- deactivate() {
79
- _BeforeCacheReset.#subscribers.delete(this);
80
- if (_BeforeCacheReset.#subscribers.size > 0) return;
81
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
107
+ schedule() {
108
+ this.#pass.schedule();
109
+ }
110
+ disconnect() {
111
+ this.#listeners.dispose();
112
+ this.#pass.cancel();
113
+ this.#element = null;
114
+ }
115
+ };
116
+
117
+ var MoveCounter = class {
118
+ #sequence = 0;
119
+ record() {
120
+ this.#sequence += 1;
121
+ return this.#sequence;
122
+ }
123
+ isLatest(token) {
124
+ return token === this.#sequence;
82
125
  }
83
126
  };
84
127
 
85
- // src/utils/tabindex_loan.ts
86
- var TabindexLoan = class {
128
+ var NUMBER_BOUNDS = {
129
+ finite: { finite: true }};
130
+ function matchesNumberBounds(value, bounds) {
131
+ if (!Number.isFinite(value)) {
132
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
133
+ if (direction === null) return false;
134
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
135
+ }
136
+ if (bounds.min !== void 0 && value < bounds.min) return false;
137
+ if (bounds.max !== void 0 && value > bounds.max) return false;
138
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
139
+ if (bounds.integer && !Number.isInteger(value)) return false;
140
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
141
+ return true;
142
+ }
143
+
144
+ function readNumber(raw, fallback, bounds) {
145
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
146
+ }
147
+
148
+ var NumberValueReader = class {
149
+ #lastRejected = /* @__PURE__ */ new Map();
150
+ read(owner, name, raw, fallback, bounds) {
151
+ const resolved = readNumber(raw, fallback, bounds);
152
+ if (matchesNumberBounds(raw, bounds)) {
153
+ this.#lastRejected.delete(name);
154
+ return resolved;
155
+ }
156
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
157
+ const literal = owner.element.getAttribute(attribute);
158
+ if (literal === null) {
159
+ this.#lastRejected.delete(name);
160
+ return resolved;
161
+ }
162
+ if (this.#lastRejected.get(name) !== literal) {
163
+ this.#lastRejected.set(name, literal);
164
+ console.warn(
165
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
166
+ );
167
+ }
168
+ return resolved;
169
+ }
170
+ };
171
+
172
+ function queryOpenDescendants(root, selector) {
173
+ const matches = [];
174
+ const roots = [root];
175
+ for (const scope of roots) {
176
+ matches.push(...scope.querySelectorAll(selector));
177
+ if (scope.nodeType === Node.ELEMENT_NODE) {
178
+ const shadow = scope.shadowRoot;
179
+ if (shadow) roots.push(shadow);
180
+ }
181
+ for (const element of scope.querySelectorAll("*")) {
182
+ if (element.shadowRoot) roots.push(element.shadowRoot);
183
+ }
184
+ }
185
+ return matches;
186
+ }
187
+
188
+ function sharedRegistry(key, create) {
189
+ const symbol = Symbol.for(key);
190
+ const scope = globalThis;
191
+ const existing = scope[symbol];
192
+ if (existing !== void 0) return existing;
193
+ const registry = create();
194
+ Object.defineProperty(globalThis, symbol, { value: registry });
195
+ return registry;
196
+ }
197
+
198
+ var TabindexLoan = class _TabindexLoan {
199
+ static #registry = sharedRegistry(
200
+ "stimeo-ui.tabindex-loan.registry.v1",
201
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
202
+ );
87
203
  #value;
204
+ #record;
88
205
  #lent = /* @__PURE__ */ new Set();
89
- /** Returns live loans before Turbo can copy them into its page snapshot. */
90
- #beforeCache = new BeforeCacheReset(() => this.returnAll());
91
- /**
92
- * @param value - the `tabindex` to lend. `"-1"` (the default) is
93
- * programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
94
- * which a scroll region with no focusable content of its own needs.
95
- */
96
- constructor(value = "-1") {
206
+ constructor(value, owner) {
97
207
  this.#value = value;
208
+ this.#record = `data-${owner}-tabindex-loan`;
209
+ _TabindexLoan.#registry.names.add(this.#record);
98
210
  }
99
- /** Lends `element` the value; no-ops when it already carries a `tabindex`. */
100
211
  lend(element) {
101
- if (element.hasAttribute("tabindex")) return;
102
- element.setAttribute("tabindex", this.#value);
212
+ const stack = this.#stack(element);
213
+ const current = element.getAttribute("tabindex");
214
+ const held = stack.find((hold) => hold.loan === this) ?? stack.find(
215
+ (hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
216
+ );
217
+ const linked = stack.at(-1)?.value === current;
218
+ if (!held && current !== null && !linked) return;
103
219
  this.#lent.add(element);
104
- this.#beforeCache.activate();
220
+ if (held) held.loan = this;
221
+ else stack.push({ loan: this, record: this.#record, value: this.#value, linked });
222
+ this.#mark(element, stack);
223
+ if (current === null || !held && current !== this.#value) {
224
+ element.setAttribute("tabindex", this.#value);
225
+ }
105
226
  }
106
- /** Takes back every loan whose value is still the one that was lent. */
107
227
  returnAll() {
108
228
  for (const element of this.#lent) {
109
- if (element.getAttribute("tabindex") === this.#value) element.removeAttribute("tabindex");
229
+ const stack = this.#stack(element);
230
+ const hold = stack.find((held) => held.loan === this);
231
+ if (hold) this.#release(element, stack, hold);
110
232
  }
111
233
  this.#lent.clear();
112
- this.#beforeCache.deactivate();
234
+ }
235
+ reclaim(element) {
236
+ if (element.getAttribute(this.#record) !== this.#value) return;
237
+ const stack = this.#stack(element);
238
+ const hold = stack.find((held) => held.loan === null && held.record === this.#record);
239
+ if (hold && !element.matches(":focus")) this.#release(element, stack, hold);
240
+ }
241
+ reclaimWithin(root) {
242
+ for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
243
+ this.reclaim(element);
244
+ }
245
+ }
246
+ #stack(element) {
247
+ const stacks = _TabindexLoan.#registry.stacks;
248
+ const stack = stacks.get(element) ?? this.#seed(element);
249
+ stacks.set(element, stack);
250
+ return stack;
251
+ }
252
+ #seed(element) {
253
+ const current = element.getAttribute("tabindex");
254
+ const holds = [];
255
+ for (const record of _TabindexLoan.#registry.names) {
256
+ const value = element.getAttribute(record);
257
+ if (value !== null) holds.push({ loan: null, record, value, linked: true });
258
+ }
259
+ holds.sort((one, other) => Number(one.value === current) - Number(other.value === current));
260
+ for (const [index, hold] of holds.entries()) hold.linked = index > 0;
261
+ return holds;
262
+ }
263
+ #release(element, stack, hold) {
264
+ const current = element.getAttribute("tabindex");
265
+ const index = stack.indexOf(hold);
266
+ const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
267
+ stack.splice(index, 1);
268
+ const above = stack[index];
269
+ if (above) above.linked &&= hold.linked;
270
+ this.#mark(element, stack);
271
+ if (!owns) return;
272
+ const below = hold.linked ? stack[index - 1] : void 0;
273
+ if (!below) element.removeAttribute("tabindex");
274
+ else if (below.value !== current) element.setAttribute("tabindex", below.value);
275
+ }
276
+ #mark(element, stack) {
277
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
278
+ for (const hold of stack) records.set(hold.record, hold.value);
279
+ for (const [name, recorded] of records) {
280
+ if (element.getAttribute(name) === recorded) continue;
281
+ if (recorded === null) element.removeAttribute(name);
282
+ else element.setAttribute(name, recorded);
283
+ }
113
284
  }
114
285
  };
115
286
 
116
- // src/controllers/pagination_controller.ts
117
- var PaginationController = class extends Controller {
287
+ function targetSelector(identifier, name) {
288
+ return `[data-${identifier}-target~="${name}"]`;
289
+ }
290
+
291
+ var PaginationController = class _PaginationController extends Controller {
292
+ #numbers = new NumberValueReader();
293
+ #moves = new MoveCounter();
294
+ #moveToken = 0;
118
295
  static targets = ["page", "prev", "next"];
119
296
  static values = {
120
297
  page: { type: Number, default: 1 },
121
298
  total: { type: Number, default: 1 }
122
299
  };
300
+ static valueConstraints = {
301
+ page: NUMBER_BOUNDS.finite,
302
+ total: NUMBER_BOUNDS.finite
303
+ };
123
304
  static actions = ["next", "prev", "select"];
124
305
  static events = ["change", "reconcile"];
125
- /** Marks a `disabled` this controller applied at a boundary, in its own namespace. */
126
306
  get #boundaryAttribute() {
127
307
  return `data-${this.identifier}-boundary-disabled`;
128
308
  }
129
- /** The `tabindex` this instance lends the root for the focus fallback. */
130
- #tabindex = new TabindexLoan();
131
- /**
132
- * Collapses the Value and target callbacks of one mutation into one pass, and
133
- * refuses the ones Stimulus delivers before `connect()`, which renders itself.
134
- */
135
- #repaint = new MicrotaskCoalescer(() => this.#reconcilePage());
136
- /** The page shown last, which the next move of the current page is measured from. */
309
+ #tabindex = new TabindexLoan("-1", this.identifier);
310
+ #repaint = new MorphRenderWatcher(() => this.#reconcilePage());
137
311
  #shown = 1;
138
- /** Renders the initial state from the clamped `page` and `total`. */
139
312
  connect() {
140
- this.#repaint.activate();
313
+ this.#tabindex.reclaim(this.element);
314
+ this.#repaint.observe(this.element);
141
315
  this.#shown = this.#page;
142
316
  this.#render();
143
317
  }
144
- /** Drops a pending pass and reverts the one attribute added outside the state hooks. */
145
318
  disconnect() {
146
- this.#repaint.cancel();
319
+ this.#repaint.disconnect();
147
320
  this.#tabindex.returnAll();
148
321
  }
149
- /** Re-renders when application code (or a Turbo morph) changes `page` at runtime. */
150
322
  pageValueChanged() {
151
323
  this.#repaint.schedule();
152
324
  }
153
- /** Re-renders when application code (or a Turbo morph) changes `total` at runtime. */
154
325
  totalValueChanged() {
155
326
  this.#repaint.schedule();
156
327
  }
157
- /** Syncs a page button appended/replaced at runtime (the consumer owns the list). */
158
328
  pageTargetConnected() {
159
329
  this.#repaint.schedule();
160
330
  }
161
- /** Syncs a `prev` button appended/replaced at runtime. */
162
331
  prevTargetConnected() {
163
332
  this.#repaint.schedule();
164
333
  }
165
- /** Syncs a `next` button appended/replaced at runtime. */
334
+ prevTargetDisconnected(button) {
335
+ this.#releaseDeparted(button, this.prevTargets);
336
+ this.#repaint.schedule();
337
+ }
166
338
  nextTargetConnected() {
167
339
  this.#repaint.schedule();
168
340
  }
169
- /** Makes the clicked page button (its `data-page`) current. */
170
- select(event) {
171
- const button = event.currentTarget;
341
+ nextTargetDisconnected(button) {
342
+ this.#releaseDeparted(button, this.nextTargets);
343
+ this.#repaint.schedule();
344
+ }
345
+ select(source) {
346
+ const { host, origin, reason } = actionSource(source);
347
+ const button = host?.closest(targetSelector(this.identifier, "page"));
348
+ if (!button || !this.pageTargets.includes(button)) return;
349
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
350
+ return;
172
351
  const raw = button.dataset.page;
173
352
  if (raw === void 0 || raw.trim() === "") return;
174
353
  const page = Number(raw);
175
354
  if (!Number.isInteger(page)) return;
176
- this.#goto(page);
355
+ this.#goto(page, reason);
177
356
  }
178
- /** Steps to the previous page. */
179
- prev() {
180
- this.#goto(this.#page - 1);
357
+ prev(event) {
358
+ this.#goto(this.#page - 1, stateReasonFor(event));
181
359
  }
182
- /** Steps to the next page. */
183
- next() {
184
- this.#goto(this.#page + 1);
360
+ next(event) {
361
+ this.#goto(this.#page + 1, stateReasonFor(event));
185
362
  }
186
- /** Moves to `page` (clamped to `[1, total]`), re-renders, and dispatches `change`. */
187
- #goto(page) {
188
- if (!Number.isFinite(page)) return;
189
- const previous = this.#page;
363
+ #goto(page, reason) {
364
+ const previous = this.#shown;
190
365
  const target = this.#clamp(page);
191
- if (target === previous) return;
366
+ if (target === previous && target === this.#page) return;
192
367
  this.pageValue = target;
193
368
  this.#shown = target;
369
+ const changed = target !== previous;
370
+ if (changed) this.#moveToken = this.#moves.record();
371
+ const token = this.#moveToken;
194
372
  this.#render();
373
+ if (!changed || !this.#moves.isLatest(token)) return;
195
374
  this.dispatch("change", {
196
- detail: { page: target, total: this.#total, previous }
375
+ detail: { page: target, total: this.#total, previous, reason }
197
376
  });
198
377
  }
199
- /**
200
- * Renders one settled batch of Value and target changes, and reports a current
201
- * page that moved from the one shown before as `reconcile`.
202
- */
203
378
  #reconcilePage() {
204
379
  const previous = this.#shown;
205
380
  const page = this.#page;
206
381
  this.#shown = page;
382
+ const token = this.#moveToken;
207
383
  this.#render();
208
- if (page !== previous) {
384
+ if (page !== previous && this.#moves.isLatest(token)) {
209
385
  this.dispatch("reconcile", { detail: { page, total: this.#total, previous } });
210
386
  }
211
387
  }
212
- /**
213
- * Syncs `aria-current` on the page buttons and the prev/next `disabled` state.
214
- *
215
- * It reads `page` and `total` through their clamps and never writes either Value,
216
- * so a declaration outside the range stays in the attributes as the page wrote it.
217
- *
218
- * @stimeoRenderRoot
219
- */
220
388
  #render() {
389
+ const token = this.#moveToken;
221
390
  const page = this.#page;
222
391
  for (const button of this.pageTargets) {
223
392
  if (Number(button.dataset.page) === page) {
@@ -230,40 +399,35 @@ var PaginationController = class extends Controller {
230
399
  const next = this.hasNextTarget ? this.nextTarget : null;
231
400
  const atStart = page <= 1;
232
401
  const atEnd = page >= this.#total;
233
- const prevStaysDisabled = this.#disabledAfter(prev, atStart);
234
402
  const nextStaysDisabled = this.#disabledAfter(next, atEnd);
235
403
  this.#release(prev, atStart);
236
404
  this.#release(next, atEnd);
237
405
  this.#disable(prev, atStart, nextStaysDisabled ? null : next);
238
- this.#disable(next, atEnd, prevStaysDisabled ? null : prev);
406
+ if (!this.#moves.isLatest(token)) return;
407
+ this.#disable(next, atEnd, prev);
239
408
  }
240
- /** Whether a boundary button will still be `disabled` once this render applies. */
241
409
  #disabledAfter(button, atBoundary) {
242
410
  if (!button) return true;
243
411
  if (atBoundary) return true;
244
412
  return button.disabled && !this.#owns(button);
245
413
  }
246
- /** Releases the boundary `disabled` this controller owns, once away from it. */
414
+ #releaseDeparted(button, targets) {
415
+ if (!targets.includes(button)) this.#release(button, false);
416
+ }
247
417
  #release(button, atBoundary) {
248
418
  if (!button || atBoundary || !this.#owns(button)) return;
249
419
  button.disabled = false;
250
420
  button.removeAttribute(this.#boundaryAttribute);
251
421
  }
252
- /**
253
- * Disables a boundary button, first moving focus off it when it is the active
254
- * element so disabling never strands focus.
255
- */
256
422
  #disable(button, atBoundary, opposite) {
257
423
  if (!button || !atBoundary) return;
258
424
  if (button.disabled && !this.#owns(button)) return;
425
+ const token = this.#moveToken;
259
426
  if (button === document.activeElement) this.#moveFocusAwayFrom(opposite);
427
+ if (!this.#moves.isLatest(token)) return;
260
428
  button.disabled = true;
261
429
  button.setAttribute(this.#boundaryAttribute, "");
262
430
  }
263
- /**
264
- * Moves focus to `opposite` (already resolved to `null` when it will stay
265
- * disabled), else to the current page button, else to the landmark itself.
266
- */
267
431
  #moveFocusAwayFrom(opposite) {
268
432
  const page = this.#page;
269
433
  const currentPage = this.pageTargets.find(
@@ -279,26 +443,38 @@ var PaginationController = class extends Controller {
279
443
  this.#tabindex.lend(this.element);
280
444
  this.element.focus();
281
445
  }
282
- /** Total pages, normalized to a finite integer >= 1. */
283
446
  get #total() {
284
- const total = this.totalValue;
285
- return Number.isFinite(total) ? Math.max(1, Math.trunc(total)) : 1;
447
+ const total = this.#safeTotal;
448
+ return Math.max(1, Math.trunc(total));
286
449
  }
287
- /** The current page, normalized into `[1, total]`. */
288
450
  get #page() {
289
- return this.#clamp(this.pageValue);
451
+ return this.#clamp(this.#safePage);
290
452
  }
291
- /** Constrains `page` to `[1, total]`; a non-finite page falls back to page 1. */
292
453
  #clamp(page) {
293
- if (!Number.isFinite(page)) return 1;
294
454
  return Math.min(this.#total, Math.max(1, Math.trunc(page)));
295
455
  }
296
- /** Whether the button's current `disabled` was applied by boundary control. */
297
456
  #owns(button) {
298
457
  return button.hasAttribute(this.#boundaryAttribute);
299
458
  }
459
+ get #safePage() {
460
+ return this.#numbers.read(
461
+ this,
462
+ "page",
463
+ this.pageValue,
464
+ _PaginationController.values.page.default,
465
+ _PaginationController.valueConstraints.page
466
+ );
467
+ }
468
+ get #safeTotal() {
469
+ return this.#numbers.read(
470
+ this,
471
+ "total",
472
+ this.totalValue,
473
+ _PaginationController.values.total.default,
474
+ _PaginationController.valueConstraints.total
475
+ );
476
+ }
300
477
  };
301
478
 
302
479
  export { PaginationController };
303
- //# sourceMappingURL=pagination_controller.js.map
304
480
  //# sourceMappingURL=pagination_controller.js.map