@hjmds/design-contracts 0.8.2

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 (353) hide show
  1. package/README.md +208 -0
  2. package/dist/alert-dialog.d.ts +102 -0
  3. package/dist/alert-dialog.d.ts.map +1 -0
  4. package/dist/alert-dialog.js +136 -0
  5. package/dist/alert-dialog.js.map +1 -0
  6. package/dist/base-recipes.d.ts +184 -0
  7. package/dist/base-recipes.d.ts.map +1 -0
  8. package/dist/base-recipes.js +129 -0
  9. package/dist/base-recipes.js.map +1 -0
  10. package/dist/behaviors.d.ts +1254 -0
  11. package/dist/behaviors.d.ts.map +1 -0
  12. package/dist/behaviors.js +972 -0
  13. package/dist/behaviors.js.map +1 -0
  14. package/dist/bottom-navigation-defaults.d.ts +13 -0
  15. package/dist/bottom-navigation-defaults.d.ts.map +1 -0
  16. package/dist/bottom-navigation-defaults.js +10 -0
  17. package/dist/bottom-navigation-defaults.js.map +1 -0
  18. package/dist/bottom-navigation.d.ts +114 -0
  19. package/dist/bottom-navigation.d.ts.map +1 -0
  20. package/dist/bottom-navigation.js +223 -0
  21. package/dist/bottom-navigation.js.map +1 -0
  22. package/dist/breadcrumb.d.ts +97 -0
  23. package/dist/breadcrumb.d.ts.map +1 -0
  24. package/dist/breadcrumb.js +99 -0
  25. package/dist/breadcrumb.js.map +1 -0
  26. package/dist/calendar.d.ts +285 -0
  27. package/dist/calendar.d.ts.map +1 -0
  28. package/dist/calendar.js +297 -0
  29. package/dist/calendar.js.map +1 -0
  30. package/dist/card.d.ts +39 -0
  31. package/dist/card.d.ts.map +1 -0
  32. package/dist/card.js +50 -0
  33. package/dist/card.js.map +1 -0
  34. package/dist/carousel.d.ts +180 -0
  35. package/dist/carousel.d.ts.map +1 -0
  36. package/dist/carousel.js +172 -0
  37. package/dist/carousel.js.map +1 -0
  38. package/dist/catalog.d.ts +7158 -0
  39. package/dist/catalog.d.ts.map +1 -0
  40. package/dist/catalog.js +220 -0
  41. package/dist/catalog.js.map +1 -0
  42. package/dist/collection.d.ts +123 -0
  43. package/dist/collection.d.ts.map +1 -0
  44. package/dist/collection.js +211 -0
  45. package/dist/collection.js.map +1 -0
  46. package/dist/color-references.d.ts +46 -0
  47. package/dist/color-references.d.ts.map +1 -0
  48. package/dist/color-references.js +39 -0
  49. package/dist/color-references.js.map +1 -0
  50. package/dist/colors.d.ts +56 -0
  51. package/dist/colors.d.ts.map +1 -0
  52. package/dist/colors.js +90 -0
  53. package/dist/colors.js.map +1 -0
  54. package/dist/command-palette.d.ts +240 -0
  55. package/dist/command-palette.d.ts.map +1 -0
  56. package/dist/command-palette.js +97 -0
  57. package/dist/command-palette.js.map +1 -0
  58. package/dist/component-contracts.d.ts +137 -0
  59. package/dist/component-contracts.d.ts.map +1 -0
  60. package/dist/component-contracts.js +53 -0
  61. package/dist/component-contracts.js.map +1 -0
  62. package/dist/component-definitions.d.ts +135 -0
  63. package/dist/component-definitions.d.ts.map +1 -0
  64. package/dist/component-definitions.js +142 -0
  65. package/dist/component-definitions.js.map +1 -0
  66. package/dist/component-recipes.d.ts +3583 -0
  67. package/dist/component-recipes.d.ts.map +1 -0
  68. package/dist/component-recipes.js +1503 -0
  69. package/dist/component-recipes.js.map +1 -0
  70. package/dist/component-references.d.ts +416 -0
  71. package/dist/component-references.d.ts.map +1 -0
  72. package/dist/component-references.js +140 -0
  73. package/dist/component-references.js.map +1 -0
  74. package/dist/content-state.d.ts +104 -0
  75. package/dist/content-state.d.ts.map +1 -0
  76. package/dist/content-state.js +116 -0
  77. package/dist/content-state.js.map +1 -0
  78. package/dist/counter-badge-recipe.d.ts +80 -0
  79. package/dist/counter-badge-recipe.d.ts.map +1 -0
  80. package/dist/counter-badge-recipe.js +44 -0
  81. package/dist/counter-badge-recipe.js.map +1 -0
  82. package/dist/counter-badge.d.ts +12 -0
  83. package/dist/counter-badge.d.ts.map +1 -0
  84. package/dist/counter-badge.js +21 -0
  85. package/dist/counter-badge.js.map +1 -0
  86. package/dist/data-table.d.ts +203 -0
  87. package/dist/data-table.d.ts.map +1 -0
  88. package/dist/data-table.js +182 -0
  89. package/dist/data-table.js.map +1 -0
  90. package/dist/date-picker.d.ts +268 -0
  91. package/dist/date-picker.d.ts.map +1 -0
  92. package/dist/date-picker.js +168 -0
  93. package/dist/date-picker.js.map +1 -0
  94. package/dist/description-list.d.ts +68 -0
  95. package/dist/description-list.d.ts.map +1 -0
  96. package/dist/description-list.js +85 -0
  97. package/dist/description-list.js.map +1 -0
  98. package/dist/design-system-provider.d.ts +80 -0
  99. package/dist/design-system-provider.d.ts.map +1 -0
  100. package/dist/design-system-provider.js +147 -0
  101. package/dist/design-system-provider.js.map +1 -0
  102. package/dist/evidence.d.ts +49 -0
  103. package/dist/evidence.d.ts.map +1 -0
  104. package/dist/evidence.js +133 -0
  105. package/dist/evidence.js.map +1 -0
  106. package/dist/file-picker.d.ts +183 -0
  107. package/dist/file-picker.d.ts.map +1 -0
  108. package/dist/file-picker.js +224 -0
  109. package/dist/file-picker.js.map +1 -0
  110. package/dist/floating-action-button.d.ts +143 -0
  111. package/dist/floating-action-button.d.ts.map +1 -0
  112. package/dist/floating-action-button.js +149 -0
  113. package/dist/floating-action-button.js.map +1 -0
  114. package/dist/form.d.ts +143 -0
  115. package/dist/form.d.ts.map +1 -0
  116. package/dist/form.js +206 -0
  117. package/dist/form.js.map +1 -0
  118. package/dist/foundations.d.ts +300 -0
  119. package/dist/foundations.d.ts.map +1 -0
  120. package/dist/foundations.js +238 -0
  121. package/dist/foundations.js.map +1 -0
  122. package/dist/grid.d.ts +75 -0
  123. package/dist/grid.d.ts.map +1 -0
  124. package/dist/grid.js +133 -0
  125. package/dist/grid.js.map +1 -0
  126. package/dist/icon-button-recipe.d.ts +99 -0
  127. package/dist/icon-button-recipe.d.ts.map +1 -0
  128. package/dist/icon-button-recipe.js +53 -0
  129. package/dist/icon-button-recipe.js.map +1 -0
  130. package/dist/icon.d.ts +41 -0
  131. package/dist/icon.d.ts.map +1 -0
  132. package/dist/icon.js +147 -0
  133. package/dist/icon.js.map +1 -0
  134. package/dist/image.d.ts +82 -0
  135. package/dist/image.d.ts.map +1 -0
  136. package/dist/image.js +100 -0
  137. package/dist/image.js.map +1 -0
  138. package/dist/index.d.ts +58 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +66 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/layout.d.ts +118 -0
  143. package/dist/layout.d.ts.map +1 -0
  144. package/dist/layout.js +118 -0
  145. package/dist/layout.js.map +1 -0
  146. package/dist/link.d.ts +57 -0
  147. package/dist/link.d.ts.map +1 -0
  148. package/dist/link.js +142 -0
  149. package/dist/link.js.map +1 -0
  150. package/dist/load-more.d.ts +64 -0
  151. package/dist/load-more.d.ts.map +1 -0
  152. package/dist/load-more.js +124 -0
  153. package/dist/load-more.js.map +1 -0
  154. package/dist/mentions.d.ts +67 -0
  155. package/dist/mentions.d.ts.map +1 -0
  156. package/dist/mentions.js +107 -0
  157. package/dist/mentions.js.map +1 -0
  158. package/dist/number-field.d.ts +208 -0
  159. package/dist/number-field.d.ts.map +1 -0
  160. package/dist/number-field.js +247 -0
  161. package/dist/number-field.js.map +1 -0
  162. package/dist/otp-field.d.ts +152 -0
  163. package/dist/otp-field.d.ts.map +1 -0
  164. package/dist/otp-field.js +117 -0
  165. package/dist/otp-field.js.map +1 -0
  166. package/dist/pagination.d.ts +181 -0
  167. package/dist/pagination.d.ts.map +1 -0
  168. package/dist/pagination.js +226 -0
  169. package/dist/pagination.js.map +1 -0
  170. package/dist/password-field.d.ts +187 -0
  171. package/dist/password-field.d.ts.map +1 -0
  172. package/dist/password-field.js +120 -0
  173. package/dist/password-field.js.map +1 -0
  174. package/dist/popover.d.ts +152 -0
  175. package/dist/popover.d.ts.map +1 -0
  176. package/dist/popover.js +137 -0
  177. package/dist/popover.js.map +1 -0
  178. package/dist/progress-recipe.d.ts +43 -0
  179. package/dist/progress-recipe.d.ts.map +1 -0
  180. package/dist/progress-recipe.js +16 -0
  181. package/dist/progress-recipe.js.map +1 -0
  182. package/dist/recipes.d.ts +31 -0
  183. package/dist/recipes.d.ts.map +1 -0
  184. package/dist/recipes.js +46 -0
  185. package/dist/recipes.js.map +1 -0
  186. package/dist/responsive.d.ts +27 -0
  187. package/dist/responsive.d.ts.map +1 -0
  188. package/dist/responsive.js +66 -0
  189. package/dist/responsive.js.map +1 -0
  190. package/dist/result.d.ts +111 -0
  191. package/dist/result.d.ts.map +1 -0
  192. package/dist/result.js +97 -0
  193. package/dist/result.js.map +1 -0
  194. package/dist/selection-helpers.d.ts +16 -0
  195. package/dist/selection-helpers.d.ts.map +1 -0
  196. package/dist/selection-helpers.js +52 -0
  197. package/dist/selection-helpers.js.map +1 -0
  198. package/dist/semantic-colors.d.ts +275 -0
  199. package/dist/semantic-colors.d.ts.map +1 -0
  200. package/dist/semantic-colors.js +84 -0
  201. package/dist/semantic-colors.js.map +1 -0
  202. package/dist/sheet.d.ts +51 -0
  203. package/dist/sheet.d.ts.map +1 -0
  204. package/dist/sheet.js +69 -0
  205. package/dist/sheet.js.map +1 -0
  206. package/dist/showcase.d.ts +153 -0
  207. package/dist/showcase.d.ts.map +1 -0
  208. package/dist/showcase.js +210 -0
  209. package/dist/showcase.js.map +1 -0
  210. package/dist/side-panel.d.ts +199 -0
  211. package/dist/side-panel.d.ts.map +1 -0
  212. package/dist/side-panel.js +111 -0
  213. package/dist/side-panel.js.map +1 -0
  214. package/dist/slider.d.ts +138 -0
  215. package/dist/slider.d.ts.map +1 -0
  216. package/dist/slider.js +150 -0
  217. package/dist/slider.js.map +1 -0
  218. package/dist/splitter.d.ts +113 -0
  219. package/dist/splitter.d.ts.map +1 -0
  220. package/dist/splitter.js +99 -0
  221. package/dist/splitter.js.map +1 -0
  222. package/dist/statistic.d.ts +41 -0
  223. package/dist/statistic.d.ts.map +1 -0
  224. package/dist/statistic.js +76 -0
  225. package/dist/statistic.js.map +1 -0
  226. package/dist/steps.d.ts +196 -0
  227. package/dist/steps.d.ts.map +1 -0
  228. package/dist/steps.js +160 -0
  229. package/dist/steps.js.map +1 -0
  230. package/dist/tag.d.ts +128 -0
  231. package/dist/tag.d.ts.map +1 -0
  232. package/dist/tag.js +93 -0
  233. package/dist/tag.js.map +1 -0
  234. package/dist/timeline.d.ts +147 -0
  235. package/dist/timeline.d.ts.map +1 -0
  236. package/dist/timeline.js +127 -0
  237. package/dist/timeline.js.map +1 -0
  238. package/dist/toast.d.ts +164 -0
  239. package/dist/toast.d.ts.map +1 -0
  240. package/dist/toast.js +529 -0
  241. package/dist/toast.js.map +1 -0
  242. package/dist/tokens.d.ts +9 -0
  243. package/dist/tokens.d.ts.map +1 -0
  244. package/dist/tokens.js +9 -0
  245. package/dist/tokens.js.map +1 -0
  246. package/dist/tooltip.d.ts +44 -0
  247. package/dist/tooltip.d.ts.map +1 -0
  248. package/dist/tooltip.js +88 -0
  249. package/dist/tooltip.js.map +1 -0
  250. package/dist/tour.d.ts +218 -0
  251. package/dist/tour.d.ts.map +1 -0
  252. package/dist/tour.js +211 -0
  253. package/dist/tour.js.map +1 -0
  254. package/dist/transfer-list.d.ts +207 -0
  255. package/dist/transfer-list.d.ts.map +1 -0
  256. package/dist/transfer-list.js +193 -0
  257. package/dist/transfer-list.js.map +1 -0
  258. package/dist/tree-select.d.ts +78 -0
  259. package/dist/tree-select.d.ts.map +1 -0
  260. package/dist/tree-select.js +132 -0
  261. package/dist/tree-select.js.map +1 -0
  262. package/dist/tree.d.ts +206 -0
  263. package/dist/tree.d.ts.map +1 -0
  264. package/dist/tree.js +223 -0
  265. package/dist/tree.js.map +1 -0
  266. package/dist/upload-item.d.ts +173 -0
  267. package/dist/upload-item.d.ts.map +1 -0
  268. package/dist/upload-item.js +156 -0
  269. package/dist/upload-item.js.map +1 -0
  270. package/dist/version.d.ts +3 -0
  271. package/dist/version.d.ts.map +1 -0
  272. package/dist/version.js +3 -0
  273. package/dist/version.js.map +1 -0
  274. package/docs/affix.md +63 -0
  275. package/docs/anchor.md +59 -0
  276. package/docs/ant-design-coverage.md +119 -0
  277. package/docs/app-provider.md +50 -0
  278. package/docs/app-rn-adoption.md +227 -0
  279. package/docs/architecture.md +320 -0
  280. package/docs/authoring-brief.md +87 -0
  281. package/docs/border-beam.md +66 -0
  282. package/docs/bottom-navigation.md +127 -0
  283. package/docs/breadcrumb.md +82 -0
  284. package/docs/calendar.md +154 -0
  285. package/docs/carousel.md +130 -0
  286. package/docs/cascader.md +93 -0
  287. package/docs/catalog-decision-status.md +306 -0
  288. package/docs/color-picker.md +72 -0
  289. package/docs/command-palette.md +116 -0
  290. package/docs/confirm-popover.md +93 -0
  291. package/docs/consistency-audit.md +383 -0
  292. package/docs/consumer-release-gate.md +90 -0
  293. package/docs/content-state.md +156 -0
  294. package/docs/context-panel.md +88 -0
  295. package/docs/cross-platform-core-normalization.md +118 -0
  296. package/docs/data-table.md +79 -0
  297. package/docs/date-picker.md +84 -0
  298. package/docs/description-list.md +75 -0
  299. package/docs/design-system-provider.md +139 -0
  300. package/docs/dropdown.md +78 -0
  301. package/docs/expansion-roadmap.md +285 -0
  302. package/docs/file-picker.md +59 -0
  303. package/docs/floating-action-button.md +111 -0
  304. package/docs/form.md +134 -0
  305. package/docs/generated/component-maturity.md +103 -0
  306. package/docs/generated/renderer-evidence.json +5597 -0
  307. package/docs/generated/renderer-evidence.md +132 -0
  308. package/docs/generated/showcase-manifest.json +3732 -0
  309. package/docs/icon.md +22 -0
  310. package/docs/identity.md +121 -0
  311. package/docs/image.md +84 -0
  312. package/docs/implementation-0.5.md +85 -0
  313. package/docs/layout-primitives.md +107 -0
  314. package/docs/layout.md +83 -0
  315. package/docs/library-reference-decisions.md +123 -0
  316. package/docs/link.md +67 -0
  317. package/docs/load-more.md +31 -0
  318. package/docs/mentions.md +82 -0
  319. package/docs/migration-0.2.md +108 -0
  320. package/docs/migration-0.3.md +72 -0
  321. package/docs/migration-0.5.md +82 -0
  322. package/docs/migration-0.6.md +197 -0
  323. package/docs/notification.md +54 -0
  324. package/docs/number-field.md +82 -0
  325. package/docs/otp-field.md +103 -0
  326. package/docs/pagination.md +139 -0
  327. package/docs/password-field.md +98 -0
  328. package/docs/popover.md +124 -0
  329. package/docs/promotion-candidates.md +169 -0
  330. package/docs/qr-code.md +69 -0
  331. package/docs/rating.md +58 -0
  332. package/docs/responsive-grid.md +90 -0
  333. package/docs/result.md +96 -0
  334. package/docs/showcase.md +97 -0
  335. package/docs/side-panel.md +71 -0
  336. package/docs/slider.md +79 -0
  337. package/docs/splitter.md +55 -0
  338. package/docs/statistic.md +16 -0
  339. package/docs/steps.md +115 -0
  340. package/docs/tag.md +57 -0
  341. package/docs/time-picker.md +88 -0
  342. package/docs/timeline.md +146 -0
  343. package/docs/toast.md +127 -0
  344. package/docs/tooltip.md +58 -0
  345. package/docs/tour.md +136 -0
  346. package/docs/transfer-list.md +94 -0
  347. package/docs/tree-select.md +101 -0
  348. package/docs/tree.md +123 -0
  349. package/docs/upload-item.md +68 -0
  350. package/docs/utility.md +53 -0
  351. package/docs/virtual-list.md +70 -0
  352. package/docs/watermark.md +58 -0
  353. package/package.json +402 -0
@@ -0,0 +1,972 @@
1
+ import { sheetBehaviorDefaults } from "./sheet.js";
2
+ import { loadMoreBehaviorDefaults } from "./load-more.js";
3
+ import { toastBehaviorDefaults } from "./toast.js";
4
+ import { bottomNavigationBehaviorDefaults } from "./bottom-navigation.js";
5
+ import { tooltipBehaviorDefaults } from "./tooltip.js";
6
+ /*
7
+ Batch 2 저작 모듈의 행동 계약. 각 컴포넌트가 자기 모듈에 spec을 두고 레지스트리는
8
+ 경로만 모은다 — 여러 저작자가 병렬로 이 파일을 고치면 서로의 변경을 덮어쓴다
9
+ (`docs/authoring-brief.md`).
10
+ */
11
+ import { breadcrumbBehaviorSpec } from "./breadcrumb.js";
12
+ import { calendarBehavior } from "./calendar.js";
13
+ import { carouselBehavior } from "./carousel.js";
14
+ import { commandPaletteBehaviorScenarios } from "./command-palette.js";
15
+ import { floatingActionButtonBehaviorScenarios } from "./floating-action-button.js";
16
+ import { tourBehaviorScenarios } from "./tour.js";
17
+ import { dataTableBehavior } from "./data-table.js";
18
+ import { layoutBehavior } from "./layout.js";
19
+ import { otpFieldBehavior } from "./otp-field.js";
20
+ import { passwordFieldBehavior } from "./password-field.js";
21
+ import { splitterBehavior } from "./splitter.js";
22
+ import { datePickerBehavior } from "./date-picker.js";
23
+ import { sidePanelBehavior } from "./side-panel.js";
24
+ import { filePickerBehavior } from "./file-picker.js";
25
+ import { paginationBehaviorScenarios } from "./pagination.js";
26
+ import { popoverBehaviorDefaults, popoverBehaviorScenarios } from "./popover.js";
27
+ import { treeBehavior } from "./tree.js";
28
+ import { uploadItemBehavior } from "./upload-item.js";
29
+ import { formBehaviorSpec } from "./form.js";
30
+ import { numberFieldBehavior } from "./number-field.js";
31
+ import { sliderBehavior } from "./slider.js";
32
+ import { checkboxBehaviorDefaults, } from "./selection-helpers.js";
33
+ export { checkboxBehaviorDefaults, getCheckboxNextState, reconcileCheckboxSelection, toggleCheckboxSelection, } from "./selection-helpers.js";
34
+ /** Resolves a renderer's required name while rejecting type-valid blank copy. */
35
+ export function resolveControlAccessibleName(label, accessibilityLabel, controlName = "Control") {
36
+ if (label !== undefined && (typeof label !== "string" || !label.trim())) {
37
+ throw new TypeError(`${controlName} label must not be empty`);
38
+ }
39
+ if (accessibilityLabel !== undefined &&
40
+ (typeof accessibilityLabel !== "string" || !accessibilityLabel.trim())) {
41
+ throw new TypeError(`${controlName} accessibilityLabel must not be empty`);
42
+ }
43
+ const resolved = accessibilityLabel?.trim() || label?.trim();
44
+ if (!resolved) {
45
+ throw new TypeError(`${controlName} needs an accessible name`);
46
+ }
47
+ return resolved;
48
+ }
49
+ export const selectionGroupBehaviorDefaults = {
50
+ orientation: "vertical",
51
+ direction: "ltr",
52
+ };
53
+ export const radioGroupBehaviorDefaults = {
54
+ ...selectionGroupBehaviorDefaults,
55
+ loop: true,
56
+ };
57
+ export const selectBehaviorDefaults = {
58
+ disallowEmptySelection: false,
59
+ loop: false,
60
+ };
61
+ export const comboboxBehaviorDefaults = {
62
+ filtering: "local",
63
+ loop: false,
64
+ };
65
+ /** Throws before a renderer can expose ambiguous or unreachable selection state. */
66
+ export function validateSelectionItems(items) {
67
+ const ids = new Set();
68
+ for (const item of items) {
69
+ if (typeof item.id !== "string" || item.id.trim().length === 0) {
70
+ throw new TypeError("Selection item id must not be empty");
71
+ }
72
+ if (typeof item.label !== "string" || item.label.trim().length === 0) {
73
+ throw new TypeError(`Selection item ${item.id} label must not be empty`);
74
+ }
75
+ if (item.description !== undefined &&
76
+ (typeof item.description !== "string" ||
77
+ item.description.trim().length === 0)) {
78
+ throw new TypeError(`Selection item ${item.id} description must not be empty when provided`);
79
+ }
80
+ if (ids.has(item.id)) {
81
+ throw new TypeError(`Duplicate selection item id: ${item.id}`);
82
+ }
83
+ ids.add(item.id);
84
+ }
85
+ }
86
+ export function validateCheckboxSelection(items, current) {
87
+ validateSelectionItems(items);
88
+ const knownIds = new Set(items.map((item) => item.id));
89
+ for (const selectedId of current) {
90
+ if (!knownIds.has(selectedId)) {
91
+ throw new RangeError(`Selected checkbox must exist: ${selectedId}`);
92
+ }
93
+ }
94
+ }
95
+ export function resolveInitialRadioValue(items, requestedValue, required = false) {
96
+ validateSelectionItems(items);
97
+ if (requestedValue != null) {
98
+ if (!items.some((item) => item.id === requestedValue)) {
99
+ throw new RangeError(`Selected radio must exist: ${requestedValue}`);
100
+ }
101
+ return requestedValue;
102
+ }
103
+ return required ? (items.find((item) => !item.disabled)?.id ?? null) : null;
104
+ }
105
+ export function validateRadioSelection(items, current) {
106
+ validateSelectionItems(items);
107
+ if (current != null && !items.some((item) => item.id === current)) {
108
+ throw new RangeError(`Selected radio must exist: ${current}`);
109
+ }
110
+ }
111
+ /** Returns the only enabled radio that may enter the Web tab order. */
112
+ export function resolveRadioTabStop(items, selectedValue) {
113
+ validateSelectionItems(items);
114
+ const selected = items.find((item) => item.id === selectedValue);
115
+ if (selected && !selected.disabled)
116
+ return selected.id;
117
+ return items.find((item) => !item.disabled)?.id;
118
+ }
119
+ /** Reconciles an uncontrolled radio when its selected item leaves the collection. */
120
+ export function reconcileRadioSelection(items, current, required = false) {
121
+ validateSelectionItems(items);
122
+ if (current != null && items.some((item) => item.id === current))
123
+ return current;
124
+ return required ? (items.find((item) => !item.disabled)?.id ?? null) : null;
125
+ }
126
+ export function getSelectionNavigationIntent(key, orientation, direction) {
127
+ if (key === "Home")
128
+ return "first";
129
+ if (key === "End")
130
+ return "last";
131
+ if (orientation === "vertical") {
132
+ if (key === "ArrowDown")
133
+ return "next";
134
+ if (key === "ArrowUp")
135
+ return "previous";
136
+ return undefined;
137
+ }
138
+ if (key === "ArrowRight")
139
+ return direction === "rtl" ? "previous" : "next";
140
+ if (key === "ArrowLeft")
141
+ return direction === "rtl" ? "next" : "previous";
142
+ return undefined;
143
+ }
144
+ export function getRadioNavigationTarget(items, currentId, intent, loop = true) {
145
+ validateSelectionItems(items);
146
+ const enabled = items.filter((item) => !item.disabled);
147
+ if (enabled.length === 0)
148
+ return undefined;
149
+ if (intent === "first")
150
+ return enabled[0]?.id;
151
+ if (intent === "last")
152
+ return enabled.at(-1)?.id;
153
+ const currentIndex = enabled.findIndex((item) => item.id === currentId);
154
+ if (currentIndex < 0)
155
+ return enabled[0]?.id;
156
+ const delta = intent === "next" ? 1 : -1;
157
+ const nextIndex = currentIndex + delta;
158
+ if (!loop && (nextIndex < 0 || nextIndex >= enabled.length))
159
+ return currentId ?? enabled[0]?.id;
160
+ return enabled[(nextIndex + enabled.length) % enabled.length]?.id;
161
+ }
162
+ export function getTabNavigationIntent(key, orientation, direction) {
163
+ if (key === "Home")
164
+ return "first";
165
+ if (key === "End")
166
+ return "last";
167
+ if (orientation === "vertical") {
168
+ if (key === "ArrowDown")
169
+ return "next";
170
+ if (key === "ArrowUp")
171
+ return "previous";
172
+ return undefined;
173
+ }
174
+ if (key === "ArrowRight")
175
+ return direction === "rtl" ? "previous" : "next";
176
+ if (key === "ArrowLeft")
177
+ return direction === "rtl" ? "next" : "previous";
178
+ return undefined;
179
+ }
180
+ /** Validates collection identity and resolves the only safe initial selection. */
181
+ export function resolveInitialTabValue(items, requestedValue) {
182
+ const ids = new Set();
183
+ for (const item of items) {
184
+ if (ids.has(item.id)) {
185
+ throw new TypeError(`Duplicate tab id: ${item.id}`);
186
+ }
187
+ ids.add(item.id);
188
+ }
189
+ if (requestedValue !== undefined) {
190
+ const requested = items.find((item) => item.id === requestedValue);
191
+ if (!requested || requested.disabled) {
192
+ throw new RangeError(`Selected tab must exist and be enabled: ${requestedValue}`);
193
+ }
194
+ return requestedValue;
195
+ }
196
+ return items.find((item) => !item.disabled)?.id;
197
+ }
198
+ /** Shared roving-focus math; renderers still own DOM/native focus APIs. */
199
+ export function getTabNavigationTarget(items, currentId, intent, loop = true) {
200
+ const enabled = items.filter((item) => !item.disabled);
201
+ if (enabled.length === 0)
202
+ return undefined;
203
+ if (intent === "first")
204
+ return enabled[0]?.id;
205
+ if (intent === "last")
206
+ return enabled.at(-1)?.id;
207
+ const currentIndex = enabled.findIndex((item) => item.id === currentId);
208
+ if (currentIndex < 0)
209
+ return enabled[0]?.id;
210
+ const delta = intent === "next" ? 1 : -1;
211
+ const nextIndex = currentIndex + delta;
212
+ if (!loop && (nextIndex < 0 || nextIndex >= enabled.length))
213
+ return currentId;
214
+ return enabled[(nextIndex + enabled.length) % enabled.length]?.id;
215
+ }
216
+ /** Safe behavior defaults: automatic activation is opt-in for instant panels. */
217
+ export const tabsBehaviorDefaults = {
218
+ activationMode: "manual",
219
+ mountPolicy: "active",
220
+ panelMode: "keyed",
221
+ orientation: "horizontal",
222
+ direction: "ltr",
223
+ loop: true,
224
+ };
225
+ /**
226
+ * Behavior is data, not a React implementation. Web/RN renderers can use any
227
+ * suitable primitive while running the same scenarios and exposing the same
228
+ * controlled state names.
229
+ */
230
+ export const behaviorRegistry = {
231
+ link: {
232
+ controlled: [],
233
+ inputs: ["destination", "label"],
234
+ stateAxes: {
235
+ interaction: ["idle", "hover", "focusVisible", "pressed"],
236
+ },
237
+ web: {
238
+ roles: ["link"],
239
+ keyboard: ["Tab", "Enter"],
240
+ focus: "native",
241
+ },
242
+ native: {
243
+ roles: ["link"],
244
+ states: [],
245
+ actions: ["activate"],
246
+ },
247
+ scenarios: [
248
+ "href-is-the-destination-source-of-truth",
249
+ "web-renders-a-real-anchor-and-preserves-modifier-context-navigation",
250
+ "native-internal-destination-uses-router-link-semantics",
251
+ "native-external-destination-uses-platform-linking-and-reports-failure",
252
+ "link-never-exposes-disabled-action-or-visited-application-state",
253
+ "download-remains-a-separate-platform-workflow",
254
+ "visible-label-owns-one-accessible-name",
255
+ "leading-and-trailing-icons-are-decorative-semantic-icons",
256
+ "logical-icon-direction-follows-rtl",
257
+ "inline-link-has-a-persistent-non-color-cue",
258
+ "standalone-link-target-is-at-least-forty-four-units",
259
+ "destination-link-never-contains-another-interactive-descendant",
260
+ ],
261
+ },
262
+ field: {
263
+ controlled: ["value", "defaultValue", "onValueChange"],
264
+ stateAxes: {
265
+ availability: ["enabled", "disabled", "readOnly"],
266
+ validation: ["valid", "invalid"],
267
+ content: ["idle", "loading", "error"],
268
+ },
269
+ web: { roles: ["textbox"], keyboard: ["Tab"], focus: "native" },
270
+ native: { roles: ["text"], states: ["disabled"], actions: ["focus", "setText"] },
271
+ scenarios: ["label-description-error-linkage", "invalid-plus-focus", "disabled-suppresses-edit"],
272
+ },
273
+ searchField: {
274
+ controlled: ["value", "defaultValue", "onValueChange", "onClear"],
275
+ stateAxes: {
276
+ availability: ["enabled", "disabled", "busy"],
277
+ value: ["empty", "filled"],
278
+ validation: ["valid", "invalid"],
279
+ content: ["idle", "loading", "error"],
280
+ },
281
+ web: { roles: ["searchbox", "button"], keyboard: ["Tab", "Enter"], focus: "native" },
282
+ native: { roles: ["text", "button"], states: ["disabled", "busy"], actions: ["focus", "setText", "clear"] },
283
+ scenarios: ["clear-has-name", "empty-hides-clear", "invalid-plus-focus", "busy-preserves-query"],
284
+ },
285
+ checkbox: {
286
+ controlled: ["checked", "defaultChecked", "onCheckedChange"],
287
+ configuration: { mixedActivation: ["check", "uncheck"] },
288
+ defaults: checkboxBehaviorDefaults,
289
+ stateAxes: {
290
+ availability: ["enabled", "disabled", "readOnly"],
291
+ value: ["checked", "mixed"],
292
+ validation: ["valid", "invalid"],
293
+ },
294
+ web: { roles: ["checkbox"], keyboard: ["Tab", "Space"], focus: "native" },
295
+ native: {
296
+ roles: ["checkbox"],
297
+ states: ["disabled", "checked"],
298
+ actions: ["activate"],
299
+ },
300
+ scenarios: [
301
+ "visible-label-is-accessible-name",
302
+ "mixed-is-announced-and-activates-to-default",
303
+ "disabled-and-read-only-suppress-change",
304
+ "controlled-value-never-mutates-internally",
305
+ "required-and-invalid-support-are-linked",
306
+ ],
307
+ },
308
+ checkboxGroup: {
309
+ controlled: ["value", "defaultValue", "onValueChange"],
310
+ configuration: {
311
+ orientation: ["horizontal", "vertical"],
312
+ direction: ["ltr", "rtl"],
313
+ },
314
+ defaults: selectionGroupBehaviorDefaults,
315
+ stateAxes: {
316
+ availability: ["enabled", "disabled", "readOnly"],
317
+ value: ["checked"],
318
+ validation: ["valid", "invalid"],
319
+ },
320
+ web: {
321
+ roles: ["group", "checkbox"],
322
+ keyboard: ["Tab", "Space"],
323
+ focus: "native",
324
+ },
325
+ native: {
326
+ roles: ["checkbox"],
327
+ states: ["disabled", "checked"],
328
+ actions: ["activate"],
329
+ },
330
+ scenarios: [
331
+ "each-enabled-checkbox-is-a-tab-stop",
332
+ "space-toggles-only-focused-checkbox",
333
+ "group-label-description-and-error-are-linked",
334
+ "disabled-selected-item-is-retained-but-not-activated",
335
+ "change-emits-a-fresh-set",
336
+ "duplicate-and-unknown-ids-are-rejected",
337
+ "uncontrolled-selection-reconciles-collection-mutation",
338
+ "required-and-invalid-are-announced",
339
+ "no-nested-interactive-descendants",
340
+ ],
341
+ },
342
+ radioGroup: {
343
+ controlled: ["value", "defaultValue", "onValueChange"],
344
+ configuration: {
345
+ orientation: ["horizontal", "vertical"],
346
+ direction: ["ltr", "rtl"],
347
+ loop: ["true", "false"],
348
+ },
349
+ defaults: radioGroupBehaviorDefaults,
350
+ stateAxes: {
351
+ availability: ["enabled", "disabled", "readOnly"],
352
+ value: ["checked", "selected"],
353
+ validation: ["valid", "invalid"],
354
+ },
355
+ web: {
356
+ roles: ["radiogroup", "radio"],
357
+ keyboard: [
358
+ "Tab",
359
+ "Space",
360
+ "ArrowUp",
361
+ "ArrowDown",
362
+ "ArrowLeft",
363
+ "ArrowRight",
364
+ "Home",
365
+ "End",
366
+ ],
367
+ focus: "roving",
368
+ },
369
+ native: {
370
+ roles: ["radio"],
371
+ states: ["disabled", "checked"],
372
+ actions: ["activate"],
373
+ },
374
+ scenarios: [
375
+ "at-most-one-selection-and-nullable-initial-value",
376
+ "checked-or-first-enabled-is-the-only-tab-stop",
377
+ "arrow-focuses-and-selects-while-skipping-disabled",
378
+ "orientation-direction-and-loop-control-navigation",
379
+ "space-selects-focused-radio",
380
+ "group-label-description-and-error-are-linked",
381
+ "disabled-selected-item-is-retained-with-enabled-tab-stop",
382
+ "required-invalid-and-read-only-are-announced",
383
+ "controlled-value-never-mutates-internally",
384
+ "duplicate-and-unknown-ids-are-rejected",
385
+ ],
386
+ },
387
+ switch: {
388
+ controlled: ["checked", "defaultChecked", "onCheckedChange"],
389
+ stateAxes: { availability: ["enabled", "disabled"], value: ["checked"] },
390
+ web: { roles: ["switch"], keyboard: ["Tab", "Space"], focus: "native" },
391
+ native: { roles: ["switch"], states: ["disabled", "checked"], actions: ["activate"] },
392
+ scenarios: ["row-and-control-are-one-target", "checked-announced", "disabled-suppresses-change"],
393
+ },
394
+ segmentedControl: {
395
+ controlled: ["value", "defaultValue", "onValueChange"],
396
+ stateAxes: { availability: ["enabled", "disabled"], value: ["selected"] },
397
+ web: { roles: ["radiogroup", "radio"], keyboard: ["Tab", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"], focus: "roving" },
398
+ native: { roles: ["radio"], states: ["disabled", "checked"], actions: ["activate"] },
399
+ scenarios: ["exactly-one-selected", "one-tab-stop", "arrow-selection", "selected-plus-focus"],
400
+ },
401
+ chip: {
402
+ controlled: ["selected", "onPress"],
403
+ stateAxes: {
404
+ availability: ["enabled", "disabled"],
405
+ value: ["selected"],
406
+ },
407
+ web: {
408
+ roles: ["button", "radio", "checkbox"],
409
+ keyboard: ["Tab", "Enter", "Space"],
410
+ focus: "native",
411
+ },
412
+ native: {
413
+ roles: ["button", "radio", "checkbox"],
414
+ states: ["disabled", "checked"],
415
+ actions: ["activate"],
416
+ },
417
+ scenarios: [
418
+ "action-never-exposes-selection",
419
+ "single-selection-uses-radio-checked",
420
+ "multiple-selection-uses-checkbox-checked",
421
+ "selected-plus-focus",
422
+ "selection-indicator-is-not-color-only",
423
+ "single-mode-composes-radio-group-behavior",
424
+ "multiple-mode-composes-checkbox-group-behavior",
425
+ ],
426
+ },
427
+ tabs: {
428
+ controlled: ["value", "defaultValue", "onValueChange"],
429
+ configuration: {
430
+ activationMode: ["automatic", "manual"],
431
+ mountPolicy: ["active", "visited", "always"],
432
+ panelMode: ["keyed", "dynamic"],
433
+ orientation: ["horizontal", "vertical"],
434
+ direction: ["ltr", "rtl"],
435
+ loop: ["true", "false"],
436
+ },
437
+ defaults: tabsBehaviorDefaults,
438
+ stateAxes: { availability: ["enabled", "disabled"], value: ["selected"] },
439
+ web: { roles: ["tablist", "tab", "tabpanel"], keyboard: ["Tab", "Enter", "Space", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End"], focus: "roving" },
440
+ native: { roles: ["tab"], states: ["disabled", "selected"], actions: ["activate"] },
441
+ scenarios: [
442
+ "tab-controls-panel",
443
+ "one-tab-stop",
444
+ "disabled-tab-skipped",
445
+ "selected-plus-focus",
446
+ "automatic-activation-only-for-instant-panels",
447
+ "manual-activation-keeps-focus-and-selection-distinct",
448
+ "active-mount-removes-inactive-panel",
449
+ "always-mount-makes-inactive-panels-inert",
450
+ "visited-mount-preserves-visited-panel-state",
451
+ "horizontal-arrows-do-not-consume-up-down",
452
+ "vertical-arrows-and-aria-orientation",
453
+ "web-tablist-has-accessible-name",
454
+ "every-tab-controls-an-existing-panel",
455
+ "active-panel-labelled-by-selected-tab",
456
+ "manual-focus-does-not-relabel-dynamic-panel",
457
+ "dynamic-panel-preserves-host-instance",
458
+ "dynamic-panel-requires-active-mount",
459
+ "panel-enters-tab-order-when-content-has-no-focus-target",
460
+ "rtl-navigation-follows-logical-direction",
461
+ "first-enabled-tab-is-uncontrolled-default",
462
+ "duplicate-and-disabled-selection-are-rejected",
463
+ "controlled-selection-never-mutates-internally",
464
+ "native-focus-does-not-select-without-activate",
465
+ "inactive-mounted-panels-are-hidden-and-inert",
466
+ "uncontrolled-selection-reconciles-collection-mutation",
467
+ ],
468
+ },
469
+ bottomNavigation: {
470
+ controlled: [],
471
+ inputs: ["items", "selectedKey", "accessibilityLabel"],
472
+ events: ["onActivate", "onLongPress"],
473
+ configuration: {
474
+ direction: ["ltr", "rtl"],
475
+ keyboardBehavior: ["hide", "remain"],
476
+ },
477
+ defaults: bottomNavigationBehaviorDefaults,
478
+ stateAxes: {
479
+ interaction: ["idle", "hover", "focusVisible", "pressed"],
480
+ availability: ["enabled", "disabled"],
481
+ value: ["selected"],
482
+ },
483
+ web: {
484
+ roles: ["navigation", "list", "link"],
485
+ keyboard: ["Tab", "Enter"],
486
+ focus: "native",
487
+ },
488
+ native: {
489
+ roles: ["tab", "button"],
490
+ states: ["disabled", "selected"],
491
+ actions: ["activate", "longpress"],
492
+ },
493
+ scenarios: [
494
+ "two-to-five-stable-top-level-destinations",
495
+ "router-selected-key-is-read-only-input",
496
+ "activation-does-not-select-before-router-commit",
497
+ "prevented-or-failed-navigation-keeps-current-selection",
498
+ "reselect-is-distinct-from-navigation",
499
+ "navigator-preserves-each-destination-state",
500
+ "disabled-destination-suppresses-activation",
501
+ "visible-label-always-names-a-decorative-icon",
502
+ "selected-state-uses-icon-emphasis-and-label-weight-not-color-alone",
503
+ "selected-content-and-focus-indicator-remain-simultaneously-visible",
504
+ "centered-sibling-action-is-excluded-from-destination-collection",
505
+ "badge-zero-is-hidden-and-visible-count-is-bounded",
506
+ "badge-subtree-is-hidden-and-item-uses-one-resolved-accessibility-name",
507
+ "badge-update-does-not-change-focus-selection-or-live-announce",
508
+ "web-uses-a-labelled-navigation-landmark-and-real-links",
509
+ "web-current-link-uses-aria-current-page",
510
+ "web-preserves-modifier-click-and-does-not-use-tab-roles-or-roving-focus",
511
+ "native-emits-tab-press-and-respects-default-prevented-before-navigation",
512
+ "native-forwards-tab-long-press",
513
+ "native-ios-may-use-button-plus-selected-when-tab-role-is-not-reliably-supported",
514
+ "safe-area-padding-is-additive",
515
+ "software-keyboard-hides-instead-of-lifting-the-navigation",
516
+ "two-hundred-percent-text-wraps-without-fixed-item-height",
517
+ "rtl-order-and-badge-anchor-follow-logical-direction",
518
+ "targets-are-at-least-forty-four-units",
519
+ "reduced-motion-removes-transform-without-changing-state",
520
+ ],
521
+ },
522
+ disclosureGroup: {
523
+ controlled: ["expandedKeys", "defaultExpandedKeys", "onExpandedChange"],
524
+ stateAxes: { availability: ["enabled", "disabled"], value: ["expanded"] },
525
+ web: { roles: ["heading", "button", "region"], keyboard: ["Tab", "Enter", "Space", "ArrowUp", "ArrowDown", "Home", "End"], focus: "native" },
526
+ native: { roles: ["button"], states: ["disabled", "expanded"], actions: ["activate"] },
527
+ scenarios: [
528
+ "single-and-multiple-invariants",
529
+ "each-enabled-trigger-is-a-tab-stop",
530
+ "toggle-keeps-trigger-focus",
531
+ "panel-removed-when-collapsed",
532
+ "disabled-trigger-suppresses-change",
533
+ "reduced-motion",
534
+ ],
535
+ },
536
+ menu: {
537
+ controlled: [
538
+ "open",
539
+ "defaultOpen",
540
+ "onOpenChange",
541
+ "onAction",
542
+ "onActionAfterDismiss",
543
+ "selection",
544
+ "asyncState",
545
+ ],
546
+ stateAxes: { availability: ["enabled", "disabled"], value: ["open", "selected"], content: ["idle", "loading", "loadingMore", "empty", "error"] },
547
+ web: { roles: ["menu", "menuitem", "menuitemradio", "menuitemcheckbox"], keyboard: ["Enter", "Space", "Escape", "ArrowUp", "ArrowDown", "Home", "End", "Typeahead"], focus: "restore", dismiss: ["escape", "outside", "selection"] },
548
+ native: { roles: ["menu", "menuitem"], states: ["disabled", "selected", "checked"], actions: ["activate"], dismiss: ["back", "outside", "selection"] },
549
+ scenarios: [
550
+ "action-and-single-close-after-selection",
551
+ "on-action-does-not-depend-on-controlled-close",
552
+ "after-dismiss-action-sequences-next-surface",
553
+ "multiple-selection-keeps-menu-open",
554
+ "disabled-item-skipped",
555
+ "escape-or-back-restores-trigger",
556
+ "danger-item-not-color-only",
557
+ "loading-more-preserves-items-and-selection",
558
+ ],
559
+ },
560
+ dialog: {
561
+ controlled: ["open", "defaultOpen", "onOpenChange"],
562
+ stateAxes: { availability: ["enabled", "busy"], value: ["open"] },
563
+ web: { roles: ["dialog"], keyboard: ["Tab", "Escape"], focus: "trap", dismiss: ["escape", "outside"] },
564
+ native: { roles: ["dialog"], states: ["busy"], actions: ["dismiss"], dismiss: ["back", "outside"] },
565
+ scenarios: ["title-description-linkage", "focus-trap-and-restore", "modal-isolation", "single-dismiss-callback"],
566
+ },
567
+ alertDialog: {
568
+ controlled: ["open", "defaultOpen", "onOpenChange"],
569
+ stateAxes: { availability: ["enabled", "busy"], value: ["open"], content: ["idle", "loading", "error"] },
570
+ web: { roles: ["alertdialog"], keyboard: ["Tab", "Escape"], focus: "trap", dismiss: ["escape"] },
571
+ native: { roles: ["alertdialog"], states: ["busy"], actions: ["confirm", "cancel"], dismiss: ["back"] },
572
+ scenarios: [
573
+ "visible-title-and-description-are-announced",
574
+ "modal-isolation-and-tab-trap",
575
+ "confirm-mode-cancel-is-initial-focus",
576
+ "alert-mode-confirm-is-initial-focus",
577
+ "outside-attempt-is-ignored-without-callback",
578
+ "escape-or-back-cancels-when-idle",
579
+ "busy-blocks-every-dismiss-and-duplicate-confirm",
580
+ "confirm-action-runs-once",
581
+ "error-is-announced-and-dialog-stays-open",
582
+ "controlled-close-settles-programmatic-cancel",
583
+ "unmount-settles-interrupted",
584
+ "result-settles-once-after-exit",
585
+ "focus-restores-after-every-close-path",
586
+ "reduced-motion-still-completes-exit",
587
+ ],
588
+ },
589
+ sheet: {
590
+ controlled: ["open", "defaultOpen", "onOpenChange", "dismissPolicy"],
591
+ defaults: sheetBehaviorDefaults,
592
+ stateAxes: { availability: ["enabled", "busy"], value: ["open"] },
593
+ web: { roles: ["dialog"], keyboard: ["Tab", "Escape"], focus: "trap", dismiss: ["escape", "outside"] },
594
+ native: { roles: ["dialog"], states: ["busy"], actions: ["dismiss"], dismiss: ["back", "outside"] },
595
+ scenarios: [
596
+ "focus-or-modal-isolation",
597
+ "scroll-lock",
598
+ "safe-area",
599
+ "single-dismiss-callback",
600
+ "busy-blocks-dismiss",
601
+ "dismiss-reason-is-reported",
602
+ "programmatic-owner-close-is-always-allowed",
603
+ "swipe-requires-enabled-policy-and-gesture-capability",
604
+ "successor-surface-opens-after-exit",
605
+ "nested-modal-surfaces-are-forbidden",
606
+ "reduced-motion-still-completes-dismiss",
607
+ ],
608
+ },
609
+ select: {
610
+ controlled: [
611
+ "selectedKey",
612
+ "defaultSelectedKey",
613
+ "onSelectionChange",
614
+ "open",
615
+ "defaultOpen",
616
+ "onOpenChange",
617
+ ],
618
+ inputs: ["asyncState", "selectedItem"],
619
+ configuration: {
620
+ disallowEmptySelection: ["true", "false"],
621
+ loop: ["true", "false"],
622
+ },
623
+ defaults: selectBehaviorDefaults,
624
+ stateAxes: {
625
+ availability: ["enabled", "disabled", "readOnly", "busy"],
626
+ value: ["empty", "selected", "open"],
627
+ validation: ["valid", "invalid"],
628
+ content: ["idle", "loading", "loadingMore", "empty", "error"],
629
+ },
630
+ web: {
631
+ roles: ["combobox", "listbox", "option"],
632
+ keyboard: [
633
+ "Tab",
634
+ "Enter",
635
+ "Space",
636
+ "Escape",
637
+ "ArrowUp",
638
+ "ArrowDown",
639
+ "Home",
640
+ "End",
641
+ "Typeahead",
642
+ ],
643
+ focus: "activeDescendant",
644
+ dismiss: ["selection", "escape", "outside", "blur"],
645
+ },
646
+ native: {
647
+ roles: ["button", "dialog", "radio"],
648
+ states: ["disabled", "checked", "busy", "expanded"],
649
+ actions: ["activate", "dismiss"],
650
+ dismiss: ["selection", "back", "outside"],
651
+ },
652
+ scenarios: [
653
+ "visible-label-or-accessibility-label-names-trigger",
654
+ "collection-ids-and-text-values-are-validated",
655
+ "highlighted-option-is-distinct-from-committed-selection",
656
+ "trigger-controls-the-mounted-listbox",
657
+ "active-descendant-references-a-mounted-option",
658
+ "active-option-scrolls-into-view",
659
+ "disabled-options-are-skipped",
660
+ "escape-closes-without-commit",
661
+ "selection-requests-close-and-restores-trigger-focus",
662
+ "controlled-owner-may-defer-a-selection-close-request",
663
+ "removed-selection-is-reconciled",
664
+ "loading-more-preserves-options-and-selection",
665
+ "transient-async-pages-preserve-committed-key-and-copy",
666
+ "empty-error-and-loading-are-announced",
667
+ "options-have-no-nested-interactive-descendants",
668
+ ],
669
+ },
670
+ combobox: {
671
+ controlled: [
672
+ "selectedKey",
673
+ "defaultSelectedKey",
674
+ "onSelectionChange",
675
+ "inputValue",
676
+ "defaultInputValue",
677
+ "onInputValueChange",
678
+ "open",
679
+ "defaultOpen",
680
+ "onOpenChange",
681
+ ],
682
+ inputs: ["asyncState", "queryValue", "resultQuery", "selectedItem"],
683
+ events: ["onCommit"],
684
+ configuration: {
685
+ filtering: ["local", "external"],
686
+ loop: ["true", "false"],
687
+ },
688
+ defaults: comboboxBehaviorDefaults,
689
+ stateAxes: {
690
+ availability: ["enabled", "disabled", "readOnly", "busy"],
691
+ value: ["empty", "filled", "selected", "open"],
692
+ validation: ["valid", "invalid"],
693
+ content: ["idle", "loading", "loadingMore", "empty", "error"],
694
+ },
695
+ web: {
696
+ roles: ["combobox", "listbox", "option"],
697
+ keyboard: [
698
+ "Tab",
699
+ "Enter",
700
+ "Escape",
701
+ "ArrowUp",
702
+ "ArrowDown",
703
+ "Home",
704
+ "End",
705
+ ],
706
+ focus: "activeDescendant",
707
+ dismiss: ["selection", "escape", "outside", "blur"],
708
+ },
709
+ native: {
710
+ roles: ["button", "text", "dialog", "radio"],
711
+ states: ["disabled", "checked", "busy", "expanded"],
712
+ actions: ["focus", "setText", "activate", "dismiss"],
713
+ dismiss: ["selection", "back", "outside"],
714
+ },
715
+ scenarios: [
716
+ "selected-key-and-input-value-are-independent",
717
+ "input-focus-and-active-option-are-distinct",
718
+ "input-controls-the-mounted-listbox",
719
+ "active-descendant-references-a-mounted-option",
720
+ "active-option-scrolls-into-view",
721
+ "ime-composition-does-not-prematurely-filter-or-commit",
722
+ "escape-restores-the-committed-label",
723
+ "query-edit-never-commits-a-stale-selection",
724
+ "external-results-do-not-implicitly-commit",
725
+ "committed-item-survives-transient-result-pages",
726
+ "stale-external-results-do-not-replace-current-query",
727
+ "loading-more-preserves-options-and-active-descendant",
728
+ "empty-error-and-loading-are-announced",
729
+ "custom-values-are-rejected-without-an-explicit-policy",
730
+ "selection-state-updates-before-commit-event-and-close-request",
731
+ "options-have-no-nested-interactive-descendants",
732
+ ],
733
+ },
734
+ toast: {
735
+ controlled: [],
736
+ inputs: ["descriptor", "queueSnapshot", "visibleSnapshot"],
737
+ events: ["onAction", "onDismiss"],
738
+ configuration: {
739
+ priority: ["normal", "high"],
740
+ duplicatePolicy: ["update", "ignore"],
741
+ timerUpdatePolicy: ["preserve", "restart"],
742
+ overflowPolicy: ["discard-oldest", "discard-newest"],
743
+ },
744
+ defaults: toastBehaviorDefaults,
745
+ stateAxes: {
746
+ value: ["open"],
747
+ content: ["idle"],
748
+ },
749
+ web: {
750
+ roles: ["region", "status", "alert", "button"],
751
+ keyboard: ["Tab", "Enter", "Space", "Escape", "F8"],
752
+ focus: "native",
753
+ dismiss: ["timeout", "action", "close-action", "escape", "swipe", "programmatic"],
754
+ },
755
+ native: {
756
+ roles: ["alert", "button"],
757
+ states: [],
758
+ actions: ["announce", "activate", "dismiss"],
759
+ dismiss: ["timeout", "action", "close-action", "swipe", "programmatic"],
760
+ },
761
+ scenarios: [
762
+ "stable-id-duplicate-updates-in-place-without-reordering",
763
+ "bounded-queue-promotes-items-in-fifo-order",
764
+ "queued-time-never-consumes-auto-dismiss-duration",
765
+ "visible-timer-pauses-for-pointer-focus-window-and-gesture",
766
+ "actionable-toast-is-persistent-unless-duration-is-explicit",
767
+ "auto-dismiss-duration-is-never-shorter-than-five-seconds",
768
+ "action-and-dismiss-callbacks-run-exactly-once",
769
+ "programmatic-close-reports-its-own-dismiss-reason",
770
+ "normal-and-high-announcements-use-the-visible-copy-or-explicit-override",
771
+ "tone-is-always-paired-with-a-non-color-mark",
772
+ "web-hotkey-focuses-the-labelled-viewport-and-last-close-restores-focus",
773
+ "exit-completes-once-with-and-without-reduced-motion",
774
+ "provider-teardown-interrupts-every-session-without-leaking-a-timer",
775
+ ],
776
+ },
777
+ loadMore: {
778
+ controlled: [],
779
+ inputs: ["state", "labels"],
780
+ events: ["onLoadMore"],
781
+ configuration: { mode: ["automatic", "manual"] },
782
+ defaults: loadMoreBehaviorDefaults,
783
+ stateAxes: {
784
+ availability: ["enabled", "busy"],
785
+ content: ["idle", "loadingMore", "error", "complete"],
786
+ },
787
+ web: {
788
+ roles: ["status", "alert", "button"],
789
+ keyboard: ["Tab", "Enter", "Space"],
790
+ focus: "native",
791
+ },
792
+ native: {
793
+ roles: ["progressbar", "alert", "button"],
794
+ states: ["busy", "disabled"],
795
+ actions: ["activate"],
796
+ },
797
+ scenarios: [
798
+ "existing-items-remain-mounted-in-every-footer-state",
799
+ "automatic-viewport-request-has-an-accessible-manual-fallback",
800
+ "one-request-is-in-flight-per-controller",
801
+ "same-request-key-is-never-issued-twice-concurrently",
802
+ "loading-and-complete-states-block-new-requests",
803
+ "error-state-retries-only-with-retry-reason",
804
+ "loading-status-and-error-alert-announce-once",
805
+ "retry-and-manual-targets-are-at-least-forty-four-units",
806
+ ],
807
+ },
808
+ tooltip: {
809
+ controlled: ["open", "defaultOpen", "onOpenChange"],
810
+ inputs: ["content"],
811
+ configuration: {
812
+ placement: ["top", "bottom", "start", "end"],
813
+ align: ["start", "center", "end"],
814
+ },
815
+ defaults: tooltipBehaviorDefaults,
816
+ stateAxes: {
817
+ interaction: ["idle", "hover", "focusVisible"],
818
+ value: ["open"],
819
+ },
820
+ web: {
821
+ roles: ["tooltip"],
822
+ keyboard: ["Tab", "Escape"],
823
+ focus: "none",
824
+ dismiss: ["escape", "blur"],
825
+ },
826
+ native: { roles: [], states: [], actions: [] },
827
+ scenarios: [
828
+ "plain-localized-copy-never-contains-interaction",
829
+ "keyboard-focus-opens-immediately-and-pointer-respects-delay",
830
+ "recent-sibling-tooltip-uses-skip-delay",
831
+ "one-tooltip-is-visible-per-provider",
832
+ "trigger-content-hover-and-pointer-corridor-keep-open",
833
+ "escape-dismisses-and-suppresses-reopen-until-input-reset",
834
+ "blur-or-pointer-leave-closes-only-after-all-active-inputs-end",
835
+ "trigger-activation-closes-without-cancelling-trigger-action",
836
+ "trigger-keeps-focus-and-tooltip-has-no-tab-stop",
837
+ "existing-aria-describedby-is-preserved",
838
+ "touch-pointer-hover-is-ignored",
839
+ "controlled-close-rejection-never-exposes-two-tooltips",
840
+ "timer-and-global-listener-cleanup-on-unmount",
841
+ "reduced-motion-exit-completes-exactly-once",
842
+ ],
843
+ },
844
+ breadcrumb: breadcrumbBehaviorSpec,
845
+ calendar: calendarBehavior,
846
+ carousel: carouselBehavior,
847
+ commandPalette: {
848
+ controlled: [
849
+ "open",
850
+ "defaultOpen",
851
+ "onOpenChange",
852
+ "inputValue",
853
+ "defaultInputValue",
854
+ "onInputValueChange",
855
+ ],
856
+ inputs: ["queryValue", "resultQuery", "asyncState", "accessibilityLabel", "searchPlaceholder"],
857
+ events: ["onActivate", "onActivateAfterDismiss"],
858
+ stateAxes: { value: ["open"], content: ["idle", "loading", "loadingMore", "empty", "error"] },
859
+ // 저작자 명세의 dismiss 사유 "activation"은 채택하지 않았다. 같은 뜻의 "action"이
860
+ // 이미 유니언에 있고(Menu가 onAction으로 쓴다), 공용 어휘에 동의어를 하나 더 두면
861
+ // 렌더러가 둘을 구별해야 하는지 매번 물어야 한다.
862
+ web: { roles: ["dialog", "listbox", "option"], keyboard: ["Tab", "Enter", "Escape", "ArrowUp", "ArrowDown"], focus: "trap", dismiss: ["escape", "outside", "action"] },
863
+ native: { roles: [], states: [], actions: [] },
864
+ scenarios: [...commandPaletteBehaviorScenarios],
865
+ },
866
+ floatingActionButton: {
867
+ controlled: [],
868
+ inputs: ["label", "icon", "layoutMode", "scrollSignal", "safeAreaInset"],
869
+ events: ["onPress"],
870
+ stateAxes: { interaction: ["idle", "hover", "pressed", "focusVisible"] },
871
+ web: { roles: ["button"], keyboard: ["Tab", "Enter", "Space"], focus: "native" },
872
+ native: { roles: ["button"], states: [], actions: ["activate"] },
873
+ scenarios: [...floatingActionButtonBehaviorScenarios],
874
+ },
875
+ // 이 항목은 `transfer-list.ts`에서 import하지 않고 여기에 직접 쓴다. 그 모듈은
876
+ // 이 파일에서 **값**을(getCheckboxNextState 등) 가져오므로, 여기서 되받으면
877
+ // 실제 순환이 닫혀 레지스트리가 undefined를 담는다(타입만 가져오는
878
+ // data-table과 다른 점이다). 두 정의가 어긋나지 않는지는 테스트로 잠근다.
879
+ transferList: {
880
+ controlled: ["targetKeys", "defaultTargetKeys", "onTargetKeysChange"],
881
+ inputs: ["items"],
882
+ events: ["onMove"],
883
+ stateAxes: {
884
+ availability: ["enabled", "disabled"],
885
+ value: ["empty", "filled", "selected", "mixed"],
886
+ },
887
+ web: {
888
+ roles: ["group", "listbox", "option", "checkbox", "button"],
889
+ keyboard: ["Tab", "Space", "Enter", "ArrowUp", "ArrowDown", "Home", "End"],
890
+ focus: "roving",
891
+ },
892
+ native: {
893
+ roles: ["list", "checkbox", "button"],
894
+ states: ["disabled", "selected", "checked"],
895
+ actions: ["toggle", "toggleSelectAll", "moveSelection", "moveItem"],
896
+ },
897
+ scenarios: [
898
+ "moving-is-reachable-entirely-by-keyboard-select-with-space-then-activate-the-move-button",
899
+ "moving-a-single-focused-row-does-not-require-first-opening-multi-select",
900
+ "focus-after-a-move-lands-on-the-item-that-slid-into-the-removed-rows-position",
901
+ "focus-after-emptying-a-panel-falls-back-to-its-empty-state-never-lost-to-the-document",
902
+ "every-move-emits-which-ids-moved-so-the-product-can-announce-a-formatted-sentence",
903
+ "disabled-items-are-never-selectable-and-never-move",
904
+ "moved-items-are-cleared-from-the-origin-panels-selection-and-left-unselected-at-the-destination",
905
+ "select-all-in-a-panel-excludes-disabled-items-from-both-the-denominator-and-the-count",
906
+ "an-item-with-nothing-selected-in-its-panel-still-supports-direct-single-item-move",
907
+ "search-and-pagination-inside-a-panel-are-product-composition-not-this-contract",
908
+ ],
909
+ },
910
+ dataTable: dataTableBehavior,
911
+ tour: {
912
+ controlled: ["open", "defaultOpen", "onOpenChange", "currentStepId", "defaultCurrentStepId", "onStepChange"],
913
+ inputs: ["steps", "labels", "anchorId"],
914
+ events: ["onClose"],
915
+ stateAxes: { value: ["open", "currentStep"] },
916
+ // outside 닫힘이 없다. 실수로 워크스루가 끊기지 않게 하려는 고정 규칙이라
917
+ // 저작자가 policy 객체조차 두지 않았고, 요약에도 그 사실을 그대로 옮긴다.
918
+ web: { roles: ["dialog"], keyboard: ["Tab", "Enter", "Escape", "ArrowLeft", "ArrowRight"], focus: "trap", dismiss: ["escape", "close-action", "programmatic"] },
919
+ native: { roles: [], states: [], actions: [] },
920
+ scenarios: [...tourBehaviorScenarios],
921
+ },
922
+ layout: layoutBehavior,
923
+ otpField: otpFieldBehavior,
924
+ passwordField: passwordFieldBehavior,
925
+ splitter: splitterBehavior,
926
+ datePicker: datePickerBehavior,
927
+ sidePanel: sidePanelBehavior,
928
+ filePicker: filePickerBehavior,
929
+ pagination: {
930
+ controlled: ["currentPage", "onPageChange"],
931
+ inputs: ["totalCount", "totalPages", "pageSize", "labels"],
932
+ configuration: { window: ["siblingCount", "boundaryCount"] },
933
+ stateAxes: {},
934
+ web: {
935
+ roles: ["navigation"],
936
+ keyboard: ["Tab", "Enter", "Space"],
937
+ focus: "native",
938
+ },
939
+ native: { roles: [], states: [], actions: [] },
940
+ scenarios: [...paginationBehaviorScenarios],
941
+ },
942
+ /*
943
+ 비모달이라 `busy` 축이 없다. Sheet/AlertDialog는 진행 중 dismiss를 막지만 Popover는
944
+ 포커스가 정당하게 밖으로 나갈 수 있어 그 축이 성립하지 않는다 — 대신 나가는 것
945
+ 자체가 dismiss 신호이므로 `outside-focus`를 별도 reason으로 둔다.
946
+ */
947
+ popover: {
948
+ controlled: ["open", "defaultOpen", "onOpenChange"],
949
+ defaults: popoverBehaviorDefaults,
950
+ stateAxes: { value: ["open"] },
951
+ /*
952
+ `trap`이 아니라 `restore`다 — Dialog·Sheet·AlertDialog는 모달이라 포커스를 가두지만
953
+ Popover는 비모달이라 Tab이 **정당하게 밖으로 나갈 수 있다.** 그래서 가두지 않고,
954
+ 닫힐 때 트리거로 돌려보낸다. 같은 자리에 있는 것은 Menu다(역시 `restore`).
955
+ 나가는 것 자체가 dismiss 신호이므로 `outside-focus`를 별도 reason으로 둔다.
956
+ */
957
+ web: {
958
+ roles: ["dialog"],
959
+ keyboard: ["Tab", "Escape"],
960
+ focus: "restore",
961
+ dismiss: ["escape", "outside"],
962
+ },
963
+ native: { roles: [], states: [], actions: [] },
964
+ scenarios: [...popoverBehaviorScenarios],
965
+ },
966
+ tree: treeBehavior,
967
+ uploadItem: uploadItemBehavior,
968
+ form: formBehaviorSpec,
969
+ numberField: numberFieldBehavior,
970
+ slider: sliderBehavior,
971
+ };
972
+ //# sourceMappingURL=behaviors.js.map