@krizaka/ui 3.2.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/{chunk-I5RVM5CW.js → chunk-2FRWNSRO.js} +2 -6
- package/dist/chunk-2FRWNSRO.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-KDXRYUD3.js +39 -0
- package/dist/chunk-KDXRYUD3.js.map +1 -0
- package/dist/{chunk-2PUDI7JS.js → chunk-ZKVQGXLD.js} +5 -3
- package/dist/{chunk-2PUDI7JS.js.map → chunk-ZKVQGXLD.js.map} +1 -1
- package/dist/command.js +1 -1
- package/dist/confirm-button.js +1 -1
- package/dist/data-table.js +3 -2
- package/dist/data-table.js.map +1 -1
- package/dist/date-picker.d.ts +3 -1
- package/dist/date-picker.js +13 -13
- package/dist/date-picker.js.map +1 -1
- package/dist/dropdown-menu.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/glow-card.js +1 -1
- package/dist/image-cropper.js +3 -1
- package/dist/image-cropper.js.map +1 -1
- package/dist/marquee.js +1 -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 +206 -2
- package/dist/native.js +1172 -541
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.js +1 -1
- package/dist/popover.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/slider.d.ts +96 -35
- package/dist/slider.js +67 -13
- package/dist/slider.js.map +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/tooltip.js +1 -1
- package/dist/video-player.js +3 -1
- package/dist/video-player.js.map +1 -1
- package/package.json +6 -6
- package/registry/combobox.json +225 -3
- package/registry/date-picker.json +16 -2
- 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/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 +24 -20
- 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/skills/choice-controls/SKILL.md +39 -0
- package/skills/form/SKILL.md +3 -0
- package/dist/chunk-I5RVM5CW.js.map +0 -1
- package/dist/chunk-RUPRYJ45.js +0 -82
- package/dist/chunk-RUPRYJ45.js.map +0 -1
package/registry/index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"items": [
|
|
5
5
|
{
|
|
6
6
|
"name": "achievement",
|
|
@@ -392,10 +392,10 @@
|
|
|
392
392
|
"description": "A text field that suggests and completes — one value or several as chips, from a static list or a server, with values created from what was typed.",
|
|
393
393
|
"status": "beta",
|
|
394
394
|
"category": "forms",
|
|
395
|
-
"platforms": "
|
|
395
|
+
"platforms": "both",
|
|
396
396
|
"examples": {
|
|
397
397
|
"web": 4,
|
|
398
|
-
"native":
|
|
398
|
+
"native": 2
|
|
399
399
|
},
|
|
400
400
|
"dependencies": [
|
|
401
401
|
"tailwind-variants@^3.3.1"
|
|
@@ -654,10 +654,10 @@
|
|
|
654
654
|
"description": "A window over the page — centred, a bottom sheet or a side panel — and `AlertDialog` for a confirmation.",
|
|
655
655
|
"status": "stable",
|
|
656
656
|
"category": "overlays",
|
|
657
|
-
"platforms": "
|
|
657
|
+
"platforms": "both",
|
|
658
658
|
"examples": {
|
|
659
659
|
"web": 7,
|
|
660
|
-
"native":
|
|
660
|
+
"native": 1
|
|
661
661
|
},
|
|
662
662
|
"dependencies": [
|
|
663
663
|
"radix-ui@^1.7.0",
|
|
@@ -794,7 +794,7 @@
|
|
|
794
794
|
"platforms": "both",
|
|
795
795
|
"examples": {
|
|
796
796
|
"web": 5,
|
|
797
|
-
"native":
|
|
797
|
+
"native": 2
|
|
798
798
|
},
|
|
799
799
|
"dependencies": [
|
|
800
800
|
"tailwind-variants@^3.3.1"
|
|
@@ -1524,20 +1524,22 @@
|
|
|
1524
1524
|
"name": "radio-group",
|
|
1525
1525
|
"type": "primitive",
|
|
1526
1526
|
"title": "Radio group",
|
|
1527
|
-
"summary": "One choice among a few options, all visible: dots with their words, or
|
|
1528
|
-
"description": "One choice among a few options, all visible: dots with their words, or
|
|
1527
|
+
"summary": "One choice among a few options, all visible: dots with their words, whole cards, tactile keys that press in, or a segmented control.",
|
|
1528
|
+
"description": "One choice among a few options, all visible: dots with their words, whole cards, tactile keys that press in, or a segmented control.",
|
|
1529
1529
|
"status": "beta",
|
|
1530
1530
|
"category": "forms",
|
|
1531
|
-
"platforms": "
|
|
1531
|
+
"platforms": "both",
|
|
1532
1532
|
"examples": {
|
|
1533
|
-
"web":
|
|
1534
|
-
"native":
|
|
1533
|
+
"web": 5,
|
|
1534
|
+
"native": 4
|
|
1535
1535
|
},
|
|
1536
1536
|
"dependencies": [
|
|
1537
1537
|
"radix-ui@^1.7.0",
|
|
1538
1538
|
"tailwind-variants@^3.3.1"
|
|
1539
1539
|
],
|
|
1540
|
-
"registryDependencies": [
|
|
1540
|
+
"registryDependencies": [
|
|
1541
|
+
"cn"
|
|
1542
|
+
]
|
|
1541
1543
|
},
|
|
1542
1544
|
{
|
|
1543
1545
|
"name": "scroll-stage",
|
|
@@ -1670,20 +1672,22 @@
|
|
|
1670
1672
|
"name": "slider",
|
|
1671
1673
|
"type": "primitive",
|
|
1672
1674
|
"title": "Slider",
|
|
1673
|
-
"summary": "A value or a range chosen by dragging a thumb along a track, with arrows and Page keys.",
|
|
1674
|
-
"description": "A value or a range chosen by dragging a thumb along a track, with arrows and Page keys.",
|
|
1675
|
+
"summary": "A value or a range chosen by dragging a thumb along a track — steps you can see, the value floating over the thumb, the brand’s gradient, horizontal or vertical — with arrows and Page keys.",
|
|
1676
|
+
"description": "A value or a range chosen by dragging a thumb along a track — steps you can see, the value floating over the thumb, the brand’s gradient, horizontal or vertical — with arrows and Page keys.",
|
|
1675
1677
|
"status": "beta",
|
|
1676
1678
|
"category": "forms",
|
|
1677
|
-
"platforms": "
|
|
1679
|
+
"platforms": "both",
|
|
1678
1680
|
"examples": {
|
|
1679
|
-
"web":
|
|
1680
|
-
"native":
|
|
1681
|
+
"web": 9,
|
|
1682
|
+
"native": 4
|
|
1681
1683
|
},
|
|
1682
1684
|
"dependencies": [
|
|
1683
1685
|
"radix-ui@^1.7.0",
|
|
1684
1686
|
"tailwind-variants@^3.3.1"
|
|
1685
1687
|
],
|
|
1686
|
-
"registryDependencies": [
|
|
1688
|
+
"registryDependencies": [
|
|
1689
|
+
"cn"
|
|
1690
|
+
]
|
|
1687
1691
|
},
|
|
1688
1692
|
{
|
|
1689
1693
|
"name": "slot",
|
|
@@ -1822,10 +1826,10 @@
|
|
|
1822
1826
|
"description": "An on/off setting that applies at once.",
|
|
1823
1827
|
"status": "beta",
|
|
1824
1828
|
"category": "forms",
|
|
1825
|
-
"platforms": "
|
|
1829
|
+
"platforms": "both",
|
|
1826
1830
|
"examples": {
|
|
1827
1831
|
"web": 5,
|
|
1828
|
-
"native":
|
|
1832
|
+
"native": 2
|
|
1829
1833
|
},
|
|
1830
1834
|
"dependencies": [
|
|
1831
1835
|
"radix-ui@^1.7.0",
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
"name": "radio-group",
|
|
3
3
|
"type": "primitive",
|
|
4
4
|
"title": "Radio group",
|
|
5
|
-
"summary": "One choice among a few options, all visible: dots with their words, or
|
|
6
|
-
"description": "One choice among a few options, all visible: dots with their words, or
|
|
5
|
+
"summary": "One choice among a few options, all visible: dots with their words, whole cards, tactile keys that press in, or a segmented control.",
|
|
6
|
+
"description": "One choice among a few options, all visible: dots with their words, whole cards, tactile keys that press in, or a segmented control.",
|
|
7
7
|
"status": "beta",
|
|
8
8
|
"category": "forms",
|
|
9
|
-
"platforms": "
|
|
9
|
+
"platforms": "both",
|
|
10
10
|
"whenToUse": [
|
|
11
11
|
"In a form, to choose exactly one of two to five options that should all be seen: a frequency, a quality.",
|
|
12
|
-
"`RadioGroup.Card` when each option needs more than a line: an amount, a plan, a payment method."
|
|
12
|
+
"`RadioGroup.Card` when each option needs more than a line: an amount, a plan, a payment method.",
|
|
13
|
+
"`RadioGroup.Button` (`variant=\"buttons\"`) for short, tactile choices made in a tap: a size, a tip, a level.",
|
|
14
|
+
"`RadioGroup.Segment` (`variant=\"segmented\"`) for two to four short choices of a form on one line: a billing period, a unit."
|
|
13
15
|
],
|
|
14
16
|
"whenNotToUse": [
|
|
15
17
|
{
|
|
@@ -47,7 +49,8 @@
|
|
|
47
49
|
],
|
|
48
50
|
"notes": [
|
|
49
51
|
"Radix RadioGroup: `role=\"radiogroup\"` named by `label`, `role=\"radio\"` with `aria-checked` on each option.",
|
|
50
|
-
"Disabled options are skipped by the arrows."
|
|
52
|
+
"Disabled options are skipped by the arrows.",
|
|
53
|
+
"A tactile key’s `hint` is part of its name; the pressed look follows the checked state, never colour alone (it sinks)."
|
|
51
54
|
]
|
|
52
55
|
},
|
|
53
56
|
"related": [
|
|
@@ -62,7 +65,7 @@
|
|
|
62
65
|
"props": [
|
|
63
66
|
{
|
|
64
67
|
"component": "RadioGroupRoot",
|
|
65
|
-
"description": "`value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form.",
|
|
68
|
+
"description": "`value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form, `variant` for the layout.",
|
|
66
69
|
"props": [
|
|
67
70
|
{
|
|
68
71
|
"name": "invalid",
|
|
@@ -77,6 +80,13 @@
|
|
|
77
80
|
"default": null,
|
|
78
81
|
"description": "The group's accessible name — passed translated (or `aria-labelledby` a visible heading).",
|
|
79
82
|
"required": false
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "variant",
|
|
86
|
+
"type": "\"list\" | \"buttons\" | \"cards\" | \"segmented\"",
|
|
87
|
+
"default": "list",
|
|
88
|
+
"description": "The layout of the radios: `list` (dots), `cards` (a grid of `Card`s), `buttons` (a row of tactile `Button`s), or\n`segmented` (a track of `Segment`s, one light gliding to the choice).",
|
|
89
|
+
"required": false
|
|
80
90
|
}
|
|
81
91
|
]
|
|
82
92
|
},
|
|
@@ -95,7 +105,25 @@
|
|
|
95
105
|
},
|
|
96
106
|
{
|
|
97
107
|
"component": "RadioGroupCard",
|
|
98
|
-
"description": "A whole card as the radio: its content is the radio's name
|
|
108
|
+
"description": "A whole card as the radio: its content is the radio's name; its check lights up when chosen.",
|
|
109
|
+
"props": []
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"component": "RadioGroupButton",
|
|
113
|
+
"description": "A tactile key: raised on its ledge, pressed in while held and once chosen — sizes, a tip, a level.",
|
|
114
|
+
"props": [
|
|
115
|
+
{
|
|
116
|
+
"name": "hint",
|
|
117
|
+
"type": "ReactNode",
|
|
118
|
+
"default": null,
|
|
119
|
+
"description": "A second line under the words (\"+15 %\"), part of the radio's name.",
|
|
120
|
+
"required": false
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"component": "RadioGroupSegment",
|
|
126
|
+
"description": "A segment of a `variant=\"segmented\"` group: equal widths, one light gliding to the choice.",
|
|
99
127
|
"props": []
|
|
100
128
|
}
|
|
101
129
|
],
|
|
@@ -114,7 +142,7 @@
|
|
|
114
142
|
"description": "Whole cards: the chosen one carries the accent.",
|
|
115
143
|
"path": "examples/radio-group/cards.tsx",
|
|
116
144
|
"module": "@krizaka/ui/registry/examples/radio-group/cards",
|
|
117
|
-
"code": "import { RadioGroup } from \"@krizaka/ui/radio-group\";\n\nconst AMOUNTS = [\n [\"5\", \"$5\", \"A coffee\"],\n [\"20\", \"$20\", \"A dinner\"],\n [\"50\", \"$50\", \"A night out\"],\n];\n\n// Whole cards: the chosen one carries the accent border and
|
|
145
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/radio-group\";\n\nconst AMOUNTS = [\n [\"5\", \"$5\", \"A coffee\"],\n [\"20\", \"$20\", \"A dinner\"],\n [\"50\", \"$50\", \"A night out\"],\n];\n\n// Whole cards: the chosen one carries the accent border, its tint and its lit check.\nexport default function RadioGroupCards() {\n return (\n <RadioGroup.Root label=\"Amount\" defaultValue=\"20\" variant=\"cards\" className=\"w-[30rem] max-w-full grid-cols-3\">\n {AMOUNTS.map(([value, amount, hint]) => (\n <RadioGroup.Card key={value} value={value}>\n <span className=\"font-display text-lg font-bold\">{amount}</span>\n <span className=\"text-xs text-fg-secondary\">{hint}</span>\n </RadioGroup.Card>\n ))}\n </RadioGroup.Root>\n );\n}\n"
|
|
118
146
|
},
|
|
119
147
|
{
|
|
120
148
|
"name": "horizontal",
|
|
@@ -123,33 +151,192 @@
|
|
|
123
151
|
"path": "examples/radio-group/horizontal.tsx",
|
|
124
152
|
"module": "@krizaka/ui/registry/examples/radio-group/horizontal",
|
|
125
153
|
"code": "import { RadioGroup } from \"@krizaka/ui/radio-group\";\n\n// `orientation=\"horizontal\"`: left and right arrows, the items in a row.\nexport default function RadioGroupHorizontal() {\n return (\n <RadioGroup.Root label=\"Quality\" defaultValue=\"1080\" orientation=\"horizontal\" className=\"gap-5\">\n <RadioGroup.Item value=\"720\">720p</RadioGroup.Item>\n <RadioGroup.Item value=\"1080\">1080p</RadioGroup.Item>\n <RadioGroup.Item value=\"2160\">4K</RadioGroup.Item>\n </RadioGroup.Root>\n );\n}\n"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "buttons",
|
|
157
|
+
"title": "Tactile keys",
|
|
158
|
+
"description": "`variant=\"buttons\"`: keys raised on their ledge, pressed in while held — the chosen one stays sunk.",
|
|
159
|
+
"path": "examples/radio-group/buttons.tsx",
|
|
160
|
+
"module": "@krizaka/ui/registry/examples/radio-group/buttons",
|
|
161
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/radio-group\";\n\nconst TIPS = [\n [\"10\", \"10 %\", \"$4.20\"],\n [\"15\", \"15 %\", \"$6.30\"],\n [\"20\", \"20 %\", \"$8.40\"],\n [\"0\", \"No tip\", \"—\"],\n];\n\n// Tactile keys: raised on their ledge, pressed in while held — the chosen one stays sunk, lit in the accent.\nexport default function RadioGroupButtons() {\n return (\n <RadioGroup.Root label=\"Tip\" defaultValue=\"15\" variant=\"buttons\">\n {TIPS.map(([value, label, amount]) => (\n <RadioGroup.Button key={value} value={value} hint={amount} className=\"w-24\">\n {label}\n </RadioGroup.Button>\n ))}\n </RadioGroup.Root>\n );\n}\n"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "segmented",
|
|
165
|
+
"title": "Segmented",
|
|
166
|
+
"description": "`variant=\"segmented\"`: one light glides to the chosen segment.",
|
|
167
|
+
"path": "examples/radio-group/segmented.tsx",
|
|
168
|
+
"module": "@krizaka/ui/registry/examples/radio-group/segmented",
|
|
169
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/radio-group\";\n\n// A segmented control that is a choice (a form's value), not a view switch: one light glides to the chosen segment.\nexport default function RadioGroupSegmented() {\n return (\n <RadioGroup.Root label=\"Billing\" defaultValue=\"yearly\" variant=\"segmented\">\n <RadioGroup.Segment value=\"monthly\">Monthly</RadioGroup.Segment>\n <RadioGroup.Segment value=\"yearly\">Yearly</RadioGroup.Segment>\n <RadioGroup.Segment value=\"lifetime\">Lifetime</RadioGroup.Segment>\n </RadioGroup.Root>\n );\n}\n"
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
"native": {
|
|
174
|
+
"entry": "@krizaka/ui/native",
|
|
175
|
+
"import": "import { RadioGroup } from \"@krizaka/ui/native\";",
|
|
176
|
+
"differences": [
|
|
177
|
+
"One component with `options` (`value`, `label`, `description`, `hint`, `disabled`) and `variant` — no `Item` / `Card` / `Button` / `Segment` parts.",
|
|
178
|
+
"Each press plays a haptic when the app passes `haptics`: `impact` for a tactile key, `selection` otherwise.",
|
|
179
|
+
"`error` shows under the group and is announced."
|
|
180
|
+
],
|
|
181
|
+
"props": [
|
|
182
|
+
{
|
|
183
|
+
"component": "RadioGroup",
|
|
184
|
+
"description": "One choice among a few, all visible — the web `RadioGroup` in its four looks. A radio group for VoiceOver and\nTalkBack; each press plays a haptic (`selection`, `impact` for a tactile key) when the app passes `haptics`.",
|
|
185
|
+
"props": [
|
|
186
|
+
{
|
|
187
|
+
"name": "label",
|
|
188
|
+
"type": "string",
|
|
189
|
+
"default": null,
|
|
190
|
+
"description": "The group's accessible name (the question), passed translated.",
|
|
191
|
+
"required": true
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"name": "options",
|
|
195
|
+
"type": "readonly RadioOption[]",
|
|
196
|
+
"default": null,
|
|
197
|
+
"description": "The choices, all visible (two to five, a few more for `buttons`).",
|
|
198
|
+
"required": true
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"name": "defaultValue",
|
|
202
|
+
"type": "string | null",
|
|
203
|
+
"default": "null",
|
|
204
|
+
"description": "The chosen value at first, uncontrolled.",
|
|
205
|
+
"required": false
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"name": "disabled",
|
|
209
|
+
"type": "boolean",
|
|
210
|
+
"default": null,
|
|
211
|
+
"description": "Not available.",
|
|
212
|
+
"required": false
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"name": "error",
|
|
216
|
+
"type": "string",
|
|
217
|
+
"default": null,
|
|
218
|
+
"description": "An error under the group, read and announced.",
|
|
219
|
+
"required": false
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"name": "onValueChange",
|
|
223
|
+
"type": "((value: string) => void)",
|
|
224
|
+
"default": null,
|
|
225
|
+
"description": "Called with the value pressed.",
|
|
226
|
+
"required": false
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"name": "style",
|
|
230
|
+
"type": "StyleProp<ViewStyle>",
|
|
231
|
+
"default": null,
|
|
232
|
+
"description": "Styles merged last.",
|
|
233
|
+
"required": false
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"name": "value",
|
|
237
|
+
"type": "string | null",
|
|
238
|
+
"default": null,
|
|
239
|
+
"description": "The chosen value (controlled).",
|
|
240
|
+
"required": false
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"name": "variant",
|
|
244
|
+
"type": "\"list\" | \"cards\" | \"buttons\" | \"segmented\"",
|
|
245
|
+
"default": "list",
|
|
246
|
+
"description": "`list` (a dot and its words, 44-pt rows), `cards` (whole cards, the check lit), `buttons` (tactile keys that press\nin: an `impact` haptic), `segmented` (a track of equal segments).",
|
|
247
|
+
"required": false
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
"examples": [
|
|
253
|
+
{
|
|
254
|
+
"name": "list",
|
|
255
|
+
"title": "List",
|
|
256
|
+
"description": "Dots and their words in 44-pt rows, one disabled.",
|
|
257
|
+
"path": "examples/radio-group/native/list.tsx",
|
|
258
|
+
"module": "@krizaka/ui/registry/examples/radio-group/native/list",
|
|
259
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/native\";\nimport { View } from \"react-native\";\n\n// Dots and their words in 44-pt rows; one option disabled.\nexport default function NativeRadioGroupList() {\n return (\n <View style={{ width: 300 }}>\n <RadioGroup\n label=\"Notification frequency\"\n defaultValue=\"daily\"\n options={[\n { value: \"instant\", label: \"Instantly\" },\n { value: \"daily\", label: \"Once a day\", description: \"At 6 pm, a summary.\" },\n { value: \"weekly\", label: \"Once a week\" },\n { value: \"never\", label: \"Never\", disabled: true },\n ]}\n />\n </View>\n );\n}\n",
|
|
260
|
+
"screenshots": {
|
|
261
|
+
"dark": "examples/radio-group/native/list.dark.png",
|
|
262
|
+
"light": "examples/radio-group/native/list.light.png"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"name": "buttons",
|
|
267
|
+
"title": "Tactile keys",
|
|
268
|
+
"description": "Keys on a ledge that sink when pressed and stay sunk once chosen.",
|
|
269
|
+
"path": "examples/radio-group/native/buttons.tsx",
|
|
270
|
+
"module": "@krizaka/ui/registry/examples/radio-group/native/buttons",
|
|
271
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/native\";\nimport { View } from \"react-native\";\n\n// Tactile keys: raised on their ledge, sunk while pressed and once chosen — an impact haptic under the thumb.\nexport default function NativeRadioGroupButtons() {\n return (\n <View style={{ width: 340 }}>\n <RadioGroup\n label=\"Tip\"\n variant=\"buttons\"\n defaultValue=\"15\"\n options={[\n { value: \"10\", label: \"10 %\", hint: \"$4.20\" },\n { value: \"15\", label: \"15 %\", hint: \"$6.30\" },\n { value: \"20\", label: \"20 %\", hint: \"$8.40\" },\n { value: \"0\", label: \"None\" },\n ]}\n />\n </View>\n );\n}\n",
|
|
272
|
+
"screenshots": {
|
|
273
|
+
"dark": "examples/radio-group/native/buttons.dark.png",
|
|
274
|
+
"light": "examples/radio-group/native/buttons.light.png"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"name": "cards",
|
|
279
|
+
"title": "Cards",
|
|
280
|
+
"description": "Whole cards, the chosen one tinted with its check lit.",
|
|
281
|
+
"path": "examples/radio-group/native/cards.tsx",
|
|
282
|
+
"module": "@krizaka/ui/registry/examples/radio-group/native/cards",
|
|
283
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/native\";\nimport { View } from \"react-native\";\n\n// Whole cards: the chosen one tinted, its check lit.\nexport default function NativeRadioGroupCards() {\n return (\n <View style={{ width: 340 }}>\n <RadioGroup\n label=\"Credit pack\"\n variant=\"cards\"\n defaultValue=\"pro\"\n options={[\n { value: \"starter\", label: \"100 credits\", description: \"$4.99\" },\n { value: \"pro\", label: \"500 credits\", description: \"$19.99 · save 20 %\" },\n ]}\n />\n </View>\n );\n}\n",
|
|
284
|
+
"screenshots": {
|
|
285
|
+
"dark": "examples/radio-group/native/cards.dark.png",
|
|
286
|
+
"light": "examples/radio-group/native/cards.light.png"
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"name": "segmented",
|
|
291
|
+
"title": "Segmented",
|
|
292
|
+
"description": "A track of equal segments: a form's choice.",
|
|
293
|
+
"path": "examples/radio-group/native/segmented.tsx",
|
|
294
|
+
"module": "@krizaka/ui/registry/examples/radio-group/native/segmented",
|
|
295
|
+
"code": "import { RadioGroup } from \"@krizaka/ui/native\";\nimport { View } from \"react-native\";\n\n// A segmented control that is a form's choice (a radio group), not a view switch.\nexport default function NativeRadioGroupSegmented() {\n return (\n <View style={{ width: 300 }}>\n <RadioGroup\n label=\"Billing\"\n variant=\"segmented\"\n defaultValue=\"yearly\"\n options={[\n { value: \"monthly\", label: \"Monthly\" },\n { value: \"yearly\", label: \"Yearly\" },\n { value: \"lifetime\", label: \"Lifetime\" },\n ]}\n />\n </View>\n );\n}\n",
|
|
296
|
+
"screenshots": {
|
|
297
|
+
"dark": "examples/radio-group/native/segmented.dark.png",
|
|
298
|
+
"light": "examples/radio-group/native/segmented.light.png"
|
|
299
|
+
}
|
|
126
300
|
}
|
|
127
301
|
]
|
|
128
302
|
},
|
|
129
|
-
"native": null,
|
|
130
303
|
"files": [
|
|
304
|
+
{
|
|
305
|
+
"path": "glyphs.tsx",
|
|
306
|
+
"content": "// The small line glyphs the data and media primitives draw (play, volume, search, chevrons…): decorative, 24-unit\n// box, `currentColor`, sized by the caller's class. Shared by the entries that need them; never exported on its own —\n// a product draws its own icons with @krizaka/icons.\nimport type * as React from \"react\";\n\ntype GlyphProps = { className?: string };\n\nfunction Glyph({ className = \"h-4 w-4\", fill, children }: GlyphProps & { fill?: boolean; children: React.ReactNode }) {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n className={className}\n fill={fill ? \"currentColor\" : \"none\"}\n stroke={fill ? \"none\" : \"currentColor\"}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n focusable=\"false\"\n >\n {children}\n </svg>\n );\n}\n\nexport const PlayGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <path d=\"M8 5.5v13a1 1 0 0 0 1.5.86l10.4-6.5a1 1 0 0 0 0-1.72L9.5 4.64A1 1 0 0 0 8 5.5Z\" />\n </Glyph>\n);\nexport const PauseGlyph = (p: GlyphProps) => (\n <Glyph {...p} fill>\n <rect x=\"6\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n <rect x=\"14\" y=\"5\" width=\"4\" height=\"14\" rx=\"1\" />\n </Glyph>\n);\nexport const VolumeGlyph = ({ muted, ...p }: GlyphProps & { muted?: boolean }) => (\n <Glyph {...p}>\n <path d=\"M11 5 6 9H3v6h3l5 4V5Z\" />\n {muted ? <path d=\"m22 9-6 6m0-6 6 6\" /> : <path d=\"M15.5 8.5a5 5 0 0 1 0 7M18.5 5.5a9 9 0 0 1 0 13\" />}\n </Glyph>\n);\nexport const FullscreenGlyph = ({ exit, ...p }: GlyphProps & { exit?: boolean }) => (\n <Glyph {...p}>\n {exit ? <path d=\"M9 4v5H4m11-5v5h5M9 20v-5H4m11 5v-5h5\" /> : <path d=\"M4 9V4h5m6 0h5v5M4 15v5h5m11-5v5h-5\" />}\n </Glyph>\n);\nexport const CaptionsGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <path d=\"M10 10.5a2 2 0 1 0 0 3M17 10.5a2 2 0 1 0 0 3\" />\n </Glyph>\n);\nexport const PipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"2\" />\n <rect x=\"12\" y=\"12\" width=\"6\" height=\"4\" rx=\"1\" />\n </Glyph>\n);\nexport const SearchGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </Glyph>\n);\nexport const CloseGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </Glyph>\n);\nexport const ChevronGlyph = ({ direction = \"down\", ...p }: GlyphProps & { direction?: \"left\" | \"right\" | \"up\" | \"down\" }) => (\n <Glyph {...p}>\n <path d={{ left: \"m15 6-6 6 6 6\", right: \"m9 6 6 6-6 6\", up: \"m6 15 6-6 6 6\", down: \"m6 9 6 6 6-6\" }[direction]} />\n </Glyph>\n);\nexport const CheckGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M20 6 9 17l-5-5\" />\n </Glyph>\n);\nexport const PlusGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 5v14M5 12h14\" />\n </Glyph>\n);\nexport const CalendarGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"3\" y=\"5\" width=\"18\" height=\"16\" rx=\"2\" />\n <path d=\"M3 10h18M8 3v4M16 3v4\" />\n </Glyph>\n);\nexport const UploadGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 16V4m0 0L7 9m5-5 5 5\" />\n <path d=\"M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2\" />\n </Glyph>\n);\nexport const FileGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z\" />\n <path d=\"M14 3v5h5\" />\n </Glyph>\n);\nexport const CameraGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 8h3l2-3h6l2 3h3a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1Z\" />\n <circle cx=\"12\" cy=\"13.5\" r=\"3.5\" />\n </Glyph>\n);\nexport const FlipGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 0 1 14-5.3M20 12a8 8 0 0 1-14 5.3\" />\n <path d=\"M18 3v4h-4M6 21v-4h4\" />\n </Glyph>\n);\nexport const RetryGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M4 12a8 8 0 1 0 2.3-5.7L4 8.5\" />\n <path d=\"M4 4v4.5h4.5\" />\n </Glyph>\n);\nexport const SortGlyph = ({ direction, ...p }: GlyphProps & { direction?: \"ascending\" | \"descending\" }) => (\n <Glyph {...p}>\n {direction === \"ascending\" ? <path d=\"m7 14 5-5 5 5\" /> : direction === \"descending\" ? <path d=\"m7 10 5 5 5-5\" /> : <path d=\"m8 9 4-4 4 4M8 15l4 4 4-4\" />}\n </Glyph>\n);\nexport const ArrowGlyph = ({ direction = \"right\", ...p }: GlyphProps & { direction?: \"left\" | \"right\" }) => (\n <Glyph {...p}>\n <path d={direction === \"left\" ? \"M19 12H5m0 0 6-6m-6 6 6 6\" : \"M5 12h14m0 0-6-6m6 6-6 6\"} />\n </Glyph>\n);\nexport const ExternalGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M14 4h6v6M20 4l-9 9\" />\n <path d=\"M18 14v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4\" />\n </Glyph>\n);\nexport const WarningGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <path d=\"M12 4 2.5 20h19L12 4Z\" />\n <path d=\"M12 10v4M12 17.2v.1\" />\n </Glyph>\n);\nexport const CopyGlyph = (p: GlyphProps) => (\n <Glyph {...p}>\n <rect x=\"9\" y=\"9\" width=\"11\" height=\"11\" rx=\"2\" />\n <path d=\"M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3\" />\n </Glyph>\n);\n"
|
|
307
|
+
},
|
|
131
308
|
{
|
|
132
309
|
"path": "meta.ts",
|
|
133
310
|
"content": "// The documentation of a component, carried by its code: each primitive has a `meta.ts` beside its source\n// (`src/<name>/meta.ts`; a component that exists only in React Native: `src/native/meta/<name>.ts`). Never built into\n// the package: `scripts/build-registry.mjs` reads them into `registry/<name>.json`, which krizaka.com renders as\n// /docs/ui/<name>, and `scripts/registry.test.mjs` fails when one is missing or incomplete.\n//\n// Why a typed module and not JSDoc tags: the fields are lists and links (`related`, `whenNotToUse[].use`, examples),\n// which a type checks and a comment does not; the file is plain data (erasable TypeScript), so Node reads it without a\n// compiler, and an editor completes it.\n\n/** Every documented component: the web primitives (one entry of the package each) and the native-only ones. */\nexport type ComponentName =\n | \"achievement\"\n | \"action-bar\"\n | \"alert\"\n | \"app-shell\"\n | \"argument-card\"\n | \"audio-player\"\n | \"auth-layout\"\n | \"avatar\"\n | \"badge\"\n | \"button\"\n | \"card\"\n | \"card-grid\"\n | \"catalogue-page\"\n | \"changelog-page\"\n | \"checkbox\"\n | \"chip\"\n | \"chip-link\"\n | \"choice-grid\"\n | \"cn\"\n | \"code-block\"\n | \"combobox\"\n | \"command\"\n | \"component-page\"\n | \"confirm-button\"\n | \"contact-page\"\n | \"continue-prompt\"\n | \"countdown\"\n | \"cta-panel\"\n | \"dashboard-layout\"\n | \"data-table\"\n | \"date-picker\"\n | \"decision-list\"\n | \"detail-layout\"\n | \"dialog\"\n | \"docs-article-page\"\n | \"docs-hub-page\"\n | \"docs-layout\"\n | \"dropdown-menu\"\n | \"empty-state\"\n | \"error-page\"\n | \"field\"\n | \"file-upload\"\n | \"filter-bar\"\n | \"form\"\n | \"gallery-page\"\n | \"glow-card\"\n | \"goal-gauge\"\n | \"how-it-works-page\"\n | \"image-cropper\"\n | \"iso\"\n | \"kbd\"\n | \"landing-page\"\n | \"landscape\"\n | \"legal-page\"\n | \"link-card\"\n | \"list\"\n | \"loader\"\n | \"longform-page\"\n | \"marketing-layout\"\n | \"marquee\"\n | \"mascot-quote\"\n | \"mascot-state\"\n | \"mascots\"\n | \"media-capture\"\n | \"meter\"\n | \"nav-bar\"\n | \"number-ticker\"\n | \"page\"\n | \"page-header\"\n | \"page-hero\"\n | \"pagination\"\n | \"pointer-light\"\n | \"popover\"\n | \"pricing-page\"\n | \"product-page\"\n | \"profile-card\"\n | \"progress\"\n | \"promise-card\"\n | \"radio-group\"\n | \"scroll-stage\"\n | \"search-field\"\n | \"section-backdrop\"\n | \"segmented\"\n | \"separator\"\n | \"settings-layout\"\n | \"skeleton\"\n | \"slider\"\n | \"slot\"\n | \"spinner\"\n | \"split-view\"\n | \"stage-banner\"\n | \"stat\"\n | \"status-page\"\n | \"stepper\"\n | \"switch\"\n | \"tabs\"\n | \"tag-input\"\n | \"terminal-replay\"\n | \"theme\"\n | \"toast\"\n | \"toc\"\n | \"tooltip\"\n | \"txt\"\n | \"video-player\"\n | \"video-trimmer\"\n | \"virtual\";\n\n/** Where a component exists: `@krizaka/ui/<name>` (web), `@krizaka/ui/native` (React Native), or both. */\nexport type Platform = \"web\" | \"native\" | \"both\";\n\n/** `stable`: its API only changes in a major. `beta`: it may still change in a minor. */\nexport type Status = \"stable\" | \"beta\";\n\n/**\n * The group of the catalogue. `media`: video, sound, camera, files, cropping. `blocks`: the page blocks — the frame,\n * the hero, sections, cards that lead or argue, decisions, code, calls to act — that every page of a site is made of.\n * `layouts`: the shells of whole screens — an application, a dashboard, settings, sign-in, an object, a list and its\n * item, a site's page, a documentation page — each with its screens. `templates`: the page templates — whole pages of a\n * site (a landing, a product, a docs hub, a long read, a changelog, pricing, status, an error…), each a distinct\n * composition of the blocks, words and content as props.\n */\nexport type Category = \"actions\" | \"forms\" | \"navigation\" | \"overlays\" | \"feedback\" | \"data-display\" | \"media\" | \"blocks\" | \"layouts\" | \"templates\" | \"layout\" | \"foundations\";\n\n/**\n * A named example: the file `registry/examples/<component>/<name>.tsx` (web) or\n * `registry/examples/<component>/native/<name>.tsx` (React Native) — at most 40 lines, imported by the stories and\n * shown (live, with its code) by the documentation.\n */\nexport interface Example {\n /** The file name, kebab-case (`primary`, `icon-button`). */\n readonly name: string;\n /** A short title (`Primary`, `Icon button`). */\n readonly title: string;\n /** One or two sentences: when this variant is the right one. */\n readonly description: string;\n /**\n * A template: a whole screen of a product composed from the platform (a layout and its blocks, real words, its phone\n * version thought out), copied as a starting point — at most 160 lines, listed on krizaka.com/docs/ui/templates.\n */\n readonly template?: boolean;\n}\n\n/** One platform's side of a component. */\nexport interface PlatformDocs {\n /** The names to import (`[\"Button\", \"IconButton\"]`): from `@krizaka/ui/<name>` on the web, `@krizaka/ui/native` in React Native. */\n readonly imports: readonly string[];\n /** The examples, in reading order; the first one is the component's preview. */\n readonly examples: readonly Example[];\n}\n\n/** The React Native side: the same concept, and what differs from the web API. */\nexport interface NativeDocs extends PlatformDocs {\n /**\n * The entry to import from when it is not `@krizaka/ui/native`: a component that needs an Expo module (an optional\n * peer) has its own, `@krizaka/ui/native/<name>`, so an app that does not use it never installs the module.\n */\n readonly entry?: string;\n /** What differs from the web API (`label` instead of children…); empty for a native-only component. */\n readonly differences: readonly string[];\n}\n\nexport interface KeyboardInteraction {\n /** The keys, as written on a keyboard: `Space`, `Enter`, `Arrow keys`, `Esc`, `Tab`. */\n readonly keys: string;\n /** What they do. */\n readonly action: string;\n}\n\nexport interface ComponentMeta {\n /** The display name: `Button`, `Dropdown menu`. */\n readonly title: string;\n /** One sentence: what it is. */\n readonly summary: string;\n readonly status: Status;\n readonly category: Category;\n /** Must match the sides below: `both` ⇔ `web` and `native`. */\n readonly platforms: Platform;\n /** When to reach for it: concrete situations, one per item. */\n readonly whenToUse: readonly string[];\n /** When not to — and what to use instead (`use`), when another component is the answer. */\n readonly whenNotToUse: readonly { readonly when: string; readonly use?: ComponentName }[];\n /** How to use it well: wording, placement, composition. */\n readonly bestPractices: readonly string[];\n readonly accessibility: {\n /** The keyboard, when it does more than Tab and Enter. */\n readonly keyboard: readonly KeyboardInteraction[];\n /** Roles, names and announcements: what the component does, what the app must provide. */\n readonly notes: readonly string[];\n };\n /** The components a reader of this page should know about. */\n readonly related: readonly ComponentName[];\n readonly web?: PlatformDocs;\n readonly native?: NativeDocs;\n}\n"
|
|
134
311
|
},
|
|
135
312
|
{
|
|
136
313
|
"path": "radio-group/index.ts",
|
|
137
|
-
"content": "/** @krizaka/ui/radio-group — `RadioGroup.Root/Item/Card`, `radioGroup` (the slots). Client (\"use client\"). */\nexport {\n RadioGroup,\n radioGroup,\n RadioGroupCard,\n RadioGroupItem,\n type RadioGroupItemProps,\n RadioGroupRoot,\n type RadioGroupRootProps,\n} from \"./radio-group\";\n"
|
|
314
|
+
"content": "/** @krizaka/ui/radio-group — `RadioGroup.Root/Item/Card/Button/Segment`, `radioGroup` (the slots). Client (\"use client\"). */\nexport {\n RadioGroup,\n radioGroup,\n RadioGroupButton,\n type RadioGroupButtonProps,\n RadioGroupCard,\n RadioGroupItem,\n type RadioGroupItemProps,\n RadioGroupRoot,\n type RadioGroupRootProps,\n RadioGroupSegment,\n} from \"./radio-group\";\n"
|
|
138
315
|
},
|
|
139
316
|
{
|
|
140
317
|
"path": "radio-group/meta.ts",
|
|
141
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Radio group\",\n summary: \"One choice among a few options, all visible: dots with their words, or
|
|
318
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Radio group\",\n summary: \"One choice among a few options, all visible: dots with their words, whole cards, tactile keys that press in, or a segmented control.\",\n status: \"beta\",\n category: \"forms\",\n platforms: \"both\",\n whenToUse: [\n \"In a form, to choose exactly one of two to five options that should all be seen: a frequency, a quality.\",\n \"`RadioGroup.Card` when each option needs more than a line: an amount, a plan, a payment method.\",\n \"`RadioGroup.Button` (`variant=\\\"buttons\\\"`) for short, tactile choices made in a tap: a size, a tip, a level.\",\n \"`RadioGroup.Segment` (`variant=\\\"segmented\\\"`) for two to four short choices of a form on one line: a billing period, a unit.\",\n ],\n whenNotToUse: [\n { when: \"For many options (a country).\", use: \"field\" },\n { when: \"For independent options that can all be chosen.\", use: \"checkbox\" },\n { when: \"To filter a list at once, without a form.\", use: \"chip\" },\n { when: \"To switch between views.\", use: \"tabs\" },\n ],\n bestPractices: [\n \"Give the group a `label`: the question being answered.\",\n \"Pre-select the safest or most common option with `defaultValue` when there is one.\",\n \"Keep the labels parallel and short.\",\n ],\n accessibility: {\n keyboard: [\n { keys: \"Tab\", action: \"Moves into the group (one tab stop, on the chosen option) and out of it.\" },\n { keys: \"Arrow keys\", action: \"Move to the next or previous option and choose it.\" },\n ],\n notes: [\"Radix RadioGroup: `role=\\\"radiogroup\\\"` named by `label`, `role=\\\"radio\\\"` with `aria-checked` on each option.\", \"Disabled options are skipped by the arrows.\", \"A tactile key\\u2019s `hint` is part of its name; the pressed look follows the checked state, never colour alone (it sinks).\"],\n },\n related: [\"checkbox\", \"chip\", \"field\", \"switch\"],\n web: {\n imports: [\"RadioGroup\"],\n examples: [\n { name: \"items\", title: \"Items\", description: \"Dots and their words, one disabled.\" },\n { name: \"cards\", title: \"Cards\", description: \"Whole cards: the chosen one carries the accent.\" },\n { name: \"horizontal\", title: \"Horizontal\", description: \"`orientation=\\\"horizontal\\\"`: the items in a row.\" },\n { name: \"buttons\", title: \"Tactile keys\", description: \"`variant=\\\"buttons\\\"`: keys raised on their ledge, pressed in while held — the chosen one stays sunk.\" },\n { name: \"segmented\", title: \"Segmented\", description: \"`variant=\\\"segmented\\\"`: one light glides to the chosen segment.\" },\n ],\n },\n native: {\n imports: [\"RadioGroup\"],\n differences: [\n \"One component with `options` (`value`, `label`, `description`, `hint`, `disabled`) and `variant` — no `Item` / `Card` / `Button` / `Segment` parts.\",\n \"Each press plays a haptic when the app passes `haptics`: `impact` for a tactile key, `selection` otherwise.\",\n \"`error` shows under the group and is announced.\",\n ],\n examples: [\n { name: \"list\", title: \"List\", description: \"Dots and their words in 44-pt rows, one disabled.\" },\n { name: \"buttons\", title: \"Tactile keys\", description: \"Keys on a ledge that sink when pressed and stay sunk once chosen.\" },\n { name: \"cards\", title: \"Cards\", description: \"Whole cards, the chosen one tinted with its check lit.\" },\n { name: \"segmented\", title: \"Segmented\", description: \"A track of equal segments: a form's choice.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
142
319
|
},
|
|
143
320
|
{
|
|
144
321
|
"path": "radio-group/radio-group.tsx",
|
|
145
|
-
"content": "// Client (a context). Radix RadioGroup: role=\"radiogroup\" / \"radio\", one tab stop, the arrows move and select, a\n// hidden input inside a form.
|
|
322
|
+
"content": "// Client (a context). Radix RadioGroup: role=\"radiogroup\" / \"radio\", one tab stop, the arrows move and select, a\n// hidden input inside a form. Four looks, one behaviour:\n// - `RadioGroup.Item`: a dot and its words (a settings list);\n// - `RadioGroup.Card`: a whole card is the radio — an amount, a pack, a plan — its check lit when chosen;\n// - `RadioGroup.Button`: a tactile key that presses in — raised on a ledge, sunk when chosen (a size, a tip, a level);\n// - `RadioGroup.Segment` in `<RadioGroup.Root variant=\"segmented\">`: a segmented control, one light gliding to the choice.\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { CheckGlyph } from \"../glyphs\";\nimport { useSlidingIndicator } from \"../sliding-indicator\";\nimport { tv } from \"../tv\";\n\nexport const radioGroup = tv({\n slots: {\n root: \"grid gap-2 data-[orientation=horizontal]:flex data-[orientation=horizontal]:flex-wrap\",\n item:\n \"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-border-strong bg-surface-1 inset-shadow-well transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:shadow-glow aria-invalid:border-danger\",\n dot: \"h-2 w-2 rounded-full bg-on-accent\",\n label: \"flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-60\",\n card:\n \"group relative flex min-w-0 flex-col items-start gap-1 rounded-2xl border border-border-default bg-surface-1 bg-sheen p-4 pr-10 text-left text-sm text-fg inset-shadow-edge \" +\n \"transition-[border-color,background-color,box-shadow,transform] duration-200 hover:border-border-strong active:scale-[0.99] motion-reduce:transition-none motion-reduce:active:scale-100 \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-danger \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent-soft data-[state=checked]:shadow-glow\",\n // The card's check: an empty ring at rest, the accent filled with its check once chosen.\n cardCheck:\n \"absolute right-3.5 top-3.5 inline-flex h-5 w-5 items-center justify-center rounded-full border border-border-strong bg-surface-1 text-on-accent transition-colors \" +\n \"group-data-[state=checked]:border-accent group-data-[state=checked]:bg-accent\",\n // The tactile key: on a ledge (its lower edge), lifted on hover, pressed in while held and once chosen.\n button:\n \"relative inline-flex min-h-12 min-w-12 select-none flex-col items-center justify-center gap-0.5 rounded-xl border border-border-default bg-surface-2 bg-sheen px-4 py-2 \" +\n \"text-sm font-semibold text-fg inset-shadow-edge shadow-[0_4px_0_0_var(--kz-border-strong)] \" +\n \"transition-[transform,box-shadow,background-color,border-color,color] duration-100 ease-out motion-reduce:transition-none \" +\n \"hover:-translate-y-px hover:shadow-[0_5px_0_0_var(--kz-border-strong)] \" +\n \"active:translate-y-[3px] active:shadow-[0_1px_0_0_var(--kz-border-strong)] \" +\n \"focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:translate-y-0 aria-invalid:border-danger \" +\n \"data-[state=checked]:translate-y-[3px] data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=checked]:bg-fill-shade \" +\n \"data-[state=checked]:text-on-accent data-[state=checked]:inset-shadow-edge-fill data-[state=checked]:shadow-[0_1px_0_0_var(--kz-accent-hover)] \" +\n \"data-[state=checked]:hover:translate-y-[3px]\",\n buttonHint: \"text-[11px] font-medium text-fg-secondary group-data-[state=checked]:text-on-accent\",\n segmented:\n \"relative isolate inline-grid auto-cols-fr grid-flow-col gap-1 rounded-xl border border-border-default bg-surface-2 p-1 inset-shadow-well \" +\n \"before:pointer-events-none before:absolute before:left-0 before:top-0 before:-z-10 before:h-(--kz-ind-h) before:w-(--kz-ind-w) \" +\n \"before:translate-x-(--kz-ind-x) before:translate-y-(--kz-ind-y) before:rounded-lg before:border before:border-border-default before:bg-surface-0 before:bg-sheen \" +\n \"before:opacity-0 before:shadow-raised before:transition-[translate,width,height,opacity] before:duration-300 motion-reduce:before:transition-none data-[indicator=on]:before:opacity-100\",\n segment:\n \"inline-flex min-h-9 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-lg px-3.5 text-sm font-semibold text-fg-secondary transition-colors \" +\n \"hover:text-fg focus-visible:outline-hidden focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border data-[state=checked]:border-border-default data-[state=checked]:bg-surface-0 data-[state=checked]:text-fg data-[state=checked]:shadow-raised \" +\n \"group-data-[indicator=on]/seg:data-[state=checked]:border-transparent group-data-[indicator=on]/seg:data-[state=checked]:bg-transparent group-data-[indicator=on]/seg:data-[state=checked]:shadow-none\",\n },\n variants: {\n variant: {\n list: {},\n cards: { root: \"grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-3\" },\n buttons: { root: \"flex flex-wrap gap-2.5 pb-1\" },\n segmented: {},\n },\n },\n defaultVariants: { variant: \"list\" },\n});\nconst s = radioGroup();\n\nexport type RadioGroupRootProps = React.ComponentProps<typeof RadioGroupPrimitive.Root> & {\n /** The group's accessible name — passed translated (or `aria-labelledby` a visible heading). */\n label?: string;\n /** Marks every radio invalid: aria-invalid (pair it with a `Field.Error`). */\n invalid?: boolean;\n /**\n * The layout of the radios: `list` (dots), `cards` (a grid of `Card`s), `buttons` (a row of tactile `Button`s), or\n * `segmented` (a track of `Segment`s, one light gliding to the choice).\n * @default \"list\"\n */\n variant?: \"list\" | \"cards\" | \"buttons\" | \"segmented\";\n};\n\n/** `value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form, `variant` for the layout. */\nexport function RadioGroupRoot({ label, invalid, variant = \"list\", className, ...props }: RadioGroupRootProps) {\n const { ref } = useSlidingIndicator<HTMLDivElement>({ item: \"[role=radio]\", active: \"[role=radio][data-state=checked]\" });\n const segmented = variant === \"segmented\";\n return (\n <RadioGroupPrimitive.Root\n ref={segmented ? ref : undefined}\n aria-label={label}\n aria-invalid={invalid || undefined}\n data-invalid={invalid ? \"\" : undefined}\n data-variant={variant}\n orientation={segmented ? \"horizontal\" : props.orientation}\n className={segmented ? s.segmented({ className: [\"group/seg\", className].filter(Boolean).join(\" \") }) : radioGroup({ variant }).root({ className })}\n {...props}\n />\n );\n}\n\nexport type RadioGroupItemProps = Omit<React.ComponentProps<typeof RadioGroupPrimitive.Item>, \"children\"> & {\n /** The words next to the dot, clickable. Otherwise name it with `Field.Label htmlFor` or `aria-label`. */\n children?: React.ReactNode;\n};\n\n/** A radio: a dot, and its words when given. */\nexport function RadioGroupItem({ className, children, ...props }: RadioGroupItemProps) {\n const item = (\n <RadioGroupPrimitive.Item className={s.item({ className: children ? \"mt-0.5\" : className })} {...props}>\n <RadioGroupPrimitive.Indicator className={s.dot()} />\n </RadioGroupPrimitive.Item>\n );\n if (!children) return item;\n return (\n <label className={s.label({ className })}>\n {item}\n <span className=\"min-w-0\">{children}</span>\n </label>\n );\n}\n\n/** A whole card as the radio: its content is the radio's name; its check lights up when chosen. */\nexport function RadioGroupCard({ className, children, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return (\n <RadioGroupPrimitive.Item className={s.card({ className })} {...props}>\n {children}\n <span aria-hidden className={s.cardCheck()}>\n <RadioGroupPrimitive.Indicator forceMount className=\"inline-flex data-[state=unchecked]:hidden\">\n <CheckGlyph className=\"h-3 w-3\" />\n </RadioGroupPrimitive.Indicator>\n </span>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport type RadioGroupButtonProps = React.ComponentProps<typeof RadioGroupPrimitive.Item> & {\n /** A second line under the words (\"+15 %\"), part of the radio's name. */\n hint?: React.ReactNode;\n};\n\n/** A tactile key: raised on its ledge, pressed in while held and once chosen — sizes, a tip, a level. */\nexport function RadioGroupButton({ className, children, hint, ...props }: RadioGroupButtonProps) {\n return (\n <RadioGroupPrimitive.Item className={s.button({ className: [\"group\", className].filter(Boolean).join(\" \") })} {...props}>\n <span>{children}</span>\n {hint ? <span className={s.buttonHint()}>{hint}</span> : null}\n </RadioGroupPrimitive.Item>\n );\n}\n\n/** A segment of a `variant=\"segmented\"` group: equal widths, one light gliding to the choice. */\nexport function RadioGroupSegment({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\n return <RadioGroupPrimitive.Item className={s.segment({ className })} {...props} />;\n}\n\n/** `RadioGroup.Root/Item/Card/Button/Segment`. */\nexport const RadioGroup = { Root: RadioGroupRoot, Item: RadioGroupItem, Card: RadioGroupCard, Button: RadioGroupButton, Segment: RadioGroupSegment };\n"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"path": "sliding-indicator.ts",
|
|
326
|
+
"content": "// The shared sliding indicator (NavBar, Tabs): one element that glides from item to item instead of each item lighting\n// up on its own. It writes the target's box as CSS variables on the list (`--kz-ind-x/y/w/h`) and `data-indicator=\"on\"`\n// — no re-render, no animation frame: the glide is a CSS transition (none under reduced motion). Before hydration the\n// items keep their own active style, so nothing flashes.\nimport * as React from \"react\";\n\nconst useIsoLayoutEffect = typeof window === \"undefined\" ? React.useEffect : React.useLayoutEffect;\n\nexport function useSlidingIndicator<T extends HTMLElement>({ item, active, follow = false }: { item: string; active: string; follow?: boolean }) {\n const ref = React.useRef<T>(null);\n const place = React.useCallback((target: Element | null) => {\n const list = ref.current;\n if (!list) return;\n if (!(target instanceof HTMLElement) || !list.contains(target)) {\n list.removeAttribute(\"data-indicator\");\n return;\n }\n const a = target.getBoundingClientRect();\n const b = list.getBoundingClientRect();\n list.style.setProperty(\"--kz-ind-x\", `${a.left - b.left + list.scrollLeft}px`);\n list.style.setProperty(\"--kz-ind-y\", `${a.top - b.top + list.scrollTop}px`);\n list.style.setProperty(\"--kz-ind-w\", `${a.width}px`);\n list.style.setProperty(\"--kz-ind-h\", `${a.height}px`);\n list.setAttribute(\"data-indicator\", \"on\");\n }, []);\n const rest = React.useCallback(() => place(ref.current?.querySelector(active) ?? null), [place, active]);\n\n useIsoLayoutEffect(() => {\n const list = ref.current;\n if (!list) return;\n rest();\n const resize = new ResizeObserver(rest);\n resize.observe(list);\n const changes = new MutationObserver(rest);\n changes.observe(list, { subtree: true, attributes: true, attributeFilter: [\"data-state\", \"aria-current\", \"data-active\"] });\n return () => {\n resize.disconnect();\n changes.disconnect();\n };\n }, [rest]);\n\n const handlers = follow\n ? {\n onPointerOver: (e: React.PointerEvent) => place((e.target as Element).closest(item)),\n onPointerLeave: rest,\n onFocus: (e: React.FocusEvent) => place((e.target as Element).closest(item)),\n onBlur: rest,\n }\n : {};\n return { ref, handlers };\n}\n"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"path": "tv.ts",
|
|
330
|
+
"content": "import { createTV } from \"tailwind-variants\";\n\nimport { twMergeConfig } from \"./cn\";\n\n/** `tv` with the preset's utilities known to the merge engine (`twMergeConfig`): what a primitive wearing the material is styled with. */\nexport const tv = createTV({ twMergeConfig });\n"
|
|
146
331
|
}
|
|
147
332
|
],
|
|
148
333
|
"dependencies": [
|
|
149
334
|
"radix-ui@^1.7.0",
|
|
150
335
|
"tailwind-variants@^3.3.1"
|
|
151
336
|
],
|
|
152
|
-
"registryDependencies": [
|
|
337
|
+
"registryDependencies": [
|
|
338
|
+
"cn"
|
|
339
|
+
],
|
|
153
340
|
"demo": {
|
|
154
341
|
"path": "examples/radio-group/items.tsx",
|
|
155
342
|
"content": "import { RadioGroup } from \"@krizaka/ui/radio-group\";\n\nexport default function RadioGroupItems() {\n return (\n <RadioGroup.Root label=\"Notification frequency\" defaultValue=\"daily\">\n <RadioGroup.Item value=\"instant\">Instantly</RadioGroup.Item>\n <RadioGroup.Item value=\"daily\">Once a day</RadioGroup.Item>\n <RadioGroup.Item value=\"weekly\">Once a week</RadioGroup.Item>\n <RadioGroup.Item value=\"never\" disabled>\n Never\n </RadioGroup.Item>\n </RadioGroup.Root>\n );\n}\n"
|
|
@@ -157,7 +344,7 @@
|
|
|
157
344
|
"props": [
|
|
158
345
|
{
|
|
159
346
|
"component": "RadioGroupRoot",
|
|
160
|
-
"description": "`value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form.",
|
|
347
|
+
"description": "`value`/`defaultValue` + `onValueChange`, `orientation`, `required`, `name` for a form, `variant` for the layout.",
|
|
161
348
|
"props": [
|
|
162
349
|
{
|
|
163
350
|
"name": "invalid",
|
|
@@ -172,6 +359,13 @@
|
|
|
172
359
|
"default": null,
|
|
173
360
|
"description": "The group's accessible name — passed translated (or `aria-labelledby` a visible heading).",
|
|
174
361
|
"required": false
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"name": "variant",
|
|
365
|
+
"type": "\"list\" | \"buttons\" | \"cards\" | \"segmented\"",
|
|
366
|
+
"default": "list",
|
|
367
|
+
"description": "The layout of the radios: `list` (dots), `cards` (a grid of `Card`s), `buttons` (a row of tactile `Button`s), or\n`segmented` (a track of `Segment`s, one light gliding to the choice).",
|
|
368
|
+
"required": false
|
|
175
369
|
}
|
|
176
370
|
]
|
|
177
371
|
},
|
|
@@ -190,7 +384,25 @@
|
|
|
190
384
|
},
|
|
191
385
|
{
|
|
192
386
|
"component": "RadioGroupCard",
|
|
193
|
-
"description": "A whole card as the radio: its content is the radio's name
|
|
387
|
+
"description": "A whole card as the radio: its content is the radio's name; its check lights up when chosen.",
|
|
388
|
+
"props": []
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"component": "RadioGroupButton",
|
|
392
|
+
"description": "A tactile key: raised on its ledge, pressed in while held and once chosen — sizes, a tip, a level.",
|
|
393
|
+
"props": [
|
|
394
|
+
{
|
|
395
|
+
"name": "hint",
|
|
396
|
+
"type": "ReactNode",
|
|
397
|
+
"default": null,
|
|
398
|
+
"description": "A second line under the words (\"+15 %\"), part of the radio's name.",
|
|
399
|
+
"required": false
|
|
400
|
+
}
|
|
401
|
+
]
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"component": "RadioGroupSegment",
|
|
405
|
+
"description": "A segment of a `variant=\"segmented\"` group: equal widths, one light gliding to the choice.",
|
|
194
406
|
"props": []
|
|
195
407
|
}
|
|
196
408
|
]
|