stimeo-ui 0.2.0 → 0.3.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 (227) hide show
  1. package/CHANGELOG.md +163 -0
  2. package/dist/cable/index.d.ts +16 -16
  3. package/dist/cable/index.js.map +1 -1
  4. package/dist/controllers/accordion_controller.d.ts +3 -2
  5. package/dist/controllers/accordion_controller.js +10 -0
  6. package/dist/controllers/accordion_controller.js.map +1 -1
  7. package/dist/controllers/alert_dialog_controller.d.ts +64 -0
  8. package/dist/controllers/alert_dialog_controller.js +318 -0
  9. package/dist/controllers/alert_dialog_controller.js.map +1 -0
  10. package/dist/controllers/breadcrumb_controller.d.ts +97 -10
  11. package/dist/controllers/breadcrumb_controller.js +225 -13
  12. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  13. package/dist/controllers/calendar_controller.d.ts +11 -1
  14. package/dist/controllers/calendar_controller.js +89 -22
  15. package/dist/controllers/calendar_controller.js.map +1 -1
  16. package/dist/controllers/carousel_controller.d.ts +115 -0
  17. package/dist/controllers/carousel_controller.js +313 -0
  18. package/dist/controllers/carousel_controller.js.map +1 -0
  19. package/dist/controllers/clipboard_controller.d.ts +72 -0
  20. package/dist/controllers/clipboard_controller.js +144 -0
  21. package/dist/controllers/clipboard_controller.js.map +1 -0
  22. package/dist/controllers/collapsible_controller.d.ts +70 -0
  23. package/dist/controllers/collapsible_controller.js +327 -0
  24. package/dist/controllers/collapsible_controller.js.map +1 -0
  25. package/dist/controllers/color_picker_controller.d.ts +87 -0
  26. package/dist/controllers/color_picker_controller.js +252 -0
  27. package/dist/controllers/color_picker_controller.js.map +1 -0
  28. package/dist/controllers/combobox_controller.d.ts +14 -1
  29. package/dist/controllers/combobox_controller.js +162 -23
  30. package/dist/controllers/combobox_controller.js.map +1 -1
  31. package/dist/controllers/command_palette_controller.d.ts +2 -0
  32. package/dist/controllers/command_palette_controller.js +194 -17
  33. package/dist/controllers/command_palette_controller.js.map +1 -1
  34. package/dist/controllers/confirm_controller.js.map +1 -1
  35. package/dist/controllers/context_menu_controller.d.ts +7 -6
  36. package/dist/controllers/context_menu_controller.js +32 -10
  37. package/dist/controllers/context_menu_controller.js.map +1 -1
  38. package/dist/controllers/count_up_controller.js +8 -1
  39. package/dist/controllers/count_up_controller.js.map +1 -1
  40. package/dist/controllers/currency_input_controller.d.ts +72 -0
  41. package/dist/controllers/currency_input_controller.js +147 -0
  42. package/dist/controllers/currency_input_controller.js.map +1 -0
  43. package/dist/controllers/data_grid_controller.d.ts +89 -0
  44. package/dist/controllers/data_grid_controller.js +246 -0
  45. package/dist/controllers/data_grid_controller.js.map +1 -0
  46. package/dist/controllers/date_range_picker_controller.d.ts +88 -0
  47. package/dist/controllers/date_range_picker_controller.js +441 -0
  48. package/dist/controllers/date_range_picker_controller.js.map +1 -0
  49. package/dist/controllers/dialog_controller.js.map +1 -1
  50. package/dist/controllers/dismissible_controller.d.ts +57 -0
  51. package/dist/controllers/dismissible_controller.js +117 -0
  52. package/dist/controllers/dismissible_controller.js.map +1 -0
  53. package/dist/controllers/drawer_controller.d.ts +96 -0
  54. package/dist/controllers/drawer_controller.js +630 -0
  55. package/dist/controllers/drawer_controller.js.map +1 -0
  56. package/dist/controllers/dropdown_controller.d.ts +1 -2
  57. package/dist/controllers/dropdown_controller.js.map +1 -1
  58. package/dist/controllers/editable_controller.d.ts +73 -0
  59. package/dist/controllers/editable_controller.js +169 -0
  60. package/dist/controllers/editable_controller.js.map +1 -0
  61. package/dist/controllers/file_dropzone_controller.d.ts +92 -0
  62. package/dist/controllers/file_dropzone_controller.js +165 -0
  63. package/dist/controllers/file_dropzone_controller.js.map +1 -0
  64. package/dist/controllers/filter_controller.d.ts +60 -0
  65. package/dist/controllers/filter_controller.js +86 -0
  66. package/dist/controllers/filter_controller.js.map +1 -0
  67. package/dist/controllers/flash_controller.js +36 -5
  68. package/dist/controllers/flash_controller.js.map +1 -1
  69. package/dist/controllers/focus_controller.d.ts +1 -1
  70. package/dist/controllers/focus_controller.js.map +1 -1
  71. package/dist/controllers/form_field_controller.js.map +1 -1
  72. package/dist/controllers/form_validation_controller.js +1 -1
  73. package/dist/controllers/form_validation_controller.js.map +1 -1
  74. package/dist/controllers/highlight_controller.js +6 -4
  75. package/dist/controllers/highlight_controller.js.map +1 -1
  76. package/dist/controllers/hover_card_controller.d.ts +1 -1
  77. package/dist/controllers/hover_card_controller.js.map +1 -1
  78. package/dist/controllers/intersection_controller.js +67 -19
  79. package/dist/controllers/intersection_controller.js.map +1 -1
  80. package/dist/controllers/lazy_frame_controller.js +54 -11
  81. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  82. package/dist/controllers/listbox_controller.d.ts +9 -2
  83. package/dist/controllers/listbox_controller.js +257 -53
  84. package/dist/controllers/listbox_controller.js.map +1 -1
  85. package/dist/controllers/local_time_controller.js +2 -2
  86. package/dist/controllers/local_time_controller.js.map +1 -1
  87. package/dist/controllers/masonry_controller.d.ts +54 -0
  88. package/dist/controllers/masonry_controller.js +142 -0
  89. package/dist/controllers/masonry_controller.js.map +1 -0
  90. package/dist/controllers/menu_controller.d.ts +29 -11
  91. package/dist/controllers/menu_controller.js +104 -17
  92. package/dist/controllers/menu_controller.js.map +1 -1
  93. package/dist/controllers/menubar_controller.d.ts +142 -0
  94. package/dist/controllers/menubar_controller.js +785 -0
  95. package/dist/controllers/menubar_controller.js.map +1 -0
  96. package/dist/controllers/meter_controller.js.map +1 -1
  97. package/dist/controllers/multi_select_controller.d.ts +141 -0
  98. package/dist/controllers/multi_select_controller.js +755 -0
  99. package/dist/controllers/multi_select_controller.js.map +1 -0
  100. package/dist/controllers/navigation_menu_controller.d.ts +146 -0
  101. package/dist/controllers/navigation_menu_controller.js +511 -0
  102. package/dist/controllers/navigation_menu_controller.js.map +1 -0
  103. package/dist/controllers/nested_form_controller.d.ts +4 -5
  104. package/dist/controllers/nested_form_controller.js.map +1 -1
  105. package/dist/controllers/number_input_controller.d.ts +1 -1
  106. package/dist/controllers/number_input_controller.js +7 -0
  107. package/dist/controllers/number_input_controller.js.map +1 -1
  108. package/dist/controllers/otp_controller.js +18 -1
  109. package/dist/controllers/otp_controller.js.map +1 -1
  110. package/dist/controllers/overflow_indicator_controller.d.ts +15 -6
  111. package/dist/controllers/overflow_indicator_controller.js +246 -27
  112. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  113. package/dist/controllers/overflow_menu_controller.d.ts +78 -19
  114. package/dist/controllers/overflow_menu_controller.js +381 -57
  115. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  116. package/dist/controllers/pagination_controller.d.ts +36 -5
  117. package/dist/controllers/pagination_controller.js +163 -32
  118. package/dist/controllers/pagination_controller.js.map +1 -1
  119. package/dist/controllers/password_reveal_controller.d.ts +49 -0
  120. package/dist/controllers/password_reveal_controller.js +117 -0
  121. package/dist/controllers/password_reveal_controller.js.map +1 -0
  122. package/dist/controllers/persist_controller.js +6 -6
  123. package/dist/controllers/persist_controller.js.map +1 -1
  124. package/dist/controllers/pointer_drag_controller.d.ts +2 -3
  125. package/dist/controllers/pointer_drag_controller.js +9 -1
  126. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  127. package/dist/controllers/popover_controller.d.ts +1 -2
  128. package/dist/controllers/popover_controller.js +2 -2
  129. package/dist/controllers/popover_controller.js.map +1 -1
  130. package/dist/controllers/portal_controller.d.ts +4 -4
  131. package/dist/controllers/portal_controller.js.map +1 -1
  132. package/dist/controllers/progress_controller.js.map +1 -1
  133. package/dist/controllers/radio_group_controller.js +22 -3
  134. package/dist/controllers/radio_group_controller.js.map +1 -1
  135. package/dist/controllers/range_slider_controller.d.ts +97 -0
  136. package/dist/controllers/range_slider_controller.js +192 -0
  137. package/dist/controllers/range_slider_controller.js.map +1 -0
  138. package/dist/controllers/rating_controller.js +16 -2
  139. package/dist/controllers/rating_controller.js.map +1 -1
  140. package/dist/controllers/read_more_controller.d.ts +52 -0
  141. package/dist/controllers/read_more_controller.js +238 -0
  142. package/dist/controllers/read_more_controller.js.map +1 -0
  143. package/dist/controllers/reading_progress_controller.js.map +1 -1
  144. package/dist/controllers/resizable_controller.d.ts +3 -1
  145. package/dist/controllers/resizable_controller.js +65 -1
  146. package/dist/controllers/resizable_controller.js.map +1 -1
  147. package/dist/controllers/roving_controller.js +17 -2
  148. package/dist/controllers/roving_controller.js.map +1 -1
  149. package/dist/controllers/scroll_area_controller.js +101 -14
  150. package/dist/controllers/scroll_area_controller.js.map +1 -1
  151. package/dist/controllers/scroll_restore_controller.d.ts +54 -0
  152. package/dist/controllers/scroll_restore_controller.js +93 -0
  153. package/dist/controllers/scroll_restore_controller.js.map +1 -0
  154. package/dist/controllers/scroll_visibility_controller.d.ts +6 -3
  155. package/dist/controllers/scroll_visibility_controller.js +40 -6
  156. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  157. package/dist/controllers/scrollspy_controller.d.ts +64 -15
  158. package/dist/controllers/scrollspy_controller.js +369 -74
  159. package/dist/controllers/scrollspy_controller.js.map +1 -1
  160. package/dist/controllers/separator_controller.d.ts +69 -0
  161. package/dist/controllers/separator_controller.js +96 -0
  162. package/dist/controllers/separator_controller.js.map +1 -0
  163. package/dist/controllers/sidebar_controller.d.ts +107 -0
  164. package/dist/controllers/sidebar_controller.js +761 -0
  165. package/dist/controllers/sidebar_controller.js.map +1 -0
  166. package/dist/controllers/skeleton_controller.js +1 -1
  167. package/dist/controllers/skeleton_controller.js.map +1 -1
  168. package/dist/controllers/slider_controller.d.ts +11 -1
  169. package/dist/controllers/slider_controller.js +32 -6
  170. package/dist/controllers/slider_controller.js.map +1 -1
  171. package/dist/controllers/sortable_controller.d.ts +3 -1
  172. package/dist/controllers/sortable_controller.js +34 -3
  173. package/dist/controllers/sortable_controller.js.map +1 -1
  174. package/dist/controllers/spinner_controller.js +1 -1
  175. package/dist/controllers/spinner_controller.js.map +1 -1
  176. package/dist/controllers/stepper_controller.d.ts +4 -0
  177. package/dist/controllers/stepper_controller.js +28 -12
  178. package/dist/controllers/stepper_controller.js.map +1 -1
  179. package/dist/controllers/stick_to_bottom_controller.d.ts +5 -6
  180. package/dist/controllers/stick_to_bottom_controller.js +9 -4
  181. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  182. package/dist/controllers/sticky_observer_controller.d.ts +17 -4
  183. package/dist/controllers/sticky_observer_controller.js +109 -20
  184. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  185. package/dist/controllers/submit_once_controller.d.ts +2 -2
  186. package/dist/controllers/submit_once_controller.js.map +1 -1
  187. package/dist/controllers/switch_controller.js +1 -0
  188. package/dist/controllers/switch_controller.js.map +1 -1
  189. package/dist/controllers/tabs_controller.d.ts +13 -4
  190. package/dist/controllers/tabs_controller.js +26 -3
  191. package/dist/controllers/tabs_controller.js.map +1 -1
  192. package/dist/controllers/tags_input_controller.d.ts +91 -0
  193. package/dist/controllers/tags_input_controller.js +295 -0
  194. package/dist/controllers/tags_input_controller.js.map +1 -0
  195. package/dist/controllers/theme_controller.d.ts +1 -1
  196. package/dist/controllers/theme_controller.js +42 -13
  197. package/dist/controllers/theme_controller.js.map +1 -1
  198. package/dist/controllers/time_picker_controller.d.ts +67 -0
  199. package/dist/controllers/time_picker_controller.js +231 -0
  200. package/dist/controllers/time_picker_controller.js.map +1 -0
  201. package/dist/controllers/toast_controller.d.ts +5 -14
  202. package/dist/controllers/toast_controller.js +40 -14
  203. package/dist/controllers/toast_controller.js.map +1 -1
  204. package/dist/controllers/toggle_group_controller.js +23 -2
  205. package/dist/controllers/toggle_group_controller.js.map +1 -1
  206. package/dist/controllers/toolbar_controller.d.ts +53 -13
  207. package/dist/controllers/toolbar_controller.js +230 -31
  208. package/dist/controllers/toolbar_controller.js.map +1 -1
  209. package/dist/controllers/tooltip_controller.d.ts +1 -1
  210. package/dist/controllers/tooltip_controller.js.map +1 -1
  211. package/dist/controllers/transition_controller.d.ts +14 -8
  212. package/dist/controllers/transition_controller.js +153 -38
  213. package/dist/controllers/transition_controller.js.map +1 -1
  214. package/dist/controllers/tree_view_controller.d.ts +135 -0
  215. package/dist/controllers/tree_view_controller.js +691 -0
  216. package/dist/controllers/tree_view_controller.js.map +1 -0
  217. package/dist/index.d.ts +27 -1818
  218. package/dist/index.js +4256 -915
  219. package/dist/index.js.map +1 -1
  220. package/dist/inspector/cli.d.ts +375 -39
  221. package/dist/inspector/cli.js +228 -14
  222. package/dist/inspector/cli.js.map +1 -1
  223. package/dist/inspector/cli_bin.js +419 -48
  224. package/dist/inspector/cli_bin.js.map +1 -1
  225. package/dist/inspector/examples.json +26 -26
  226. package/dist/inspector/manifest.json +1054 -127
  227. package/package.json +5 -6
@@ -267,12 +267,17 @@ var DIAGNOSTIC_CODES = [
267
267
  "unknown-action-method",
268
268
  "orphan-target",
269
269
  "missing-required-target",
270
+ "missing-conditional-target",
270
271
  "missing-aria",
271
272
  "invalid-aria-value",
272
273
  "keyboard-inaccessible",
273
274
  "unresolved-idref",
274
275
  "managed-aria",
275
276
  "composition-mismatch",
277
+ "missing-companion",
278
+ "undeclared-target",
279
+ "cardinality-violation",
280
+ "forbidden-aria",
276
281
  "unknown-ignore-code"
277
282
  ];
278
283
 
@@ -290,6 +295,14 @@ function checkSource(source, manifest) {
290
295
  if (valueStart === void 0 || valueEnd === void 0) return false;
291
296
  return erbRanges.some(([start, end]) => start < valueEnd && end > valueStart);
292
297
  };
298
+ const conditionHolds = (scope, identifier, when) => {
299
+ if (!when) return true;
300
+ const hostAttr = scope.attrs.find(
301
+ (a) => a.name === `data-${identifier}-${dasherize(when.value)}-value`
302
+ );
303
+ if (hostAttr && isDynamicValue(hostAttr)) return null;
304
+ return when.equals.includes(hostAttr ? hostAttr.value.trim() : when.default);
305
+ };
293
306
  const suppressed = (node, code) => {
294
307
  if (code === "unknown-ignore-code") return false;
295
308
  let current = node;
@@ -337,7 +350,40 @@ function checkSource(source, manifest) {
337
350
  if (found) found.push(el);
338
351
  else byName.set(target, [el]);
339
352
  };
353
+ const contentHolds = (scope, identifier, element, condition) => {
354
+ if (!condition) return true;
355
+ const nodes = targetNodes.get(scope)?.get(identifier)?.get(condition.target) ?? [];
356
+ let held = 0;
357
+ for (const node of nodes) if (isWithin(node, element)) held += 1;
358
+ if (condition.min !== void 0 && held < condition.min) return false;
359
+ if (condition.max !== void 0 && held > condition.max) return false;
360
+ return true;
361
+ };
362
+ const elementHolds = (element, condition) => !condition?.exceptTags.includes(element.tag);
363
+ const roleTally = /* @__PURE__ */ new Map();
364
+ const focusableRoleTally = /* @__PURE__ */ new Map();
365
+ const documentHolds = (condition) => {
366
+ if (condition === void 0) return false;
367
+ const tally = condition.focusable ? focusableRoleTally : roleTally;
368
+ return (tally.get(condition.role) ?? 0) >= condition.atLeast;
369
+ };
370
+ const declarationAttrs = /* @__PURE__ */ new Set();
371
+ for (const controller of Object.values(known)) {
372
+ for (const rule of controller.targetDeclarations) declarationAttrs.add(rule.attr);
373
+ }
374
+ const declarationCandidates = [];
340
375
  walk(tree, (node) => {
376
+ if (declarationAttrs.size > 0 && node.attrs.some((a) => declarationAttrs.has(a.name))) {
377
+ declarationCandidates.push(node);
378
+ }
379
+ const roleAttr = node.attrs.find((a) => a.name === "role");
380
+ if (roleAttr && !isDynamicValue(roleAttr)) {
381
+ const role = roleAttr.value.trim();
382
+ roleTally.set(role, (roleTally.get(role) ?? 0) + 1);
383
+ if (isFocusable(node, "tab")) {
384
+ focusableRoleTally.set(role, (focusableRoleTally.get(role) ?? 0) + 1);
385
+ }
386
+ }
341
387
  for (const attr of node.attrs) {
342
388
  if (attr.name === IGNORE_ATTR) {
343
389
  for (const token of attr.value.split(/\s+/).filter((t) => t.length > 0)) {
@@ -416,7 +462,7 @@ function checkSource(source, manifest) {
416
462
  );
417
463
  continue;
418
464
  }
419
- const targetName = attr.value.trim();
465
+ const targetName = isDynamicValue(attr) ? "" : attr.value.trim();
420
466
  if (targetName.length > 0 && !controller.targets.includes(targetName)) {
421
467
  const best = nearestName(targetName, controller.targets);
422
468
  report(
@@ -520,13 +566,36 @@ function checkSource(source, manifest) {
520
566
  }
521
567
  }
522
568
  }
569
+ for (const { node, identifier } of scopes) {
570
+ const controller = known[identifier];
571
+ if (!controller) continue;
572
+ const present = presence.get(node)?.get(identifier) ?? /* @__PURE__ */ new Set();
573
+ for (const rule of controller.conditionalTargets) {
574
+ if (!present.has(rule.whenPresent)) continue;
575
+ const missing = rule.require.filter((target) => !present.has(target));
576
+ if (missing.length === 0) continue;
577
+ report(
578
+ node,
579
+ "missing-conditional-target",
580
+ "error",
581
+ `"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
582
+ node,
583
+ rule.suggestion
584
+ );
585
+ }
586
+ }
523
587
  for (const { node, identifier } of scopes) {
524
588
  const controller = known[identifier];
525
589
  if (!controller) continue;
526
590
  for (const req of controller.a11y) {
591
+ if (conditionHolds(node, identifier, req.when) !== true) continue;
527
592
  const elements = req.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(req.target) ?? [];
593
+ if (req.whenDocument && !documentHolds(req.whenDocument)) continue;
594
+ const severity = documentHolds(req.escalateWhen) ? "error" : req.severity ?? "error";
528
595
  for (const el of elements) {
529
- checkA11y(report, el, identifier, req);
596
+ if (!contentHolds(node, identifier, el, req.whenContains)) continue;
597
+ if (!elementHolds(el, req.whenElement)) continue;
598
+ checkA11y(report, el, identifier, req, severity);
530
599
  }
531
600
  }
532
601
  }
@@ -573,6 +642,32 @@ function checkSource(source, manifest) {
573
642
  }
574
643
  }
575
644
  }
645
+ for (const { node, identifier } of scopes) {
646
+ const controller = known[identifier];
647
+ if (!controller) continue;
648
+ for (const rule of controller.forbiddenAria) {
649
+ const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
650
+ const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
651
+ for (const el of elements) {
652
+ if (!contentHolds(node, identifier, el, rule.whenContains)) continue;
653
+ for (const name of rule.attrs) {
654
+ const attr = el.attrs.find((a) => a.name === name);
655
+ if (!attr) continue;
656
+ if (rule.values && (isDynamicValue(attr) || !rule.values.includes(attr.value.trim()))) {
657
+ continue;
658
+ }
659
+ report(
660
+ el,
661
+ "forbidden-aria",
662
+ "warning",
663
+ `${name} on the ${where} of "${identifier}" contradicts the markup around it.`,
664
+ attr,
665
+ rule.suggestion
666
+ );
667
+ }
668
+ }
669
+ }
670
+ }
576
671
  for (const { node, identifier } of scopes) {
577
672
  const controller = known[identifier];
578
673
  if (!controller) continue;
@@ -581,14 +676,7 @@ function checkSource(source, manifest) {
581
676
  const elements = rule.target === "" ? [node] : targeted ?? (rule.fallbackToScope ? [node] : []);
582
677
  const where = rule.target === "" || !targeted ? "scope element" : `"${rule.target}" target`;
583
678
  const when = rule.when;
584
- if (when) {
585
- const hostAttr = node.attrs.find(
586
- (a) => a.name === `data-${identifier}-${dasherize(when.value)}-value`
587
- );
588
- if (hostAttr && isDynamicValue(hostAttr)) continue;
589
- const hostEffective = hostAttr ? hostAttr.value.trim() : when.default;
590
- if (!when.equals.includes(hostEffective)) continue;
591
- }
679
+ if (conditionHolds(node, identifier, when) !== true) continue;
592
680
  const hostContext = when ? ` (${when.value} ${list(when.equals)})` : "";
593
681
  for (const el of elements) {
594
682
  const declared = el.attrs.find((a) => a.name === "data-controller");
@@ -624,6 +712,113 @@ function checkSource(source, manifest) {
624
712
  }
625
713
  }
626
714
  }
715
+ for (const { node, identifier } of scopes) {
716
+ const controller = known[identifier];
717
+ if (!controller) continue;
718
+ for (const rule of controller.companions) {
719
+ const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
720
+ const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
721
+ for (const el of elements) {
722
+ const declared = el.attrs.find((a) => a.name === "data-controller");
723
+ if (declared && isDynamicValue(declared)) continue;
724
+ if (declared && controllerIdentifiers(declared.value).includes(rule.controller)) continue;
725
+ report(
726
+ el,
727
+ "missing-companion",
728
+ "error",
729
+ `The ${where} of "${identifier}" must also declare "${rule.controller}" in data-controller.`,
730
+ declared ?? el,
731
+ rule.suggestion,
732
+ // Appending to an existing list is unambiguous; creating the
733
+ // attribute from nothing is an insertion the anchor cannot express.
734
+ declared ? valueFix(
735
+ declared,
736
+ `${declared.value.trim()} ${rule.controller}`.trim(),
737
+ `Add "${rule.controller}" to data-controller`
738
+ ) : void 0
739
+ );
740
+ }
741
+ }
742
+ }
743
+ for (const { node, identifier } of scopes) {
744
+ const controller = known[identifier];
745
+ if (!controller) continue;
746
+ if (controller.targetDeclarations.length === 0) continue;
747
+ const targetAttrName = `data-${identifier}-target`;
748
+ for (const rule of controller.targetDeclarations) {
749
+ for (const el of declarationCandidates) {
750
+ const marker = el.attrs.find((a) => a.name === rule.attr);
751
+ if (!marker || isDynamicValue(marker)) continue;
752
+ const value = marker.value.trim();
753
+ if (value.length === 0) continue;
754
+ if (rule.values && !rule.values.includes(value)) continue;
755
+ if (findOwner(el, identifier) !== node) continue;
756
+ const declaredAs = el.attrs.find((a) => a.name === targetAttrName);
757
+ if (declaredAs && !isDynamicValue(declaredAs) && declaredAs.value.trim() === rule.target) {
758
+ continue;
759
+ }
760
+ if (declaredAs && isDynamicValue(declaredAs)) continue;
761
+ report(
762
+ el,
763
+ "undeclared-target",
764
+ "error",
765
+ `${rule.attr}="${value}" inside "${identifier}" is not declared as its "${rule.target}" target, so the controller never manages it.`,
766
+ marker,
767
+ rule.suggestion
768
+ );
769
+ }
770
+ }
771
+ }
772
+ for (const { node, identifier } of scopes) {
773
+ const controller = known[identifier];
774
+ if (!controller) continue;
775
+ for (const rule of controller.cardinality) {
776
+ if (conditionHolds(node, identifier, rule.when) !== true) continue;
777
+ const byName = targetNodes.get(node)?.get(identifier);
778
+ const counted = byName?.get(rule.target) ?? [];
779
+ const containers = rule.within === "" ? [node] : byName?.get(rule.within) ?? [];
780
+ const what = describeCounted(rule.target, rule.attr, rule.values);
781
+ const where = rule.within === "" ? "controller scope" : `"${rule.within}" target`;
782
+ for (const container of containers) {
783
+ const matched = [];
784
+ for (const el of counted) {
785
+ if (!isWithin(el, container)) continue;
786
+ if (rule.attr === void 0) {
787
+ matched.push({ el, at: el });
788
+ continue;
789
+ }
790
+ const attr = el.attrs.find((a) => a.name === rule.attr);
791
+ if (!attr) continue;
792
+ if (rule.values && (isDynamicValue(attr) || !rule.values.includes(attr.value.trim()))) {
793
+ continue;
794
+ }
795
+ matched.push({ el, at: attr });
796
+ }
797
+ if (rule.max !== void 0 && matched.length > rule.max) {
798
+ for (const { el, at } of matched.slice(rule.max)) {
799
+ report(
800
+ el,
801
+ "cardinality-violation",
802
+ "error",
803
+ `"${identifier}" allows at most ${rule.max} ${what} per ${where}, but found ${matched.length}.`,
804
+ at,
805
+ rule.suggestion
806
+ );
807
+ }
808
+ }
809
+ if (rule.min !== void 0 && matched.length < rule.min) {
810
+ report(
811
+ container,
812
+ "cardinality-violation",
813
+ "error",
814
+ `"${identifier}" requires at least ${rule.min} ${what} per ${where}, but found ${matched.length}.`,
815
+ container,
816
+ rule.suggestion
817
+ );
818
+ }
819
+ }
820
+ }
821
+ }
627
822
  const ids = /* @__PURE__ */ new Set();
628
823
  walk(tree, (node) => {
629
824
  const id = node.attrs.find((a) => a.name === "id");
@@ -661,7 +856,7 @@ function checkSource(source, manifest) {
661
856
  diagnostics.sort((a, b) => a.line - b.line || a.column - b.column);
662
857
  return diagnostics;
663
858
  }
664
- function checkA11y(report, el, identifier, req) {
859
+ function checkA11y(report, el, identifier, req, severity) {
665
860
  const groups = [
666
861
  { attrs: req.attrs, values: req.values },
667
862
  ...req.or ?? []
@@ -690,11 +885,12 @@ function checkA11y(report, el, identifier, req) {
690
885
  }
691
886
  }
692
887
  if (!anyPresent) {
888
+ const verb = severity === "warning" ? "recommends" : "requires";
693
889
  report(
694
890
  el,
695
891
  "missing-aria",
696
- "error",
697
- `"${identifier}" requires ${describeAttrs([...allowedByAttr.keys()])} on its ${where}.`,
892
+ severity,
893
+ `"${identifier}" ${verb} ${describeAttrs([...allowedByAttr.keys()])} on its ${where}.`,
698
894
  el,
699
895
  req.suggestion
700
896
  );
@@ -713,6 +909,19 @@ function checkA11y(report, el, identifier, req) {
713
909
  );
714
910
  }
715
911
  }
912
+ function isWithin(node, container) {
913
+ let current = node;
914
+ while (current) {
915
+ if (current === container) return true;
916
+ current = current.parent;
917
+ }
918
+ return false;
919
+ }
920
+ function describeCounted(target, attr, values) {
921
+ if (attr === void 0) return `"${target}" targets`;
922
+ const constrained = values && values.length > 0 ? ` set to ${list(values)}` : "";
923
+ return `"${target}" targets with ${attr}${constrained}`;
924
+ }
716
925
  var NATIVE_FOCUSABLE_TAGS = /* @__PURE__ */ new Set(["button", "select", "textarea", "summary"]);
717
926
  function isNativelyFocusable(el) {
718
927
  if (NATIVE_FOCUSABLE_TAGS.has(el.tag)) return true;
@@ -866,10 +1075,15 @@ var CONTROLLER_ARRAY_FIELDS = [
866
1075
  "actions",
867
1076
  "events",
868
1077
  "requiredTargets",
1078
+ "conditionalTargets",
869
1079
  "a11y",
870
1080
  "keyboard",
871
1081
  "managedAria",
872
- "compositions"
1082
+ "compositions",
1083
+ "companions",
1084
+ "targetDeclarations",
1085
+ "cardinality",
1086
+ "forbiddenAria"
873
1087
  ];
874
1088
  function isCompatibleManifest(value) {
875
1089
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;