@krizaka/ui 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/dist/achievement.js +1 -1
  2. package/dist/action-bar.js +1 -1
  3. package/dist/app-shell.js +1 -1
  4. package/dist/argument-card.js +2 -2
  5. package/dist/card-grid.js +1 -1
  6. package/dist/card.js +1 -1
  7. package/dist/choice-grid.js +1 -1
  8. package/dist/chunk-2FRWNSRO.js +23 -0
  9. package/dist/chunk-2FRWNSRO.js.map +1 -0
  10. package/dist/chunk-4LOMIQVX.js +21 -0
  11. package/dist/chunk-4LOMIQVX.js.map +1 -0
  12. package/dist/chunk-BTMFZ66D.js +135 -0
  13. package/dist/chunk-BTMFZ66D.js.map +1 -0
  14. package/dist/chunk-C2BRIFJG.js +13 -0
  15. package/dist/chunk-C2BRIFJG.js.map +1 -0
  16. package/dist/{chunk-K3SWZXYT.js → chunk-DGQMDJXK.js} +2 -2
  17. package/dist/chunk-G3ETON2A.js +22 -0
  18. package/dist/chunk-G3ETON2A.js.map +1 -0
  19. package/dist/chunk-IZ7MV6HM.js +26 -0
  20. package/dist/chunk-IZ7MV6HM.js.map +1 -0
  21. package/dist/chunk-KDXRYUD3.js +39 -0
  22. package/dist/chunk-KDXRYUD3.js.map +1 -0
  23. package/dist/{chunk-OGJI5X4W.js → chunk-ZKVQGXLD.js} +5 -3
  24. package/dist/{chunk-OGJI5X4W.js.map → chunk-ZKVQGXLD.js.map} +1 -1
  25. package/dist/chunk-ZNMFG62E.js +20 -0
  26. package/dist/chunk-ZNMFG62E.js.map +1 -0
  27. package/dist/cn.d.ts +9 -1
  28. package/dist/cn.js +5 -3
  29. package/dist/code-block.js +1 -1
  30. package/dist/command.js +1 -1
  31. package/dist/confirm-button.js +1 -1
  32. package/dist/continue-prompt.js +1 -1
  33. package/dist/cta-panel.js +1 -1
  34. package/dist/data-table.js +3 -2
  35. package/dist/data-table.js.map +1 -1
  36. package/dist/date-picker.d.ts +652 -61
  37. package/dist/date-picker.js +918 -122
  38. package/dist/date-picker.js.map +1 -1
  39. package/dist/decision-list.js +1 -1
  40. package/dist/dropdown-menu.js +1 -1
  41. package/dist/error-page.js +1 -1
  42. package/dist/field.js +1 -1
  43. package/dist/filter-bar.js +3 -2
  44. package/dist/form.d.ts +23 -12
  45. package/dist/form.js +11 -47
  46. package/dist/form.js.map +1 -1
  47. package/dist/gallery-page.js +1 -1
  48. package/dist/glow-card.js +1 -1
  49. package/dist/goal-gauge.js +1 -1
  50. package/dist/how-it-works-page.js +1 -1
  51. package/dist/image-cropper.js +3 -1
  52. package/dist/image-cropper.js.map +1 -1
  53. package/dist/landscape.js +1 -1
  54. package/dist/link-card.js +1 -1
  55. package/dist/longform-page.js +1 -1
  56. package/dist/marquee.js +1 -1
  57. package/dist/mascot-quote.js +1 -1
  58. package/dist/motion.css +18 -1
  59. package/dist/native/form.d.ts +60 -15
  60. package/dist/native/form.js +103 -42
  61. package/dist/native/form.js.map +1 -1
  62. package/dist/native.d.ts +443 -2
  63. package/dist/native.js +1655 -448
  64. package/dist/native.js.map +1 -1
  65. package/dist/nav-bar.js +1 -1
  66. package/dist/page-hero.js +2 -2
  67. package/dist/page.js +2 -2
  68. package/dist/popover.js +1 -1
  69. package/dist/pricing-page.js +1 -1
  70. package/dist/product-page.js +1 -1
  71. package/dist/profile-card.js +1 -1
  72. package/dist/promise-card.js +1 -1
  73. package/dist/radio-group.d.ts +66 -32
  74. package/dist/radio-group.js +54 -10
  75. package/dist/radio-group.js.map +1 -1
  76. package/dist/scroll-stage.js +1 -1
  77. package/dist/slider.d.ts +96 -35
  78. package/dist/slider.js +67 -13
  79. package/dist/slider.js.map +1 -1
  80. package/dist/stage-banner.js +1 -1
  81. package/dist/terminal-replay.js +34 -60
  82. package/dist/terminal-replay.js.map +1 -1
  83. package/dist/toast.js +1 -1
  84. package/dist/toc.js +1 -1
  85. package/dist/tooltip.js +1 -1
  86. package/dist/video-player.js +3 -1
  87. package/dist/video-player.js.map +1 -1
  88. package/package.json +8 -8
  89. package/registry/cn.json +1 -1
  90. package/registry/combobox.json +225 -3
  91. package/registry/date-picker.json +1739 -288
  92. package/registry/dialog.json +74 -3
  93. package/registry/examples/combobox/native/multiple.d.ts +4 -0
  94. package/registry/examples/combobox/native/multiple.dark.png +0 -0
  95. package/registry/examples/combobox/native/multiple.js +15 -0
  96. package/registry/examples/combobox/native/multiple.light.png +0 -0
  97. package/registry/examples/combobox/native/multiple.tsx +15 -0
  98. package/registry/examples/combobox/native/search.d.ts +4 -0
  99. package/registry/examples/combobox/native/search.dark.png +0 -0
  100. package/registry/examples/combobox/native/search.js +15 -0
  101. package/registry/examples/combobox/native/search.light.png +0 -0
  102. package/registry/examples/combobox/native/search.tsx +15 -0
  103. package/registry/examples/date-picker/birth-date-wheel.d.ts +4 -0
  104. package/registry/examples/date-picker/birth-date-wheel.js +13 -0
  105. package/registry/examples/date-picker/birth-date-wheel.tsx +11 -0
  106. package/registry/examples/date-picker/birth-date.d.ts +4 -0
  107. package/registry/examples/date-picker/birth-date.js +25 -0
  108. package/registry/examples/date-picker/birth-date.tsx +21 -0
  109. package/registry/examples/date-picker/calendar.js +1 -1
  110. package/registry/examples/date-picker/calendar.tsx +2 -6
  111. package/registry/examples/date-picker/day-strip.d.ts +4 -0
  112. package/registry/examples/date-picker/day-strip.js +28 -0
  113. package/registry/examples/date-picker/day-strip.tsx +27 -0
  114. package/registry/examples/date-picker/month-year.d.ts +4 -0
  115. package/registry/examples/date-picker/month-year.js +13 -0
  116. package/registry/examples/date-picker/month-year.tsx +10 -0
  117. package/registry/examples/date-picker/native/birth-date-wheel.d.ts +4 -0
  118. package/registry/examples/date-picker/native/birth-date-wheel.dark.png +0 -0
  119. package/registry/examples/date-picker/native/birth-date-wheel.js +13 -0
  120. package/registry/examples/date-picker/native/birth-date-wheel.light.png +0 -0
  121. package/registry/examples/date-picker/native/birth-date-wheel.tsx +9 -0
  122. package/registry/examples/date-picker/native/calendar.d.ts +4 -0
  123. package/registry/examples/date-picker/native/calendar.dark.png +0 -0
  124. package/registry/examples/date-picker/native/calendar.js +13 -0
  125. package/registry/examples/date-picker/native/calendar.light.png +0 -0
  126. package/registry/examples/date-picker/native/calendar.tsx +8 -0
  127. package/registry/examples/date-picker/native/day-strip.d.ts +4 -0
  128. package/registry/examples/date-picker/native/day-strip.dark.png +0 -0
  129. package/registry/examples/date-picker/native/day-strip.js +29 -0
  130. package/registry/examples/date-picker/native/day-strip.light.png +0 -0
  131. package/registry/examples/date-picker/native/day-strip.tsx +26 -0
  132. package/registry/examples/date-picker/native/month-year.d.ts +4 -0
  133. package/registry/examples/date-picker/native/month-year.dark.png +0 -0
  134. package/registry/examples/date-picker/native/month-year.js +13 -0
  135. package/registry/examples/date-picker/native/month-year.light.png +0 -0
  136. package/registry/examples/date-picker/native/month-year.tsx +8 -0
  137. package/registry/examples/date-picker/native/time-slots.d.ts +4 -0
  138. package/registry/examples/date-picker/native/time-slots.dark.png +0 -0
  139. package/registry/examples/date-picker/native/time-slots.js +15 -0
  140. package/registry/examples/date-picker/native/time-slots.light.png +0 -0
  141. package/registry/examples/date-picker/native/time-slots.tsx +15 -0
  142. package/registry/examples/date-picker/native/time-wheel.d.ts +4 -0
  143. package/registry/examples/date-picker/native/time-wheel.dark.png +0 -0
  144. package/registry/examples/date-picker/native/time-wheel.js +13 -0
  145. package/registry/examples/date-picker/native/time-wheel.light.png +0 -0
  146. package/registry/examples/date-picker/native/time-wheel.tsx +8 -0
  147. package/registry/examples/date-picker/time-slots.d.ts +4 -0
  148. package/registry/examples/date-picker/time-slots.js +26 -0
  149. package/registry/examples/date-picker/time-slots.tsx +25 -0
  150. package/registry/examples/date-picker/time-wheel.d.ts +4 -0
  151. package/registry/examples/date-picker/time-wheel.js +13 -0
  152. package/registry/examples/date-picker/time-wheel.tsx +10 -0
  153. package/registry/examples/date-picker/wheel.d.ts +4 -0
  154. package/registry/examples/date-picker/wheel.js +14 -0
  155. package/registry/examples/date-picker/wheel.tsx +16 -0
  156. package/registry/examples/dialog/native/sheet.d.ts +4 -0
  157. package/registry/examples/dialog/native/sheet.dark.png +0 -0
  158. package/registry/examples/dialog/native/sheet.js +21 -0
  159. package/registry/examples/dialog/native/sheet.light.png +0 -0
  160. package/registry/examples/dialog/native/sheet.tsx +18 -0
  161. package/registry/examples/field/native/select.d.ts +4 -0
  162. package/registry/examples/field/native/select.dark.png +0 -0
  163. package/registry/examples/field/native/select.js +29 -0
  164. package/registry/examples/field/native/select.light.png +0 -0
  165. package/registry/examples/field/native/select.tsx +25 -0
  166. package/registry/examples/form/native/audience.dark.png +0 -0
  167. package/registry/examples/form/native/audience.light.png +0 -0
  168. package/registry/examples/form/native/profile.dark.png +0 -0
  169. package/registry/examples/form/native/profile.light.png +0 -0
  170. package/registry/examples/form/native/settings.dark.png +0 -0
  171. package/registry/examples/form/native/settings.light.png +0 -0
  172. package/registry/examples/form/native/sign-up.dark.png +0 -0
  173. package/registry/examples/form/native/sign-up.light.png +0 -0
  174. package/registry/examples/form/native/upload.dark.png +0 -0
  175. package/registry/examples/form/native/upload.light.png +0 -0
  176. package/registry/examples/radio-group/buttons.d.ts +4 -0
  177. package/registry/examples/radio-group/buttons.js +17 -0
  178. package/registry/examples/radio-group/buttons.tsx +21 -0
  179. package/registry/examples/radio-group/cards.js +1 -1
  180. package/registry/examples/radio-group/cards.tsx +2 -2
  181. package/registry/examples/radio-group/native/buttons.d.ts +4 -0
  182. package/registry/examples/radio-group/native/buttons.dark.png +0 -0
  183. package/registry/examples/radio-group/native/buttons.js +25 -0
  184. package/registry/examples/radio-group/native/buttons.light.png +0 -0
  185. package/registry/examples/radio-group/native/buttons.tsx +21 -0
  186. package/registry/examples/radio-group/native/cards.d.ts +4 -0
  187. package/registry/examples/radio-group/native/cards.dark.png +0 -0
  188. package/registry/examples/radio-group/native/cards.js +23 -0
  189. package/registry/examples/radio-group/native/cards.light.png +0 -0
  190. package/registry/examples/radio-group/native/cards.tsx +19 -0
  191. package/registry/examples/radio-group/native/list.d.ts +4 -0
  192. package/registry/examples/radio-group/native/list.dark.png +0 -0
  193. package/registry/examples/radio-group/native/list.js +24 -0
  194. package/registry/examples/radio-group/native/list.light.png +0 -0
  195. package/registry/examples/radio-group/native/list.tsx +20 -0
  196. package/registry/examples/radio-group/native/segmented.d.ts +4 -0
  197. package/registry/examples/radio-group/native/segmented.dark.png +0 -0
  198. package/registry/examples/radio-group/native/segmented.js +24 -0
  199. package/registry/examples/radio-group/native/segmented.light.png +0 -0
  200. package/registry/examples/radio-group/native/segmented.tsx +20 -0
  201. package/registry/examples/radio-group/segmented.d.ts +4 -0
  202. package/registry/examples/radio-group/segmented.js +15 -0
  203. package/registry/examples/radio-group/segmented.tsx +12 -0
  204. package/registry/examples/slider/floating.d.ts +4 -0
  205. package/registry/examples/slider/floating.js +11 -0
  206. package/registry/examples/slider/floating.tsx +12 -0
  207. package/registry/examples/slider/native/default.d.ts +4 -0
  208. package/registry/examples/slider/native/default.dark.png +0 -0
  209. package/registry/examples/slider/native/default.js +14 -0
  210. package/registry/examples/slider/native/default.light.png +0 -0
  211. package/registry/examples/slider/native/default.tsx +13 -0
  212. package/registry/examples/slider/native/range.d.ts +4 -0
  213. package/registry/examples/slider/native/range.dark.png +0 -0
  214. package/registry/examples/slider/native/range.js +12 -0
  215. package/registry/examples/slider/native/range.light.png +0 -0
  216. package/registry/examples/slider/native/range.tsx +11 -0
  217. package/registry/examples/slider/native/stepped.d.ts +4 -0
  218. package/registry/examples/slider/native/stepped.dark.png +0 -0
  219. package/registry/examples/slider/native/stepped.js +28 -0
  220. package/registry/examples/slider/native/stepped.light.png +0 -0
  221. package/registry/examples/slider/native/stepped.tsx +24 -0
  222. package/registry/examples/slider/native/vertical.d.ts +4 -0
  223. package/registry/examples/slider/native/vertical.dark.png +0 -0
  224. package/registry/examples/slider/native/vertical.js +16 -0
  225. package/registry/examples/slider/native/vertical.light.png +0 -0
  226. package/registry/examples/slider/native/vertical.tsx +13 -0
  227. package/registry/examples/slider/price-range.d.ts +4 -0
  228. package/registry/examples/slider/price-range.js +29 -0
  229. package/registry/examples/slider/price-range.tsx +27 -0
  230. package/registry/examples/slider/stepped.d.ts +4 -0
  231. package/registry/examples/slider/stepped.js +28 -0
  232. package/registry/examples/slider/stepped.tsx +24 -0
  233. package/registry/examples/slider/vertical.d.ts +4 -0
  234. package/registry/examples/slider/vertical.js +15 -0
  235. package/registry/examples/slider/vertical.tsx +14 -0
  236. package/registry/examples/switch/native/default.d.ts +4 -0
  237. package/registry/examples/switch/native/default.dark.png +0 -0
  238. package/registry/examples/switch/native/default.js +16 -0
  239. package/registry/examples/switch/native/default.light.png +0 -0
  240. package/registry/examples/switch/native/default.tsx +13 -0
  241. package/registry/examples/switch/native/settings.d.ts +4 -0
  242. package/registry/examples/switch/native/settings.dark.png +0 -0
  243. package/registry/examples/switch/native/settings.js +20 -0
  244. package/registry/examples/switch/native/settings.light.png +0 -0
  245. package/registry/examples/switch/native/settings.tsx +17 -0
  246. package/registry/field.json +15 -3
  247. package/registry/form.json +250 -42
  248. package/registry/index.json +29 -24
  249. package/registry/radio-group.json +227 -15
  250. package/registry/slider.json +314 -13
  251. package/registry/switch.json +115 -3
  252. package/registry/tabs.json +2 -2
  253. package/registry/theme.json +7 -0
  254. package/skills/choice-controls/SKILL.md +39 -0
  255. package/skills/date-picker/SKILL.md +43 -0
  256. package/skills/form/SKILL.md +3 -0
  257. package/dist/chunk-2USC2YT6.js +0 -17
  258. package/dist/chunk-2USC2YT6.js.map +0 -1
  259. package/dist/chunk-5AXLA4LK.js +0 -13
  260. package/dist/chunk-5AXLA4LK.js.map +0 -1
  261. package/dist/chunk-FJDSLNXK.js +0 -11
  262. package/dist/chunk-FJDSLNXK.js.map +0 -1
  263. package/dist/chunk-KNBYZGSQ.js +0 -11
  264. package/dist/chunk-KNBYZGSQ.js.map +0 -1
  265. package/dist/chunk-RUPRYJ45.js +0 -82
  266. package/dist/chunk-RUPRYJ45.js.map +0 -1
  267. package/dist/chunk-VCAAHCHL.js +0 -17
  268. package/dist/chunk-VCAAHCHL.js.map +0 -1
  269. /package/dist/{chunk-K3SWZXYT.js.map → chunk-DGQMDJXK.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cn
3
- } from "./chunk-KNBYZGSQ.js";
3
+ } from "./chunk-ZNMFG62E.js";
4
4
 
5
5
  // src/gallery-page/gallery-page.tsx
6
6
  import { CtaPanel } from "@krizaka/ui/cta-panel";
package/dist/glow-card.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-5AXLA4LK.js";
4
+ } from "./chunk-2FRWNSRO.js";
5
5
 
6
6
  // src/glow-card/glow-card.tsx
7
7
  import { Slot } from "radix-ui";
@@ -2,7 +2,7 @@ import {
2
2
  brandClass,
3
3
  cn,
4
4
  storyVariantClass
5
- } from "./chunk-VCAAHCHL.js";
5
+ } from "./chunk-IZ7MV6HM.js";
6
6
 
7
7
  // src/goal-gauge/goal-gauge.tsx
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cn
3
- } from "./chunk-KNBYZGSQ.js";
3
+ } from "./chunk-ZNMFG62E.js";
4
4
 
5
5
  // src/how-it-works-page/how-it-works-page.tsx
6
6
  import { CtaPanel } from "@krizaka/ui/cta-panel";
@@ -1,7 +1,9 @@
1
1
  "use client";
2
2
  import {
3
3
  Slider
4
- } from "./chunk-RUPRYJ45.js";
4
+ } from "./chunk-BTMFZ66D.js";
5
+ import "./chunk-C2BRIFJG.js";
6
+ import "./chunk-2FRWNSRO.js";
5
7
  import {
6
8
  clamp
7
9
  } from "./chunk-BRKMQ6JV.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/image-cropper/image-cropper.tsx"],"sourcesContent":["// Client. A fixed frame (a profile picture's square, a vertical 9:16, a banner) over a picture or a video the person\n// drags and zooms to choose what stays in. The value is a focus point (0–1 on each axis) and a zoom — independent of\n// the screen, the same on the server — and `cropRect` turns it into pixels of the source. Keyboard: the frame takes the\n// focus, the arrows move (Shift: faster), + / − zoom; the zoom is also a slider. No motion but the person's own.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { clamp } from \"../media\";\nimport { Slider } from \"../slider\";\n\nexport const imageCropper = tv({\n slots: {\n root: \"flex w-full flex-col gap-3\",\n frame:\n \"relative w-full cursor-grab touch-none overflow-hidden rounded-xl bg-media active:cursor-grabbing \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0\",\n media: \"pointer-events-none absolute max-w-none select-none\",\n grid:\n \"pointer-events-none absolute inset-0 rounded-xl border-2 border-fg-on-media/80 \" +\n \"bg-[linear-gradient(to_right,transparent_33.2%,var(--kz-text-on-media)_33.3%,transparent_33.5%,transparent_66.5%,var(--kz-text-on-media)_66.6%,transparent_66.8%),linear-gradient(to_bottom,transparent_33.2%,var(--kz-text-on-media)_33.3%,transparent_33.5%,transparent_66.5%,var(--kz-text-on-media)_66.6%,transparent_66.8%)] opacity-40\",\n round: \"pointer-events-none absolute inset-0 rounded-full shadow-[0_0_0_9999px_var(--kz-scrim-strong)]\",\n },\n});\n\n/** Where the frame looks: the focus point (0–1 from the left and the top of the source) and the zoom (1 = covers). */\nexport type CropValue = { x: number; y: number; zoom: number };\n\n/** The crop in pixels of the source: what to cut from an image of `width` × `height` for a frame of `aspect`. */\nexport function cropRect(value: CropValue, source: { width: number; height: number }, aspect: number) {\n const mediaAspect = source.width / source.height;\n const width = (mediaAspect > aspect ? source.height * aspect : source.width) / value.zoom;\n const height = width / aspect;\n const x = clamp(value.x * source.width - width / 2, 0, source.width - width);\n const y = clamp(value.y * source.height - height / 2, 0, source.height - height);\n return { x: Math.round(x), y: Math.round(y), width: Math.round(width), height: Math.round(height) };\n}\n\nexport type ImageCropperLabels = {\n /** The frame's accessible name (\"Crop area\"). */\n frame: string;\n /** How to use it with the keyboard, read with the frame (\"Use the arrow keys to move the picture, + and − to zoom.\"). */\n instructions: string;\n /** The zoom slider (\"Zoom\"). */\n zoom: string;\n};\n\nexport const imageCropperLabels: ImageCropperLabels = {\n frame: \"Crop area\",\n instructions: \"Drag or use the arrow keys to move the picture, + and − to zoom.\",\n zoom: \"Zoom\",\n};\n\nexport type ImageCropperProps = {\n /** The picture to crop. Or `children`: a `<video>` (with the class it is given through `mediaClassName`). */\n src?: string;\n /** The picture's alternative text, passed translated (empty when the frame's name says enough). */\n alt?: string;\n /** A media element instead of `src` — a `<video>` to choose the frame of a clip. Its natural size is in `naturalSize`. */\n children?: React.ReactElement<{ className?: string; style?: React.CSSProperties }>;\n /** The source's size when `children` is a video: `{ width: video.videoWidth, height: video.videoHeight }`. */\n naturalSize?: { width: number; height: number };\n /** The frame's width / height: 1 (a square), 9 / 16 (vertical), 3 (a banner). */\n aspect: number;\n /** A round mask over the square frame (a profile picture). */\n round?: boolean;\n /** The crop (controlled). */\n value?: CropValue;\n /** The crop at first, uncontrolled. Default: centred, no zoom. */\n defaultValue?: CropValue;\n /** Called on every move. */\n onValueChange?: (value: CropValue) => void;\n /** The largest zoom. Default 4. */\n maxZoom?: number;\n /** Shows the zoom slider under the frame. Default `true`. */\n showZoom?: boolean;\n /** The words, passed translated; English by default. */\n labels?: Partial<ImageCropperLabels>;\n /** Classes merged on the root. */\n className?: string;\n};\n\n/** Chooses what a fixed frame keeps of a picture or a video: drag, zoom, keyboard; a focus point and a zoom out. */\nexport function ImageCropper({\n src,\n alt = \"\",\n children,\n naturalSize,\n aspect,\n round,\n value: valueProp,\n defaultValue = { x: 0.5, y: 0.5, zoom: 1 },\n onValueChange,\n maxZoom = 4,\n showZoom = true,\n labels: given,\n className,\n}: ImageCropperProps) {\n const labels = { ...imageCropperLabels, ...given };\n const s = imageCropper();\n const frame = React.useRef<HTMLDivElement>(null);\n const drag = React.useRef<{ x: number; y: number; from: CropValue } | null>(null);\n const [inner, setInner] = React.useState(defaultValue);\n const [loaded, setLoaded] = React.useState<{ width: number; height: number } | null>(null);\n const value = valueProp ?? inner;\n const id = React.useId();\n const size = naturalSize ?? loaded;\n const mediaAspect = size ? size.width / size.height : aspect;\n\n // The media's box, in % of the frame: it covers the frame at zoom 1.\n const widthPct = (mediaAspect > aspect ? (mediaAspect / aspect) * 100 : 100) * value.zoom;\n const heightPct = (mediaAspect > aspect ? 100 : (aspect / mediaAspect) * 100) * value.zoom;\n // The focus point's limits: the media never leaves a gap in the frame.\n const halfX = 50 / widthPct;\n const halfY = 50 / heightPct;\n\n const set = (next: CropValue) => {\n const zoom = clamp(next.zoom, 1, maxZoom);\n const wPct = (mediaAspect > aspect ? (mediaAspect / aspect) * 100 : 100) * zoom;\n const hPct = (mediaAspect > aspect ? 100 : (aspect / mediaAspect) * 100) * zoom;\n const fixed = { x: clamp(next.x, 50 / wPct, 1 - 50 / wPct), y: clamp(next.y, 50 / hPct, 1 - 50 / hPct), zoom };\n if (!valueProp) setInner(fixed);\n onValueChange?.(fixed);\n };\n\n const x = clamp(value.x, halfX, 1 - halfX);\n const y = clamp(value.y, halfY, 1 - halfY);\n const style: React.CSSProperties = {\n width: `${widthPct}%`,\n height: `${heightPct}%`,\n left: `${50 - x * widthPct}%`,\n top: `${50 - y * heightPct}%`,\n };\n\n return (\n <div className={s.root({ className })}>\n <div\n ref={frame}\n role=\"group\"\n tabIndex={0}\n aria-label={labels.frame}\n aria-describedby={`${id}-help`}\n className={s.frame()}\n style={{ aspectRatio: String(aspect) }}\n onPointerDown={(event) => {\n frame.current?.setPointerCapture?.(event.pointerId);\n drag.current = { x: event.clientX, y: event.clientY, from: { x, y, zoom: value.zoom } };\n }}\n onPointerMove={(event) => {\n const start = drag.current;\n const box = frame.current?.getBoundingClientRect();\n if (!start || !box?.width) return;\n set({\n x: start.from.x - (event.clientX - start.x) / ((box.width * widthPct) / 100),\n y: start.from.y - (event.clientY - start.y) / ((box.height * heightPct) / 100),\n zoom: value.zoom,\n });\n }}\n onPointerUp={() => {\n drag.current = null;\n }}\n onWheel={(event) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n set({ x, y, zoom: value.zoom - event.deltaY * 0.01 });\n }}\n onKeyDown={(event) => {\n const nudge = event.shiftKey ? 0.1 : 0.02;\n const moves: Record<string, CropValue> = {\n ArrowLeft: { x: x - nudge, y, zoom: value.zoom },\n ArrowRight: { x: x + nudge, y, zoom: value.zoom },\n ArrowUp: { x, y: y - nudge, zoom: value.zoom },\n ArrowDown: { x, y: y + nudge, zoom: value.zoom },\n \"+\": { x, y, zoom: value.zoom + 0.1 },\n \"=\": { x, y, zoom: value.zoom + 0.1 },\n \"-\": { x, y, zoom: value.zoom - 0.1 },\n };\n const next = moves[event.key];\n if (!next) return;\n event.preventDefault();\n set(next);\n }}\n >\n {children ? (\n React.cloneElement(children, { className: s.media({ className: children.props.className }), style: { ...children.props.style, ...style } })\n ) : src ? (\n <img\n src={src}\n alt={alt}\n draggable={false}\n className={s.media()}\n style={style}\n onLoad={(event) => setLoaded({ width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight })}\n />\n ) : null}\n <span className={round ? s.round() : s.grid()} aria-hidden />\n <span id={`${id}-help`} className=\"sr-only\">\n {labels.instructions}\n </span>\n </div>\n {showZoom && (\n <Slider\n label={labels.zoom}\n min={1}\n max={maxZoom}\n step={0.05}\n value={value.zoom}\n onValueChange={(zoom) => set({ x, y, zoom })}\n formatValue={(v) => `${Math.round(v * 100)} %`}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/image-cropper/image-cropper.tsx"],"sourcesContent":["// Client. A fixed frame (a profile picture's square, a vertical 9:16, a banner) over a picture or a video the person\n// drags and zooms to choose what stays in. The value is a focus point (0–1 on each axis) and a zoom — independent of\n// the screen, the same on the server — and `cropRect` turns it into pixels of the source. Keyboard: the frame takes the\n// focus, the arrows move (Shift: faster), + / − zoom; the zoom is also a slider. No motion but the person's own.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { clamp } from \"../media\";\nimport { Slider } from \"../slider\";\n\nexport const imageCropper = tv({\n slots: {\n root: \"flex w-full flex-col gap-3\",\n frame:\n \"relative w-full cursor-grab touch-none overflow-hidden rounded-xl bg-media active:cursor-grabbing \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0\",\n media: \"pointer-events-none absolute max-w-none select-none\",\n grid:\n \"pointer-events-none absolute inset-0 rounded-xl border-2 border-fg-on-media/80 \" +\n \"bg-[linear-gradient(to_right,transparent_33.2%,var(--kz-text-on-media)_33.3%,transparent_33.5%,transparent_66.5%,var(--kz-text-on-media)_66.6%,transparent_66.8%),linear-gradient(to_bottom,transparent_33.2%,var(--kz-text-on-media)_33.3%,transparent_33.5%,transparent_66.5%,var(--kz-text-on-media)_66.6%,transparent_66.8%)] opacity-40\",\n round: \"pointer-events-none absolute inset-0 rounded-full shadow-[0_0_0_9999px_var(--kz-scrim-strong)]\",\n },\n});\n\n/** Where the frame looks: the focus point (0–1 from the left and the top of the source) and the zoom (1 = covers). */\nexport type CropValue = { x: number; y: number; zoom: number };\n\n/** The crop in pixels of the source: what to cut from an image of `width` × `height` for a frame of `aspect`. */\nexport function cropRect(value: CropValue, source: { width: number; height: number }, aspect: number) {\n const mediaAspect = source.width / source.height;\n const width = (mediaAspect > aspect ? source.height * aspect : source.width) / value.zoom;\n const height = width / aspect;\n const x = clamp(value.x * source.width - width / 2, 0, source.width - width);\n const y = clamp(value.y * source.height - height / 2, 0, source.height - height);\n return { x: Math.round(x), y: Math.round(y), width: Math.round(width), height: Math.round(height) };\n}\n\nexport type ImageCropperLabels = {\n /** The frame's accessible name (\"Crop area\"). */\n frame: string;\n /** How to use it with the keyboard, read with the frame (\"Use the arrow keys to move the picture, + and − to zoom.\"). */\n instructions: string;\n /** The zoom slider (\"Zoom\"). */\n zoom: string;\n};\n\nexport const imageCropperLabels: ImageCropperLabels = {\n frame: \"Crop area\",\n instructions: \"Drag or use the arrow keys to move the picture, + and − to zoom.\",\n zoom: \"Zoom\",\n};\n\nexport type ImageCropperProps = {\n /** The picture to crop. Or `children`: a `<video>` (with the class it is given through `mediaClassName`). */\n src?: string;\n /** The picture's alternative text, passed translated (empty when the frame's name says enough). */\n alt?: string;\n /** A media element instead of `src` — a `<video>` to choose the frame of a clip. Its natural size is in `naturalSize`. */\n children?: React.ReactElement<{ className?: string; style?: React.CSSProperties }>;\n /** The source's size when `children` is a video: `{ width: video.videoWidth, height: video.videoHeight }`. */\n naturalSize?: { width: number; height: number };\n /** The frame's width / height: 1 (a square), 9 / 16 (vertical), 3 (a banner). */\n aspect: number;\n /** A round mask over the square frame (a profile picture). */\n round?: boolean;\n /** The crop (controlled). */\n value?: CropValue;\n /** The crop at first, uncontrolled. Default: centred, no zoom. */\n defaultValue?: CropValue;\n /** Called on every move. */\n onValueChange?: (value: CropValue) => void;\n /** The largest zoom. Default 4. */\n maxZoom?: number;\n /** Shows the zoom slider under the frame. Default `true`. */\n showZoom?: boolean;\n /** The words, passed translated; English by default. */\n labels?: Partial<ImageCropperLabels>;\n /** Classes merged on the root. */\n className?: string;\n};\n\n/** Chooses what a fixed frame keeps of a picture or a video: drag, zoom, keyboard; a focus point and a zoom out. */\nexport function ImageCropper({\n src,\n alt = \"\",\n children,\n naturalSize,\n aspect,\n round,\n value: valueProp,\n defaultValue = { x: 0.5, y: 0.5, zoom: 1 },\n onValueChange,\n maxZoom = 4,\n showZoom = true,\n labels: given,\n className,\n}: ImageCropperProps) {\n const labels = { ...imageCropperLabels, ...given };\n const s = imageCropper();\n const frame = React.useRef<HTMLDivElement>(null);\n const drag = React.useRef<{ x: number; y: number; from: CropValue } | null>(null);\n const [inner, setInner] = React.useState(defaultValue);\n const [loaded, setLoaded] = React.useState<{ width: number; height: number } | null>(null);\n const value = valueProp ?? inner;\n const id = React.useId();\n const size = naturalSize ?? loaded;\n const mediaAspect = size ? size.width / size.height : aspect;\n\n // The media's box, in % of the frame: it covers the frame at zoom 1.\n const widthPct = (mediaAspect > aspect ? (mediaAspect / aspect) * 100 : 100) * value.zoom;\n const heightPct = (mediaAspect > aspect ? 100 : (aspect / mediaAspect) * 100) * value.zoom;\n // The focus point's limits: the media never leaves a gap in the frame.\n const halfX = 50 / widthPct;\n const halfY = 50 / heightPct;\n\n const set = (next: CropValue) => {\n const zoom = clamp(next.zoom, 1, maxZoom);\n const wPct = (mediaAspect > aspect ? (mediaAspect / aspect) * 100 : 100) * zoom;\n const hPct = (mediaAspect > aspect ? 100 : (aspect / mediaAspect) * 100) * zoom;\n const fixed = { x: clamp(next.x, 50 / wPct, 1 - 50 / wPct), y: clamp(next.y, 50 / hPct, 1 - 50 / hPct), zoom };\n if (!valueProp) setInner(fixed);\n onValueChange?.(fixed);\n };\n\n const x = clamp(value.x, halfX, 1 - halfX);\n const y = clamp(value.y, halfY, 1 - halfY);\n const style: React.CSSProperties = {\n width: `${widthPct}%`,\n height: `${heightPct}%`,\n left: `${50 - x * widthPct}%`,\n top: `${50 - y * heightPct}%`,\n };\n\n return (\n <div className={s.root({ className })}>\n <div\n ref={frame}\n role=\"group\"\n tabIndex={0}\n aria-label={labels.frame}\n aria-describedby={`${id}-help`}\n className={s.frame()}\n style={{ aspectRatio: String(aspect) }}\n onPointerDown={(event) => {\n frame.current?.setPointerCapture?.(event.pointerId);\n drag.current = { x: event.clientX, y: event.clientY, from: { x, y, zoom: value.zoom } };\n }}\n onPointerMove={(event) => {\n const start = drag.current;\n const box = frame.current?.getBoundingClientRect();\n if (!start || !box?.width) return;\n set({\n x: start.from.x - (event.clientX - start.x) / ((box.width * widthPct) / 100),\n y: start.from.y - (event.clientY - start.y) / ((box.height * heightPct) / 100),\n zoom: value.zoom,\n });\n }}\n onPointerUp={() => {\n drag.current = null;\n }}\n onWheel={(event) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n set({ x, y, zoom: value.zoom - event.deltaY * 0.01 });\n }}\n onKeyDown={(event) => {\n const nudge = event.shiftKey ? 0.1 : 0.02;\n const moves: Record<string, CropValue> = {\n ArrowLeft: { x: x - nudge, y, zoom: value.zoom },\n ArrowRight: { x: x + nudge, y, zoom: value.zoom },\n ArrowUp: { x, y: y - nudge, zoom: value.zoom },\n ArrowDown: { x, y: y + nudge, zoom: value.zoom },\n \"+\": { x, y, zoom: value.zoom + 0.1 },\n \"=\": { x, y, zoom: value.zoom + 0.1 },\n \"-\": { x, y, zoom: value.zoom - 0.1 },\n };\n const next = moves[event.key];\n if (!next) return;\n event.preventDefault();\n set(next);\n }}\n >\n {children ? (\n React.cloneElement(children, { className: s.media({ className: children.props.className }), style: { ...children.props.style, ...style } })\n ) : src ? (\n <img\n src={src}\n alt={alt}\n draggable={false}\n className={s.media()}\n style={style}\n onLoad={(event) => setLoaded({ width: event.currentTarget.naturalWidth, height: event.currentTarget.naturalHeight })}\n />\n ) : null}\n <span className={round ? s.round() : s.grid()} aria-hidden />\n <span id={`${id}-help`} className=\"sr-only\">\n {labels.instructions}\n </span>\n </div>\n {showZoom && (\n <Slider\n label={labels.zoom}\n min={1}\n max={maxZoom}\n step={0.05}\n value={value.zoom}\n onValueChange={(zoom) => set({ x, y, zoom })}\n formatValue={(v) => `${Math.round(v * 100)} %`}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/landscape.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-5AXLA4LK.js";
4
+ } from "./chunk-G3ETON2A.js";
5
5
 
6
6
  // src/landscape/landscape.tsx
7
7
  import { useId } from "react";
package/dist/link-card.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-XCBVFMNV.js";
9
9
  import {
10
10
  cn
11
- } from "./chunk-FJDSLNXK.js";
11
+ } from "./chunk-4LOMIQVX.js";
12
12
 
13
13
  // src/link-card/link-card.tsx
14
14
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cn
3
- } from "./chunk-KNBYZGSQ.js";
3
+ } from "./chunk-ZNMFG62E.js";
4
4
 
5
5
  // src/longform-page/longform-page.tsx
6
6
  import { Page } from "@krizaka/ui/page";
package/dist/marquee.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-5AXLA4LK.js";
4
+ } from "./chunk-2FRWNSRO.js";
5
5
 
6
6
  // src/marquee/marquee.tsx
7
7
  import * as React from "react";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-5AXLA4LK.js";
4
+ } from "./chunk-G3ETON2A.js";
5
5
 
6
6
  // src/mascots/mascot.tsx
7
7
  import { useId } from "react";
package/dist/motion.css CHANGED
@@ -186,6 +186,23 @@
186
186
  from { opacity: 0; transform: translateY(-6px) scale(0.96); }
187
187
  }
188
188
 
189
+ /* The calendar turns its page: the month slides in from the side it comes from (Calendar, `data-dir`). */
190
+ .kz-month[data-dir="next"] { animation: kz-month-next 0.28s var(--kz-ease) backwards; }
191
+ .kz-month[data-dir="previous"] { animation: kz-month-previous 0.28s var(--kz-ease) backwards; }
192
+ @keyframes kz-month-next { from { opacity: 0; transform: translateX(14px); } }
193
+ @keyframes kz-month-previous { from { opacity: 0; transform: translateX(-14px); } }
194
+ /* A wheel curves: each value tilts as it leaves the band — a scroll-driven animation, no script per frame, flat where
195
+ the browser has no view timelines (the fade is the list's mask: the words keep their contrast). Scroll-linked, not
196
+ timed: it stays under reduced motion. */
197
+ @supports (animation-timeline: view()) {
198
+ .kz-wheel-item { animation: kz-wheel-turn linear both; animation-timeline: view(block); }
199
+ @keyframes kz-wheel-turn {
200
+ 0% { transform: perspective(240px) rotateX(48deg) scale(0.9); }
201
+ 50% { transform: none; }
202
+ 100% { transform: perspective(240px) rotateX(-48deg) scale(0.9); }
203
+ }
204
+ }
205
+
189
206
  /* Section backdrop (SectionBackdrop): the brand's section gradient (--kz-brand-gradient-*), sections that glide from
190
207
  one temperature to the next (each ends on the page surface, so the next starts where it stopped), a light dome, a
191
208
  perspective grid, a blurred foreground for depth of field, and a very slow drift. */
@@ -612,7 +629,7 @@ svg[data-kz-icon="ExternalIcon"] {
612
629
  .kz-page, .kz-word, .kz-gradient-text, .kz-marquee, .kz-backdrop-drift, .kz-overlay, .kz-dialog, .kz-pop, .kz-fade, .kz-progress,
613
630
  .kz-skeleton, .kz-skeleton-shape *, .kz-toast::after, .kz-glow::before,
614
631
  .kz-rotating > *, .kz-loop, .kz-iso-rise, .kz-count-roll, .kz-loader-trace, .kz-loader-node, .kz-loader-mark,
615
- [data-kz-status], .kz-replay-caret { animation: none !important; }
632
+ [data-kz-status], .kz-replay-caret, .kz-month { animation: none !important; }
616
633
  /* At rest, the trace stands still where a reader expects it: a third of the hexagon from the top. */
617
634
  .kz-loader-trace:not([stroke-dasharray]) { stroke-dashoffset: -8; }
618
635
  [data-reveal] { opacity: 1; transform: none; filter: none; transition: none; }
@@ -1,6 +1,6 @@
1
1
  import { AnyFieldApi, StandardSchemaV1, DeepKeys, AnyFormApi } from '@tanstack/react-form';
2
2
  import * as React from 'react';
3
- import { DateFieldLabels, BrandId, TextFieldProps as TextFieldProps$1 } from '@krizaka/ui/native';
3
+ import { ComboboxLabels, DateFieldLabels, BrandId, TextFieldProps as TextFieldProps$1 } from '@krizaka/ui/native';
4
4
  import { StyleProp, ViewStyle } from 'react-native';
5
5
 
6
6
  /** A value a message placeholder takes (`{minimum}`, `{suggestion}`). */
@@ -294,32 +294,75 @@ type CheckboxProps = Omit<Base, "label" | "hint"> & {
294
294
  };
295
295
  /** A box of the form (`boolean`): terms to accept, an option. */
296
296
  declare function Checkbox({ name, label, accessibleLabel, disabled, style }: CheckboxProps): React.JSX.Element;
297
- /** An on/off setting of the form (`boolean`): its label and hint beside the system's switch. */
297
+ /** An on/off setting of the form (`boolean`): a settings row — the label and its hint beside the switch, all pressable. */
298
298
  declare function Switch({ name, label, hint, disabled, style }: Base): React.JSX.Element;
299
- /** A choice of the form: the value stored and its words (passed translated). */
299
+ /** A choice of the form: the value stored and its words (passed translated), a line under them. */
300
300
  type FormOption = {
301
301
  value: string;
302
302
  label: string;
303
+ description?: string;
303
304
  disabled?: boolean;
304
305
  };
305
306
  type RadioGroupProps = Base & {
306
- /** The choices, as chips (one chosen). */
307
+ /** The choices, all visible (two to five). */
307
308
  options: readonly FormOption[];
308
- /** Scrolls the chips sideways instead of wrapping them (many choices). */
309
- scrollable?: boolean;
309
+ /** `list` (dots, default), `cards`, `buttons` (tactile keys) or `segmented` — the native `RadioGroup`'s looks. */
310
+ variant?: "list" | "cards" | "buttons" | "segmented";
310
311
  };
311
- /** One choice among a few, as chips (a radio group). On native, `Select` is the same control. */
312
- declare function RadioGroup({ name, label, hint, disabled, style, options, scrollable }: RadioGroupProps): React.JSX.Element;
313
- /** One choice among several (the web's `<select>`): chips on native, scrolled sideways when they are many. */
314
- declare function Select(props: RadioGroupProps): React.JSX.Element;
312
+ /** One choice among a few, all visible: the native `RadioGroup` (a haptic on each choice). */
313
+ declare function RadioGroup({ name, label, hint, disabled, style, options, variant }: RadioGroupProps): React.JSX.Element;
314
+ type SelectProps = Base & {
315
+ /** The choices, in a sheet. */
316
+ options: readonly FormOption[];
317
+ /** Shown while nothing is chosen ("Choose a category"), passed translated. */
318
+ placeholder?: string;
319
+ };
320
+ /** One choice among many (the web's `<select>`): a field that opens a sheet of options. */
321
+ declare function Select({ name, label, hint, disabled, style, options, placeholder }: SelectProps): React.JSX.Element;
322
+ type ComboboxProps = Base & {
323
+ /** The options, filtered as the person types. */
324
+ options?: readonly FormOption[];
325
+ /** Fetches the options for a query (debounced, aborted by a newer one). */
326
+ onSearch?: (query: string, signal: AbortSignal) => Promise<readonly FormOption[]>;
327
+ /** Several values (`string[]`); one (`string`) by default. */
328
+ multiple?: boolean;
329
+ /** Shown while nothing is chosen, passed translated. */
330
+ placeholder?: string;
331
+ /** The words of the control, passed translated. */
332
+ labels?: Partial<ComboboxLabels>;
333
+ };
334
+ /** A choice among many, searched in a sheet: one value (`string`) or several (`string[]`). */
335
+ declare function Combobox({ name, label, hint, disabled, style, options, onSearch, multiple, placeholder, labels }: ComboboxProps): React.JSX.Element;
315
336
  type DateFieldProps = Base & {
316
- /** The names of the boxes (`day`, `month`, `year`), passed translated. */
337
+ /** `wheel` (default: day, month and year as wheels) or `parts` (typed on the numeric keypad). */
338
+ entry?: "wheel" | "parts";
339
+ /** The earliest date (ISO) of the wheels. */
340
+ min?: string;
341
+ /** The latest date (ISO) of the wheels: `latestBirthDate(18)` for an age gate. */
342
+ max?: string;
343
+ /** The names of the wheels or boxes (`day`, `month`, `year`), passed translated. */
317
344
  labels?: Partial<DateFieldLabels>;
318
- /** `bday` for a date of birth. */
345
+ /** `bday` for a date of birth: the wheels rest on 1 January 2000 until turned; typed boxes are autofilled. */
319
346
  autoComplete?: "bday" | "off";
320
347
  };
321
- /** A date of the form (`YYYY-MM-DD`): day, month and year typed in the locale's order — the schema checks it. */
322
- declare function DateField({ name, label, hint, disabled, style, labels, autoComplete }: DateFieldProps): React.JSX.Element;
348
+ /** A date of the form (`YYYY-MM-DD`): wheels in the locale's order (or day · month · year typed) — the schema checks it. */
349
+ declare function DateField({ name, label, hint, disabled, style, labels, autoComplete, entry, min, max }: DateFieldProps): React.JSX.Element;
350
+ type SliderProps = Base & {
351
+ /** The lowest value. Default 0. */
352
+ min?: number;
353
+ /** The highest value. Default 100. */
354
+ max?: number;
355
+ /** The step. Default 1. */
356
+ step?: number;
357
+ /** The value as words ("15 %", "3 min"), shown and read. */
358
+ formatValue?: (value: number) => string;
359
+ /** For a range (`[number, number]`): each thumb's name, passed translated. */
360
+ thumbLabels?: [string, string];
361
+ /** A graduation per step (`true`) or at the values given. */
362
+ ticks?: boolean | readonly number[];
363
+ };
364
+ /** A number (or a range) of the form, chosen by feel: a share, a volume, a price range. */
365
+ declare function Slider({ name, label, hint, disabled, style, min, max, step, formatValue, thumbLabels, ticks }: SliderProps): React.JSX.Element;
323
366
  type MoneyProps = Base & {
324
367
  /** The currency (ISO 4217): its sign beside the amount, its decimals. */
325
368
  currency: string;
@@ -364,8 +407,10 @@ declare const createKrizakaForm: <S extends FormSchema>(definition: KrizakaFormD
364
407
  Switch: typeof Switch;
365
408
  RadioGroup: typeof RadioGroup;
366
409
  Select: typeof Select;
410
+ Combobox: typeof Combobox;
367
411
  DateField: typeof DateField;
368
412
  Money: typeof Money;
413
+ Slider: typeof Slider;
369
414
  Field: typeof FormField;
370
415
  FieldArray: typeof FieldArray;
371
416
  Watch: typeof Watch;
@@ -373,4 +418,4 @@ declare const createKrizakaForm: <S extends FormSchema>(definition: KrizakaFormD
373
418
  FormError: typeof FormError;
374
419
  }, FormProps>;
375
420
 
376
- export { Checkbox, type CheckboxProps, DateField, type DateFieldProps, FieldArray, type FieldArrayProps, type FieldCheck, type FormActionResult, FormError, type FormErrorProps, FormField, type FormFieldError, type FormFieldProps, type FormI18n, type FormOption, type FormOutput, type FormProblem, type FormProps, type FormSchema, type FormValues, type KrizakaField, type KrizakaFieldArray, type KrizakaFormFactory, type KrizakaFormInstance, Money, type MoneyProps, RadioGroup, type RadioGroupProps, Select, Submit, type SubmitProps, Switch, TextArea, type TextAreaProps, TextField, type TextFieldProps, Watch, type WatchProps, createKrizakaForm, formMessage, formMessages, isRealDate, latestBirthDate, useFormContext, useFormStatus, useFormValue, useKrizakaField, useKrizakaFieldArray };
421
+ export { Checkbox, type CheckboxProps, Combobox, type ComboboxProps, DateField, type DateFieldProps, FieldArray, type FieldArrayProps, type FieldCheck, type FormActionResult, FormError, type FormErrorProps, FormField, type FormFieldError, type FormFieldProps, type FormI18n, type FormOption, type FormOutput, type FormProblem, type FormProps, type FormSchema, type FormValues, type KrizakaField, type KrizakaFieldArray, type KrizakaFormFactory, type KrizakaFormInstance, Money, type MoneyProps, RadioGroup, type RadioGroupProps, Select, type SelectProps, Slider, type SliderProps, Submit, type SubmitProps, Switch, TextArea, type TextAreaProps, TextField, type TextFieldProps, Watch, type WatchProps, createKrizakaForm, formMessage, formMessages, isRealDate, latestBirthDate, useFormContext, useFormStatus, useFormValue, useKrizakaField, useKrizakaFieldArray };
@@ -1,7 +1,22 @@
1
1
  // src/native/form.tsx
2
- import { Button, Checkbox as CheckboxControl, Chip, DateField as DateFieldControl, ProductMark, Spinner, TextField as TextFieldControl, Txt, useTheme } from "@krizaka/ui/native";
2
+ import {
3
+ Button,
4
+ Checkbox as CheckboxControl,
5
+ Combobox as ComboboxControl,
6
+ DateField as DateFieldControl,
7
+ DateWheel,
8
+ ProductMark,
9
+ RadioGroup as RadioGroupControl,
10
+ Select as SelectControl,
11
+ Slider as SliderControl,
12
+ Spinner,
13
+ Switch as SwitchControl,
14
+ TextField as TextFieldControl,
15
+ Txt,
16
+ useTheme
17
+ } from "@krizaka/ui/native";
3
18
  import * as React2 from "react";
4
- import { AccessibilityInfo, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Switch as SwitchControl, View } from "react-native";
19
+ import { AccessibilityInfo, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, View } from "react-native";
5
20
 
6
21
  // src/form/core.tsx
7
22
  import { useField, useForm, useStore } from "@tanstack/react-form";
@@ -420,12 +435,12 @@ function useChain(ctx) {
420
435
  };
421
436
  }, [ctx]);
422
437
  }
423
- function useChained(override) {
438
+ function useChained(override, skip = false) {
424
439
  const chain = React2.useContext(ChainContext);
425
440
  const key = React2.useId();
426
441
  const ref = React2.useRef(null);
427
442
  const [last, setLast] = React2.useState(false);
428
- React2.useLayoutEffect(() => chain?.register({ key, focus: () => ref.current?.focus(), setLast }), [chain, key]);
443
+ React2.useLayoutEffect(() => skip ? void 0 : chain?.register({ key, focus: () => ref.current?.focus(), setLast }), [chain, key, skip]);
429
444
  if (!chain || override.returnKeyType !== void 0 || override.onSubmitEditing !== void 0) return { inputRef: ref };
430
445
  return {
431
446
  inputRef: ref,
@@ -512,61 +527,107 @@ function Checkbox({ name, label, accessibleLabel, disabled, style }) {
512
527
  }
513
528
  function Switch({ name, label, hint, disabled, style }) {
514
529
  const f = useKrizakaField(name);
515
- const { theme } = useTheme();
516
- return /* @__PURE__ */ jsxs(View, { style: [styles.switchRow, style], children: [
517
- /* @__PURE__ */ jsxs(View, { style: styles.switchText, children: [
518
- /* @__PURE__ */ jsx2(Txt, { variant: "label", children: label }),
519
- f.error ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "danger", role: "alert", children: f.error }) : hint ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", children: hint }) : null
520
- ] }),
521
- /* @__PURE__ */ jsx2(
522
- SwitchControl,
523
- {
524
- "aria-label": label,
525
- accessibilityHint: f.error ?? hint,
526
- value: f.value === true,
527
- disabled,
528
- onValueChange: (on) => {
529
- f.setValue(on);
530
- f.onBlur();
531
- },
532
- trackColor: { false: theme.surface3, true: theme.accent },
533
- thumbColor: theme.textOnMedia,
534
- ...{ activeThumbColor: theme.textOnMedia },
535
- ios_backgroundColor: theme.surface3
530
+ return /* @__PURE__ */ jsx2(
531
+ SwitchControl,
532
+ {
533
+ label,
534
+ description: hint,
535
+ showLabel: true,
536
+ error: f.error,
537
+ checked: f.value === true,
538
+ disabled,
539
+ style,
540
+ onCheckedChange: (on) => {
541
+ f.setValue(on);
542
+ f.onBlur();
536
543
  }
537
- )
538
- ] });
544
+ }
545
+ );
546
+ }
547
+ function Caption({ field, hint }) {
548
+ if (field.error)
549
+ return /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "danger", role: "alert", children: field.error });
550
+ return hint ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", children: hint }) : null;
539
551
  }
540
- function RadioGroup({ name, label, hint, disabled, style, options, scrollable }) {
552
+ function RadioGroup({ name, label, hint, disabled, style, options, variant = "list" }) {
541
553
  const f = useKrizakaField(name);
542
554
  return /* @__PURE__ */ jsxs(View, { style: [styles.field, style], children: [
543
555
  /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", style: styles.groupLabel, children: label }),
544
556
  /* @__PURE__ */ jsx2(
545
- Chip.Group,
557
+ RadioGroupControl,
546
558
  {
547
- type: "single",
548
- "aria-label": label,
559
+ label,
560
+ variant,
561
+ options,
549
562
  value: f.value ?? "",
550
563
  disabled,
551
- scrollable,
552
564
  onValueChange: (v) => {
553
565
  f.setValue(v);
554
566
  f.onBlur();
555
- },
556
- children: options.map((o) => /* @__PURE__ */ jsx2(Chip, { value: o.value, disabled: o.disabled, children: o.label }, o.value))
567
+ }
557
568
  }
558
569
  ),
559
- f.error ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "danger", role: "alert", children: f.error }) : hint ? /* @__PURE__ */ jsx2(Txt, { variant: "caption", tone: "secondary", children: hint }) : null
570
+ /* @__PURE__ */ jsx2(Caption, { field: f, hint })
560
571
  ] });
561
572
  }
562
- function Select(props) {
563
- return /* @__PURE__ */ jsx2(RadioGroup, { scrollable: props.options.length > 4, ...props });
573
+ function Select({ name, label, hint, disabled, style, options, placeholder }) {
574
+ const f = useKrizakaField(name);
575
+ return /* @__PURE__ */ jsx2(SelectControl, { label, hint, error: f.error, disabled, style, options, placeholder, value: f.value ?? "", onValueChange: (v) => f.setValue(v), onBlur: f.onBlur });
564
576
  }
565
- function DateField({ name, label, hint, disabled, style, labels, autoComplete }) {
577
+ function Combobox({ name, label, hint, disabled, style, options, onSearch, multiple, placeholder, labels }) {
566
578
  const f = useKrizakaField(name);
567
- const chained = useChained({});
579
+ return /* @__PURE__ */ jsx2(
580
+ ComboboxControl,
581
+ {
582
+ label,
583
+ hint,
584
+ error: f.error,
585
+ disabled,
586
+ style,
587
+ options,
588
+ onSearch,
589
+ multiple,
590
+ placeholder,
591
+ labels,
592
+ value: f.value ?? (multiple ? [] : ""),
593
+ onValueChange: (v) => f.setValue(v),
594
+ onBlur: f.onBlur
595
+ }
596
+ );
597
+ }
598
+ function DateField({ name, label, hint, disabled, style, labels, autoComplete, entry = "wheel", min, max }) {
599
+ const f = useKrizakaField(name);
600
+ const chained = useChained({}, entry === "wheel");
601
+ if (entry === "wheel")
602
+ return /* @__PURE__ */ jsx2(
603
+ DateWheel,
604
+ {
605
+ label,
606
+ hint,
607
+ error: f.error,
608
+ disabled,
609
+ style,
610
+ labels,
611
+ locale: f.locale,
612
+ min,
613
+ max,
614
+ defaultView: autoComplete === "bday" ? "2000-01-01" : void 0,
615
+ value: f.value ?? "",
616
+ onValueChange: (v) => {
617
+ f.setValue(v);
618
+ f.onBlur();
619
+ }
620
+ }
621
+ );
568
622
  return /* @__PURE__ */ jsx2(DateFieldControl, { ...chained, returnKeyType: chained.returnKeyType, label, hint, error: f.error, disabled, style, labels, autoComplete, locale: f.locale, value: f.value ?? "", onValueChange: (v) => f.setValue(v), onBlur: f.onBlur });
569
623
  }
624
+ function Slider({ name, label, hint, disabled, style, min, max, step, formatValue, thumbLabels, ticks }) {
625
+ const f = useKrizakaField(name);
626
+ return /* @__PURE__ */ jsxs(View, { style: [styles.field, style], children: [
627
+ /* @__PURE__ */ jsx2(SliderControl, { label, showLabel: true, floatingValue: true, value: f.value, min, max, step, disabled, formatValue, thumbLabels, ticks, onValueChange: (v) => f.setValue(v), onValueCommit: () => f.onBlur() }),
628
+ /* @__PURE__ */ jsx2(Caption, { field: f, hint })
629
+ ] });
630
+ }
570
631
  function Money({ name, label, hint, disabled, style, currency, placeholder }) {
571
632
  const f = useKrizakaField(name);
572
633
  const [text, setText] = React2.useState(() => moneyInputText(f.value, currency, f.locale));
@@ -610,7 +671,7 @@ function FieldArray({ name, children }) {
610
671
  function Watch({ name, children }) {
611
672
  return /* @__PURE__ */ jsx2(Fragment, { children: children(useFormValue(name)) });
612
673
  }
613
- var fields = { TextField, TextArea, Checkbox, Switch, RadioGroup, Select, DateField, Money, Field: FormField, FieldArray, Watch, Submit, FormError };
674
+ var fields = { TextField, TextArea, Checkbox, Switch, RadioGroup, Select, Combobox, DateField, Money, Slider, Field: FormField, FieldArray, Watch, Submit, FormError };
614
675
  var createKrizakaForm = makeCreateKrizakaForm(fields, FormRoot);
615
676
  var styles = StyleSheet.create({
616
677
  form: { gap: 16, alignSelf: "stretch" },
@@ -618,12 +679,11 @@ var styles = StyleSheet.create({
618
679
  field: { gap: 6, alignSelf: "stretch" },
619
680
  suggestion: { alignSelf: "flex-start", minHeight: 32, justifyContent: "center" },
620
681
  alert: { borderWidth: 1, padding: 12, gap: 4 },
621
- switchRow: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 16, minHeight: 44 },
622
- switchText: { flex: 1, gap: 2 },
623
682
  groupLabel: { fontWeight: "600" }
624
683
  });
625
684
  export {
626
685
  Checkbox,
686
+ Combobox,
627
687
  DateField,
628
688
  FieldArray,
629
689
  FormError,
@@ -631,6 +691,7 @@ export {
631
691
  Money,
632
692
  RadioGroup,
633
693
  Select,
694
+ Slider,
634
695
  Submit,
635
696
  Switch,
636
697
  TextArea,