@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,44 @@
1
+ export type TooltipPlacement = "top" | "bottom" | "start" | "end";
2
+ export type TooltipAlign = "start" | "center" | "end";
3
+ export type TooltipOpenChangeReason = "pointer" | "focus" | "pointer-leave" | "blur" | "escape" | "trigger-activation" | "another-tooltip";
4
+ export type TooltipOpenChangeDetails = Readonly<{
5
+ reason: TooltipOpenChangeReason;
6
+ }>;
7
+ /** Tooltip copy is intentionally plain text and never owns an action. */
8
+ export type TooltipDescriptor = Readonly<{
9
+ content: string;
10
+ placement?: TooltipPlacement;
11
+ align?: TooltipAlign;
12
+ }>;
13
+ export type ResolvedTooltipDescriptor = Readonly<{
14
+ content: string;
15
+ placement: TooltipPlacement;
16
+ align: TooltipAlign;
17
+ }>;
18
+ export type ControlledTooltipOpenState = Readonly<{
19
+ open: boolean;
20
+ defaultOpen?: never;
21
+ onOpenChange(open: boolean, details: TooltipOpenChangeDetails): void;
22
+ }>;
23
+ export type UncontrolledTooltipOpenState = Readonly<{
24
+ open?: never;
25
+ defaultOpen?: boolean;
26
+ onOpenChange?: (open: boolean, details: TooltipOpenChangeDetails) => void;
27
+ }>;
28
+ export type TooltipOpenState = ControlledTooltipOpenState | UncontrolledTooltipOpenState;
29
+ export declare const tooltipDescriptorDefaults: {
30
+ readonly placement: "top";
31
+ readonly align: "center";
32
+ };
33
+ export declare const tooltipBehaviorDefaults: {
34
+ readonly pointerOpenDelayMs: 500;
35
+ readonly focusOpenDelayMs: 0;
36
+ readonly skipDelayMs: 300;
37
+ readonly hoverable: true;
38
+ readonly touchHover: false;
39
+ readonly oneVisiblePerProvider: true;
40
+ };
41
+ export declare function validateTooltipDescriptor(descriptor: TooltipDescriptor): void;
42
+ export declare function resolveTooltipDescriptor(descriptor: TooltipDescriptor): ResolvedTooltipDescriptor;
43
+ export declare function validateTooltipOpenState(state: TooltipOpenState): void;
44
+ //# sourceMappingURL=tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAClE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtD,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,OAAO,GACP,eAAe,GACf,MAAM,GACN,QAAQ,GACR,oBAAoB,GACpB,iBAAiB,CAAC;AAEtB,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC,CAAC;AAEH,yEAAyE;AACzE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC,CAAC;AAEH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,KAAK,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAAC;CACtE,CAAC,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CAAC;IAClD,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CACb,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,wBAAwB,KAC9B,IAAI,CAAC;CACX,CAAC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GACxB,0BAA0B,GAC1B,4BAA4B,CAAC;AAEjC,eAAO,MAAM,yBAAyB;;;CAMpC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;CAO1B,CAAC;AA4BX,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,iBAAiB,GAC5B,IAAI,CA6BN;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,iBAAiB,GAC5B,yBAAyB,CAO3B;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAoCtE"}
@@ -0,0 +1,88 @@
1
+ export const tooltipDescriptorDefaults = {
2
+ placement: "top",
3
+ align: "center",
4
+ };
5
+ export const tooltipBehaviorDefaults = {
6
+ pointerOpenDelayMs: 500,
7
+ focusOpenDelayMs: 0,
8
+ skipDelayMs: 300,
9
+ hoverable: true,
10
+ touchHover: false,
11
+ oneVisiblePerProvider: true,
12
+ };
13
+ const placements = new Set([
14
+ "top",
15
+ "bottom",
16
+ "start",
17
+ "end",
18
+ ]);
19
+ const alignments = new Set(["start", "center", "end"]);
20
+ const descriptorKeys = new Set(["content", "placement", "align"]);
21
+ const openStateKeys = new Set(["open", "defaultOpen", "onOpenChange"]);
22
+ function isObject(value) {
23
+ return value !== null && typeof value === "object" && !Array.isArray(value);
24
+ }
25
+ function rejectUnknownKeys(value, allowed, field) {
26
+ for (const key of Object.keys(value)) {
27
+ if (!allowed.has(key)) {
28
+ throw new TypeError(`Unsupported Tooltip ${field} field: ${key}`);
29
+ }
30
+ }
31
+ }
32
+ export function validateTooltipDescriptor(descriptor) {
33
+ if (!isObject(descriptor)) {
34
+ throw new TypeError("Tooltip descriptor must be an object");
35
+ }
36
+ rejectUnknownKeys(descriptor, descriptorKeys, "descriptor");
37
+ if (typeof descriptor.content !== "string" ||
38
+ descriptor.content.trim().length === 0) {
39
+ throw new TypeError("Tooltip content must not be empty");
40
+ }
41
+ if (descriptor.content !== descriptor.content.trim()) {
42
+ throw new TypeError("Tooltip content must not start or end with whitespace");
43
+ }
44
+ if (descriptor.placement !== undefined &&
45
+ !placements.has(descriptor.placement)) {
46
+ throw new TypeError(`Unsupported Tooltip placement: ${String(descriptor.placement)}`);
47
+ }
48
+ if (descriptor.align !== undefined && !alignments.has(descriptor.align)) {
49
+ throw new TypeError(`Unsupported Tooltip align: ${String(descriptor.align)}`);
50
+ }
51
+ }
52
+ export function resolveTooltipDescriptor(descriptor) {
53
+ validateTooltipDescriptor(descriptor);
54
+ return {
55
+ content: descriptor.content,
56
+ placement: descriptor.placement ?? tooltipDescriptorDefaults.placement,
57
+ align: descriptor.align ?? tooltipDescriptorDefaults.align,
58
+ };
59
+ }
60
+ export function validateTooltipOpenState(state) {
61
+ if (!isObject(state)) {
62
+ throw new TypeError("Tooltip open state must be an object");
63
+ }
64
+ rejectUnknownKeys(state, openStateKeys, "open state");
65
+ const runtime = state;
66
+ const hasOpen = Object.prototype.hasOwnProperty.call(runtime, "open");
67
+ const hasDefaultOpen = Object.prototype.hasOwnProperty.call(runtime, "defaultOpen");
68
+ if (hasOpen) {
69
+ if (typeof runtime.open !== "boolean") {
70
+ throw new TypeError("Tooltip open must be a boolean");
71
+ }
72
+ if (hasDefaultOpen) {
73
+ throw new TypeError("Controlled Tooltip must not provide defaultOpen");
74
+ }
75
+ if (typeof runtime.onOpenChange !== "function") {
76
+ throw new TypeError("Controlled Tooltip must provide onOpenChange");
77
+ }
78
+ return;
79
+ }
80
+ if (hasDefaultOpen && typeof runtime.defaultOpen !== "boolean") {
81
+ throw new TypeError("Tooltip defaultOpen must be a boolean");
82
+ }
83
+ if (runtime.onOpenChange !== undefined &&
84
+ typeof runtime.onOpenChange !== "function") {
85
+ throw new TypeError("Tooltip onOpenChange must be a function");
86
+ }
87
+ }
88
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"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","sourcesContent":["export type TooltipPlacement = \"top\" | \"bottom\" | \"start\" | \"end\";\nexport type TooltipAlign = \"start\" | \"center\" | \"end\";\n\nexport type TooltipOpenChangeReason =\n | \"pointer\"\n | \"focus\"\n | \"pointer-leave\"\n | \"blur\"\n | \"escape\"\n | \"trigger-activation\"\n | \"another-tooltip\";\n\nexport type TooltipOpenChangeDetails = Readonly<{\n reason: TooltipOpenChangeReason;\n}>;\n\n/** Tooltip copy is intentionally plain text and never owns an action. */\nexport type TooltipDescriptor = Readonly<{\n content: string;\n placement?: TooltipPlacement;\n align?: TooltipAlign;\n}>;\n\nexport type ResolvedTooltipDescriptor = Readonly<{\n content: string;\n placement: TooltipPlacement;\n align: TooltipAlign;\n}>;\n\nexport type ControlledTooltipOpenState = Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, details: TooltipOpenChangeDetails): void;\n}>;\n\nexport type UncontrolledTooltipOpenState = Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (\n open: boolean,\n details: TooltipOpenChangeDetails,\n ) => void;\n}>;\n\nexport type TooltipOpenState =\n | ControlledTooltipOpenState\n | UncontrolledTooltipOpenState;\n\nexport const tooltipDescriptorDefaults = {\n placement: \"top\",\n align: \"center\",\n} as const satisfies Readonly<{\n placement: TooltipPlacement;\n align: TooltipAlign;\n}>;\n\nexport const tooltipBehaviorDefaults = {\n pointerOpenDelayMs: 500,\n focusOpenDelayMs: 0,\n skipDelayMs: 300,\n hoverable: true,\n touchHover: false,\n oneVisiblePerProvider: true,\n} as const;\n\nconst placements = new Set<TooltipPlacement>([\n \"top\",\n \"bottom\",\n \"start\",\n \"end\",\n]);\nconst alignments = new Set<TooltipAlign>([\"start\", \"center\", \"end\"]);\nconst descriptorKeys = new Set([\"content\", \"placement\", \"align\"]);\nconst openStateKeys = new Set([\"open\", \"defaultOpen\", \"onOpenChange\"]);\n\nfunction isObject(value: unknown): value is Readonly<Record<string, unknown>> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction rejectUnknownKeys(\n value: Readonly<Record<string, unknown>>,\n allowed: ReadonlySet<string>,\n field: string,\n): void {\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) {\n throw new TypeError(`Unsupported Tooltip ${field} field: ${key}`);\n }\n }\n}\n\nexport function validateTooltipDescriptor(\n descriptor: TooltipDescriptor,\n): void {\n if (!isObject(descriptor)) {\n throw new TypeError(\"Tooltip descriptor must be an object\");\n }\n rejectUnknownKeys(descriptor, descriptorKeys, \"descriptor\");\n if (\n typeof descriptor.content !== \"string\" ||\n descriptor.content.trim().length === 0\n ) {\n throw new TypeError(\"Tooltip content must not be empty\");\n }\n if (descriptor.content !== descriptor.content.trim()) {\n throw new TypeError(\n \"Tooltip content must not start or end with whitespace\",\n );\n }\n if (\n descriptor.placement !== undefined &&\n !placements.has(descriptor.placement)\n ) {\n throw new TypeError(\n `Unsupported Tooltip placement: ${String(descriptor.placement)}`,\n );\n }\n if (descriptor.align !== undefined && !alignments.has(descriptor.align)) {\n throw new TypeError(\n `Unsupported Tooltip align: ${String(descriptor.align)}`,\n );\n }\n}\n\nexport function resolveTooltipDescriptor(\n descriptor: TooltipDescriptor,\n): ResolvedTooltipDescriptor {\n validateTooltipDescriptor(descriptor);\n return {\n content: descriptor.content,\n placement: descriptor.placement ?? tooltipDescriptorDefaults.placement,\n align: descriptor.align ?? tooltipDescriptorDefaults.align,\n };\n}\n\nexport function validateTooltipOpenState(state: TooltipOpenState): void {\n if (!isObject(state)) {\n throw new TypeError(\"Tooltip open state must be an object\");\n }\n rejectUnknownKeys(state, openStateKeys, \"open state\");\n const runtime = state as Readonly<Record<string, unknown>>;\n const hasOpen = Object.prototype.hasOwnProperty.call(runtime, \"open\");\n const hasDefaultOpen = Object.prototype.hasOwnProperty.call(\n runtime,\n \"defaultOpen\",\n );\n if (hasOpen) {\n if (typeof runtime.open !== \"boolean\") {\n throw new TypeError(\"Tooltip open must be a boolean\");\n }\n if (hasDefaultOpen) {\n throw new TypeError(\n \"Controlled Tooltip must not provide defaultOpen\",\n );\n }\n if (typeof runtime.onOpenChange !== \"function\") {\n throw new TypeError(\n \"Controlled Tooltip must provide onOpenChange\",\n );\n }\n return;\n }\n if (hasDefaultOpen && typeof runtime.defaultOpen !== \"boolean\") {\n throw new TypeError(\"Tooltip defaultOpen must be a boolean\");\n }\n if (\n runtime.onOpenChange !== undefined &&\n typeof runtime.onOpenChange !== \"function\"\n ) {\n throw new TypeError(\"Tooltip onOpenChange must be a function\");\n }\n}\n"]}
package/dist/tour.d.ts ADDED
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Tour has no substitute in this package. Unlike FloatingActionButton
3
+ * (BottomCTA already owns the full-width conclusion) or ConfirmPopover
4
+ * (Popover + AlertDialog already cover its space), walking a user through
5
+ * several distinct screen elements in sequence is not already solved by any
6
+ * existing contract.
7
+ *
8
+ * The hardest boundary: this package knows nothing about the DOM or RN view
9
+ * tree. A step names its target with an opaque `anchorId` the product already
10
+ * uses to identify that element; measuring it, scrolling it into view, and
11
+ * drawing any highlight/cutout is entirely the renderer's job. This module
12
+ * never accepts a ref, node, or coordinate.
13
+ */
14
+ export type TourPlacement = "top" | "bottom" | "start" | "end";
15
+ export type TourAlign = "start" | "center" | "end";
16
+ export type TourStepDescriptor<Id extends string = string> = Readonly<{
17
+ id: Id;
18
+ /** Opaque product-owned key; never a DOM node, RN ref, or measured rect. */
19
+ anchorId: string;
20
+ title: string;
21
+ description: string;
22
+ placement?: TourPlacement;
23
+ align?: TourAlign;
24
+ }>;
25
+ export type TourLabels = Readonly<{
26
+ next: string;
27
+ previous: string;
28
+ skip: string;
29
+ /** Shown instead of `next` on the last step. */
30
+ done: string;
31
+ }>;
32
+ /**
33
+ * A single stable cursor (`currentStepId`) is the source of truth, same
34
+ * reasoning as Steps: no per-step status array that could represent two
35
+ * "current" steps at once. Unlike Steps, `anchorId` values are not required
36
+ * to be unique — a product may legitimately explain one element across two
37
+ * consecutive steps ("tap here" / "now hold to reorder").
38
+ */
39
+ export type TourDescriptor<Id extends string = string> = Readonly<{
40
+ /** Announced once when the tour opens, before any step content. */
41
+ accessibilityLabel: string;
42
+ steps: readonly TourStepDescriptor<Id>[];
43
+ currentStepId: Id;
44
+ labels: TourLabels;
45
+ }>;
46
+ export type TourAdvanceReason = "next" | "previous";
47
+ /**
48
+ * `skip`/`escape` are the user backing out early, `complete` is finishing the
49
+ * last step, `programmatic` is the controlled owner closing directly, and
50
+ * `interrupted` is an unmount or route replace settling the tour without any
51
+ * of the above — mirroring AlertDialog's cancel-reason vocabulary. There is
52
+ * deliberately no `outside` reason: unlike Popover, an accidental pointer
53
+ * outside the card must not silently end a walkthrough the user did not ask
54
+ * to leave.
55
+ */
56
+ export type TourCloseReason = "skip" | "escape" | "complete" | "programmatic" | "interrupted";
57
+ /**
58
+ * `onOpenChange`는 여는 것과 닫는 것을 모두 보고하는데 `TourCloseReason`에는 여는
59
+ * 사유가 없었다. 타입 이름이 약속하는 것과 내용이 어긋나 있었고, 오버레이 어휘를
60
+ * 보고하는 다른 일곱 모듈은 전부 `"trigger"`를 쓴다(`docs/architecture.md`의
61
+ * 「오버레이 어휘 규칙」). 여기만 예외로 둘 이유가 없어 같은 이름을 들인다.
62
+ */
63
+ export type TourOpenReason = "trigger" | TourCloseReason;
64
+ export type TourOpenChangeDetail = Readonly<{
65
+ reason: TourOpenReason;
66
+ }>;
67
+ export type ControlledTourOpenState = Readonly<{
68
+ open: boolean;
69
+ defaultOpen?: never;
70
+ onOpenChange(open: boolean, detail: TourOpenChangeDetail): void;
71
+ }>;
72
+ export type UncontrolledTourOpenState = Readonly<{
73
+ open?: never;
74
+ defaultOpen?: boolean;
75
+ onOpenChange?: (open: boolean, detail: TourOpenChangeDetail) => void;
76
+ }>;
77
+ export type TourOpenState = ControlledTourOpenState | UncontrolledTourOpenState;
78
+ export type TourStepChangeHandler<Id extends string = string> = (stepId: Id, reason: TourAdvanceReason) => void;
79
+ export type TourStepAnnouncementInfo = Readonly<{
80
+ position: number;
81
+ total: number;
82
+ title: string;
83
+ description: string;
84
+ }>;
85
+ /**
86
+ * Products compose the exact phrase order (Korean "5단계 중 2단계" does not
87
+ * translate structurally to "step 2 of 5"), same reasoning as Steps'
88
+ * `composeAccessibleName`. The composed string is what a renderer moves
89
+ * accessibility focus to and announces on every step change — it is the
90
+ * entire answer to "what does a screen reader user get instead of a visual
91
+ * pointer": the same title, description, and position a sighted user reads.
92
+ */
93
+ export type ComposeTourStepAnnouncement = (info: TourStepAnnouncementInfo) => string;
94
+ export type ResolveTourOptions = Readonly<{
95
+ composeAnnouncement: ComposeTourStepAnnouncement;
96
+ }>;
97
+ export type ResolvedTourStepDescriptor<Id extends string = string> = TourStepDescriptor<Id> & Readonly<{
98
+ /** 1-indexed so position and title both preserve reading order for renderers. */
99
+ position: number;
100
+ total: number;
101
+ announcement: string;
102
+ }>;
103
+ export type ResolvedTourDescriptor<Id extends string = string> = Readonly<{
104
+ accessibilityLabel: string;
105
+ steps: readonly ResolvedTourStepDescriptor<Id>[];
106
+ currentStep: ResolvedTourStepDescriptor<Id>;
107
+ isFirstStep: boolean;
108
+ isLastStep: boolean;
109
+ }>;
110
+ export type TourAdvanceOutcome<Id extends string = string> = Readonly<{
111
+ type: "step";
112
+ stepId: Id;
113
+ }> | Readonly<{
114
+ type: "close";
115
+ reason: "complete";
116
+ }> | Readonly<{
117
+ type: "no-op";
118
+ }>;
119
+ /**
120
+ * The only fixed policy this contract has: outside dismiss is never allowed.
121
+ * There is no `dismissible`/`escapeDismiss` bag to configure, unlike Popover
122
+ * — Escape and the Skip action always work (the brief's "탈출" requirement is
123
+ * non-negotiable), so there is nothing left to make optional.
124
+ */
125
+ export declare const tourBehaviorDefaults: {
126
+ readonly outsideDismiss: false;
127
+ };
128
+ export declare function validateTourStepDescriptor<Id extends string>(step: TourStepDescriptor<Id>): void;
129
+ export declare function validateTourLabels(labels: TourLabels): void;
130
+ /**
131
+ * Duplicate `anchorId` values across steps are accepted on purpose (see the
132
+ * type doc above) — only `id` must be unique. A validator that rejected
133
+ * repeated anchors would block a legitimate two-part explanation of one
134
+ * element.
135
+ */
136
+ export declare function validateTourDescriptor<Id extends string>(descriptor: TourDescriptor<Id>): void;
137
+ export declare function validateTourOpenState(state: TourOpenState): void;
138
+ /**
139
+ * Resolves every step eagerly (not just the current one) so a renderer can
140
+ * pre-render adjacent step cards without re-deriving position/total, mirroring
141
+ * `resolveStepsDescriptor`. Throws if the composer returns an empty
142
+ * announcement instead of silently leaving screen reader users with nothing.
143
+ */
144
+ export declare function resolveTourDescriptor<Id extends string>(descriptor: TourDescriptor<Id>, options: ResolveTourOptions): ResolvedTourDescriptor<Id>;
145
+ /**
146
+ * Pure decision, no stateful session: Tour has no async side effect to guard
147
+ * (unlike AlertDialog's confirm), so there is no busy/error phase to own —
148
+ * only "what step comes next," which is exactly what a renderer needs to call
149
+ * `onStepChange`/`onOpenChange` itself. This follows Popover's lighter
150
+ * pattern rather than AlertDialog's session, since nothing here needs
151
+ * exactly-once side-effect guarding, only the ordinary exactly-once event
152
+ * delivery a renderer's own callback already provides.
153
+ */
154
+ export declare function resolveTourAdvance<Id extends string>(descriptor: TourDescriptor<Id>, reason: TourAdvanceReason): TourAdvanceOutcome<Id>;
155
+ /**
156
+ * The step card reuses the same anchored floating-surface anatomy as
157
+ * Tooltip/Popover instead of declaring new chrome. The backdrop reuses the
158
+ * stronger `backdrop.modal` veil (not `backdrop.veil`) because Tour, like
159
+ * Dialog/Sheet, blocks background interaction while open — `veil` is used
160
+ * where the surface underneath stays reachable, which Tour's fixed
161
+ * `outsideDismiss: false` policy says it does not.
162
+ */
163
+ export declare const tourRecipe: {
164
+ readonly slots: readonly ["backdrop", "card", "title", "description", "counter", "previousAction", "nextAction", "skipAction"];
165
+ readonly card: {
166
+ readonly background: Readonly<{
167
+ source: "theme";
168
+ key: "bg";
169
+ alpha?: number;
170
+ }>;
171
+ readonly border: Readonly<{
172
+ source: "theme";
173
+ key: "border";
174
+ alpha?: number;
175
+ }>;
176
+ readonly borderWidth: 1;
177
+ readonly radius: "md";
178
+ readonly shadow: {
179
+ readonly color: "#000000";
180
+ readonly opacity: 0.12;
181
+ readonly radius: 12;
182
+ readonly offsetY: 4;
183
+ };
184
+ readonly padding: 8;
185
+ };
186
+ readonly backdrop: {
187
+ readonly color: "#000000";
188
+ readonly opacity: 0.6;
189
+ };
190
+ readonly maxWidth: 320;
191
+ readonly gap: 12;
192
+ readonly sideOffset: 8;
193
+ /**
194
+ * `context` (320ms, opacity under Reduce Motion): moving from one step's
195
+ * card/highlight to the next is a jump between unrelated parts of the
196
+ * screen, the same "큰 화면 요소와 맥락 전환" tier Sheet/Dialog use, not a
197
+ * small in-place state change.
198
+ */
199
+ readonly transition: {
200
+ readonly enter: {
201
+ readonly duration: 320;
202
+ readonly easing: "emphasized";
203
+ readonly reducedMotion: "opacity";
204
+ };
205
+ readonly exit: {
206
+ readonly duration: 120;
207
+ readonly easing: "exit";
208
+ readonly reducedMotion: "instant";
209
+ };
210
+ };
211
+ };
212
+ /**
213
+ * Literal scenario names for behaviorRegistry.tour (lead wires into
214
+ * src/behaviors.ts). Kept here, not there, so this module stays
215
+ * self-contained per the authoring brief.
216
+ */
217
+ export declare const tourBehaviorScenarios: readonly ["focus-moves-to-the-step-card-on-every-step-change-not-the-anchor", "background-is-inert-while-a-tour-is-open", "escape-and-skip-always-exit-regardless-of-step", "outside-pointer-does-not-dismiss", "next-on-the-last-step-closes-with-reason-complete", "previous-on-the-first-step-is-a-no-op", "controlled-owner-programmatic-close-always-wins", "unmount-or-route-change-settles-as-interrupted-exactly-once", "reduced-motion-crossfades-the-card-without-traveling-between-anchors"];
218
+ //# sourceMappingURL=tour.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tour.d.ts","sourceRoot":"","sources":["../src/tour.ts"],"names":[],"mappings":"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"}
package/dist/tour.js ADDED
@@ -0,0 +1,211 @@
1
+ import { backdrop, motionPreset, spacing } from "./foundations.js";
2
+ import { floatingSurfaceContract } from "./component-contracts.js";
3
+ /**
4
+ * The only fixed policy this contract has: outside dismiss is never allowed.
5
+ * There is no `dismissible`/`escapeDismiss` bag to configure, unlike Popover
6
+ * — Escape and the Skip action always work (the brief's "탈출" requirement is
7
+ * non-negotiable), so there is nothing left to make optional.
8
+ */
9
+ export const tourBehaviorDefaults = {
10
+ outsideDismiss: false,
11
+ };
12
+ function assertNonEmpty(value, field) {
13
+ if (typeof value !== "string" || value.trim().length === 0) {
14
+ throw new TypeError(`Tour ${field} must not be empty`);
15
+ }
16
+ }
17
+ const placements = new Set(["top", "bottom", "start", "end"]);
18
+ const aligns = new Set(["start", "center", "end"]);
19
+ export function validateTourStepDescriptor(step) {
20
+ assertNonEmpty(step.id, "step id");
21
+ if (step.id !== step.id.trim()) {
22
+ throw new TypeError("Tour step id must not start or end with whitespace");
23
+ }
24
+ assertNonEmpty(step.anchorId, "step anchorId");
25
+ assertNonEmpty(step.title, "step title");
26
+ assertNonEmpty(step.description, "step description");
27
+ if (step.placement !== undefined && !placements.has(step.placement)) {
28
+ throw new TypeError(`Unsupported Tour step placement: ${String(step.placement)}`);
29
+ }
30
+ if (step.align !== undefined && !aligns.has(step.align)) {
31
+ throw new TypeError(`Unsupported Tour step align: ${String(step.align)}`);
32
+ }
33
+ }
34
+ export function validateTourLabels(labels) {
35
+ if (labels === null || typeof labels !== "object") {
36
+ throw new TypeError("Tour labels must be an object");
37
+ }
38
+ for (const field of ["next", "previous", "skip", "done"]) {
39
+ const value = labels[field];
40
+ if (typeof value !== "string" || value.trim().length === 0) {
41
+ throw new TypeError(`Tour labels.${field} must not be empty`);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Duplicate `anchorId` values across steps are accepted on purpose (see the
47
+ * type doc above) — only `id` must be unique. A validator that rejected
48
+ * repeated anchors would block a legitimate two-part explanation of one
49
+ * element.
50
+ */
51
+ export function validateTourDescriptor(descriptor) {
52
+ assertNonEmpty(descriptor.accessibilityLabel, "accessibilityLabel");
53
+ if (!Array.isArray(descriptor.steps) || descriptor.steps.length === 0) {
54
+ throw new RangeError("Tour must contain at least one step");
55
+ }
56
+ validateTourLabels(descriptor.labels);
57
+ const ids = new Set();
58
+ for (const step of descriptor.steps) {
59
+ validateTourStepDescriptor(step);
60
+ if (ids.has(step.id)) {
61
+ throw new TypeError(`Duplicate Tour step id: ${step.id}`);
62
+ }
63
+ ids.add(step.id);
64
+ }
65
+ if (!ids.has(descriptor.currentStepId)) {
66
+ throw new RangeError(`Tour currentStepId does not match any step id: ${String(descriptor.currentStepId)}`);
67
+ }
68
+ }
69
+ const openStateKeys = new Set(["open", "defaultOpen", "onOpenChange"]);
70
+ export function validateTourOpenState(state) {
71
+ if (state === null || typeof state !== "object") {
72
+ throw new TypeError("Tour open state must be an object");
73
+ }
74
+ const runtime = state;
75
+ for (const key of Object.keys(runtime)) {
76
+ if (!openStateKeys.has(key)) {
77
+ throw new TypeError(`Unsupported Tour open state field: ${key}`);
78
+ }
79
+ }
80
+ const hasOpen = Object.prototype.hasOwnProperty.call(runtime, "open");
81
+ const hasDefaultOpen = Object.prototype.hasOwnProperty.call(runtime, "defaultOpen");
82
+ if (hasOpen) {
83
+ if (typeof runtime.open !== "boolean") {
84
+ throw new TypeError("Tour open must be a boolean");
85
+ }
86
+ if (hasDefaultOpen) {
87
+ throw new TypeError("Controlled Tour must not provide defaultOpen");
88
+ }
89
+ if (typeof runtime.onOpenChange !== "function") {
90
+ throw new TypeError("Controlled Tour must provide onOpenChange");
91
+ }
92
+ return;
93
+ }
94
+ if (hasDefaultOpen && typeof runtime.defaultOpen !== "boolean") {
95
+ throw new TypeError("Tour defaultOpen must be a boolean");
96
+ }
97
+ if (runtime.onOpenChange !== undefined && typeof runtime.onOpenChange !== "function") {
98
+ throw new TypeError("Tour onOpenChange must be a function");
99
+ }
100
+ }
101
+ /**
102
+ * Resolves every step eagerly (not just the current one) so a renderer can
103
+ * pre-render adjacent step cards without re-deriving position/total, mirroring
104
+ * `resolveStepsDescriptor`. Throws if the composer returns an empty
105
+ * announcement instead of silently leaving screen reader users with nothing.
106
+ */
107
+ export function resolveTourDescriptor(descriptor, options) {
108
+ validateTourDescriptor(descriptor);
109
+ if (typeof options.composeAnnouncement !== "function") {
110
+ throw new TypeError("Tour composeAnnouncement must be a function");
111
+ }
112
+ const total = descriptor.steps.length;
113
+ const cursorIndex = descriptor.steps.findIndex((step) => step.id === descriptor.currentStepId);
114
+ const steps = descriptor.steps.map((step, index) => {
115
+ const position = index + 1;
116
+ const announcement = options.composeAnnouncement({
117
+ position,
118
+ total,
119
+ title: step.title,
120
+ description: step.description,
121
+ });
122
+ if (typeof announcement !== "string" || announcement.trim().length === 0) {
123
+ throw new TypeError("Tour composeAnnouncement must return a non-empty string");
124
+ }
125
+ return { ...step, position, total, announcement };
126
+ });
127
+ return {
128
+ accessibilityLabel: descriptor.accessibilityLabel,
129
+ steps,
130
+ // Safe: validateTourDescriptor already confirmed currentStepId matches a step.
131
+ currentStep: steps[cursorIndex],
132
+ isFirstStep: cursorIndex === 0,
133
+ isLastStep: cursorIndex === total - 1,
134
+ };
135
+ }
136
+ /**
137
+ * Pure decision, no stateful session: Tour has no async side effect to guard
138
+ * (unlike AlertDialog's confirm), so there is no busy/error phase to own —
139
+ * only "what step comes next," which is exactly what a renderer needs to call
140
+ * `onStepChange`/`onOpenChange` itself. This follows Popover's lighter
141
+ * pattern rather than AlertDialog's session, since nothing here needs
142
+ * exactly-once side-effect guarding, only the ordinary exactly-once event
143
+ * delivery a renderer's own callback already provides.
144
+ */
145
+ export function resolveTourAdvance(descriptor, reason) {
146
+ validateTourDescriptor(descriptor);
147
+ if (reason !== "next" && reason !== "previous") {
148
+ throw new TypeError(`Unsupported Tour advance reason: ${String(reason)}`);
149
+ }
150
+ const cursorIndex = descriptor.steps.findIndex((step) => step.id === descriptor.currentStepId);
151
+ if (reason === "previous") {
152
+ if (cursorIndex === 0)
153
+ return { type: "no-op" };
154
+ // Safe: cursorIndex > 0 here, so cursorIndex - 1 is always in bounds.
155
+ return { type: "step", stepId: descriptor.steps[cursorIndex - 1].id };
156
+ }
157
+ if (cursorIndex === descriptor.steps.length - 1) {
158
+ return { type: "close", reason: "complete" };
159
+ }
160
+ // Safe: cursorIndex is not the last index here, so cursorIndex + 1 is in bounds.
161
+ return { type: "step", stepId: descriptor.steps[cursorIndex + 1].id };
162
+ }
163
+ /**
164
+ * The step card reuses the same anchored floating-surface anatomy as
165
+ * Tooltip/Popover instead of declaring new chrome. The backdrop reuses the
166
+ * stronger `backdrop.modal` veil (not `backdrop.veil`) because Tour, like
167
+ * Dialog/Sheet, blocks background interaction while open — `veil` is used
168
+ * where the surface underneath stays reachable, which Tour's fixed
169
+ * `outsideDismiss: false` policy says it does not.
170
+ */
171
+ export const tourRecipe = {
172
+ slots: [
173
+ "backdrop",
174
+ "card",
175
+ "title",
176
+ "description",
177
+ "counter",
178
+ "previousAction",
179
+ "nextAction",
180
+ "skipAction",
181
+ ],
182
+ card: floatingSurfaceContract,
183
+ backdrop: backdrop.modal,
184
+ maxWidth: 320,
185
+ gap: spacing.sm,
186
+ sideOffset: spacing.xs,
187
+ /**
188
+ * `context` (320ms, opacity under Reduce Motion): moving from one step's
189
+ * card/highlight to the next is a jump between unrelated parts of the
190
+ * screen, the same "큰 화면 요소와 맥락 전환" tier Sheet/Dialog use, not a
191
+ * small in-place state change.
192
+ */
193
+ transition: { enter: motionPreset.context, exit: motionPreset.exit },
194
+ };
195
+ /**
196
+ * Literal scenario names for behaviorRegistry.tour (lead wires into
197
+ * src/behaviors.ts). Kept here, not there, so this module stays
198
+ * self-contained per the authoring brief.
199
+ */
200
+ export const tourBehaviorScenarios = [
201
+ "focus-moves-to-the-step-card-on-every-step-change-not-the-anchor",
202
+ "background-is-inert-while-a-tour-is-open",
203
+ "escape-and-skip-always-exit-regardless-of-step",
204
+ "outside-pointer-does-not-dismiss",
205
+ "next-on-the-last-step-closes-with-reason-complete",
206
+ "previous-on-the-first-step-is-a-no-op",
207
+ "controlled-owner-programmatic-close-always-wins",
208
+ "unmount-or-route-change-settles-as-interrupted-exactly-once",
209
+ "reduced-motion-crossfades-the-card-without-traveling-between-anchors",
210
+ ];
211
+ //# sourceMappingURL=tour.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tour.js","sourceRoot":"","sources":["../src/tour.ts"],"names":[],"mappings":"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","sourcesContent":["import { backdrop, motionPreset, spacing } from \"./foundations.js\";\nimport { floatingSurfaceContract } from \"./component-contracts.js\";\n\n/**\n * Tour has no substitute in this package. Unlike FloatingActionButton\n * (BottomCTA already owns the full-width conclusion) or ConfirmPopover\n * (Popover + AlertDialog already cover its space), walking a user through\n * several distinct screen elements in sequence is not already solved by any\n * existing contract.\n *\n * The hardest boundary: this package knows nothing about the DOM or RN view\n * tree. A step names its target with an opaque `anchorId` the product already\n * uses to identify that element; measuring it, scrolling it into view, and\n * drawing any highlight/cutout is entirely the renderer's job. This module\n * never accepts a ref, node, or coordinate.\n */\nexport type TourPlacement = \"top\" | \"bottom\" | \"start\" | \"end\";\nexport type TourAlign = \"start\" | \"center\" | \"end\";\n\nexport type TourStepDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n /** Opaque product-owned key; never a DOM node, RN ref, or measured rect. */\n anchorId: string;\n title: string;\n description: string;\n placement?: TourPlacement;\n align?: TourAlign;\n}>;\n\nexport type TourLabels = Readonly<{\n next: string;\n previous: string;\n skip: string;\n /** Shown instead of `next` on the last step. */\n done: string;\n}>;\n\n/**\n * A single stable cursor (`currentStepId`) is the source of truth, same\n * reasoning as Steps: no per-step status array that could represent two\n * \"current\" steps at once. Unlike Steps, `anchorId` values are not required\n * to be unique — a product may legitimately explain one element across two\n * consecutive steps (\"tap here\" / \"now hold to reorder\").\n */\nexport type TourDescriptor<Id extends string = string> = Readonly<{\n /** Announced once when the tour opens, before any step content. */\n accessibilityLabel: string;\n steps: readonly TourStepDescriptor<Id>[];\n currentStepId: Id;\n labels: TourLabels;\n}>;\n\nexport type TourAdvanceReason = \"next\" | \"previous\";\n\n/**\n * `skip`/`escape` are the user backing out early, `complete` is finishing the\n * last step, `programmatic` is the controlled owner closing directly, and\n * `interrupted` is an unmount or route replace settling the tour without any\n * of the above — mirroring AlertDialog's cancel-reason vocabulary. There is\n * deliberately no `outside` reason: unlike Popover, an accidental pointer\n * outside the card must not silently end a walkthrough the user did not ask\n * to leave.\n */\nexport type TourCloseReason =\n | \"skip\"\n | \"escape\"\n | \"complete\"\n | \"programmatic\"\n | \"interrupted\";\n\n/**\n * `onOpenChange`는 여는 것과 닫는 것을 모두 보고하는데 `TourCloseReason`에는 여는\n * 사유가 없었다. 타입 이름이 약속하는 것과 내용이 어긋나 있었고, 오버레이 어휘를\n * 보고하는 다른 일곱 모듈은 전부 `\"trigger\"`를 쓴다(`docs/architecture.md`의\n * 「오버레이 어휘 규칙」). 여기만 예외로 둘 이유가 없어 같은 이름을 들인다.\n */\nexport type TourOpenReason = \"trigger\" | TourCloseReason;\n\nexport type TourOpenChangeDetail = Readonly<{ reason: TourOpenReason }>;\n\nexport type ControlledTourOpenState = Readonly<{\n open: boolean;\n defaultOpen?: never;\n onOpenChange(open: boolean, detail: TourOpenChangeDetail): void;\n}>;\n\nexport type UncontrolledTourOpenState = Readonly<{\n open?: never;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, detail: TourOpenChangeDetail) => void;\n}>;\n\nexport type TourOpenState = ControlledTourOpenState | UncontrolledTourOpenState;\n\nexport type TourStepChangeHandler<Id extends string = string> = (\n stepId: Id,\n reason: TourAdvanceReason,\n) => void;\n\nexport type TourStepAnnouncementInfo = Readonly<{\n position: number;\n total: number;\n title: string;\n description: string;\n}>;\n\n/**\n * Products compose the exact phrase order (Korean \"5단계 중 2단계\" does not\n * translate structurally to \"step 2 of 5\"), same reasoning as Steps'\n * `composeAccessibleName`. The composed string is what a renderer moves\n * accessibility focus to and announces on every step change — it is the\n * entire answer to \"what does a screen reader user get instead of a visual\n * pointer\": the same title, description, and position a sighted user reads.\n */\nexport type ComposeTourStepAnnouncement = (info: TourStepAnnouncementInfo) => string;\n\nexport type ResolveTourOptions = Readonly<{\n composeAnnouncement: ComposeTourStepAnnouncement;\n}>;\n\nexport type ResolvedTourStepDescriptor<Id extends string = string> =\n TourStepDescriptor<Id> &\n Readonly<{\n /** 1-indexed so position and title both preserve reading order for renderers. */\n position: number;\n total: number;\n announcement: string;\n }>;\n\nexport type ResolvedTourDescriptor<Id extends string = string> = Readonly<{\n accessibilityLabel: string;\n steps: readonly ResolvedTourStepDescriptor<Id>[];\n currentStep: ResolvedTourStepDescriptor<Id>;\n isFirstStep: boolean;\n isLastStep: boolean;\n}>;\n\nexport type TourAdvanceOutcome<Id extends string = string> =\n | Readonly<{ type: \"step\"; stepId: Id }>\n | Readonly<{ type: \"close\"; reason: \"complete\" }>\n | Readonly<{ type: \"no-op\" }>;\n\n/**\n * The only fixed policy this contract has: outside dismiss is never allowed.\n * There is no `dismissible`/`escapeDismiss` bag to configure, unlike Popover\n * — Escape and the Skip action always work (the brief's \"탈출\" requirement is\n * non-negotiable), so there is nothing left to make optional.\n */\nexport const tourBehaviorDefaults = {\n outsideDismiss: false,\n} as const satisfies Readonly<{ outsideDismiss: false }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Tour ${field} must not be empty`);\n }\n}\n\nconst placements = new Set<TourPlacement>([\"top\", \"bottom\", \"start\", \"end\"]);\nconst aligns = new Set<TourAlign>([\"start\", \"center\", \"end\"]);\n\nexport function validateTourStepDescriptor<Id extends string>(\n step: TourStepDescriptor<Id>,\n): void {\n assertNonEmpty(step.id, \"step id\");\n if (step.id !== step.id.trim()) {\n throw new TypeError(\"Tour step id must not start or end with whitespace\");\n }\n assertNonEmpty(step.anchorId, \"step anchorId\");\n assertNonEmpty(step.title, \"step title\");\n assertNonEmpty(step.description, \"step description\");\n if (step.placement !== undefined && !placements.has(step.placement)) {\n throw new TypeError(`Unsupported Tour step placement: ${String(step.placement)}`);\n }\n if (step.align !== undefined && !aligns.has(step.align)) {\n throw new TypeError(`Unsupported Tour step align: ${String(step.align)}`);\n }\n}\n\nexport function validateTourLabels(labels: TourLabels): void {\n if (labels === null || typeof labels !== \"object\") {\n throw new TypeError(\"Tour labels must be an object\");\n }\n for (const field of [\"next\", \"previous\", \"skip\", \"done\"] as const) {\n const value = labels[field];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Tour labels.${field} must not be empty`);\n }\n }\n}\n\n/**\n * Duplicate `anchorId` values across steps are accepted on purpose (see the\n * type doc above) — only `id` must be unique. A validator that rejected\n * repeated anchors would block a legitimate two-part explanation of one\n * element.\n */\nexport function validateTourDescriptor<Id extends string>(\n descriptor: TourDescriptor<Id>,\n): void {\n assertNonEmpty(descriptor.accessibilityLabel, \"accessibilityLabel\");\n if (!Array.isArray(descriptor.steps) || descriptor.steps.length === 0) {\n throw new RangeError(\"Tour must contain at least one step\");\n }\n validateTourLabels(descriptor.labels);\n\n const ids = new Set<Id>();\n for (const step of descriptor.steps) {\n validateTourStepDescriptor(step);\n if (ids.has(step.id)) {\n throw new TypeError(`Duplicate Tour step id: ${step.id}`);\n }\n ids.add(step.id);\n }\n if (!ids.has(descriptor.currentStepId)) {\n throw new RangeError(\n `Tour currentStepId does not match any step id: ${String(descriptor.currentStepId)}`,\n );\n }\n}\n\nconst openStateKeys = new Set([\"open\", \"defaultOpen\", \"onOpenChange\"]);\n\nexport function validateTourOpenState(state: TourOpenState): void {\n if (state === null || typeof state !== \"object\") {\n throw new TypeError(\"Tour open state must be an object\");\n }\n const runtime = state as Readonly<Record<string, unknown>>;\n for (const key of Object.keys(runtime)) {\n if (!openStateKeys.has(key)) {\n throw new TypeError(`Unsupported Tour open state field: ${key}`);\n }\n }\n const hasOpen = Object.prototype.hasOwnProperty.call(runtime, \"open\");\n const hasDefaultOpen = Object.prototype.hasOwnProperty.call(runtime, \"defaultOpen\");\n if (hasOpen) {\n if (typeof runtime.open !== \"boolean\") {\n throw new TypeError(\"Tour open must be a boolean\");\n }\n if (hasDefaultOpen) {\n throw new TypeError(\"Controlled Tour must not provide defaultOpen\");\n }\n if (typeof runtime.onOpenChange !== \"function\") {\n throw new TypeError(\"Controlled Tour must provide onOpenChange\");\n }\n return;\n }\n if (hasDefaultOpen && typeof runtime.defaultOpen !== \"boolean\") {\n throw new TypeError(\"Tour defaultOpen must be a boolean\");\n }\n if (runtime.onOpenChange !== undefined && typeof runtime.onOpenChange !== \"function\") {\n throw new TypeError(\"Tour onOpenChange must be a function\");\n }\n}\n\n/**\n * Resolves every step eagerly (not just the current one) so a renderer can\n * pre-render adjacent step cards without re-deriving position/total, mirroring\n * `resolveStepsDescriptor`. Throws if the composer returns an empty\n * announcement instead of silently leaving screen reader users with nothing.\n */\nexport function resolveTourDescriptor<Id extends string>(\n descriptor: TourDescriptor<Id>,\n options: ResolveTourOptions,\n): ResolvedTourDescriptor<Id> {\n validateTourDescriptor(descriptor);\n if (typeof options.composeAnnouncement !== \"function\") {\n throw new TypeError(\"Tour composeAnnouncement must be a function\");\n }\n\n const total = descriptor.steps.length;\n const cursorIndex = descriptor.steps.findIndex(\n (step) => step.id === descriptor.currentStepId,\n );\n\n const steps = descriptor.steps.map((step, index) => {\n const position = index + 1;\n const announcement = options.composeAnnouncement({\n position,\n total,\n title: step.title,\n description: step.description,\n });\n if (typeof announcement !== \"string\" || announcement.trim().length === 0) {\n throw new TypeError(\"Tour composeAnnouncement must return a non-empty string\");\n }\n return { ...step, position, total, announcement };\n });\n\n return {\n accessibilityLabel: descriptor.accessibilityLabel,\n steps,\n // Safe: validateTourDescriptor already confirmed currentStepId matches a step.\n currentStep: steps[cursorIndex]!,\n isFirstStep: cursorIndex === 0,\n isLastStep: cursorIndex === total - 1,\n };\n}\n\n/**\n * Pure decision, no stateful session: Tour has no async side effect to guard\n * (unlike AlertDialog's confirm), so there is no busy/error phase to own —\n * only \"what step comes next,\" which is exactly what a renderer needs to call\n * `onStepChange`/`onOpenChange` itself. This follows Popover's lighter\n * pattern rather than AlertDialog's session, since nothing here needs\n * exactly-once side-effect guarding, only the ordinary exactly-once event\n * delivery a renderer's own callback already provides.\n */\nexport function resolveTourAdvance<Id extends string>(\n descriptor: TourDescriptor<Id>,\n reason: TourAdvanceReason,\n): TourAdvanceOutcome<Id> {\n validateTourDescriptor(descriptor);\n if (reason !== \"next\" && reason !== \"previous\") {\n throw new TypeError(`Unsupported Tour advance reason: ${String(reason)}`);\n }\n\n const cursorIndex = descriptor.steps.findIndex(\n (step) => step.id === descriptor.currentStepId,\n );\n\n if (reason === \"previous\") {\n if (cursorIndex === 0) return { type: \"no-op\" };\n // Safe: cursorIndex > 0 here, so cursorIndex - 1 is always in bounds.\n return { type: \"step\", stepId: descriptor.steps[cursorIndex - 1]!.id };\n }\n if (cursorIndex === descriptor.steps.length - 1) {\n return { type: \"close\", reason: \"complete\" };\n }\n // Safe: cursorIndex is not the last index here, so cursorIndex + 1 is in bounds.\n return { type: \"step\", stepId: descriptor.steps[cursorIndex + 1]!.id };\n}\n\n/**\n * The step card reuses the same anchored floating-surface anatomy as\n * Tooltip/Popover instead of declaring new chrome. The backdrop reuses the\n * stronger `backdrop.modal` veil (not `backdrop.veil`) because Tour, like\n * Dialog/Sheet, blocks background interaction while open — `veil` is used\n * where the surface underneath stays reachable, which Tour's fixed\n * `outsideDismiss: false` policy says it does not.\n */\nexport const tourRecipe = {\n slots: [\n \"backdrop\",\n \"card\",\n \"title\",\n \"description\",\n \"counter\",\n \"previousAction\",\n \"nextAction\",\n \"skipAction\",\n ] as const,\n card: floatingSurfaceContract,\n backdrop: backdrop.modal,\n maxWidth: 320,\n gap: spacing.sm,\n sideOffset: spacing.xs,\n /**\n * `context` (320ms, opacity under Reduce Motion): moving from one step's\n * card/highlight to the next is a jump between unrelated parts of the\n * screen, the same \"큰 화면 요소와 맥락 전환\" tier Sheet/Dialog use, not a\n * small in-place state change.\n */\n transition: { enter: motionPreset.context, exit: motionPreset.exit },\n} as const;\n\n/**\n * Literal scenario names for behaviorRegistry.tour (lead wires into\n * src/behaviors.ts). Kept here, not there, so this module stays\n * self-contained per the authoring brief.\n */\nexport const tourBehaviorScenarios = [\n \"focus-moves-to-the-step-card-on-every-step-change-not-the-anchor\",\n \"background-is-inert-while-a-tour-is-open\",\n \"escape-and-skip-always-exit-regardless-of-step\",\n \"outside-pointer-does-not-dismiss\",\n \"next-on-the-last-step-closes-with-reason-complete\",\n \"previous-on-the-first-step-is-a-no-op\",\n \"controlled-owner-programmatic-close-always-wins\",\n \"unmount-or-route-change-settles-as-interrupted-exactly-once\",\n \"reduced-motion-crossfades-the-card-without-traveling-between-anchors\",\n] as const;\n"]}