@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,138 @@
1
+ /**
2
+ * Slider always has a value — there is no `empty` axis to model, unlike a
3
+ * NumberField that can start unset. Range selection (two handles) is out of
4
+ * scope until a real product needs it; adding it speculatively would mean
5
+ * guessing at a value-pair contract nobody has validated.
6
+ */
7
+ export type SliderDescriptor = Readonly<{
8
+ /** Visible or accessibility label; announced before the value. */
9
+ label: string;
10
+ value: number;
11
+ min: number;
12
+ max: number;
13
+ step?: number;
14
+ /**
15
+ * Product-formatted copy overriding the raw numeric announcement, e.g.
16
+ * "75점" or ".357". Slider never formats numbers itself.
17
+ */
18
+ valueText?: string;
19
+ }>;
20
+ export type ResolvedSliderDescriptor = Omit<SliderDescriptor, "step"> & Readonly<{
21
+ step: number;
22
+ }>;
23
+ export declare function validateSliderDescriptor(descriptor: SliderDescriptor): void;
24
+ export declare function resolveSliderDescriptor(descriptor: SliderDescriptor): ResolvedSliderDescriptor;
25
+ /** 0..1 position of `value` between `min` and `max`, for track fill — not a display string. */
26
+ export declare function resolveSliderFillFraction(descriptor: SliderDescriptor): number;
27
+ /**
28
+ * Snaps user-originated input with NumberField's min-origin range math while
29
+ * preserving Slider's exact endpoints. Public controlled values may be
30
+ * off-grid; they remain untouched until a user interaction proposes a value.
31
+ */
32
+ export declare function resolveSliderValue(descriptor: SliderDescriptor, candidate: number): number;
33
+ export type SliderDirection = "ltr" | "rtl";
34
+ /**
35
+ * Maps a pointer/touch offset to a min-origin step. RTL mirrors the physical
36
+ * track while preserving the logical meaning of increment/decrement.
37
+ */
38
+ export declare function resolveSliderValueFromOffset(descriptor: SliderDescriptor, offset: number, extent: number, direction: SliderDirection): number;
39
+ export type SliderStepIntent = "increment" | "decrement" | "increment-page" | "decrement-page" | "first" | "last";
40
+ export declare const sliderBehaviorDefaults: {
41
+ readonly pageMultiplier: 10;
42
+ };
43
+ /**
44
+ * One function for every keyboard/RN step intent so Web arrow/Home/End/
45
+ * PageUp/PageDown and RN increment/decrement actions resolve through the same
46
+ * snap-and-clamp judgment as the stepper itself.
47
+ */
48
+ export declare function getSliderStepTarget(descriptor: SliderDescriptor, intent: SliderStepIntent): number;
49
+ export type SliderSize = "medium";
50
+ export declare const sliderRecipe: {
51
+ readonly slots: readonly ["root", "track", "filledTrack", "thumb", "label", "valueLabel"];
52
+ readonly defaults: {
53
+ readonly size: "medium";
54
+ };
55
+ readonly sizes: {
56
+ readonly medium: {
57
+ readonly trackHeight: 4;
58
+ readonly thumbDiameter: 20;
59
+ readonly hitTarget: 44;
60
+ readonly labelVariant: "body";
61
+ readonly valueLabelVariant: "label";
62
+ };
63
+ };
64
+ readonly colors: {
65
+ readonly trackFilled: Readonly<{
66
+ source: "theme";
67
+ key: "contentBrand";
68
+ alpha?: number;
69
+ }>;
70
+ readonly trackUnfilled: Readonly<{
71
+ source: "theme";
72
+ key: "surfaceAlt";
73
+ alpha?: number;
74
+ }>;
75
+ readonly trackUnfilledBorder: Readonly<{
76
+ source: "theme";
77
+ key: "textMuted";
78
+ alpha?: number;
79
+ }>;
80
+ readonly thumb: Readonly<{
81
+ source: "theme";
82
+ key: "bg";
83
+ alpha?: number;
84
+ }>;
85
+ readonly thumbBorder: Readonly<{
86
+ source: "theme";
87
+ key: "contentBrand";
88
+ alpha?: number;
89
+ }>;
90
+ readonly label: Readonly<{
91
+ source: "theme";
92
+ key: "textBody";
93
+ alpha?: number;
94
+ }>;
95
+ readonly valueLabel: Readonly<{
96
+ source: "theme";
97
+ key: "textMuted";
98
+ alpha?: number;
99
+ }>;
100
+ };
101
+ readonly states: {
102
+ readonly focus: {
103
+ readonly color: Readonly<{
104
+ source: "theme";
105
+ key: "contentBrand";
106
+ alpha?: number;
107
+ }>;
108
+ readonly width: 2;
109
+ readonly offset: 2;
110
+ };
111
+ readonly disabledOpacity: 0.5;
112
+ readonly draggedOpacity: 0.64;
113
+ };
114
+ readonly radius: "full";
115
+ };
116
+ export declare const sliderBehavior: {
117
+ readonly controlled: readonly ["value", "defaultValue", "onValueChange"];
118
+ readonly events: readonly ["onValueChangeEnd"];
119
+ readonly defaults: {
120
+ readonly pageMultiplier: 10;
121
+ };
122
+ readonly stateAxes: {
123
+ readonly interaction: readonly ["idle", "hover", "focusVisible", "pressed", "dragged"];
124
+ readonly availability: readonly ["enabled", "disabled"];
125
+ };
126
+ readonly web: {
127
+ readonly roles: readonly ["slider"];
128
+ readonly keyboard: readonly ["Tab", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "PageUp", "PageDown"];
129
+ readonly focus: "native";
130
+ };
131
+ readonly native: {
132
+ readonly roles: readonly ["adjustable"];
133
+ readonly states: readonly ["disabled"];
134
+ readonly actions: readonly ["increment", "decrement"];
135
+ };
136
+ readonly scenarios: readonly ["label-then-value-then-range-announcement-order", "arrow-keys-step-by-one-increment", "page-keys-step-by-the-page-multiplier", "home-and-end-jump-to-min-and-max", "rtl-mirrors-the-track-without-reversing-logical-increment-actions", "continuous-change-and-interaction-end-are-separate-events", "non-divisible-ranges-preserve-exact-min-and-max-endpoints", "native-disable-during-drag-finishes-once-and-ignores-later-moves", "value-is-always-present-no-empty-state", "fill-color-is-never-the-only-value-cue", "range-two-handle-selection-is-out-of-scope-until-a-real-product-need-exists"];
137
+ };
138
+ //# sourceMappingURL=slider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slider.d.ts","sourceRoot":"","sources":["../src/slider.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,GACnE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAQ7B,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAU3E;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,gBAAgB,GAC3B,wBAAwB,CAG1B;AAED,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAG9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,gBAAgB,EAC5B,SAAS,EAAE,MAAM,GAChB,MAAM,CAMR;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,eAAe,GACzB,MAAM,CAWR;AAED,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,gBAAgB,GAChB,OAAO,GACP,MAAM,CAAC;AAEX,eAAO,MAAM,sBAAsB;;CAEsB,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,gBAAgB,GACvB,MAAM,CAcR;AAED,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;AAElC,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDxB,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;CAyCU,CAAC"}
package/dist/slider.js ADDED
@@ -0,0 +1,150 @@
1
+ import { focusIndicatorContract } from "./component-contracts.js";
2
+ import { control, opacity } from "./foundations.js";
3
+ import { assertFiniteNumber, numericRangeDefaults, snapToStep, stepNumericValue, validateNumericRangeConfig, } from "./number-field.js";
4
+ import { semanticColors } from "./semantic-colors.js";
5
+ function assertNonEmptyCopy(value, field) {
6
+ if (value.trim().length === 0) {
7
+ throw new TypeError(`Slider ${field} must not be empty`);
8
+ }
9
+ }
10
+ export function validateSliderDescriptor(descriptor) {
11
+ assertNonEmptyCopy(descriptor.label, "label");
12
+ validateNumericRangeConfig(descriptor);
13
+ assertFiniteNumber(descriptor.value, "value");
14
+ if (descriptor.value < descriptor.min || descriptor.value > descriptor.max) {
15
+ throw new RangeError("Slider value must be within min and max");
16
+ }
17
+ if (descriptor.valueText !== undefined) {
18
+ assertNonEmptyCopy(descriptor.valueText, "valueText");
19
+ }
20
+ }
21
+ export function resolveSliderDescriptor(descriptor) {
22
+ validateSliderDescriptor(descriptor);
23
+ return { ...descriptor, step: descriptor.step ?? numericRangeDefaults.step };
24
+ }
25
+ /** 0..1 position of `value` between `min` and `max`, for track fill — not a display string. */
26
+ export function resolveSliderFillFraction(descriptor) {
27
+ validateSliderDescriptor(descriptor);
28
+ return (descriptor.value - descriptor.min) / (descriptor.max - descriptor.min);
29
+ }
30
+ /**
31
+ * Snaps user-originated input with NumberField's min-origin range math while
32
+ * preserving Slider's exact endpoints. Public controlled values may be
33
+ * off-grid; they remain untouched until a user interaction proposes a value.
34
+ */
35
+ export function resolveSliderValue(descriptor, candidate) {
36
+ validateSliderDescriptor(descriptor);
37
+ assertFiniteNumber(candidate, "candidate");
38
+ if (candidate <= descriptor.min)
39
+ return descriptor.min;
40
+ if (candidate >= descriptor.max)
41
+ return descriptor.max;
42
+ return snapToStep(candidate, descriptor);
43
+ }
44
+ /**
45
+ * Maps a pointer/touch offset to a min-origin step. RTL mirrors the physical
46
+ * track while preserving the logical meaning of increment/decrement.
47
+ */
48
+ export function resolveSliderValueFromOffset(descriptor, offset, extent, direction) {
49
+ validateSliderDescriptor(descriptor);
50
+ assertFiniteNumber(offset, "offset");
51
+ assertFiniteNumber(extent, "extent");
52
+ if (extent <= 0)
53
+ throw new RangeError("Slider extent must be greater than 0");
54
+ const physicalFraction = Math.min(1, Math.max(0, offset / extent));
55
+ const logicalFraction = direction === "rtl" ? 1 - physicalFraction : physicalFraction;
56
+ return resolveSliderValue(descriptor, descriptor.min + logicalFraction * (descriptor.max - descriptor.min));
57
+ }
58
+ export const sliderBehaviorDefaults = {
59
+ pageMultiplier: 10,
60
+ };
61
+ /**
62
+ * One function for every keyboard/RN step intent so Web arrow/Home/End/
63
+ * PageUp/PageDown and RN increment/decrement actions resolve through the same
64
+ * snap-and-clamp judgment as the stepper itself.
65
+ */
66
+ export function getSliderStepTarget(descriptor, intent) {
67
+ validateSliderDescriptor(descriptor);
68
+ if (intent === "first")
69
+ return descriptor.min;
70
+ if (intent === "last")
71
+ return descriptor.max;
72
+ const step = descriptor.step ?? numericRangeDefaults.step;
73
+ const config = { min: descriptor.min, max: descriptor.max, step };
74
+ if (intent === "increment" || intent === "decrement") {
75
+ return stepNumericValue(descriptor.value, config, intent);
76
+ }
77
+ const direction = intent === "increment-page" ? 1 : -1;
78
+ return resolveSliderValue(descriptor, descriptor.value + direction * step * sliderBehaviorDefaults.pageMultiplier);
79
+ }
80
+ export const sliderRecipe = {
81
+ slots: ["root", "track", "filledTrack", "thumb", "label", "valueLabel"],
82
+ defaults: { size: "medium" },
83
+ sizes: {
84
+ medium: {
85
+ trackHeight: 4,
86
+ thumbDiameter: 20,
87
+ hitTarget: control.minTouchTarget,
88
+ labelVariant: "body",
89
+ valueLabelVariant: "label",
90
+ },
91
+ },
92
+ colors: {
93
+ trackFilled: semanticColors.content.brand,
94
+ trackUnfilled: semanticColors.surface.sunken,
95
+ trackUnfilledBorder: semanticColors.content.secondary,
96
+ thumb: semanticColors.canvas,
97
+ thumbBorder: semanticColors.border.focus,
98
+ label: semanticColors.content.body,
99
+ valueLabel: semanticColors.content.secondary,
100
+ },
101
+ states: {
102
+ focus: focusIndicatorContract,
103
+ disabledOpacity: opacity.disabled,
104
+ draggedOpacity: opacity.dragged,
105
+ },
106
+ radius: "full",
107
+ };
108
+ export const sliderBehavior = {
109
+ controlled: ["value", "defaultValue", "onValueChange"],
110
+ events: ["onValueChangeEnd"],
111
+ defaults: sliderBehaviorDefaults,
112
+ stateAxes: {
113
+ interaction: ["idle", "hover", "focusVisible", "pressed", "dragged"],
114
+ availability: ["enabled", "disabled"],
115
+ },
116
+ web: {
117
+ roles: ["slider"],
118
+ keyboard: [
119
+ "Tab",
120
+ "ArrowLeft",
121
+ "ArrowRight",
122
+ "ArrowUp",
123
+ "ArrowDown",
124
+ "Home",
125
+ "End",
126
+ "PageUp",
127
+ "PageDown",
128
+ ],
129
+ focus: "native",
130
+ },
131
+ native: {
132
+ roles: ["adjustable"],
133
+ states: ["disabled"],
134
+ actions: ["increment", "decrement"],
135
+ },
136
+ scenarios: [
137
+ "label-then-value-then-range-announcement-order",
138
+ "arrow-keys-step-by-one-increment",
139
+ "page-keys-step-by-the-page-multiplier",
140
+ "home-and-end-jump-to-min-and-max",
141
+ "rtl-mirrors-the-track-without-reversing-logical-increment-actions",
142
+ "continuous-change-and-interaction-end-are-separate-events",
143
+ "non-divisible-ranges-preserve-exact-min-and-max-endpoints",
144
+ "native-disable-during-drag-finishes-once-and-ignores-later-moves",
145
+ "value-is-always-present-no-empty-state",
146
+ "fill-color-is-never-the-only-value-cue",
147
+ "range-two-handle-selection-is-out-of-scope-until-a-real-product-need-exists",
148
+ ],
149
+ };
150
+ //# sourceMappingURL=slider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slider.js","sourceRoot":"","sources":["../src/slider.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport { control, opacity, type TextVariant } from \"./foundations.js\";\nimport {\n assertFiniteNumber,\n numericRangeDefaults,\n snapToStep,\n stepNumericValue,\n validateNumericRangeConfig,\n type NumericRangeConfig,\n} from \"./number-field.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Slider always has a value — there is no `empty` axis to model, unlike a\n * NumberField that can start unset. Range selection (two handles) is out of\n * scope until a real product needs it; adding it speculatively would mean\n * guessing at a value-pair contract nobody has validated.\n */\nexport type SliderDescriptor = Readonly<{\n /** Visible or accessibility label; announced before the value. */\n label: string;\n value: number;\n min: number;\n max: number;\n step?: number;\n /**\n * Product-formatted copy overriding the raw numeric announcement, e.g.\n * \"75점\" or \".357\". Slider never formats numbers itself.\n */\n valueText?: string;\n}>;\n\nexport type ResolvedSliderDescriptor = Omit<SliderDescriptor, \"step\"> &\n Readonly<{ step: number }>;\n\nfunction assertNonEmptyCopy(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Slider ${field} must not be empty`);\n }\n}\n\nexport function validateSliderDescriptor(descriptor: SliderDescriptor): void {\n assertNonEmptyCopy(descriptor.label, \"label\");\n validateNumericRangeConfig(descriptor);\n assertFiniteNumber(descriptor.value, \"value\");\n if (descriptor.value < descriptor.min || descriptor.value > descriptor.max) {\n throw new RangeError(\"Slider value must be within min and max\");\n }\n if (descriptor.valueText !== undefined) {\n assertNonEmptyCopy(descriptor.valueText, \"valueText\");\n }\n}\n\nexport function resolveSliderDescriptor(\n descriptor: SliderDescriptor,\n): ResolvedSliderDescriptor {\n validateSliderDescriptor(descriptor);\n return { ...descriptor, step: descriptor.step ?? numericRangeDefaults.step };\n}\n\n/** 0..1 position of `value` between `min` and `max`, for track fill — not a display string. */\nexport function resolveSliderFillFraction(descriptor: SliderDescriptor): number {\n validateSliderDescriptor(descriptor);\n return (descriptor.value - descriptor.min) / (descriptor.max - descriptor.min);\n}\n\n/**\n * Snaps user-originated input with NumberField's min-origin range math while\n * preserving Slider's exact endpoints. Public controlled values may be\n * off-grid; they remain untouched until a user interaction proposes a value.\n */\nexport function resolveSliderValue(\n descriptor: SliderDescriptor,\n candidate: number,\n): number {\n validateSliderDescriptor(descriptor);\n assertFiniteNumber(candidate, \"candidate\");\n if (candidate <= descriptor.min) return descriptor.min;\n if (candidate >= descriptor.max) return descriptor.max;\n return snapToStep(candidate, descriptor);\n}\n\nexport type SliderDirection = \"ltr\" | \"rtl\";\n\n/**\n * Maps a pointer/touch offset to a min-origin step. RTL mirrors the physical\n * track while preserving the logical meaning of increment/decrement.\n */\nexport function resolveSliderValueFromOffset(\n descriptor: SliderDescriptor,\n offset: number,\n extent: number,\n direction: SliderDirection,\n): number {\n validateSliderDescriptor(descriptor);\n assertFiniteNumber(offset, \"offset\");\n assertFiniteNumber(extent, \"extent\");\n if (extent <= 0) throw new RangeError(\"Slider extent must be greater than 0\");\n const physicalFraction = Math.min(1, Math.max(0, offset / extent));\n const logicalFraction = direction === \"rtl\" ? 1 - physicalFraction : physicalFraction;\n return resolveSliderValue(\n descriptor,\n descriptor.min + logicalFraction * (descriptor.max - descriptor.min),\n );\n}\n\nexport type SliderStepIntent =\n | \"increment\"\n | \"decrement\"\n | \"increment-page\"\n | \"decrement-page\"\n | \"first\"\n | \"last\";\n\nexport const sliderBehaviorDefaults = {\n pageMultiplier: 10,\n} as const satisfies Readonly<{ pageMultiplier: number }>;\n\n/**\n * One function for every keyboard/RN step intent so Web arrow/Home/End/\n * PageUp/PageDown and RN increment/decrement actions resolve through the same\n * snap-and-clamp judgment as the stepper itself.\n */\nexport function getSliderStepTarget(\n descriptor: SliderDescriptor,\n intent: SliderStepIntent,\n): number {\n validateSliderDescriptor(descriptor);\n if (intent === \"first\") return descriptor.min;\n if (intent === \"last\") return descriptor.max;\n const step = descriptor.step ?? numericRangeDefaults.step;\n const config: NumericRangeConfig = { min: descriptor.min, max: descriptor.max, step };\n if (intent === \"increment\" || intent === \"decrement\") {\n return stepNumericValue(descriptor.value, config, intent);\n }\n const direction = intent === \"increment-page\" ? 1 : -1;\n return resolveSliderValue(\n descriptor,\n descriptor.value + direction * step * sliderBehaviorDefaults.pageMultiplier,\n );\n}\n\nexport type SliderSize = \"medium\";\n\nexport const sliderRecipe = {\n slots: [\"root\", \"track\", \"filledTrack\", \"thumb\", \"label\", \"valueLabel\"] as const,\n defaults: { size: \"medium\" },\n sizes: {\n medium: {\n trackHeight: 4,\n thumbDiameter: 20,\n hitTarget: control.minTouchTarget,\n labelVariant: \"body\",\n valueLabelVariant: \"label\",\n },\n },\n colors: {\n trackFilled: semanticColors.content.brand,\n trackUnfilled: semanticColors.surface.sunken,\n trackUnfilledBorder: semanticColors.content.secondary,\n thumb: semanticColors.canvas,\n thumbBorder: semanticColors.border.focus,\n label: semanticColors.content.body,\n valueLabel: semanticColors.content.secondary,\n },\n states: {\n focus: focusIndicatorContract,\n disabledOpacity: opacity.disabled,\n draggedOpacity: opacity.dragged,\n },\n radius: \"full\",\n} as const satisfies {\n slots: readonly [\"root\", \"track\", \"filledTrack\", \"thumb\", \"label\", \"valueLabel\"];\n defaults: { size: SliderSize };\n sizes: Record<\n SliderSize,\n {\n trackHeight: number;\n thumbDiameter: number;\n hitTarget: number;\n labelVariant: TextVariant;\n valueLabelVariant: TextVariant;\n }\n >;\n colors: {\n trackFilled: ColorReference;\n trackUnfilled: ColorReference;\n trackUnfilledBorder: ColorReference;\n thumb: ColorReference;\n thumbBorder: ColorReference;\n label: ColorReference;\n valueLabel: ColorReference;\n };\n states: {\n focus: typeof focusIndicatorContract;\n disabledOpacity: number;\n draggedOpacity: number;\n };\n radius: \"full\";\n};\n\nexport const sliderBehavior = {\n controlled: [\"value\", \"defaultValue\", \"onValueChange\"],\n events: [\"onValueChangeEnd\"],\n defaults: sliderBehaviorDefaults,\n stateAxes: {\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\", \"dragged\"],\n availability: [\"enabled\", \"disabled\"],\n },\n web: {\n roles: [\"slider\"],\n keyboard: [\n \"Tab\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n ],\n focus: \"native\",\n },\n native: {\n roles: [\"adjustable\"],\n states: [\"disabled\"],\n actions: [\"increment\", \"decrement\"],\n },\n scenarios: [\n \"label-then-value-then-range-announcement-order\",\n \"arrow-keys-step-by-one-increment\",\n \"page-keys-step-by-the-page-multiplier\",\n \"home-and-end-jump-to-min-and-max\",\n \"rtl-mirrors-the-track-without-reversing-logical-increment-actions\",\n \"continuous-change-and-interaction-end-are-separate-events\",\n \"non-divisible-ranges-preserve-exact-min-and-max-endpoints\",\n \"native-disable-during-drag-finishes-once-and-ignores-later-moves\",\n \"value-is-always-present-no-empty-state\",\n \"fill-color-is-never-the-only-value-cue\",\n \"range-two-handle-selection-is-out-of-scope-until-a-real-product-need-exists\",\n ],\n} as const satisfies BehaviorContract;\n"]}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `"horizontal"` means panes sit side by side (the separator is a vertical
3
+ * bar); `"vertical"` means panes stack top/bottom (the separator is a
4
+ * horizontal bar). See `resolveSplitterSeparatorOrientation` — the two are
5
+ * intentionally not spelled the same way to keep that inversion visible.
6
+ */
7
+ export type SplitterAxis = "horizontal" | "vertical";
8
+ /**
9
+ * "Pick a number within a range" — the same problem NumberField and Slider
10
+ * already solve, applied to a pane size instead of a displayed value. This
11
+ * descriptor reuses `min`/`max`/`step`/`value` verbatim and calls
12
+ * `src/number-field.ts`'s judgment functions directly rather than
13
+ * re-deriving clamp/snap/step arithmetic a third time.
14
+ */
15
+ export type SplitterDescriptor = Readonly<{
16
+ /** The primary pane's current size, in whatever unit the product chose (fraction or px). */
17
+ value: number;
18
+ min: number;
19
+ max: number;
20
+ step?: number;
21
+ axis?: SplitterAxis;
22
+ /** Required accessible name for the separator (e.g. "패널 크기 조절"). */
23
+ label: string;
24
+ /** Product-formatted announcement of the current size, e.g. "35%" or "320px". */
25
+ valueText?: string;
26
+ }>;
27
+ export declare const splitterDefaults: {
28
+ readonly axis: "horizontal";
29
+ };
30
+ export declare function validateSplitterDescriptor(descriptor: SplitterDescriptor): void;
31
+ /**
32
+ * A WAI-ARIA `separator` reports orientation as the axis the pointer moves
33
+ * *along*, which is perpendicular to how the panes are arranged — a
34
+ * side-by-side (`"horizontal"`) splitter has a *vertical* separator bar.
35
+ * Named and exported so renderers translate this once instead of
36
+ * re-deriving the inversion at each call site.
37
+ */
38
+ export declare function resolveSplitterSeparatorOrientation(axis: SplitterAxis): "horizontal" | "vertical";
39
+ /** Keyboard step, reusing NumberField's stepper arithmetic unchanged. */
40
+ export declare function getNextSplitterValue(descriptor: SplitterDescriptor, direction: "increment" | "decrement"): number;
41
+ /** Drag resolution: snaps a raw pointer-derived value to the same step grid keyboard resize uses. */
42
+ export declare function resolveSplitterDragValue(descriptor: SplitterDescriptor, rawValue: number): number;
43
+ /** Boundary jump for Home/End, reusing NumberField's clamp unchanged. */
44
+ export declare function resolveSplitterBoundaryValue(descriptor: SplitterDescriptor, boundary: "min" | "max"): number;
45
+ /**
46
+ * Reused visual language from Slider: a thin visible line (`thickness`) with
47
+ * a much larger `hitTarget` — the same "small handle, 44-unit hit area"
48
+ * split documented in `docs/slider.md`.
49
+ */
50
+ export declare const splitterRecipe: {
51
+ readonly slots: readonly ["root", "pane", "separator", "handle"];
52
+ readonly defaults: {
53
+ readonly axis: "horizontal";
54
+ };
55
+ readonly separator: {
56
+ readonly thickness: 1;
57
+ readonly hitTarget: 44;
58
+ readonly color: Readonly<{
59
+ source: "theme";
60
+ key: "border";
61
+ alpha?: number;
62
+ }>;
63
+ readonly hoverColor: Readonly<{
64
+ source: "theme";
65
+ key: "textWeak";
66
+ alpha?: number;
67
+ }>;
68
+ readonly activeColor: Readonly<{
69
+ source: "theme";
70
+ key: "contentBrand";
71
+ alpha?: number;
72
+ }>;
73
+ };
74
+ readonly states: {
75
+ readonly focus: {
76
+ readonly color: Readonly<{
77
+ source: "theme";
78
+ key: "contentBrand";
79
+ alpha?: number;
80
+ }>;
81
+ readonly width: 2;
82
+ readonly offset: 2;
83
+ };
84
+ };
85
+ };
86
+ /**
87
+ * Deliberately excluded: collapse-to-hidden-pane (antd's collapsible
88
+ * arrows) and N-pane/multi-separator layouts. Neither has measured product
89
+ * demand, and both would roughly double this contract's surface (a
90
+ * collapsed pane needs its own reveal affordance and bypasses `min`; N
91
+ * panes need an ordered list of separators instead of one value) — the
92
+ * same reasoning Slider used to leave out two-handle `range` mode.
93
+ */
94
+ export declare const splitterBehavior: {
95
+ readonly controlled: readonly ["value", "defaultValue", "onValueChange"];
96
+ readonly inputs: readonly ["min", "max", "step", "label", "valueText"];
97
+ readonly stateAxes: {
98
+ readonly interaction: readonly ["idle", "hover", "focusVisible", "pressed", "dragged"];
99
+ readonly availability: readonly ["enabled", "disabled"];
100
+ };
101
+ readonly web: {
102
+ readonly roles: readonly ["separator"];
103
+ readonly keyboard: readonly ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"];
104
+ readonly focus: "native";
105
+ };
106
+ readonly native: {
107
+ readonly roles: readonly [];
108
+ readonly states: readonly [];
109
+ readonly actions: readonly [];
110
+ };
111
+ readonly scenarios: readonly ["separator-role-carries-aria-valuenow-min-max-and-optional-valuetext", "separator-orientation-is-perpendicular-to-the-splitter-axis-not-equal-to-it", "keyboard-arrow-keys-step-like-numberfield-home-and-end-jump-to-the-boundary", "drag-and-keyboard-resize-produce-the-same-snapped-value", "reuses-number-field-range-judgment-instead-of-a-new-numeric-domain", "no-collapse-to-hidden-pane-or-multi-separator-layout-in-this-contract"];
112
+ };
113
+ //# sourceMappingURL=splitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splitter.d.ts","sourceRoot":"","sources":["../src/splitter.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,4FAA4F;IAC5F,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,gBAAgB;;CAEwB,CAAC;AAQtD,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAc/E;AAED;;;;;;GAMG;AACH,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,YAAY,GACjB,YAAY,GAAG,UAAU,CAE3B;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,WAAW,GAAG,WAAW,GACnC,MAAM,CAGR;AAED,qGAAqG;AACrG,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGjG;AAED,yEAAyE;AACzE,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,kBAAkB,EAC9B,QAAQ,EAAE,KAAK,GAAG,KAAK,GACtB,MAAM,CAGR;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsB1B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;CAqBQ,CAAC"}
@@ -0,0 +1,99 @@
1
+ import { focusIndicatorContract } from "./component-contracts.js";
2
+ import { clampToRange, snapToStep, stepNumericValue, validateNumericRangeConfig, } from "./number-field.js";
3
+ import { control } from "./foundations.js";
4
+ import { semanticColors } from "./semantic-colors.js";
5
+ export const splitterDefaults = {
6
+ axis: "horizontal",
7
+ };
8
+ function assertNonEmpty(value, field) {
9
+ if (typeof value !== "string" || value.trim().length === 0) {
10
+ throw new TypeError(`Splitter ${field} must not be empty`);
11
+ }
12
+ }
13
+ export function validateSplitterDescriptor(descriptor) {
14
+ validateNumericRangeConfig(descriptor);
15
+ if (descriptor.value < descriptor.min || descriptor.value > descriptor.max) {
16
+ throw new RangeError("Splitter value must be within min and max");
17
+ }
18
+ if (descriptor.axis !== undefined &&
19
+ descriptor.axis !== "horizontal" &&
20
+ descriptor.axis !== "vertical") {
21
+ throw new TypeError(`Unsupported Splitter axis: ${String(descriptor.axis)}`);
22
+ }
23
+ assertNonEmpty(descriptor.label, "label");
24
+ if (descriptor.valueText !== undefined)
25
+ assertNonEmpty(descriptor.valueText, "valueText");
26
+ }
27
+ /**
28
+ * A WAI-ARIA `separator` reports orientation as the axis the pointer moves
29
+ * *along*, which is perpendicular to how the panes are arranged — a
30
+ * side-by-side (`"horizontal"`) splitter has a *vertical* separator bar.
31
+ * Named and exported so renderers translate this once instead of
32
+ * re-deriving the inversion at each call site.
33
+ */
34
+ export function resolveSplitterSeparatorOrientation(axis) {
35
+ return axis === "horizontal" ? "vertical" : "horizontal";
36
+ }
37
+ /** Keyboard step, reusing NumberField's stepper arithmetic unchanged. */
38
+ export function getNextSplitterValue(descriptor, direction) {
39
+ validateSplitterDescriptor(descriptor);
40
+ return stepNumericValue(descriptor.value, descriptor, direction);
41
+ }
42
+ /** Drag resolution: snaps a raw pointer-derived value to the same step grid keyboard resize uses. */
43
+ export function resolveSplitterDragValue(descriptor, rawValue) {
44
+ validateSplitterDescriptor(descriptor);
45
+ return snapToStep(rawValue, descriptor);
46
+ }
47
+ /** Boundary jump for Home/End, reusing NumberField's clamp unchanged. */
48
+ export function resolveSplitterBoundaryValue(descriptor, boundary) {
49
+ validateSplitterDescriptor(descriptor);
50
+ return clampToRange(boundary === "min" ? descriptor.min : descriptor.max, descriptor);
51
+ }
52
+ /**
53
+ * Reused visual language from Slider: a thin visible line (`thickness`) with
54
+ * a much larger `hitTarget` — the same "small handle, 44-unit hit area"
55
+ * split documented in `docs/slider.md`.
56
+ */
57
+ export const splitterRecipe = {
58
+ slots: ["root", "pane", "separator", "handle"],
59
+ defaults: { axis: "horizontal" },
60
+ separator: {
61
+ thickness: 1,
62
+ hitTarget: control.minTouchTarget,
63
+ color: semanticColors.border.default,
64
+ hoverColor: semanticColors.border.strong,
65
+ activeColor: semanticColors.border.focus,
66
+ },
67
+ states: { focus: focusIndicatorContract },
68
+ };
69
+ /**
70
+ * Deliberately excluded: collapse-to-hidden-pane (antd's collapsible
71
+ * arrows) and N-pane/multi-separator layouts. Neither has measured product
72
+ * demand, and both would roughly double this contract's surface (a
73
+ * collapsed pane needs its own reveal affordance and bypasses `min`; N
74
+ * panes need an ordered list of separators instead of one value) — the
75
+ * same reasoning Slider used to leave out two-handle `range` mode.
76
+ */
77
+ export const splitterBehavior = {
78
+ controlled: ["value", "defaultValue", "onValueChange"],
79
+ inputs: ["min", "max", "step", "label", "valueText"],
80
+ stateAxes: {
81
+ interaction: ["idle", "hover", "focusVisible", "pressed", "dragged"],
82
+ availability: ["enabled", "disabled"],
83
+ },
84
+ web: {
85
+ roles: ["separator"],
86
+ keyboard: ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"],
87
+ focus: "native",
88
+ },
89
+ native: { roles: [], states: [], actions: [] },
90
+ scenarios: [
91
+ "separator-role-carries-aria-valuenow-min-max-and-optional-valuetext",
92
+ "separator-orientation-is-perpendicular-to-the-splitter-axis-not-equal-to-it",
93
+ "keyboard-arrow-keys-step-like-numberfield-home-and-end-jump-to-the-boundary",
94
+ "drag-and-keyboard-resize-produce-the-same-snapped-value",
95
+ "reuses-number-field-range-judgment-instead-of-a-new-numeric-domain",
96
+ "no-collapse-to-hidden-pane-or-multi-separator-layout-in-this-contract",
97
+ ],
98
+ };
99
+ //# sourceMappingURL=splitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splitter.js","sourceRoot":"","sources":["../src/splitter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport {\n clampToRange,\n snapToStep,\n stepNumericValue,\n validateNumericRangeConfig,\n} from \"./number-field.js\";\nimport { control } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * `\"horizontal\"` means panes sit side by side (the separator is a vertical\n * bar); `\"vertical\"` means panes stack top/bottom (the separator is a\n * horizontal bar). See `resolveSplitterSeparatorOrientation` — the two are\n * intentionally not spelled the same way to keep that inversion visible.\n */\nexport type SplitterAxis = \"horizontal\" | \"vertical\";\n\n/**\n * \"Pick a number within a range\" — the same problem NumberField and Slider\n * already solve, applied to a pane size instead of a displayed value. This\n * descriptor reuses `min`/`max`/`step`/`value` verbatim and calls\n * `src/number-field.ts`'s judgment functions directly rather than\n * re-deriving clamp/snap/step arithmetic a third time.\n */\nexport type SplitterDescriptor = Readonly<{\n /** The primary pane's current size, in whatever unit the product chose (fraction or px). */\n value: number;\n min: number;\n max: number;\n step?: number;\n axis?: SplitterAxis;\n /** Required accessible name for the separator (e.g. \"패널 크기 조절\"). */\n label: string;\n /** Product-formatted announcement of the current size, e.g. \"35%\" or \"320px\". */\n valueText?: string;\n}>;\n\nexport const splitterDefaults = {\n axis: \"horizontal\",\n} as const satisfies Readonly<{ axis: SplitterAxis }>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Splitter ${field} must not be empty`);\n }\n}\n\nexport function validateSplitterDescriptor(descriptor: SplitterDescriptor): void {\n validateNumericRangeConfig(descriptor);\n if (descriptor.value < descriptor.min || descriptor.value > descriptor.max) {\n throw new RangeError(\"Splitter value must be within min and max\");\n }\n if (\n descriptor.axis !== undefined &&\n descriptor.axis !== \"horizontal\" &&\n descriptor.axis !== \"vertical\"\n ) {\n throw new TypeError(`Unsupported Splitter axis: ${String(descriptor.axis)}`);\n }\n assertNonEmpty(descriptor.label, \"label\");\n if (descriptor.valueText !== undefined) assertNonEmpty(descriptor.valueText, \"valueText\");\n}\n\n/**\n * A WAI-ARIA `separator` reports orientation as the axis the pointer moves\n * *along*, which is perpendicular to how the panes are arranged — a\n * side-by-side (`\"horizontal\"`) splitter has a *vertical* separator bar.\n * Named and exported so renderers translate this once instead of\n * re-deriving the inversion at each call site.\n */\nexport function resolveSplitterSeparatorOrientation(\n axis: SplitterAxis,\n): \"horizontal\" | \"vertical\" {\n return axis === \"horizontal\" ? \"vertical\" : \"horizontal\";\n}\n\n/** Keyboard step, reusing NumberField's stepper arithmetic unchanged. */\nexport function getNextSplitterValue(\n descriptor: SplitterDescriptor,\n direction: \"increment\" | \"decrement\",\n): number {\n validateSplitterDescriptor(descriptor);\n return stepNumericValue(descriptor.value, descriptor, direction);\n}\n\n/** Drag resolution: snaps a raw pointer-derived value to the same step grid keyboard resize uses. */\nexport function resolveSplitterDragValue(descriptor: SplitterDescriptor, rawValue: number): number {\n validateSplitterDescriptor(descriptor);\n return snapToStep(rawValue, descriptor);\n}\n\n/** Boundary jump for Home/End, reusing NumberField's clamp unchanged. */\nexport function resolveSplitterBoundaryValue(\n descriptor: SplitterDescriptor,\n boundary: \"min\" | \"max\",\n): number {\n validateSplitterDescriptor(descriptor);\n return clampToRange(boundary === \"min\" ? descriptor.min : descriptor.max, descriptor);\n}\n\n/**\n * Reused visual language from Slider: a thin visible line (`thickness`) with\n * a much larger `hitTarget` — the same \"small handle, 44-unit hit area\"\n * split documented in `docs/slider.md`.\n */\nexport const splitterRecipe = {\n slots: [\"root\", \"pane\", \"separator\", \"handle\"] as const,\n defaults: { axis: \"horizontal\" },\n separator: {\n thickness: 1,\n hitTarget: control.minTouchTarget,\n color: semanticColors.border.default,\n hoverColor: semanticColors.border.strong,\n activeColor: semanticColors.border.focus,\n },\n states: { focus: focusIndicatorContract },\n} as const satisfies {\n slots: readonly [\"root\", \"pane\", \"separator\", \"handle\"];\n defaults: { axis: SplitterAxis };\n separator: {\n thickness: number;\n hitTarget: number;\n color: ColorReference;\n hoverColor: ColorReference;\n activeColor: ColorReference;\n };\n states: { focus: typeof focusIndicatorContract };\n};\n\n/**\n * Deliberately excluded: collapse-to-hidden-pane (antd's collapsible\n * arrows) and N-pane/multi-separator layouts. Neither has measured product\n * demand, and both would roughly double this contract's surface (a\n * collapsed pane needs its own reveal affordance and bypasses `min`; N\n * panes need an ordered list of separators instead of one value) — the\n * same reasoning Slider used to leave out two-handle `range` mode.\n */\nexport const splitterBehavior = {\n controlled: [\"value\", \"defaultValue\", \"onValueChange\"],\n inputs: [\"min\", \"max\", \"step\", \"label\", \"valueText\"],\n stateAxes: {\n interaction: [\"idle\", \"hover\", \"focusVisible\", \"pressed\", \"dragged\"],\n availability: [\"enabled\", \"disabled\"],\n },\n web: {\n roles: [\"separator\"],\n keyboard: [\"ArrowLeft\", \"ArrowRight\", \"ArrowUp\", \"ArrowDown\", \"Home\", \"End\"],\n focus: \"native\",\n },\n native: { roles: [], states: [], actions: [] },\n scenarios: [\n \"separator-role-carries-aria-valuenow-min-max-and-optional-valuetext\",\n \"separator-orientation-is-perpendicular-to-the-splitter-axis-not-equal-to-it\",\n \"keyboard-arrow-keys-step-like-numberfield-home-and-end-jump-to-the-boundary\",\n \"drag-and-keyboard-resize-produce-the-same-snapped-value\",\n \"reuses-number-field-range-judgment-instead-of-a-new-numeric-domain\",\n \"no-collapse-to-hidden-pane-or-multi-separator-layout-in-this-contract\",\n ],\n} as const satisfies BehaviorContract;\n"]}
@@ -0,0 +1,41 @@
1
+ export type StatisticTrendDirection = "up" | "down" | "flat";
2
+ export type StatisticTrendTone = "neutral" | "success" | "warning" | "danger";
3
+ export declare const statisticDefaults: {
4
+ readonly trendTone: "neutral";
5
+ };
6
+ export type StatisticTrendDescriptor = Readonly<{
7
+ direction: StatisticTrendDirection;
8
+ /** Direction and meaning are independent: an increase is not always good. */
9
+ tone?: StatisticTrendTone;
10
+ /** Visible localized copy ensures the trend is never communicated by color alone. */
11
+ label: string;
12
+ }>;
13
+ export type StatisticDescriptor<Id extends string = string> = Readonly<{
14
+ id: Id;
15
+ label: string;
16
+ /** Products own number/date/unit formatting and pass the final visible string. */
17
+ value: string;
18
+ prefix?: string;
19
+ suffix?: string;
20
+ hint?: string;
21
+ trend?: StatisticTrendDescriptor;
22
+ }>;
23
+ export type StatisticGroupDescriptor<Id extends string = string> = Readonly<{
24
+ items: readonly StatisticDescriptor<Id>[];
25
+ columns?: 1 | 2 | 3 | 4;
26
+ }>;
27
+ export type ResolvedStatisticTrendDescriptor = Omit<StatisticTrendDescriptor, "tone"> & Readonly<{
28
+ tone: StatisticTrendTone;
29
+ }>;
30
+ export type ResolvedStatisticDescriptor<Id extends string = string> = Omit<StatisticDescriptor<Id>, "trend"> & Readonly<{
31
+ trend?: ResolvedStatisticTrendDescriptor;
32
+ }>;
33
+ export declare function validateStatisticDescriptor<Id extends string>(descriptor: StatisticDescriptor<Id>): void;
34
+ export declare function resolveStatisticDescriptor<Id extends string>(descriptor: StatisticDescriptor<Id>): ResolvedStatisticDescriptor<Id>;
35
+ export declare function validateStatisticGroup<Id extends string>(group: StatisticGroupDescriptor<Id>): void;
36
+ export declare const statisticTrendMarks: {
37
+ readonly up: "trendUp";
38
+ readonly down: "trendDown";
39
+ readonly flat: "trendFlat";
40
+ };
41
+ //# sourceMappingURL=statistic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistic.d.ts","sourceRoot":"","sources":["../src/statistic.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9E,eAAO,MAAM,iBAAiB;;CAEkC,CAAC;AAEjE,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,SAAS,EAAE,uBAAuB,CAAC;IACnC,6EAA6E;IAC7E,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,mBAAmB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACrE,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,wBAAwB,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC1E,KAAK,EAAE,SAAS,mBAAmB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1C,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,MAAM,gCAAgC,GAAG,IAAI,CACjD,wBAAwB,EACxB,MAAM,CACP,GACC,QAAQ,CAAC;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAAC;AAEzC,MAAM,MAAM,2BAA2B,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,CACxE,mBAAmB,CAAC,EAAE,CAAC,EACvB,OAAO,CACR,GACC,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,gCAAgC,CAAA;CAAE,CAAC,CAAC;AAQzD,wBAAgB,2BAA2B,CAAC,EAAE,SAAS,MAAM,EAC3D,UAAU,EAAE,mBAAmB,CAAC,EAAE,CAAC,GAClC,IAAI,CAiCN;AAED,wBAAgB,0BAA0B,CAAC,EAAE,SAAS,MAAM,EAC1D,UAAU,EAAE,mBAAmB,CAAC,EAAE,CAAC,GAClC,2BAA2B,CAAC,EAAE,CAAC,CAWjC;AAED,wBAAgB,sBAAsB,CAAC,EAAE,SAAS,MAAM,EACtD,KAAK,EAAE,wBAAwB,CAAC,EAAE,CAAC,GAClC,IAAI,CAqBN;AAED,eAAO,MAAM,mBAAmB;;;;CAI4B,CAAC"}