stimeo-ui 0.14.0 → 0.16.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 (279) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/README.md +120 -0
  3. package/dist/cable/index.js +123 -29
  4. package/dist/cable/index.js.map +1 -1
  5. package/dist/controllers/accordion_controller.d.ts +30 -3
  6. package/dist/controllers/accordion_controller.js +98 -9
  7. package/dist/controllers/accordion_controller.js.map +1 -1
  8. package/dist/controllers/alert_dialog_controller.d.ts +8 -7
  9. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  10. package/dist/controllers/announcer_controller.d.ts +3 -2
  11. package/dist/controllers/announcer_controller.js +96 -62
  12. package/dist/controllers/announcer_controller.js.map +1 -1
  13. package/dist/controllers/auto_submit_controller.js +83 -7
  14. package/dist/controllers/auto_submit_controller.js.map +1 -1
  15. package/dist/controllers/avatar_controller.js +1 -1
  16. package/dist/controllers/avatar_controller.js.map +1 -1
  17. package/dist/controllers/breadcrumb_controller.d.ts +10 -7
  18. package/dist/controllers/breadcrumb_controller.js +38 -11
  19. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  20. package/dist/controllers/bulk_select_controller.d.ts +4 -4
  21. package/dist/controllers/bulk_select_controller.js +7 -6
  22. package/dist/controllers/bulk_select_controller.js.map +1 -1
  23. package/dist/controllers/calendar_controller.d.ts +101 -23
  24. package/dist/controllers/calendar_controller.js +340 -123
  25. package/dist/controllers/calendar_controller.js.map +1 -1
  26. package/dist/controllers/carousel_controller.d.ts +67 -22
  27. package/dist/controllers/carousel_controller.js +263 -38
  28. package/dist/controllers/carousel_controller.js.map +1 -1
  29. package/dist/controllers/character_counter_controller.d.ts +1 -1
  30. package/dist/controllers/character_counter_controller.js +40 -2
  31. package/dist/controllers/character_counter_controller.js.map +1 -1
  32. package/dist/controllers/checkbox_controller.js +81 -12
  33. package/dist/controllers/checkbox_controller.js.map +1 -1
  34. package/dist/controllers/clipboard_controller.d.ts +28 -2
  35. package/dist/controllers/clipboard_controller.js +63 -5
  36. package/dist/controllers/clipboard_controller.js.map +1 -1
  37. package/dist/controllers/collapsible_controller.d.ts +25 -1
  38. package/dist/controllers/collapsible_controller.js +99 -14
  39. package/dist/controllers/collapsible_controller.js.map +1 -1
  40. package/dist/controllers/color_picker_controller.d.ts +29 -8
  41. package/dist/controllers/color_picker_controller.js +80 -34
  42. package/dist/controllers/color_picker_controller.js.map +1 -1
  43. package/dist/controllers/combobox_controller.d.ts +10 -1
  44. package/dist/controllers/combobox_controller.js +106 -16
  45. package/dist/controllers/combobox_controller.js.map +1 -1
  46. package/dist/controllers/command_palette_controller.d.ts +3 -3
  47. package/dist/controllers/command_palette_controller.js +35 -3
  48. package/dist/controllers/command_palette_controller.js.map +1 -1
  49. package/dist/controllers/conditional_fields_controller.js +85 -17
  50. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  51. package/dist/controllers/confirm_controller.js +3 -0
  52. package/dist/controllers/confirm_controller.js.map +1 -1
  53. package/dist/controllers/context_menu_controller.d.ts +6 -0
  54. package/dist/controllers/context_menu_controller.js +32 -12
  55. package/dist/controllers/context_menu_controller.js.map +1 -1
  56. package/dist/controllers/count_up_controller.js.map +1 -1
  57. package/dist/controllers/countdown_controller.d.ts +30 -1
  58. package/dist/controllers/countdown_controller.js +129 -26
  59. package/dist/controllers/countdown_controller.js.map +1 -1
  60. package/dist/controllers/currency_input_controller.d.ts +77 -16
  61. package/dist/controllers/currency_input_controller.js +221 -67
  62. package/dist/controllers/currency_input_controller.js.map +1 -1
  63. package/dist/controllers/data_grid_controller.d.ts +63 -18
  64. package/dist/controllers/data_grid_controller.js +195 -29
  65. package/dist/controllers/data_grid_controller.js.map +1 -1
  66. package/dist/controllers/date_range_picker_controller.d.ts +41 -6
  67. package/dist/controllers/date_range_picker_controller.js +151 -30
  68. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  69. package/dist/controllers/dialog_controller.d.ts +10 -3
  70. package/dist/controllers/dialog_controller.js +35 -8
  71. package/dist/controllers/dialog_controller.js.map +1 -1
  72. package/dist/controllers/direct_upload_controller.js +22 -4
  73. package/dist/controllers/direct_upload_controller.js.map +1 -1
  74. package/dist/controllers/dirty_form_controller.d.ts +2 -2
  75. package/dist/controllers/dirty_form_controller.js +46 -13
  76. package/dist/controllers/dirty_form_controller.js.map +1 -1
  77. package/dist/controllers/dismissible_controller.js +1 -0
  78. package/dist/controllers/dismissible_controller.js.map +1 -1
  79. package/dist/controllers/drawer_controller.d.ts +18 -10
  80. package/dist/controllers/drawer_controller.js +54 -19
  81. package/dist/controllers/drawer_controller.js.map +1 -1
  82. package/dist/controllers/dropdown_controller.d.ts +9 -3
  83. package/dist/controllers/dropdown_controller.js +36 -9
  84. package/dist/controllers/dropdown_controller.js.map +1 -1
  85. package/dist/controllers/editable_controller.js +34 -0
  86. package/dist/controllers/editable_controller.js.map +1 -1
  87. package/dist/controllers/file_dropzone_controller.js +144 -51
  88. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  89. package/dist/controllers/filter_controller.d.ts +10 -4
  90. package/dist/controllers/filter_controller.js +20 -6
  91. package/dist/controllers/filter_controller.js.map +1 -1
  92. package/dist/controllers/flash_controller.d.ts +26 -6
  93. package/dist/controllers/flash_controller.js +432 -71
  94. package/dist/controllers/flash_controller.js.map +1 -1
  95. package/dist/controllers/focus_controller.js +1 -0
  96. package/dist/controllers/focus_controller.js.map +1 -1
  97. package/dist/controllers/form_field_controller.js +7 -5
  98. package/dist/controllers/form_field_controller.js.map +1 -1
  99. package/dist/controllers/form_validation_controller.js +19 -13
  100. package/dist/controllers/form_validation_controller.js.map +1 -1
  101. package/dist/controllers/frame_loading_controller.js +45 -8
  102. package/dist/controllers/frame_loading_controller.js.map +1 -1
  103. package/dist/controllers/highlight_controller.js +82 -25
  104. package/dist/controllers/highlight_controller.js.map +1 -1
  105. package/dist/controllers/hover_card_controller.d.ts +10 -2
  106. package/dist/controllers/hover_card_controller.js +40 -14
  107. package/dist/controllers/hover_card_controller.js.map +1 -1
  108. package/dist/controllers/idle_controller.d.ts +16 -3
  109. package/dist/controllers/idle_controller.js +90 -5
  110. package/dist/controllers/idle_controller.js.map +1 -1
  111. package/dist/controllers/input_mask_controller.d.ts +5 -2
  112. package/dist/controllers/input_mask_controller.js +65 -9
  113. package/dist/controllers/input_mask_controller.js.map +1 -1
  114. package/dist/controllers/intersection_controller.js +3 -0
  115. package/dist/controllers/intersection_controller.js.map +1 -1
  116. package/dist/controllers/lazy_frame_controller.js +11 -2
  117. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  118. package/dist/controllers/listbox_controller.d.ts +50 -8
  119. package/dist/controllers/listbox_controller.js +203 -45
  120. package/dist/controllers/listbox_controller.js.map +1 -1
  121. package/dist/controllers/local_time_controller.js +10 -5
  122. package/dist/controllers/local_time_controller.js.map +1 -1
  123. package/dist/controllers/masonry_controller.d.ts +3 -3
  124. package/dist/controllers/masonry_controller.js +31 -15
  125. package/dist/controllers/masonry_controller.js.map +1 -1
  126. package/dist/controllers/menu_controller.d.ts +9 -3
  127. package/dist/controllers/menu_controller.js +45 -16
  128. package/dist/controllers/menu_controller.js.map +1 -1
  129. package/dist/controllers/menubar_controller.d.ts +11 -0
  130. package/dist/controllers/menubar_controller.js +58 -24
  131. package/dist/controllers/menubar_controller.js.map +1 -1
  132. package/dist/controllers/meter_controller.js +9 -5
  133. package/dist/controllers/meter_controller.js.map +1 -1
  134. package/dist/controllers/multi_select_controller.d.ts +18 -3
  135. package/dist/controllers/multi_select_controller.js +278 -104
  136. package/dist/controllers/multi_select_controller.js.map +1 -1
  137. package/dist/controllers/navigation_menu_controller.d.ts +11 -0
  138. package/dist/controllers/navigation_menu_controller.js +48 -15
  139. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  140. package/dist/controllers/nested_form_controller.js +37 -8
  141. package/dist/controllers/nested_form_controller.js.map +1 -1
  142. package/dist/controllers/network_status_controller.js +9 -1
  143. package/dist/controllers/network_status_controller.js.map +1 -1
  144. package/dist/controllers/number_input_controller.d.ts +36 -8
  145. package/dist/controllers/number_input_controller.js +124 -21
  146. package/dist/controllers/number_input_controller.js.map +1 -1
  147. package/dist/controllers/optimistic_controller.js +42 -5
  148. package/dist/controllers/optimistic_controller.js.map +1 -1
  149. package/dist/controllers/otp_controller.d.ts +22 -7
  150. package/dist/controllers/otp_controller.js +198 -55
  151. package/dist/controllers/otp_controller.js.map +1 -1
  152. package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
  153. package/dist/controllers/overflow_indicator_controller.js +115 -21
  154. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  155. package/dist/controllers/overflow_menu_controller.d.ts +26 -6
  156. package/dist/controllers/overflow_menu_controller.js +141 -44
  157. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  158. package/dist/controllers/pagination_controller.d.ts +19 -11
  159. package/dist/controllers/pagination_controller.js +74 -28
  160. package/dist/controllers/pagination_controller.js.map +1 -1
  161. package/dist/controllers/password_reveal_controller.d.ts +15 -1
  162. package/dist/controllers/password_reveal_controller.js +59 -2
  163. package/dist/controllers/password_reveal_controller.js.map +1 -1
  164. package/dist/controllers/persist_controller.js +30 -8
  165. package/dist/controllers/persist_controller.js.map +1 -1
  166. package/dist/controllers/pointer_drag_controller.js +131 -52
  167. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  168. package/dist/controllers/popover_controller.d.ts +9 -3
  169. package/dist/controllers/popover_controller.js +45 -11
  170. package/dist/controllers/popover_controller.js.map +1 -1
  171. package/dist/controllers/portal_controller.d.ts +1 -1
  172. package/dist/controllers/portal_controller.js +6 -2
  173. package/dist/controllers/portal_controller.js.map +1 -1
  174. package/dist/controllers/preview_guard_controller.js +16 -1
  175. package/dist/controllers/preview_guard_controller.js.map +1 -1
  176. package/dist/controllers/progress_controller.js +8 -4
  177. package/dist/controllers/progress_controller.js.map +1 -1
  178. package/dist/controllers/radio_group_controller.d.ts +6 -4
  179. package/dist/controllers/radio_group_controller.js +42 -17
  180. package/dist/controllers/radio_group_controller.js.map +1 -1
  181. package/dist/controllers/range_slider_controller.d.ts +49 -1
  182. package/dist/controllers/range_slider_controller.js +88 -42
  183. package/dist/controllers/range_slider_controller.js.map +1 -1
  184. package/dist/controllers/rating_controller.d.ts +14 -3
  185. package/dist/controllers/rating_controller.js +39 -15
  186. package/dist/controllers/rating_controller.js.map +1 -1
  187. package/dist/controllers/read_more_controller.d.ts +24 -2
  188. package/dist/controllers/read_more_controller.js +100 -7
  189. package/dist/controllers/read_more_controller.js.map +1 -1
  190. package/dist/controllers/reading_progress_controller.js +65 -19
  191. package/dist/controllers/reading_progress_controller.js.map +1 -1
  192. package/dist/controllers/relative_time_controller.js +10 -5
  193. package/dist/controllers/relative_time_controller.js.map +1 -1
  194. package/dist/controllers/resizable_controller.d.ts +16 -2
  195. package/dist/controllers/resizable_controller.js +82 -22
  196. package/dist/controllers/resizable_controller.js.map +1 -1
  197. package/dist/controllers/scroll_area_controller.js +75 -27
  198. package/dist/controllers/scroll_area_controller.js.map +1 -1
  199. package/dist/controllers/scroll_restore_controller.js +37 -16
  200. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  201. package/dist/controllers/scroll_visibility_controller.js +49 -30
  202. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  203. package/dist/controllers/scrollspy_controller.d.ts +3 -2
  204. package/dist/controllers/scrollspy_controller.js +71 -26
  205. package/dist/controllers/scrollspy_controller.js.map +1 -1
  206. package/dist/controllers/separator_controller.d.ts +41 -14
  207. package/dist/controllers/separator_controller.js +66 -37
  208. package/dist/controllers/separator_controller.js.map +1 -1
  209. package/dist/controllers/sidebar_controller.d.ts +20 -3
  210. package/dist/controllers/sidebar_controller.js +77 -18
  211. package/dist/controllers/sidebar_controller.js.map +1 -1
  212. package/dist/controllers/skeleton_controller.js +6 -1
  213. package/dist/controllers/skeleton_controller.js.map +1 -1
  214. package/dist/controllers/slider_controller.d.ts +45 -7
  215. package/dist/controllers/slider_controller.js +82 -47
  216. package/dist/controllers/slider_controller.js.map +1 -1
  217. package/dist/controllers/smart_sticky_header_controller.js +60 -26
  218. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  219. package/dist/controllers/sortable_controller.js +17 -2
  220. package/dist/controllers/sortable_controller.js.map +1 -1
  221. package/dist/controllers/spinner_controller.js +10 -2
  222. package/dist/controllers/spinner_controller.js.map +1 -1
  223. package/dist/controllers/step_indicator_controller.d.ts +19 -17
  224. package/dist/controllers/step_indicator_controller.js +18 -17
  225. package/dist/controllers/step_indicator_controller.js.map +1 -1
  226. package/dist/controllers/stepper_controller.d.ts +34 -9
  227. package/dist/controllers/stepper_controller.js +101 -19
  228. package/dist/controllers/stepper_controller.js.map +1 -1
  229. package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
  230. package/dist/controllers/stick_to_bottom_controller.js +104 -8
  231. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  232. package/dist/controllers/submit_once_controller.d.ts +3 -2
  233. package/dist/controllers/submit_once_controller.js +45 -9
  234. package/dist/controllers/submit_once_controller.js.map +1 -1
  235. package/dist/controllers/switch_controller.d.ts +29 -7
  236. package/dist/controllers/switch_controller.js +101 -10
  237. package/dist/controllers/switch_controller.js.map +1 -1
  238. package/dist/controllers/tabs_controller.d.ts +12 -0
  239. package/dist/controllers/tabs_controller.js +21 -2
  240. package/dist/controllers/tabs_controller.js.map +1 -1
  241. package/dist/controllers/tags_input_controller.d.ts +15 -3
  242. package/dist/controllers/tags_input_controller.js +209 -59
  243. package/dist/controllers/tags_input_controller.js.map +1 -1
  244. package/dist/controllers/textarea_autosize_controller.js +29 -3
  245. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  246. package/dist/controllers/theme_controller.d.ts +20 -3
  247. package/dist/controllers/theme_controller.js +64 -14
  248. package/dist/controllers/theme_controller.js.map +1 -1
  249. package/dist/controllers/time_picker_controller.js +23 -8
  250. package/dist/controllers/time_picker_controller.js.map +1 -1
  251. package/dist/controllers/toast_controller.d.ts +55 -15
  252. package/dist/controllers/toast_controller.js +451 -105
  253. package/dist/controllers/toast_controller.js.map +1 -1
  254. package/dist/controllers/toggle_group_controller.d.ts +49 -7
  255. package/dist/controllers/toggle_group_controller.js +159 -23
  256. package/dist/controllers/toggle_group_controller.js.map +1 -1
  257. package/dist/controllers/toolbar_controller.js +32 -0
  258. package/dist/controllers/toolbar_controller.js.map +1 -1
  259. package/dist/controllers/tooltip_controller.d.ts +8 -0
  260. package/dist/controllers/tooltip_controller.js +39 -13
  261. package/dist/controllers/tooltip_controller.js.map +1 -1
  262. package/dist/controllers/transition_controller.js +4 -0
  263. package/dist/controllers/transition_controller.js.map +1 -1
  264. package/dist/controllers/tree_view_controller.d.ts +39 -8
  265. package/dist/controllers/tree_view_controller.js +169 -16
  266. package/dist/controllers/tree_view_controller.js.map +1 -1
  267. package/dist/index.d.ts +28 -1
  268. package/dist/index.js +5002 -1911
  269. package/dist/index.js.map +1 -1
  270. package/dist/inspector/cli.d.ts +72 -6
  271. package/dist/inspector/cli.js +262 -51
  272. package/dist/inspector/cli.js.map +1 -1
  273. package/dist/inspector/cli_bin.js +309 -51
  274. package/dist/inspector/cli_bin.js.map +1 -1
  275. package/dist/inspector/examples.json +40 -40
  276. package/dist/inspector/manifest.json +529 -61
  277. package/dist/positioning/index.js +2 -0
  278. package/dist/positioning/index.js.map +1 -1
  279. package/package.json +2 -2
@@ -982,12 +982,24 @@ var NAMESPACE_PREFIX = "stimeo--";
982
982
  function dasherize(value) {
983
983
  return value.replace(/([A-Z])/g, (_match, char) => `-${char.toLowerCase()}`);
984
984
  }
985
- function controllerIdentifiers(dataControllerValue) {
985
+ function attributeTokens(value) {
986
+ const tokens = [];
986
987
  const seen = /* @__PURE__ */ new Set();
987
- for (const token of dataControllerValue.split(/\s+/)) {
988
- if (token.startsWith(NAMESPACE_PREFIX)) seen.add(token);
988
+ const pattern = /\S+/g;
989
+ for (let match = pattern.exec(value); match; match = pattern.exec(value)) {
990
+ if (seen.has(match[0])) continue;
991
+ seen.add(match[0]);
992
+ tokens.push({ name: match[0], start: match.index });
989
993
  }
990
- return [...seen];
994
+ return tokens;
995
+ }
996
+ function controllerIdentifiers(dataControllerValue) {
997
+ return controllerIdentifierTokens(dataControllerValue).map((token) => token.name);
998
+ }
999
+ function controllerIdentifierTokens(dataControllerValue) {
1000
+ return attributeTokens(dataControllerValue).filter(
1001
+ (token) => token.name.startsWith(NAMESPACE_PREFIX)
1002
+ );
991
1003
  }
992
1004
  function parseTargetAttr(attrName) {
993
1005
  const match = /^data-(stimeo--[a-z0-9][a-z0-9-]*)-target$/.exec(attrName);
@@ -1029,7 +1041,7 @@ function defaultEventFor(host) {
1029
1041
  }
1030
1042
  function actionDescriptors(dataActionValue, host) {
1031
1043
  const descriptors = [];
1032
- for (const descriptor of dataActionValue.split(/\s+/)) {
1044
+ for (const { name: descriptor, start } of attributeTokens(dataActionValue)) {
1033
1045
  const hash = descriptor.indexOf("#");
1034
1046
  if (hash === -1) continue;
1035
1047
  let lhs = descriptor.slice(0, hash);
@@ -1045,7 +1057,14 @@ function actionDescriptors(dataActionValue, host) {
1045
1057
  }
1046
1058
  if (!lhs.startsWith(NAMESPACE_PREFIX)) continue;
1047
1059
  const method = /^[a-zA-Z_$][\w$]*/.exec(descriptor.slice(hash + 1))?.[0] ?? "";
1048
- descriptors.push({ identifier: lhs, method, eventType });
1060
+ descriptors.push({
1061
+ identifier: lhs,
1062
+ identifierStart: start + (arrow === -1 ? 0 : arrow + 2),
1063
+ method,
1064
+ methodStart: start + hash + 1,
1065
+ eventType,
1066
+ eventStart: start
1067
+ });
1049
1068
  }
1050
1069
  return descriptors;
1051
1070
  }
@@ -1061,9 +1080,11 @@ var DIAGNOSTIC_CODES = [
1061
1080
  "invalid-value",
1062
1081
  "unknown-action-controller",
1063
1082
  "unknown-action-method",
1083
+ "unknown-action-event",
1064
1084
  "orphan-target",
1065
1085
  "missing-required-target",
1066
1086
  "missing-conditional-target",
1087
+ "invalid-template-root",
1067
1088
  "missing-required-action",
1068
1089
  "missing-action-completion",
1069
1090
  "missing-action-event",
@@ -1079,11 +1100,14 @@ var DIAGNOSTIC_CODES = [
1079
1100
  "cardinality-violation",
1080
1101
  "forbidden-aria",
1081
1102
  "missing-announcer",
1103
+ "missing-action-param",
1104
+ "invalid-action-param",
1105
+ "confusable-action-param",
1082
1106
  "unknown-ignore-code"
1083
1107
  ];
1084
1108
 
1085
1109
  // src/inspector/check.ts
1086
- function isInside(node, ancestor) {
1110
+ function isDescendantOf(node, ancestor) {
1087
1111
  for (let current = node.parent; current; current = current.parent) {
1088
1112
  if (current === ancestor) return true;
1089
1113
  }
@@ -1176,11 +1200,13 @@ function checkSource(source, manifest) {
1176
1200
  const unnamedTargets = [];
1177
1201
  const targetsUnreadable = (container, scope, identifier) => {
1178
1202
  for (const node of unresolved) {
1179
- if (isWithin(node, container) && findOwner(node, identifier) === scope) return true;
1203
+ if (isSelfOrDescendantOf(node, container) && findOwner(node, identifier) === scope)
1204
+ return true;
1180
1205
  }
1181
1206
  for (const { node, identifier: hidden } of unnamedTargets) {
1182
1207
  if (hidden !== identifier) continue;
1183
- if (isWithin(node, container) && findOwner(node, identifier) === scope) return true;
1208
+ if (isSelfOrDescendantOf(node, container) && findOwner(node, identifier) === scope)
1209
+ return true;
1184
1210
  }
1185
1211
  return false;
1186
1212
  };
@@ -1239,7 +1265,7 @@ function checkSource(source, manifest) {
1239
1265
  const nodes = targetNodes.get(scope)?.get(identifier)?.get(condition.target) ?? [];
1240
1266
  const container = condition.within === "scope" ? scope : element;
1241
1267
  let held = 0;
1242
- for (const node of nodes) if (isWithin(node, container)) held += 1;
1268
+ for (const node of nodes) if (isSelfOrDescendantOf(node, container)) held += 1;
1243
1269
  if (condition.min !== void 0 && held < condition.min) return false;
1244
1270
  if (condition.max !== void 0 && held > condition.max) return false;
1245
1271
  return true;
@@ -1276,9 +1302,9 @@ function checkSource(source, manifest) {
1276
1302
  const authoredValueIdentifiers = /* @__PURE__ */ new Set();
1277
1303
  for (const attr of node.attrs) {
1278
1304
  if (attr.name === IGNORE_ATTR) {
1279
- for (const token of attr.value.split(/\s+/).filter((t) => t.length > 0)) {
1280
- if (!DIAGNOSTIC_CODES.includes(token)) {
1281
- const best = nearestName(token, DIAGNOSTIC_CODES);
1305
+ for (const token of attributeTokens(attr.value)) {
1306
+ if (!DIAGNOSTIC_CODES.includes(token.name)) {
1307
+ const best = nearestName(token.name, DIAGNOSTIC_CODES);
1282
1308
  push(
1283
1309
  diagnostics,
1284
1310
  "unknown-ignore-code",
@@ -1293,7 +1319,7 @@ function checkSource(source, manifest) {
1293
1319
  continue;
1294
1320
  }
1295
1321
  if (attr.name === FRAGMENT_ATTR) {
1296
- const identifiers = attr.value.split(/\s+/).filter((token) => token.length > 0);
1322
+ const identifiers = attributeTokens(attr.value);
1297
1323
  if (identifiers.length === 0) {
1298
1324
  report(
1299
1325
  node,
@@ -1304,13 +1330,13 @@ function checkSource(source, manifest) {
1304
1330
  );
1305
1331
  }
1306
1332
  for (const identifier of identifiers) {
1307
- if (!(identifier in known)) {
1308
- const best = nearestName(identifier, knownIdentifiers);
1333
+ if (!(identifier.name in known)) {
1334
+ const best = nearestName(identifier.name, knownIdentifiers);
1309
1335
  report(
1310
1336
  node,
1311
1337
  "unknown-controller",
1312
1338
  "error",
1313
- `Fragment declaration references unknown Stimeo controller "${identifier}".`,
1339
+ `Fragment declaration references unknown Stimeo controller "${identifier.name}".`,
1314
1340
  attr,
1315
1341
  asDidYouMean(best),
1316
1342
  tokenFix(attr, identifier, best)
@@ -1320,7 +1346,8 @@ function checkSource(source, manifest) {
1320
1346
  continue;
1321
1347
  }
1322
1348
  if (attr.name === "data-controller") {
1323
- for (const identifier of controllerIdentifiers(attr.value)) {
1349
+ for (const token of controllerIdentifierTokens(attr.value)) {
1350
+ const identifier = token.name;
1324
1351
  if (identifier in known) {
1325
1352
  scopes.push({ node, identifier });
1326
1353
  } else {
@@ -1332,7 +1359,7 @@ function checkSource(source, manifest) {
1332
1359
  `Unknown Stimeo controller "${identifier}".`,
1333
1360
  attr,
1334
1361
  asDidYouMean(best),
1335
- tokenFix(attr, identifier, best)
1362
+ tokenFix(attr, token, best)
1336
1363
  );
1337
1364
  }
1338
1365
  }
@@ -1352,24 +1379,27 @@ function checkSource(source, manifest) {
1352
1379
  );
1353
1380
  continue;
1354
1381
  }
1355
- const targetName = isDynamicValue(attr) ? "" : attr.value.trim();
1356
- if (targetName.length > 0 && !controller.targets.includes(targetName)) {
1357
- const best = nearestName(targetName, controller.targets);
1382
+ const declared = isDynamicValue(attr) ? [] : attributeTokens(attr.value);
1383
+ for (const token of declared) {
1384
+ if (controller.targets.includes(token.name)) continue;
1385
+ const best = nearestName(token.name, controller.targets);
1358
1386
  report(
1359
1387
  node,
1360
1388
  "unknown-target",
1361
1389
  "error",
1362
- `Unknown target "${targetName}" for "${targetIdentifier}". Known targets: ${list(controller.targets)}.`,
1390
+ `Unknown target "${token.name}" for "${targetIdentifier}". Known targets: ${list(controller.targets)}.`,
1363
1391
  attr,
1364
1392
  asDidYouMean(best),
1365
- tokenFix(attr, targetName, best)
1393
+ tokenFix(attr, token, best)
1366
1394
  );
1367
1395
  }
1368
1396
  const owner = findOwner(node, targetIdentifier);
1369
1397
  if (owner) {
1370
- if (targetName.length > 0) {
1371
- recordPresence(owner, targetIdentifier, targetName);
1372
- recordTargetNode(owner, targetIdentifier, targetName, node);
1398
+ if (declared.length > 0) {
1399
+ for (const token of declared) {
1400
+ recordPresence(owner, targetIdentifier, token.name);
1401
+ recordTargetNode(owner, targetIdentifier, token.name, node);
1402
+ }
1373
1403
  } else if (isDynamicValue(attr)) {
1374
1404
  unnamedTargets.push({ node, identifier: targetIdentifier });
1375
1405
  }
@@ -1433,7 +1463,15 @@ function checkSource(source, manifest) {
1433
1463
  tag: node.tag,
1434
1464
  inputType: node.attrs.find((a) => a.name === "type")?.value
1435
1465
  };
1436
- for (const { identifier, method, eventType } of actionDescriptors(attr.value, host)) {
1466
+ const paramsChecked = /* @__PURE__ */ new Set();
1467
+ for (const {
1468
+ identifier,
1469
+ identifierStart,
1470
+ method,
1471
+ methodStart,
1472
+ eventType,
1473
+ eventStart
1474
+ } of actionDescriptors(attr.value, host)) {
1437
1475
  const controller = known[identifier];
1438
1476
  if (controller && method.length > 0) {
1439
1477
  const owner = findOwner(node, identifier);
@@ -1458,7 +1496,7 @@ function checkSource(source, manifest) {
1458
1496
  `Action references unknown Stimeo controller "${identifier}".`,
1459
1497
  attr,
1460
1498
  asDidYouMean(best),
1461
- tokenFix(attr, identifier, best)
1499
+ tokenFix(attr, { name: identifier, start: identifierStart }, best)
1462
1500
  );
1463
1501
  } else if (method.length > 0 && !controller.actions.includes(method)) {
1464
1502
  const best = nearestName(method, controller.actions);
@@ -1469,11 +1507,123 @@ function checkSource(source, manifest) {
1469
1507
  `Unknown action "${method}" for "${identifier}". Known actions: ${list(controller.actions)}.`,
1470
1508
  attr,
1471
1509
  asDidYouMean(best),
1472
- tokenFix(attr, method, best)
1510
+ tokenFix(attr, { name: method, start: methodStart }, best)
1473
1511
  );
1474
1512
  }
1513
+ if (controller && method.length > 0 && !paramsChecked.has(`${identifier}#${method}`)) {
1514
+ paramsChecked.add(`${identifier}#${method}`);
1515
+ checkActionParams(node, attr, identifier, method, controller);
1516
+ }
1517
+ if (!isDynamicValue(attr)) {
1518
+ const emitted = /^(stimeo--[a-z0-9-]+):(.+)$/.exec(eventType);
1519
+ const source2 = emitted ? known[emitted[1]] : void 0;
1520
+ const name = emitted?.[2];
1521
+ if (source2 && name !== void 0 && !source2.events.includes(name)) {
1522
+ const best = nearestName(name, source2.events);
1523
+ report(
1524
+ node,
1525
+ "unknown-action-event",
1526
+ "error",
1527
+ `Unknown event "${name}" for "${emitted?.[1]}". Known events: ${list(source2.events)}.`,
1528
+ attr,
1529
+ asDidYouMean(best),
1530
+ tokenFix(attr, { name, start: eventStart + eventType.length - name.length }, best)
1531
+ );
1532
+ }
1533
+ }
1534
+ }
1535
+ }
1536
+ }
1537
+ function checkActionParams(element, action, identifier, method, controller) {
1538
+ const rules = controller.actionParams.filter((rule) => rule.action === method);
1539
+ if (rules.length === 0) return;
1540
+ const readable = !isDynamicValue(action) && element.origin === "markup" && !element.opaque;
1541
+ const expected = new Map(
1542
+ rules.map((rule) => [`data-${identifier}-${dasherize(rule.param)}-param`, rule])
1543
+ );
1544
+ for (const [attrName, rule] of expected) {
1545
+ const written = element.attrs.find((candidate) => candidate.name === attrName);
1546
+ if (!written) {
1547
+ if (!rule.required || !readable) continue;
1548
+ const confusable = confusableParamAttr(element, attrName, rule.param);
1549
+ report(
1550
+ element,
1551
+ "missing-action-param",
1552
+ "error",
1553
+ confusable ? `"${identifier}#${method}" reads its "${rule.param}" param, and "${confusable.name}" is not the attribute Stimulus builds it from.` : `"${identifier}#${method}" reads its "${rule.param}" param, which nothing on this element supplies.`,
1554
+ confusable ?? action,
1555
+ rule.suggestion
1556
+ );
1557
+ continue;
1558
+ }
1559
+ if (isDynamicValue(written)) continue;
1560
+ const value = written.value;
1561
+ if (value.length === 0) {
1562
+ if (!rule.required || !readable) continue;
1563
+ report(
1564
+ element,
1565
+ "missing-action-param",
1566
+ "error",
1567
+ `"${identifier}#${method}" reads its "${rule.param}" param, and "${attrName}" carries no value.`,
1568
+ written,
1569
+ rule.suggestion
1570
+ );
1571
+ continue;
1572
+ }
1573
+ if (rule.integer && !Number.isInteger(Number(value))) {
1574
+ report(
1575
+ element,
1576
+ "invalid-action-param",
1577
+ "error",
1578
+ `"${value}" is not an "${rule.param}" this action reads. It accepts a whole number.`,
1579
+ written,
1580
+ rule.suggestion
1581
+ );
1582
+ continue;
1475
1583
  }
1584
+ if (!rule.allowedValues || rule.allowedValues.includes(value)) continue;
1585
+ const best = nearestName(value, rule.allowedValues);
1586
+ report(
1587
+ element,
1588
+ "invalid-action-param",
1589
+ "error",
1590
+ `"${value}" is not a "${rule.param}" this action reads. It accepts ${list(rule.allowedValues)}.`,
1591
+ written,
1592
+ asDidYouMean(best) ?? rule.suggestion,
1593
+ best !== void 0 && written.valueStart !== void 0 ? {
1594
+ start: written.valueStart,
1595
+ end: written.valueStart + written.value.length,
1596
+ text: best,
1597
+ title: `Replace with "${best}"`
1598
+ } : void 0
1599
+ );
1476
1600
  }
1601
+ if (!readable) return;
1602
+ for (const candidate of element.attrs) {
1603
+ if (!candidate.name.startsWith(`data-${identifier}-`)) continue;
1604
+ if (expected.has(candidate.name)) continue;
1605
+ if (parseTargetAttr(candidate.name) || candidate.name.endsWith("-value")) continue;
1606
+ const best = [...expected.keys()].find((name) => namesTheParam(candidate.name, name));
1607
+ if (best === void 0) continue;
1608
+ report(
1609
+ element,
1610
+ "confusable-action-param",
1611
+ "warning",
1612
+ `"${candidate.name}" is not an attribute Stimulus reads. Action params are named "<param>-param".`,
1613
+ candidate,
1614
+ `Did you mean ${best}?`
1615
+ );
1616
+ }
1617
+ }
1618
+ function namesTheParam(written, attrName) {
1619
+ if (written === attrName) return false;
1620
+ return `${written}-param` === attrName || nearestName(written, [attrName]) !== void 0;
1621
+ }
1622
+ function confusableParamAttr(element, attrName, param) {
1623
+ const bare = `data-${dasherize(param)}`;
1624
+ return element.attrs.find(
1625
+ (candidate) => candidate.name === bare || namesTheParam(candidate.name, attrName)
1626
+ );
1477
1627
  }
1478
1628
  for (const identifier of authoredValueIdentifiers) {
1479
1629
  const controller = known[identifier];
@@ -1534,19 +1684,30 @@ function checkSource(source, manifest) {
1534
1684
  const owners = nodesOf(rule.whenPresent);
1535
1685
  const hosts = rule.requireSameHost;
1536
1686
  const sharesHost = (target, selectors) => {
1537
- const ownerHosts = owners.map((owner) => nearestHost(owner, selectors));
1538
- if (ownerHosts.some((host) => !host)) return true;
1687
+ const hostOf = (el) => nearestHost(el, selectors);
1688
+ const ownerHosts = owners.map(hostOf).filter((host) => host !== null && host !== void 0);
1539
1689
  const candidates = nodesOf(target);
1540
- if (candidates.length === 0) return false;
1541
- return candidates.some((el) => {
1542
- const host = nearestHost(el, selectors);
1543
- return host ? ownerHosts.includes(host) : true;
1544
- });
1690
+ return ownerHosts.every(
1691
+ (ownerHost) => candidates.some((el) => {
1692
+ const host = hostOf(el);
1693
+ if (host === null) return false;
1694
+ return host ? host === ownerHost : true;
1695
+ })
1696
+ );
1697
+ };
1698
+ const sharesTargetHost = (target, hostTarget) => {
1699
+ const hostNodes = nodesOf(hostTarget);
1700
+ const hostOf = (el) => hostNodes.find((host) => isDescendantOf(el, host)) ?? null;
1701
+ const ownerHosts = owners.map(hostOf).filter((host) => host !== null);
1702
+ const candidates = nodesOf(target);
1703
+ return ownerHosts.every((ownerHost) => candidates.some((el) => hostOf(el) === ownerHost));
1545
1704
  };
1546
1705
  const satisfied = (target) => {
1706
+ const hostTarget = rule.requireSameTargetHost;
1707
+ if (hostTarget) return sharesTargetHost(target, hostTarget);
1547
1708
  if (hosts) return sharesHost(target, hosts);
1548
1709
  if (rule.requireInside)
1549
- return nodesOf(target).some((el) => owners.some((owner) => isInside(el, owner)));
1710
+ return nodesOf(target).some((el) => owners.some((owner) => isDescendantOf(el, owner)));
1550
1711
  return present.has(target);
1551
1712
  };
1552
1713
  const missing = rule.require.filter((target) => !satisfied(target));
@@ -1555,12 +1716,62 @@ function checkSource(source, manifest) {
1555
1716
  node,
1556
1717
  "missing-conditional-target",
1557
1718
  complete ? "error" : "warning",
1558
- hosts ? `"${identifier}" requires ${list(missing)} in the same ${rule.hostLabel ?? "element"} as its "${rule.whenPresent}".` : rule.requireInside ? `"${identifier}" requires ${list(missing)} inside its "${rule.whenPresent}".` : `"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
1719
+ rule.requireSameTargetHost ? `"${identifier}" requires ${list(missing)} in the same "${rule.requireSameTargetHost}" as its "${rule.whenPresent}".` : hosts ? `"${identifier}" requires ${list(missing)} in the same ${rule.hostLabel ?? "element"} as its "${rule.whenPresent}".` : rule.requireInside ? `"${identifier}" requires ${list(missing)} inside its "${rule.whenPresent}".` : `"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
1559
1720
  node,
1560
1721
  complete ? rule.suggestion : UNRESOLVED_TARGET_HINT
1561
1722
  );
1562
1723
  }
1563
1724
  }
1725
+ for (const { node, identifier } of scopes) {
1726
+ const controller = known[identifier];
1727
+ if (!controller) continue;
1728
+ const complete = scopeIsComplete(node, node, identifier);
1729
+ const nodesOf = (target) => targetNodes.get(node)?.get(identifier)?.get(target) ?? [];
1730
+ const hint = complete ? void 0 : UNRESOLVED_TARGET_HINT;
1731
+ for (const rule of controller.templateRoots) {
1732
+ for (const template of nodesOf(rule.template)) {
1733
+ if (template.origin === "markup" && template.tag !== "template") {
1734
+ report(
1735
+ node,
1736
+ "invalid-template-root",
1737
+ complete ? "error" : "warning",
1738
+ `"${identifier}" requires its "${rule.template}" to be a <template>, and it is a <${template.tag}>.`,
1739
+ template,
1740
+ hint ?? rule.suggestion
1741
+ );
1742
+ continue;
1743
+ }
1744
+ if (template.children.some((child) => child.origin === "erb")) continue;
1745
+ const rows = template.children;
1746
+ const row = rows[0];
1747
+ if (rows.length === 0) continue;
1748
+ if (rows.length !== 1 || !row) {
1749
+ report(
1750
+ node,
1751
+ "invalid-template-root",
1752
+ complete ? "error" : "warning",
1753
+ `"${identifier}" requires its "${rule.template}" to hold exactly one element, and it holds ${rows.length}.`,
1754
+ template,
1755
+ hint ?? rule.suggestion
1756
+ );
1757
+ continue;
1758
+ }
1759
+ if (!rule.rootTarget) continue;
1760
+ if (unnamedTargets.some((entry) => entry.node === row && entry.identifier === identifier)) {
1761
+ continue;
1762
+ }
1763
+ if (nodesOf(rule.rootTarget).includes(row)) continue;
1764
+ report(
1765
+ node,
1766
+ "invalid-template-root",
1767
+ complete ? "error" : "warning",
1768
+ `"${identifier}" requires the one element in its "${rule.template}" to be its "${rule.rootTarget}" itself.`,
1769
+ row,
1770
+ hint ?? rule.suggestion
1771
+ );
1772
+ }
1773
+ }
1774
+ }
1564
1775
  for (const { node, identifier } of scopes) {
1565
1776
  const controller = known[identifier];
1566
1777
  if (!controller) continue;
@@ -1811,7 +2022,7 @@ function checkSource(source, manifest) {
1811
2022
  if (rule.values && !rule.values.includes(value)) continue;
1812
2023
  if (findOwner(el, identifier) !== node) continue;
1813
2024
  const declaredAs = el.attrs.find((a) => a.name === targetAttrName);
1814
- if (declaredAs && !isDynamicValue(declaredAs) && declaredAs.value.trim() === rule.target) {
2025
+ if (declaredAs && !isDynamicValue(declaredAs) && attributeTokens(declaredAs.value).some((token) => token.name === rule.target)) {
1815
2026
  continue;
1816
2027
  }
1817
2028
  if (declaredAs && isDynamicValue(declaredAs)) continue;
@@ -1839,7 +2050,7 @@ function checkSource(source, manifest) {
1839
2050
  for (const container of containers) {
1840
2051
  const matched = [];
1841
2052
  for (const el of counted) {
1842
- if (!isWithin(el, container)) continue;
2053
+ if (!isSelfOrDescendantOf(el, container)) continue;
1843
2054
  if (rule.attr === void 0) {
1844
2055
  matched.push({ el, at: el });
1845
2056
  continue;
@@ -1895,14 +2106,14 @@ function checkSource(source, manifest) {
1895
2106
  for (const el of anchored) {
1896
2107
  for (const attr of el.attrs) {
1897
2108
  if (!IDREF_ATTRS.has(attr.name) || isDynamicValue(attr)) continue;
1898
- for (const token of attr.value.split(/\s+/).filter((t) => t.length > 0)) {
1899
- if (!ids.has(token)) {
1900
- const best = nearestName(token, [...ids]);
2109
+ for (const token of attributeTokens(attr.value)) {
2110
+ if (!ids.has(token.name)) {
2111
+ const best = nearestName(token.name, [...ids]);
1901
2112
  report(
1902
2113
  el,
1903
2114
  "unresolved-idref",
1904
2115
  "warning",
1905
- `${attr.name} references id "${token}", but no element in this file declares it.`,
2116
+ `${attr.name} references id "${token.name}", but no element in this file declares it.`,
1906
2117
  attr,
1907
2118
  asDidYouMean(best),
1908
2119
  tokenFix(attr, token, best)
@@ -2018,7 +2229,7 @@ function checkA11y(report, el, identifier, req, severity, isDynamicValue) {
2018
2229
  );
2019
2230
  }
2020
2231
  }
2021
- function isWithin(node, container) {
2232
+ function isSelfOrDescendantOf(node, container) {
2022
2233
  let current = node;
2023
2234
  while (current) {
2024
2235
  if (current === container) return true;
@@ -2123,15 +2334,13 @@ function push(out, code, severity, message, at, suggestion, fix) {
2123
2334
  });
2124
2335
  }
2125
2336
  function tokenFix(attr, token, replacement) {
2126
- if (replacement === void 0 || attr.valueStart === void 0 || token.length === 0) {
2337
+ if (replacement === void 0 || attr.valueStart === void 0 || token.name.length === 0) {
2127
2338
  return void 0;
2128
2339
  }
2129
- const index = attr.value.indexOf(token);
2130
- if (index < 0) return void 0;
2131
- const start = attr.valueStart + index;
2340
+ const start = attr.valueStart + token.start;
2132
2341
  return {
2133
2342
  start,
2134
- end: start + token.length,
2343
+ end: start + token.name.length,
2135
2344
  text: replacement,
2136
2345
  title: `Replace with "${replacement}"`
2137
2346
  };
@@ -2198,10 +2407,12 @@ var CONTROLLER_ARRAY_FIELDS = [
2198
2407
  "values",
2199
2408
  "valueConstraints",
2200
2409
  "valueRelations",
2410
+ "actionParams",
2201
2411
  "actions",
2202
2412
  "events",
2203
2413
  "requiredTargets",
2204
2414
  "conditionalTargets",
2415
+ "templateRoots",
2205
2416
  "requiredActions",
2206
2417
  "actionCompletion",
2207
2418
  "a11y",
@@ -2762,6 +2973,31 @@ var CONTROLLER_CONTRACT_SCHEMA = {
2762
2973
  ...STRING_ARRAY,
2763
2974
  description: "Targets that must be present at least once inside the controller scope."
2764
2975
  },
2976
+ actionParams: {
2977
+ type: "array",
2978
+ description: "What each action reads out of `event.params`. Stimulus builds them from `data-<identifier>-<param>-param` on the element carrying the `data-action`; a missing or misspelled one leaves the action bound and doing nothing.",
2979
+ items: {
2980
+ type: "object",
2981
+ properties: {
2982
+ action: { type: "string", description: "The action method the param belongs to." },
2983
+ param: { type: "string", description: "The param name, as `event.params` reads it." },
2984
+ required: {
2985
+ type: "boolean",
2986
+ description: "True when the action can reach the value no other way."
2987
+ },
2988
+ allowedValues: {
2989
+ ...STRING_ARRAY,
2990
+ description: "The literal values the reader accepts, when it accepts a fixed set."
2991
+ },
2992
+ integer: {
2993
+ type: "boolean",
2994
+ description: "True when the reader takes a whole number and returns on anything else."
2995
+ },
2996
+ suggestion: { type: "string" }
2997
+ },
2998
+ required: ["action", "param", "suggestion"]
2999
+ }
3000
+ },
2765
3001
  conditionalTargets: {
2766
3002
  type: "array",
2767
3003
  description: "Targets that become required only once another optional target appears \u2014 an opt-in feature that is incomplete without its whole set. Omitting one half passes every other check and silently does nothing.",
@@ -2770,11 +3006,31 @@ var CONTROLLER_CONTRACT_SCHEMA = {
2770
3006
  properties: {
2771
3007
  whenPresent: { type: "string", description: "The optional target that turns it on." },
2772
3008
  require: { ...STRING_ARRAY, description: "Targets required once it appears." },
3009
+ requireSameTargetHost: {
3010
+ type: "string",
3011
+ description: "Target name of the element each required entry must share with `whenPresent`. A pair resolved per host \u2014 halves split across two of them leave both incomplete."
3012
+ },
2773
3013
  suggestion: { type: "string" }
2774
3014
  },
2775
3015
  required: ["whenPresent", "require", "suggestion"]
2776
3016
  }
2777
3017
  },
3018
+ templateRoots: {
3019
+ type: "array",
3020
+ description: "`<template>` targets whose content must be exactly one element \u2014 the row the controller clones. A row is cloned on its own, so a wrapper around it, or a second element beside it, is never rendered and the widget adds nothing.",
3021
+ items: {
3022
+ type: "object",
3023
+ properties: {
3024
+ template: { type: "string", description: "Target name of the `<template>`." },
3025
+ rootTarget: {
3026
+ type: "string",
3027
+ description: "Target its one element must carry, where the row is a declared part."
3028
+ },
3029
+ suggestion: { type: "string" }
3030
+ },
3031
+ required: ["template", "suggestion"]
3032
+ }
3033
+ },
2778
3034
  requiredActions: {
2779
3035
  type: "array",
2780
3036
  description: "Action bindings required on a particular element in one configured Value state.",
@@ -3011,10 +3267,12 @@ var CONTROLLER_CONTRACT_SCHEMA = {
3011
3267
  "values",
3012
3268
  "valueConstraints",
3013
3269
  "valueRelations",
3270
+ "actionParams",
3014
3271
  "actions",
3015
3272
  "events",
3016
3273
  "requiredTargets",
3017
3274
  "conditionalTargets",
3275
+ "templateRoots",
3018
3276
  "requiredActions",
3019
3277
  "actionCompletion",
3020
3278
  "a11y",