@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.
- package/dist/achievement.js +1 -1
- package/dist/action-bar.js +1 -1
- package/dist/app-shell.js +1 -1
- package/dist/argument-card.js +2 -2
- package/dist/card-grid.js +1 -1
- package/dist/card.js +1 -1
- package/dist/choice-grid.js +1 -1
- package/dist/chunk-2FRWNSRO.js +23 -0
- package/dist/chunk-2FRWNSRO.js.map +1 -0
- package/dist/chunk-4LOMIQVX.js +21 -0
- package/dist/chunk-4LOMIQVX.js.map +1 -0
- package/dist/chunk-BTMFZ66D.js +135 -0
- package/dist/chunk-BTMFZ66D.js.map +1 -0
- package/dist/chunk-C2BRIFJG.js +13 -0
- package/dist/chunk-C2BRIFJG.js.map +1 -0
- package/dist/{chunk-K3SWZXYT.js → chunk-DGQMDJXK.js} +2 -2
- package/dist/chunk-G3ETON2A.js +22 -0
- package/dist/chunk-G3ETON2A.js.map +1 -0
- package/dist/chunk-IZ7MV6HM.js +26 -0
- package/dist/chunk-IZ7MV6HM.js.map +1 -0
- package/dist/chunk-KDXRYUD3.js +39 -0
- package/dist/chunk-KDXRYUD3.js.map +1 -0
- package/dist/{chunk-OGJI5X4W.js → chunk-ZKVQGXLD.js} +5 -3
- package/dist/{chunk-OGJI5X4W.js.map → chunk-ZKVQGXLD.js.map} +1 -1
- package/dist/chunk-ZNMFG62E.js +20 -0
- package/dist/chunk-ZNMFG62E.js.map +1 -0
- package/dist/cn.d.ts +9 -1
- package/dist/cn.js +5 -3
- package/dist/code-block.js +1 -1
- package/dist/command.js +1 -1
- package/dist/confirm-button.js +1 -1
- package/dist/continue-prompt.js +1 -1
- package/dist/cta-panel.js +1 -1
- package/dist/data-table.js +3 -2
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +652 -61
- package/dist/date-picker.js +918 -122
- package/dist/date-picker.js.map +1 -1
- package/dist/decision-list.js +1 -1
- package/dist/dropdown-menu.js +1 -1
- package/dist/error-page.js +1 -1
- package/dist/field.js +1 -1
- package/dist/filter-bar.js +3 -2
- package/dist/form.d.ts +23 -12
- package/dist/form.js +11 -47
- package/dist/form.js.map +1 -1
- package/dist/gallery-page.js +1 -1
- package/dist/glow-card.js +1 -1
- package/dist/goal-gauge.js +1 -1
- package/dist/how-it-works-page.js +1 -1
- package/dist/image-cropper.js +3 -1
- package/dist/image-cropper.js.map +1 -1
- package/dist/landscape.js +1 -1
- package/dist/link-card.js +1 -1
- package/dist/longform-page.js +1 -1
- package/dist/marquee.js +1 -1
- package/dist/mascot-quote.js +1 -1
- package/dist/motion.css +18 -1
- package/dist/native/form.d.ts +60 -15
- package/dist/native/form.js +103 -42
- package/dist/native/form.js.map +1 -1
- package/dist/native.d.ts +443 -2
- package/dist/native.js +1655 -448
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.js +1 -1
- package/dist/page-hero.js +2 -2
- package/dist/page.js +2 -2
- package/dist/popover.js +1 -1
- package/dist/pricing-page.js +1 -1
- package/dist/product-page.js +1 -1
- package/dist/profile-card.js +1 -1
- package/dist/promise-card.js +1 -1
- package/dist/radio-group.d.ts +66 -32
- package/dist/radio-group.js +54 -10
- package/dist/radio-group.js.map +1 -1
- package/dist/scroll-stage.js +1 -1
- package/dist/slider.d.ts +96 -35
- package/dist/slider.js +67 -13
- package/dist/slider.js.map +1 -1
- package/dist/stage-banner.js +1 -1
- package/dist/terminal-replay.js +34 -60
- package/dist/terminal-replay.js.map +1 -1
- package/dist/toast.js +1 -1
- package/dist/toc.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/video-player.js +3 -1
- package/dist/video-player.js.map +1 -1
- package/package.json +8 -8
- package/registry/cn.json +1 -1
- package/registry/combobox.json +225 -3
- package/registry/date-picker.json +1739 -288
- package/registry/dialog.json +74 -3
- package/registry/examples/combobox/native/multiple.d.ts +4 -0
- package/registry/examples/combobox/native/multiple.dark.png +0 -0
- package/registry/examples/combobox/native/multiple.js +15 -0
- package/registry/examples/combobox/native/multiple.light.png +0 -0
- package/registry/examples/combobox/native/multiple.tsx +15 -0
- package/registry/examples/combobox/native/search.d.ts +4 -0
- package/registry/examples/combobox/native/search.dark.png +0 -0
- package/registry/examples/combobox/native/search.js +15 -0
- package/registry/examples/combobox/native/search.light.png +0 -0
- package/registry/examples/combobox/native/search.tsx +15 -0
- package/registry/examples/date-picker/birth-date-wheel.d.ts +4 -0
- package/registry/examples/date-picker/birth-date-wheel.js +13 -0
- package/registry/examples/date-picker/birth-date-wheel.tsx +11 -0
- package/registry/examples/date-picker/birth-date.d.ts +4 -0
- package/registry/examples/date-picker/birth-date.js +25 -0
- package/registry/examples/date-picker/birth-date.tsx +21 -0
- package/registry/examples/date-picker/calendar.js +1 -1
- package/registry/examples/date-picker/calendar.tsx +2 -6
- package/registry/examples/date-picker/day-strip.d.ts +4 -0
- package/registry/examples/date-picker/day-strip.js +28 -0
- package/registry/examples/date-picker/day-strip.tsx +27 -0
- package/registry/examples/date-picker/month-year.d.ts +4 -0
- package/registry/examples/date-picker/month-year.js +13 -0
- package/registry/examples/date-picker/month-year.tsx +10 -0
- package/registry/examples/date-picker/native/birth-date-wheel.d.ts +4 -0
- package/registry/examples/date-picker/native/birth-date-wheel.dark.png +0 -0
- package/registry/examples/date-picker/native/birth-date-wheel.js +13 -0
- package/registry/examples/date-picker/native/birth-date-wheel.light.png +0 -0
- package/registry/examples/date-picker/native/birth-date-wheel.tsx +9 -0
- package/registry/examples/date-picker/native/calendar.d.ts +4 -0
- package/registry/examples/date-picker/native/calendar.dark.png +0 -0
- package/registry/examples/date-picker/native/calendar.js +13 -0
- package/registry/examples/date-picker/native/calendar.light.png +0 -0
- package/registry/examples/date-picker/native/calendar.tsx +8 -0
- package/registry/examples/date-picker/native/day-strip.d.ts +4 -0
- package/registry/examples/date-picker/native/day-strip.dark.png +0 -0
- package/registry/examples/date-picker/native/day-strip.js +29 -0
- package/registry/examples/date-picker/native/day-strip.light.png +0 -0
- package/registry/examples/date-picker/native/day-strip.tsx +26 -0
- package/registry/examples/date-picker/native/month-year.d.ts +4 -0
- package/registry/examples/date-picker/native/month-year.dark.png +0 -0
- package/registry/examples/date-picker/native/month-year.js +13 -0
- package/registry/examples/date-picker/native/month-year.light.png +0 -0
- package/registry/examples/date-picker/native/month-year.tsx +8 -0
- package/registry/examples/date-picker/native/time-slots.d.ts +4 -0
- package/registry/examples/date-picker/native/time-slots.dark.png +0 -0
- package/registry/examples/date-picker/native/time-slots.js +15 -0
- package/registry/examples/date-picker/native/time-slots.light.png +0 -0
- package/registry/examples/date-picker/native/time-slots.tsx +15 -0
- package/registry/examples/date-picker/native/time-wheel.d.ts +4 -0
- package/registry/examples/date-picker/native/time-wheel.dark.png +0 -0
- package/registry/examples/date-picker/native/time-wheel.js +13 -0
- package/registry/examples/date-picker/native/time-wheel.light.png +0 -0
- package/registry/examples/date-picker/native/time-wheel.tsx +8 -0
- package/registry/examples/date-picker/time-slots.d.ts +4 -0
- package/registry/examples/date-picker/time-slots.js +26 -0
- package/registry/examples/date-picker/time-slots.tsx +25 -0
- package/registry/examples/date-picker/time-wheel.d.ts +4 -0
- package/registry/examples/date-picker/time-wheel.js +13 -0
- package/registry/examples/date-picker/time-wheel.tsx +10 -0
- package/registry/examples/date-picker/wheel.d.ts +4 -0
- package/registry/examples/date-picker/wheel.js +14 -0
- package/registry/examples/date-picker/wheel.tsx +16 -0
- package/registry/examples/dialog/native/sheet.d.ts +4 -0
- package/registry/examples/dialog/native/sheet.dark.png +0 -0
- package/registry/examples/dialog/native/sheet.js +21 -0
- package/registry/examples/dialog/native/sheet.light.png +0 -0
- package/registry/examples/dialog/native/sheet.tsx +18 -0
- package/registry/examples/field/native/select.d.ts +4 -0
- package/registry/examples/field/native/select.dark.png +0 -0
- package/registry/examples/field/native/select.js +29 -0
- package/registry/examples/field/native/select.light.png +0 -0
- package/registry/examples/field/native/select.tsx +25 -0
- package/registry/examples/form/native/audience.dark.png +0 -0
- package/registry/examples/form/native/audience.light.png +0 -0
- package/registry/examples/form/native/profile.dark.png +0 -0
- package/registry/examples/form/native/profile.light.png +0 -0
- package/registry/examples/form/native/settings.dark.png +0 -0
- package/registry/examples/form/native/settings.light.png +0 -0
- package/registry/examples/form/native/sign-up.dark.png +0 -0
- package/registry/examples/form/native/sign-up.light.png +0 -0
- package/registry/examples/form/native/upload.dark.png +0 -0
- package/registry/examples/form/native/upload.light.png +0 -0
- package/registry/examples/radio-group/buttons.d.ts +4 -0
- package/registry/examples/radio-group/buttons.js +17 -0
- package/registry/examples/radio-group/buttons.tsx +21 -0
- package/registry/examples/radio-group/cards.js +1 -1
- package/registry/examples/radio-group/cards.tsx +2 -2
- package/registry/examples/radio-group/native/buttons.d.ts +4 -0
- package/registry/examples/radio-group/native/buttons.dark.png +0 -0
- package/registry/examples/radio-group/native/buttons.js +25 -0
- package/registry/examples/radio-group/native/buttons.light.png +0 -0
- package/registry/examples/radio-group/native/buttons.tsx +21 -0
- package/registry/examples/radio-group/native/cards.d.ts +4 -0
- package/registry/examples/radio-group/native/cards.dark.png +0 -0
- package/registry/examples/radio-group/native/cards.js +23 -0
- package/registry/examples/radio-group/native/cards.light.png +0 -0
- package/registry/examples/radio-group/native/cards.tsx +19 -0
- package/registry/examples/radio-group/native/list.d.ts +4 -0
- package/registry/examples/radio-group/native/list.dark.png +0 -0
- package/registry/examples/radio-group/native/list.js +24 -0
- package/registry/examples/radio-group/native/list.light.png +0 -0
- package/registry/examples/radio-group/native/list.tsx +20 -0
- package/registry/examples/radio-group/native/segmented.d.ts +4 -0
- package/registry/examples/radio-group/native/segmented.dark.png +0 -0
- package/registry/examples/radio-group/native/segmented.js +24 -0
- package/registry/examples/radio-group/native/segmented.light.png +0 -0
- package/registry/examples/radio-group/native/segmented.tsx +20 -0
- package/registry/examples/radio-group/segmented.d.ts +4 -0
- package/registry/examples/radio-group/segmented.js +15 -0
- package/registry/examples/radio-group/segmented.tsx +12 -0
- package/registry/examples/slider/floating.d.ts +4 -0
- package/registry/examples/slider/floating.js +11 -0
- package/registry/examples/slider/floating.tsx +12 -0
- package/registry/examples/slider/native/default.d.ts +4 -0
- package/registry/examples/slider/native/default.dark.png +0 -0
- package/registry/examples/slider/native/default.js +14 -0
- package/registry/examples/slider/native/default.light.png +0 -0
- package/registry/examples/slider/native/default.tsx +13 -0
- package/registry/examples/slider/native/range.d.ts +4 -0
- package/registry/examples/slider/native/range.dark.png +0 -0
- package/registry/examples/slider/native/range.js +12 -0
- package/registry/examples/slider/native/range.light.png +0 -0
- package/registry/examples/slider/native/range.tsx +11 -0
- package/registry/examples/slider/native/stepped.d.ts +4 -0
- package/registry/examples/slider/native/stepped.dark.png +0 -0
- package/registry/examples/slider/native/stepped.js +28 -0
- package/registry/examples/slider/native/stepped.light.png +0 -0
- package/registry/examples/slider/native/stepped.tsx +24 -0
- package/registry/examples/slider/native/vertical.d.ts +4 -0
- package/registry/examples/slider/native/vertical.dark.png +0 -0
- package/registry/examples/slider/native/vertical.js +16 -0
- package/registry/examples/slider/native/vertical.light.png +0 -0
- package/registry/examples/slider/native/vertical.tsx +13 -0
- package/registry/examples/slider/price-range.d.ts +4 -0
- package/registry/examples/slider/price-range.js +29 -0
- package/registry/examples/slider/price-range.tsx +27 -0
- package/registry/examples/slider/stepped.d.ts +4 -0
- package/registry/examples/slider/stepped.js +28 -0
- package/registry/examples/slider/stepped.tsx +24 -0
- package/registry/examples/slider/vertical.d.ts +4 -0
- package/registry/examples/slider/vertical.js +15 -0
- package/registry/examples/slider/vertical.tsx +14 -0
- package/registry/examples/switch/native/default.d.ts +4 -0
- package/registry/examples/switch/native/default.dark.png +0 -0
- package/registry/examples/switch/native/default.js +16 -0
- package/registry/examples/switch/native/default.light.png +0 -0
- package/registry/examples/switch/native/default.tsx +13 -0
- package/registry/examples/switch/native/settings.d.ts +4 -0
- package/registry/examples/switch/native/settings.dark.png +0 -0
- package/registry/examples/switch/native/settings.js +20 -0
- package/registry/examples/switch/native/settings.light.png +0 -0
- package/registry/examples/switch/native/settings.tsx +17 -0
- package/registry/field.json +15 -3
- package/registry/form.json +250 -42
- package/registry/index.json +29 -24
- package/registry/radio-group.json +227 -15
- package/registry/slider.json +314 -13
- package/registry/switch.json +115 -3
- package/registry/tabs.json +2 -2
- package/registry/theme.json +7 -0
- package/skills/choice-controls/SKILL.md +39 -0
- package/skills/date-picker/SKILL.md +43 -0
- package/skills/form/SKILL.md +3 -0
- package/dist/chunk-2USC2YT6.js +0 -17
- package/dist/chunk-2USC2YT6.js.map +0 -1
- package/dist/chunk-5AXLA4LK.js +0 -13
- package/dist/chunk-5AXLA4LK.js.map +0 -1
- package/dist/chunk-FJDSLNXK.js +0 -11
- package/dist/chunk-FJDSLNXK.js.map +0 -1
- package/dist/chunk-KNBYZGSQ.js +0 -11
- package/dist/chunk-KNBYZGSQ.js.map +0 -1
- package/dist/chunk-RUPRYJ45.js +0 -82
- package/dist/chunk-RUPRYJ45.js.map +0 -1
- package/dist/chunk-VCAAHCHL.js +0 -17
- package/dist/chunk-VCAAHCHL.js.map +0 -1
- /package/dist/{chunk-K3SWZXYT.js.map → chunk-DGQMDJXK.js.map} +0 -0
package/dist/gallery-page.js
CHANGED
package/dist/glow-card.js
CHANGED
package/dist/goal-gauge.js
CHANGED
package/dist/image-cropper.js
CHANGED
|
@@ -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
package/dist/link-card.js
CHANGED
package/dist/longform-page.js
CHANGED
package/dist/marquee.js
CHANGED
package/dist/mascot-quote.js
CHANGED
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; }
|
package/dist/native/form.d.ts
CHANGED
|
@@ -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`):
|
|
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,
|
|
307
|
+
/** The choices, all visible (two to five). */
|
|
307
308
|
options: readonly FormOption[];
|
|
308
|
-
/**
|
|
309
|
-
|
|
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,
|
|
312
|
-
declare function RadioGroup({ name, label, hint, disabled, style, options,
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
/**
|
|
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`):
|
|
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 };
|
package/dist/native/form.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
// src/native/form.tsx
|
|
2
|
-
import {
|
|
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,
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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,
|
|
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
|
-
|
|
557
|
+
RadioGroupControl,
|
|
546
558
|
{
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
570
|
+
/* @__PURE__ */ jsx2(Caption, { field: f, hint })
|
|
560
571
|
] });
|
|
561
572
|
}
|
|
562
|
-
function Select(
|
|
563
|
-
|
|
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
|
|
577
|
+
function Combobox({ name, label, hint, disabled, style, options, onSearch, multiple, placeholder, labels }) {
|
|
566
578
|
const f = useKrizakaField(name);
|
|
567
|
-
|
|
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,
|