@krizaka/ui 2.0.0-beta.2 → 2.0.0-beta.3
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/README.md +30 -0
- package/dist/alert.d.ts +2 -0
- package/dist/alert.js.map +1 -1
- package/dist/avatar.d.ts +5 -0
- package/dist/avatar.js.map +1 -1
- package/dist/badge.d.ts +12 -0
- package/dist/badge.js.map +1 -1
- package/dist/{button-G5qY4qSY.d.ts → button-8La7l0V4.d.ts} +15 -0
- package/dist/button.d.ts +19 -0
- package/dist/button.js.map +1 -1
- package/dist/card.d.ts +24 -1
- package/dist/card.js.map +1 -1
- package/dist/chip.d.ts +23 -0
- package/dist/chip.js.map +1 -1
- package/dist/{chunk-5FRYKFTO.js → chunk-3X5MVH54.js} +2 -2
- package/dist/chunk-3X5MVH54.js.map +1 -0
- package/dist/{chunk-QRG5RYEI.js → chunk-ZUD4U22H.js} +1 -1
- package/dist/chunk-ZUD4U22H.js.map +1 -0
- package/dist/command.d.ts +4 -1
- package/dist/command.js +2 -2
- package/dist/command.js.map +1 -1
- package/dist/confirm-button.d.ts +1 -1
- package/dist/confirm-button.js +1 -1
- package/dist/countdown.d.ts +3 -0
- package/dist/countdown.js.map +1 -1
- package/dist/{dialog-DGBY2grk.d.ts → dialog-GQ4Nvuyf.d.ts} +10 -0
- package/dist/dialog.d.ts +9 -1
- package/dist/dialog.js +2 -2
- package/dist/dialog.js.map +1 -1
- package/dist/dropdown-menu.d.ts +1 -0
- package/dist/dropdown-menu.js.map +1 -1
- package/dist/empty-state.d.ts +2 -0
- package/dist/empty-state.js.map +1 -1
- package/dist/kbd.d.ts +7 -1
- package/dist/kbd.js.map +1 -1
- package/dist/page-header.d.ts +2 -0
- package/dist/page-header.js.map +1 -1
- package/dist/progress.d.ts +5 -0
- package/dist/progress.js.map +1 -1
- package/dist/skeleton.d.ts +7 -1
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.d.ts +2 -0
- package/dist/slider.js.map +1 -1
- package/dist/spinner.d.ts +5 -0
- package/dist/spinner.js.map +1 -1
- package/dist/stat.d.ts +2 -0
- package/dist/stat.js.map +1 -1
- package/dist/switch.d.ts +7 -3
- package/dist/switch.js.map +1 -1
- package/dist/tabs.d.ts +4 -1
- package/dist/tabs.js.map +1 -1
- package/dist/theme.d.ts +10 -2
- package/dist/theme.js +4 -2
- package/dist/theme.js.map +1 -1
- package/dist/tooltip.d.ts +3 -0
- package/dist/tooltip.js.map +1 -1
- package/package.json +14 -3
- package/registry/alert.json +59 -0
- package/registry/avatar.json +114 -0
- package/registry/badge.json +59 -0
- package/registry/button.json +115 -0
- package/registry/card.json +154 -0
- package/registry/checkbox.json +46 -0
- package/registry/chip.json +164 -0
- package/registry/cn.json +21 -0
- package/registry/command.json +162 -0
- package/registry/confirm-button.json +104 -0
- package/registry/countdown.json +84 -0
- package/registry/demos/alert.d.ts +5 -0
- package/registry/demos/alert.js +12 -0
- package/registry/demos/alert.tsx +15 -0
- package/registry/demos/avatar.d.ts +5 -0
- package/registry/demos/avatar.js +14 -0
- package/registry/demos/avatar.tsx +13 -0
- package/registry/demos/badge.d.ts +5 -0
- package/registry/demos/badge.js +11 -0
- package/registry/demos/badge.tsx +5 -0
- package/registry/demos/button.d.ts +5 -0
- package/registry/demos/button.js +11 -0
- package/registry/demos/button.tsx +5 -0
- package/registry/demos/card.d.ts +5 -0
- package/registry/demos/card.js +30 -0
- package/registry/demos/card.tsx +37 -0
- package/registry/demos/checkbox.d.ts +5 -0
- package/registry/demos/checkbox.js +11 -0
- package/registry/demos/checkbox.tsx +5 -0
- package/registry/demos/chip.d.ts +5 -0
- package/registry/demos/chip.js +15 -0
- package/registry/demos/chip.tsx +11 -0
- package/registry/demos/cn.d.ts +7 -0
- package/registry/demos/cn.js +17 -0
- package/registry/demos/cn.tsx +9 -0
- package/registry/demos/command.d.ts +5 -0
- package/registry/demos/command.js +32 -0
- package/registry/demos/command.tsx +28 -0
- package/registry/demos/confirm-button.d.ts +5 -0
- package/registry/demos/confirm-button.js +12 -0
- package/registry/demos/confirm-button.tsx +13 -0
- package/registry/demos/countdown.d.ts +5 -0
- package/registry/demos/countdown.js +13 -0
- package/registry/demos/countdown.tsx +10 -0
- package/registry/demos/dialog.d.ts +7 -0
- package/registry/demos/dialog.js +29 -0
- package/registry/demos/dialog.tsx +31 -0
- package/registry/demos/dropdown-menu.d.ts +7 -0
- package/registry/demos/dropdown-menu.js +36 -0
- package/registry/demos/dropdown-menu.tsx +27 -0
- package/registry/demos/empty-state.d.ts +5 -0
- package/registry/demos/empty-state.js +15 -0
- package/registry/demos/empty-state.tsx +12 -0
- package/registry/demos/field.d.ts +5 -0
- package/registry/demos/field.js +15 -0
- package/registry/demos/field.tsx +11 -0
- package/registry/demos/kbd.d.ts +5 -0
- package/registry/demos/kbd.js +11 -0
- package/registry/demos/kbd.tsx +5 -0
- package/registry/demos/page-header.d.ts +5 -0
- package/registry/demos/page-header.js +11 -0
- package/registry/demos/page-header.tsx +5 -0
- package/registry/demos/popover.d.ts +7 -0
- package/registry/demos/popover.js +25 -0
- package/registry/demos/popover.tsx +31 -0
- package/registry/demos/progress.d.ts +5 -0
- package/registry/demos/progress.js +11 -0
- package/registry/demos/progress.tsx +5 -0
- package/registry/demos/radio-group.d.ts +5 -0
- package/registry/demos/radio-group.js +16 -0
- package/registry/demos/radio-group.tsx +14 -0
- package/registry/demos/separator.d.ts +5 -0
- package/registry/demos/separator.js +15 -0
- package/registry/demos/separator.tsx +11 -0
- package/registry/demos/skeleton.d.ts +5 -0
- package/registry/demos/skeleton.js +20 -0
- package/registry/demos/skeleton.tsx +16 -0
- package/registry/demos/slider.d.ts +5 -0
- package/registry/demos/slider.js +11 -0
- package/registry/demos/slider.tsx +5 -0
- package/registry/demos/slot.d.ts +5 -0
- package/registry/demos/slot.js +14 -0
- package/registry/demos/slot.tsx +13 -0
- package/registry/demos/spinner.d.ts +5 -0
- package/registry/demos/spinner.js +11 -0
- package/registry/demos/spinner.tsx +5 -0
- package/registry/demos/stat.d.ts +5 -0
- package/registry/demos/stat.js +11 -0
- package/registry/demos/stat.tsx +5 -0
- package/registry/demos/switch.d.ts +5 -0
- package/registry/demos/switch.js +11 -0
- package/registry/demos/switch.tsx +5 -0
- package/registry/demos/tabs.d.ts +5 -0
- package/registry/demos/tabs.js +22 -0
- package/registry/demos/tabs.tsx +28 -0
- package/registry/demos/theme.d.ts +5 -0
- package/registry/demos/theme.js +11 -0
- package/registry/demos/theme.tsx +9 -0
- package/registry/demos/toast.d.ts +8 -0
- package/registry/demos/toast.js +29 -0
- package/registry/demos/toast.tsx +31 -0
- package/registry/demos/tooltip.d.ts +7 -0
- package/registry/demos/tooltip.js +15 -0
- package/registry/demos/tooltip.tsx +15 -0
- package/registry/dialog.json +218 -0
- package/registry/dropdown-menu.json +63 -0
- package/registry/empty-state.json +59 -0
- package/registry/field.json +66 -0
- package/registry/index.json +320 -0
- package/registry/kbd.json +38 -0
- package/registry/page-header.json +66 -0
- package/registry/popover.json +44 -0
- package/registry/progress.json +80 -0
- package/registry/radio-group.json +64 -0
- package/registry/separator.json +25 -0
- package/registry/skeleton.json +38 -0
- package/registry/slider.json +95 -0
- package/registry/slot.json +31 -0
- package/registry/spinner.json +45 -0
- package/registry/stat.json +66 -0
- package/registry/switch.json +53 -0
- package/registry/tabs.json +44 -0
- package/registry/theme.json +78 -0
- package/registry/toast.json +54 -0
- package/registry/tooltip.json +91 -0
- package/dist/chunk-5FRYKFTO.js.map +0 -1
- package/dist/chunk-QRG5RYEI.js.map +0 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "card",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"description": "`Card.*`, `card` (the slots). Server-safe.",
|
|
5
|
+
"files": [
|
|
6
|
+
{
|
|
7
|
+
"path": "card/card.tsx",
|
|
8
|
+
"content": "// Server-safe: no hook, no context. One `tv` definition with slots: each part takes its slot, and a product overrides\n// any part through `className` (merged last by tailwind-merge).\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\n\nexport const card = tv({\n slots: {\n root:\n \"group relative flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n media: \"relative overflow-hidden bg-media\",\n image: \"h-full w-full object-cover transition-transform duration-500 motion-safe:group-hover:scale-105\",\n fallback: \"flex h-full w-full items-center justify-center bg-linear-to-br from-accent/15 via-media to-accent-2/15 text-accent/60\",\n overlay: \"absolute z-10 flex items-center gap-1.5\",\n body: \"relative flex flex-1 flex-col gap-3\",\n title: \"line-clamp-1 text-sm font-semibold text-fg transition-colors group-hover:text-accent\",\n description: \"line-clamp-2 text-xs text-fg-secondary\",\n stat: \"min-w-0\",\n statLabel: \"text-[10px] font-semibold uppercase tracking-wider text-fg-secondary\",\n statValue: \"font-display text-xl font-black tabular-nums text-fg\",\n footer: \"relative mt-auto flex items-center gap-2 border-t border-border-subtle pt-3 text-xs text-fg-secondary\",\n },\n variants: {\n radius: { md: { root: \"rounded-md\" }, lg: { root: \"rounded-lg\" }, xl: { root: \"rounded-xl\" } },\n tone: { default: {}, elevated: { root: \"bg-surface-2 shadow-md\" }, glass: { root: \"bg-surface-1/70 backdrop-blur-md\" } },\n interactive: { true: { root: \"kz-spotlight kz-lift hover:border-accent/50\" } },\n padding: { none: { body: \"p-0\" }, sm: { body: \"p-3\" }, md: { body: \"p-4\" }, lg: { body: \"p-6\" } },\n },\n defaultVariants: { radius: \"xl\", tone: \"default\", padding: \"md\" },\n});\n\ntype CardVariants = VariantProps<typeof card>;\n/** The parts without a variant read the default slots: no context, so server-safe. */\nconst slots = card();\n\nexport type CardRootProps = React.ComponentProps<\"div\"> &\n Pick<CardVariants, \"radius\" | \"tone\" | \"interactive\"> & {\n /**\n * md · lg · xl.\n * @default \"xl\"\n */\n radius?: CardVariants[\"radius\"];\n /**\n * default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred).\n * @default \"default\"\n */\n tone?: CardVariants[\"tone\"];\n /** The spotlight that follows the pointer and the lift on hover: for a card that is a link. */\n interactive?: CardVariants[\"interactive\"];\n /** Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most). */\n reveal?: number;\n };\n\nexport function CardRoot({ asChild, reveal, radius, tone, interactive, className, style, ...props }: CardRootProps) {\n const Comp = asChild ? Slot.Root : \"div\";\n const revealStyle =\n reveal === undefined ? style : ({ ...style, \"--kz-delay\": `${Math.min(Math.max(reveal, 0), 8) * 50}ms` } as React.CSSProperties);\n return (\n <Comp\n data-reveal={reveal === undefined ? undefined : \"\"}\n data-interactive={interactive ? \"\" : undefined}\n style={revealStyle}\n className={card({ radius, tone, interactive }).root({ className })}\n {...props}\n />\n );\n}\n\nconst ASPECT = { video: \"aspect-video\", square: \"aspect-square\", portrait: \"aspect-[3/4]\", auto: \"\" } as const;\nexport type CardMediaProps = React.ComponentProps<\"div\"> & {\n /** The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's). */\n aspect?: keyof typeof ASPECT;\n};\n\nexport function CardMedia({ aspect = \"video\", className, ...props }: CardMediaProps) {\n return <div data-aspect={aspect} className={slots.media({ className: cn(ASPECT[aspect], className) })} {...props} />;\n}\n\nexport type CardImageProps = Omit<React.ComponentProps<\"img\">, \"src\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** Shown when there is no image: an icon, an illustration (decorative). */\n fallback?: React.ReactNode;\n};\n\n/** The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card. */\nexport function CardImage({ src, alt = \"\", fallback, className, ...props }: CardImageProps) {\n if (!src) {\n return (\n <div data-fallback=\"\" aria-hidden className={slots.fallback({ className })}>\n {fallback}\n </div>\n );\n }\n return <img src={src} alt={alt} loading=\"lazy\" decoding=\"async\" className={slots.image({ className })} {...props} />;\n}\n\nconst CORNER = {\n \"top-left\": \"left-2.5 top-2.5\",\n \"top-right\": \"right-2.5 top-2.5\",\n \"bottom-left\": \"bottom-2.5 left-2.5\",\n \"bottom-right\": \"bottom-2.5 right-2.5\",\n} as const;\nexport type CardOverlayProps = React.ComponentProps<\"div\"> & {\n /** The corner of the media it sits in. */\n corner?: keyof typeof CORNER;\n};\n\n/** What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light. */\nexport function CardOverlay({ corner = \"top-left\", className, ...props }: CardOverlayProps) {\n return <div data-corner={corner} className={slots.overlay({ className: cn(CORNER[corner], className) })} {...props} />;\n}\n\nexport type CardBodyProps = React.ComponentProps<\"div\"> & {\n /**\n * none · sm · md · lg.\n * @default \"md\"\n */\n padding?: CardVariants[\"padding\"];\n};\n\nexport function CardBody({ padding, className, ...props }: CardBodyProps) {\n return <div className={card({ padding }).body({ className })} {...props} />;\n}\n\nexport type CardTitleProps = React.ComponentProps<\"h3\"> & {\n /** The heading level, to fit the page's outline. */\n as?: \"h2\" | \"h3\" | \"h4\";\n};\n\nexport function CardTitle({ as: Tag = \"h3\", className, ...props }: CardTitleProps) {\n return <Tag className={slots.title({ className })} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return <p className={slots.description({ className })} {...props} />;\n}\n\nexport type CardStatProps = React.ComponentProps<\"div\"> & {\n /** What the figure is, in small capitals above it (the figure is the children). */\n label: React.ReactNode;\n};\n\n/** A figure put forward: the label in capitals, the value in tabular digits. */\nexport function CardStat({ label, className, children, ...props }: CardStatProps) {\n return (\n <div className={slots.stat({ className })} {...props}>\n <p className={slots.statLabel()}>{label}</p>\n <p className={slots.statValue()}>{children}</p>\n </div>\n );\n}\n\nexport function CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={slots.footer({ className })} {...props} />;\n}\n\n/** `Card.Root/Media/Image/Overlay/Body/Title/Description/Stat/Footer`. */\nexport const Card = {\n Root: CardRoot,\n Media: CardMedia,\n Image: CardImage,\n Overlay: CardOverlay,\n Body: CardBody,\n Title: CardTitle,\n Description: CardDescription,\n Stat: CardStat,\n Footer: CardFooter,\n};\n"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"path": "card/index.ts",
|
|
12
|
+
"content": "/** @krizaka/ui/card — `Card.*`, `card` (the slots). Server-safe. */\nexport {\n Card,\n card,\n CardBody,\n type CardBodyProps,\n CardDescription,\n CardFooter,\n CardImage,\n type CardImageProps,\n CardMedia,\n type CardMediaProps,\n CardOverlay,\n type CardOverlayProps,\n CardRoot,\n type CardRootProps,\n CardStat,\n type CardStatProps,\n CardTitle,\n type CardTitleProps,\n} from \"./card\";\n"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"dependencies": [
|
|
16
|
+
"radix-ui@^1.7.0",
|
|
17
|
+
"tailwind-variants@^3.3.1"
|
|
18
|
+
],
|
|
19
|
+
"registryDependencies": [
|
|
20
|
+
"cn"
|
|
21
|
+
],
|
|
22
|
+
"demo": {
|
|
23
|
+
"path": "demos/card.tsx",
|
|
24
|
+
"content": "import { Badge } from \"@krizaka/ui/badge\";\nimport { Card } from \"@krizaka/ui/card\";\n\n// A self-contained cover (no network).\nconst COVER =\n \"data:image/svg+xml;utf8,\" +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 160 90\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">' +\n '<stop offset=\"0\" stop-color=\"#1e3a8a\"/><stop offset=\"1\" stop-color=\"#7c3aed\"/></linearGradient></defs>' +\n '<rect width=\"160\" height=\"90\" fill=\"url(#g)\"/><circle cx=\"118\" cy=\"30\" r=\"14\" fill=\"#fde68a\"/>' +\n '<path d=\"M0 90 50 44l30 28 22-18 58 36z\" fill=\"#0f172a\" opacity=\".55\"/></svg>',\n );\n\nexport default function CardDemo() {\n return (\n <Card.Root>\n <Card.Media>\n <Card.Image src={COVER} />\n <Card.Overlay>\n <Badge tone=\"scrim\">4K</Badge>\n </Card.Overlay>\n <Card.Overlay corner=\"bottom-right\">\n <Badge tone=\"scrim\">12:04</Badge>\n </Card.Overlay>\n </Card.Media>\n <Card.Body>\n <Card.Title>Night ride across the city</Card.Title>\n <Card.Description>Ten minutes of neon, rain and empty avenues, filmed in one take.</Card.Description>\n <Card.Footer>\n <span>1.2k views</span>\n <span aria-hidden>·</span>\n <span>2 days ago</span>\n </Card.Footer>\n </Card.Body>\n </Card.Root>\n );\n}\n"
|
|
25
|
+
},
|
|
26
|
+
"props": [
|
|
27
|
+
{
|
|
28
|
+
"component": "CardRoot",
|
|
29
|
+
"description": "",
|
|
30
|
+
"props": [
|
|
31
|
+
{
|
|
32
|
+
"name": "asChild",
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"default": null,
|
|
35
|
+
"description": "Renders the child (a link, an article) instead of the <div>, with props, classes and ref merged.",
|
|
36
|
+
"required": false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "interactive",
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"default": null,
|
|
42
|
+
"description": "The spotlight that follows the pointer and the lift on hover: for a card that is a link.",
|
|
43
|
+
"required": false
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "radius",
|
|
47
|
+
"type": "\"md\" | \"lg\" | \"xl\"",
|
|
48
|
+
"default": "xl",
|
|
49
|
+
"description": "md · lg · xl.",
|
|
50
|
+
"required": false
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "reveal",
|
|
54
|
+
"type": "number",
|
|
55
|
+
"default": null,
|
|
56
|
+
"description": "Enters on scroll (MotionObserver): the index in the grid becomes the delay (50 ms a step, 8 steps at most).",
|
|
57
|
+
"required": false
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "tone",
|
|
61
|
+
"type": "\"default\" | \"elevated\" | \"glass\"",
|
|
62
|
+
"default": "default",
|
|
63
|
+
"description": "default (the base surface) · elevated (raised, a shadow) · glass (translucent, blurred).",
|
|
64
|
+
"required": false
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"component": "CardMedia",
|
|
70
|
+
"description": "",
|
|
71
|
+
"props": [
|
|
72
|
+
{
|
|
73
|
+
"name": "aspect",
|
|
74
|
+
"type": "\"video\" | \"auto\" | \"square\" | \"portrait\"",
|
|
75
|
+
"default": "video",
|
|
76
|
+
"description": "The ratio of the media: video (16:9) · square · portrait (3:4) · auto (the content's).",
|
|
77
|
+
"required": false
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"component": "CardImage",
|
|
83
|
+
"description": "The image of a media, or its `fallback` when there is none. `alt` is empty by default: the title names the card.",
|
|
84
|
+
"props": [
|
|
85
|
+
{
|
|
86
|
+
"name": "fallback",
|
|
87
|
+
"type": "ReactNode",
|
|
88
|
+
"default": null,
|
|
89
|
+
"description": "Shown when there is no image: an icon, an illustration (decorative).",
|
|
90
|
+
"required": false
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "src",
|
|
94
|
+
"type": "string | null",
|
|
95
|
+
"default": null,
|
|
96
|
+
"description": "The image's URL; `null` or nothing shows the `fallback`.",
|
|
97
|
+
"required": false
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"component": "CardOverlay",
|
|
103
|
+
"description": "What sits on the media (badges, a caption). `scrim` and `fg-on-media` are invariant: nothing to do for light.",
|
|
104
|
+
"props": [
|
|
105
|
+
{
|
|
106
|
+
"name": "corner",
|
|
107
|
+
"type": "\"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\"",
|
|
108
|
+
"default": "top-left",
|
|
109
|
+
"description": "The corner of the media it sits in.",
|
|
110
|
+
"required": false
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"component": "CardBody",
|
|
116
|
+
"description": "",
|
|
117
|
+
"props": [
|
|
118
|
+
{
|
|
119
|
+
"name": "padding",
|
|
120
|
+
"type": "\"none\" | \"sm\" | \"md\" | \"lg\"",
|
|
121
|
+
"default": "md",
|
|
122
|
+
"description": "none · sm · md · lg.",
|
|
123
|
+
"required": false
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"component": "CardTitle",
|
|
129
|
+
"description": "",
|
|
130
|
+
"props": [
|
|
131
|
+
{
|
|
132
|
+
"name": "as",
|
|
133
|
+
"type": "\"h2\" | \"h3\" | \"h4\"",
|
|
134
|
+
"default": "h3",
|
|
135
|
+
"description": "The heading level, to fit the page's outline.",
|
|
136
|
+
"required": false
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"component": "CardStat",
|
|
142
|
+
"description": "A figure put forward: the label in capitals, the value in tabular digits.",
|
|
143
|
+
"props": [
|
|
144
|
+
{
|
|
145
|
+
"name": "label",
|
|
146
|
+
"type": "ReactNode",
|
|
147
|
+
"default": null,
|
|
148
|
+
"description": "What the figure is, in small capitals above it (the figure is the children).",
|
|
149
|
+
"required": true
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "checkbox",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"description": "`Checkbox` (checked · unchecked · indeterminate), `checkbox` (the slots). Client (\"use client\").",
|
|
5
|
+
"files": [
|
|
6
|
+
{
|
|
7
|
+
"path": "checkbox/checkbox.tsx",
|
|
8
|
+
"content": "// Client. Radix Checkbox: role=\"checkbox\", aria-checked (true · false · mixed), Space toggles, a hidden input inside a\n// form. It sits in a `Field` like the other controls: `<Field.Label htmlFor={id}>`, `invalid`, aria-describedby.\nimport { Checkbox as CheckboxPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const checkbox = tv({\n slots: {\n root:\n \"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-md border border-border-strong bg-surface-1 text-on-accent transition-colors \" +\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 \"hover:border-accent disabled:cursor-not-allowed disabled:opacity-40 \" +\n \"data-[state=checked]:border-accent data-[state=checked]:bg-accent data-[state=indeterminate]:border-accent data-[state=indeterminate]:bg-accent \" +\n \"aria-invalid:border-danger aria-invalid:ring-danger/30\",\n indicator: \"flex items-center justify-center\",\n label: \"flex cursor-pointer items-start gap-3 text-sm text-fg has-disabled:cursor-not-allowed has-disabled:opacity-60\",\n },\n});\n\nfunction Check() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\nfunction Dash() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M5 12h14\" />\n </svg>\n );\n}\n\nexport type CheckboxProps = Omit<React.ComponentProps<typeof CheckboxPrimitive.Root>, \"children\"> & {\n /** Marks it invalid: aria-invalid and data-invalid (pair it with a `Field.Error` in aria-describedby). */\n invalid?: boolean;\n /** The text next to the box, clickable: the box and its words in one `<label>`. Otherwise name it with\n * `Field.Label htmlFor` or `aria-label`. */\n children?: React.ReactNode;\n};\n\n/** A checkbox: `checked`/`defaultChecked` (`true` · `false` · `\"indeterminate\"`) + `onCheckedChange`. */\nexport function Checkbox({ invalid, className, children, ...props }: CheckboxProps) {\n const s = checkbox();\n const box = (\n <CheckboxPrimitive.Root\n aria-invalid={invalid || undefined}\n data-invalid={invalid ? \"\" : undefined}\n className={s.root({ className: children ? \"mt-0.5\" : className })}\n {...props}\n >\n <CheckboxPrimitive.Indicator className={s.indicator()}>\n {props.checked === \"indeterminate\" ? <Dash /> : <Check />}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n if (!children) return box;\n return (\n <label className={s.label({ className })}>\n {box}\n <span className=\"min-w-0\">{children}</span>\n </label>\n );\n}\n"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"path": "checkbox/index.ts",
|
|
12
|
+
"content": "/** @krizaka/ui/checkbox — `Checkbox` (checked · unchecked · indeterminate), `checkbox` (the slots). Client (\"use client\"). */\nexport { Checkbox, checkbox, type CheckboxProps } from \"./checkbox\";\n"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"dependencies": [
|
|
16
|
+
"radix-ui@^1.7.0",
|
|
17
|
+
"tailwind-variants@^3.3.1"
|
|
18
|
+
],
|
|
19
|
+
"registryDependencies": [],
|
|
20
|
+
"demo": {
|
|
21
|
+
"path": "demos/checkbox.tsx",
|
|
22
|
+
"content": "import { Checkbox } from \"@krizaka/ui/checkbox\";\n\nexport default function CheckboxDemo() {\n return <Checkbox>I accept the terms of use</Checkbox>;\n}\n"
|
|
23
|
+
},
|
|
24
|
+
"props": [
|
|
25
|
+
{
|
|
26
|
+
"component": "Checkbox",
|
|
27
|
+
"description": "A checkbox: `checked`/`defaultChecked` (`true` · `false` · `\"indeterminate\"`) + `onCheckedChange`.",
|
|
28
|
+
"props": [
|
|
29
|
+
{
|
|
30
|
+
"name": "children",
|
|
31
|
+
"type": "ReactNode",
|
|
32
|
+
"default": null,
|
|
33
|
+
"description": "The text next to the box, clickable: the box and its words in one `<label>`. Otherwise name it with\n`Field.Label htmlFor` or `aria-label`.",
|
|
34
|
+
"required": false
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "invalid",
|
|
38
|
+
"type": "boolean",
|
|
39
|
+
"default": null,
|
|
40
|
+
"description": "Marks it invalid: aria-invalid and data-invalid (pair it with a `Field.Error` in aria-describedby).",
|
|
41
|
+
"required": false
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chip",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"description": "`Chip` (selectable, removable), `Chip.Group` (single · multiple), `chip` (the slots). Client (\"use client\").",
|
|
5
|
+
"files": [
|
|
6
|
+
{
|
|
7
|
+
"path": "chip/chip.tsx",
|
|
8
|
+
"content": "// Client (a context). A chip is a pressable pill: alone it is a Radix Toggle (aria-pressed), inside `Chip.Group` an\n// item of a Radix Toggle Group (roving focus with the arrows; `single` = radio semantics, `multiple` = pressed\n// buttons). A `removable` chip is a static label with its own remove button (two buttons cannot nest).\nimport { Toggle as TogglePrimitive, ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const chip = tv({\n slots: {\n group: \"flex flex-wrap items-center gap-2 data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch\",\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-1.5 whitespace-nowrap rounded-full border font-semibold transition-colors \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40 \" +\n \"border-border-default bg-surface-1 text-fg-secondary hover:border-border-strong hover:text-fg \" +\n \"data-[state=on]:border-accent data-[state=on]:bg-accent-soft data-[state=on]:text-fg\",\n remove:\n \"-mr-1.5 inline-flex h-6 w-6 items-center justify-center rounded-full text-fg-secondary transition-colors hover:bg-surface-3 hover:text-fg \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n },\n variants: {\n size: { sm: { base: \"h-7 px-3 text-xs\" }, md: { base: \"h-9 px-3.5 text-xs\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type ChipVariants = VariantProps<typeof chip>;\n\ntype GroupContext = { size: ChipVariants[\"size\"] };\nconst ChipGroupContext = React.createContext<GroupContext | null>(null);\n\nfunction RemoveIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype ToggleChipProps = Omit<React.ComponentProps<\"button\">, \"value\" | \"defaultValue\"> &\n ChipVariants & {\n /**\n * sm · md (inside `Chip.Group`, the group's size by default).\n * @default \"md\"\n */\n size?: ChipVariants[\"size\"];\n /** Inside `Chip.Group`: the value this chip stands for (required there, ignored alone). */\n value?: string;\n /** Alone: pressed or not (`defaultSelected` uncontrolled), `onSelectedChange` on every press. */\n selected?: boolean;\n /** Alone: pressed at first, uncontrolled. */\n defaultSelected?: boolean;\n /** Alone: called with the new state on every press. */\n onSelectedChange?: (selected: boolean) => void;\n /** A pressable chip (the default); `true` makes it a static chip with a remove button. */\n removable?: false;\n };\n\ntype RemovableChipProps = Omit<React.ComponentProps<\"span\">, \"onSelect\"> &\n ChipVariants & {\n /** A static chip (a chosen tag, an applied filter) with a remove button. */\n removable: true;\n /** The accessible name of the remove button — passed translated, e.g. \"Remove #night\". */\n removeLabel: string;\n /** Runs when the remove button is pressed. */\n onRemove: () => void;\n /** Disables the remove button. */\n disabled?: boolean;\n };\n\nexport type ChipProps = ToggleChipProps | RemovableChipProps;\n\n/** A selectable pill (`selected` / `onSelectedChange`), an item of `Chip.Group` (`value`), or `removable`. */\nfunction ChipBase(props: ChipProps) {\n const group = React.useContext(ChipGroupContext);\n if (props.removable) {\n const { removable: _removable, removeLabel, onRemove, disabled, size, className, children, ...rest } = props;\n const s = chip({ size: size ?? group?.size });\n return (\n <span data-removable=\"\" className={s.base({ className })} {...rest}>\n {children}\n <button type=\"button\" aria-label={removeLabel} title={removeLabel} disabled={disabled} onClick={onRemove} className={s.remove()}>\n <RemoveIcon />\n </button>\n </span>\n );\n }\n const { removable: _removable, value, selected, defaultSelected, onSelectedChange, size, className, ...rest } = props;\n const s = chip({ size: size ?? group?.size });\n if (group) {\n if (value === undefined) throw new Error(\"Chip: a chip inside Chip.Group needs a `value`.\");\n return <ToggleGroupPrimitive.Item value={value} className={s.base({ className })} {...rest} />;\n }\n return (\n <TogglePrimitive.Root\n pressed={selected}\n defaultPressed={defaultSelected}\n onPressedChange={onSelectedChange}\n className={s.base({ className })}\n {...rest}\n />\n );\n}\n\ntype GroupBase = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"dir\"> &\n ChipVariants & {\n /**\n * sm · md: the size of every chip of the group.\n * @default \"md\"\n */\n size?: ChipVariants[\"size\"];\n /** The group's accessible name — passed translated (\"Filter by format\"). */\n label?: string;\n /** The arrow keys that move: left / right (horizontal, a row) or up / down (vertical, a column). */\n orientation?: \"horizontal\" | \"vertical\";\n /** Arrow keys wrap from the last chip to the first (default `true`). */\n loop?: boolean;\n /** Disables every chip of the group. */\n disabled?: boolean;\n };\n\nexport type ChipGroupProps =\n | (GroupBase & {\n /** One choice: the chips behave as radios. */\n type: \"single\";\n /** The chosen value (controlled), `\"\"` when none. */\n value?: string;\n /** The value chosen at first, uncontrolled. */\n defaultValue?: string;\n /** Called with the new value on every choice. */\n onValueChange?: (value: string) => void;\n /** Keeps one chip selected: pressing the selected chip does not clear it (default `false`). */\n required?: boolean;\n })\n | (GroupBase & {\n /** Any number of choices: the chips are pressed buttons. */\n type: \"multiple\";\n /** The chosen values (controlled). */\n value?: string[];\n /** The values chosen at first, uncontrolled. */\n defaultValue?: string[];\n /** Called with the new values on every press. */\n onValueChange?: (value: string[]) => void;\n required?: never;\n });\n\n/** Chips that choose together: `type` single · multiple, `value`/`defaultValue` + `onValueChange`. */\nexport function ChipGroup({ label, size, className, children, ...props }: ChipGroupProps) {\n const s = chip({ size });\n const context = React.useMemo(() => ({ size }), [size]);\n const common = { \"aria-label\": label, className: s.group({ className }) };\n let group: React.ReactNode;\n if (props.type === \"single\") {\n const { type: _type, value, defaultValue, onValueChange, required, ...rest } = props;\n group = <SingleGroup {...rest} {...common} value={value} defaultValue={defaultValue} onValueChange={onValueChange} required={required}>{children}</SingleGroup>;\n } else {\n const { type: _type, required: _required, ...rest } = props;\n group = (\n <ToggleGroupPrimitive.Root type=\"multiple\" {...rest} {...common}>\n {children}\n </ToggleGroupPrimitive.Root>\n );\n }\n return <ChipGroupContext.Provider value={context}>{group}</ChipGroupContext.Provider>;\n}\n\n/** Radix's single group, plus `required`: the value is held here so that an empty change can be refused. */\nfunction SingleGroup({\n value,\n defaultValue,\n onValueChange,\n required,\n ...props\n}: Omit<GroupBase, \"size\" | \"label\"> & {\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n required?: boolean;\n}) {\n const [inner, setInner] = React.useState(defaultValue ?? \"\");\n const current = value ?? inner;\n return (\n <ToggleGroupPrimitive.Root\n type=\"single\"\n value={current}\n onValueChange={(next) => {\n if (required && !next) return;\n if (value === undefined) setInner(next);\n onValueChange?.(next);\n }}\n {...props}\n />\n );\n}\n\n/** `Chip` (selectable, removable) and `Chip.Group` (single · multiple). */\nexport const Chip = Object.assign(ChipBase, { Group: ChipGroup });\n"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"path": "chip/index.ts",
|
|
12
|
+
"content": "/** @krizaka/ui/chip — `Chip` (selectable, removable), `Chip.Group` (single · multiple), `chip` (the slots). Client (\"use client\"). */\nexport { Chip, chip, ChipGroup, type ChipGroupProps, type ChipProps, type ChipVariants } from \"./chip\";\n"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"dependencies": [
|
|
16
|
+
"radix-ui@^1.7.0",
|
|
17
|
+
"tailwind-variants@^3.3.1"
|
|
18
|
+
],
|
|
19
|
+
"registryDependencies": [],
|
|
20
|
+
"demo": {
|
|
21
|
+
"path": "demos/chip.tsx",
|
|
22
|
+
"content": "import { Chip } from \"@krizaka/ui/chip\";\n\nexport default function ChipDemo() {\n return (\n <div className=\"flex gap-2\">\n <Chip>Subtitles</Chip>\n <Chip defaultSelected>HD</Chip>\n <Chip disabled>4K</Chip>\n </div>\n );\n}\n"
|
|
23
|
+
},
|
|
24
|
+
"props": [
|
|
25
|
+
{
|
|
26
|
+
"component": "Chip",
|
|
27
|
+
"description": "`Chip` (selectable, removable) and `Chip.Group` (single · multiple).",
|
|
28
|
+
"props": [
|
|
29
|
+
{
|
|
30
|
+
"name": "onRemove",
|
|
31
|
+
"type": "() => void",
|
|
32
|
+
"default": null,
|
|
33
|
+
"description": "Runs when the remove button is pressed.",
|
|
34
|
+
"required": true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "removeLabel",
|
|
38
|
+
"type": "string",
|
|
39
|
+
"default": null,
|
|
40
|
+
"description": "The accessible name of the remove button — passed translated, e.g. \"Remove #night\".",
|
|
41
|
+
"required": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "defaultSelected",
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": null,
|
|
47
|
+
"description": "Alone: pressed at first, uncontrolled.",
|
|
48
|
+
"required": false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "onSelectedChange",
|
|
52
|
+
"type": "((selected: boolean) => void)",
|
|
53
|
+
"default": null,
|
|
54
|
+
"description": "Alone: called with the new state on every press.",
|
|
55
|
+
"required": false
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "removable",
|
|
59
|
+
"type": "boolean",
|
|
60
|
+
"default": null,
|
|
61
|
+
"description": "A pressable chip (the default); `true` makes it a static chip with a remove button.\nA static chip (a chosen tag, an applied filter) with a remove button.",
|
|
62
|
+
"required": false
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "selected",
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"default": null,
|
|
68
|
+
"description": "Alone: pressed or not (`defaultSelected` uncontrolled), `onSelectedChange` on every press.",
|
|
69
|
+
"required": false
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "size",
|
|
73
|
+
"type": "\"sm\" | \"md\"",
|
|
74
|
+
"default": "md",
|
|
75
|
+
"description": "sm · md (inside `Chip.Group`, the group's size by default).",
|
|
76
|
+
"required": false
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "value",
|
|
80
|
+
"type": "string",
|
|
81
|
+
"default": null,
|
|
82
|
+
"description": "Inside `Chip.Group`: the value this chip stands for (required there, ignored alone).",
|
|
83
|
+
"required": false
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"component": "ChipGroup",
|
|
89
|
+
"description": "Chips that choose together: `type` single · multiple, `value`/`defaultValue` + `onValueChange`.",
|
|
90
|
+
"props": [
|
|
91
|
+
{
|
|
92
|
+
"name": "type",
|
|
93
|
+
"type": "\"single\" | \"multiple\"",
|
|
94
|
+
"default": null,
|
|
95
|
+
"description": "One choice: the chips behave as radios.\nAny number of choices: the chips are pressed buttons.",
|
|
96
|
+
"required": true
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "defaultValue",
|
|
100
|
+
"type": "string | string[]",
|
|
101
|
+
"default": null,
|
|
102
|
+
"description": "The value chosen at first, uncontrolled.\nThe values chosen at first, uncontrolled.",
|
|
103
|
+
"required": false
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "disabled",
|
|
107
|
+
"type": "boolean",
|
|
108
|
+
"default": null,
|
|
109
|
+
"description": "Disables every chip of the group.",
|
|
110
|
+
"required": false
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "label",
|
|
114
|
+
"type": "string",
|
|
115
|
+
"default": null,
|
|
116
|
+
"description": "The group's accessible name — passed translated (\"Filter by format\").",
|
|
117
|
+
"required": false
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "loop",
|
|
121
|
+
"type": "boolean",
|
|
122
|
+
"default": null,
|
|
123
|
+
"description": "Arrow keys wrap from the last chip to the first (default `true`).",
|
|
124
|
+
"required": false
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "onValueChange",
|
|
128
|
+
"type": "((value: string) => void) | ((value: string[]) => void)",
|
|
129
|
+
"default": null,
|
|
130
|
+
"description": "Called with the new value on every choice.\nCalled with the new values on every press.",
|
|
131
|
+
"required": false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "orientation",
|
|
135
|
+
"type": "\"horizontal\" | \"vertical\"",
|
|
136
|
+
"default": null,
|
|
137
|
+
"description": "The arrow keys that move: left / right (horizontal, a row) or up / down (vertical, a column).",
|
|
138
|
+
"required": false
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "required",
|
|
142
|
+
"type": "boolean",
|
|
143
|
+
"default": null,
|
|
144
|
+
"description": "Keeps one chip selected: pressing the selected chip does not clear it (default `false`).",
|
|
145
|
+
"required": false
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "size",
|
|
149
|
+
"type": "\"sm\" | \"md\"",
|
|
150
|
+
"default": "md",
|
|
151
|
+
"description": "sm · md: the size of every chip of the group.",
|
|
152
|
+
"required": false
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"name": "value",
|
|
156
|
+
"type": "string | string[]",
|
|
157
|
+
"default": null,
|
|
158
|
+
"description": "The chosen value (controlled), `\"\"` when none.\nThe chosen values (controlled).",
|
|
159
|
+
"required": false
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
package/registry/cn.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cn",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"description": "Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it. tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer): hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean install of the packed package.",
|
|
5
|
+
"files": [
|
|
6
|
+
{
|
|
7
|
+
"path": "cn.ts",
|
|
8
|
+
"content": "import { type ClassValue, clsx } from \"clsx\";\nimport { cn as merge } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return merge(clsx(inputs)) ?? \"\";\n}\n"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"clsx@^2.1.1",
|
|
13
|
+
"tailwind-variants@^3.3.1"
|
|
14
|
+
],
|
|
15
|
+
"registryDependencies": [],
|
|
16
|
+
"demo": {
|
|
17
|
+
"path": "demos/cn.tsx",
|
|
18
|
+
"content": "import { cn } from \"@krizaka/ui/cn\";\n\nexport default function CnDemo({ selected = true }: { selected?: boolean }) {\n return (\n <p className={cn(\"rounded-lg border border-border-default px-3 py-2 text-sm text-fg-secondary\", selected && \"border-accent text-fg\", \"px-4\")}>\n The last class wins: <code>px-4</code> replaces <code>px-3</code>, and the selected colours replace the idle ones.\n </p>\n );\n}\n"
|
|
19
|
+
},
|
|
20
|
+
"props": []
|
|
21
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "command",
|
|
3
|
+
"type": "primitive",
|
|
4
|
+
"description": "`Command.*` (cmdk) and `CommandDialog`, `command` (the slots). Client (\"use client\").",
|
|
5
|
+
"files": [
|
|
6
|
+
{
|
|
7
|
+
"path": "command/command.tsx",
|
|
8
|
+
"content": "// Client. A command palette on cmdk: the combobox / listbox roles, the active option (aria-activedescendant), the\n// arrows, Home / End, Enter, and the filtering (turn it off with `shouldFilter={false}` when the server searches).\n// `CommandDialog` puts it in the platform's Dialog. Words arrive as props.\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { DialogContent, type DialogContentProps } from \"../dialog/dialog\";\n\nexport const command = tv({\n slots: {\n root: \"flex h-full w-full flex-col overflow-hidden bg-surface-1 text-fg\",\n inputWrap: \"flex items-center gap-3 border-b border-border-default px-4\",\n icon: \"h-5 w-5 shrink-0 text-fg-secondary\",\n input:\n \"h-13 w-full min-w-0 flex-1 bg-transparent text-sm text-fg outline-hidden placeholder:text-fg-muted disabled:cursor-not-allowed disabled:opacity-50 sm:text-base\",\n list: \"max-h-[min(60dvh,26rem)] scroll-py-2 overflow-y-auto overscroll-contain p-2\",\n empty: \"px-3 py-8 text-center text-sm text-fg-secondary\",\n loading: \"px-3 py-6 text-center text-sm text-fg-secondary\",\n group:\n \"[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:pb-1.5 [&_[cmdk-group-heading]]:pt-3 [&_[cmdk-group-heading]]:text-xs \" +\n \"[&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider [&_[cmdk-group-heading]]:text-fg-secondary\",\n item:\n \"relative flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2.5 text-sm text-fg outline-hidden transition-colors \" +\n \"before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-transparent \" +\n \"data-[selected=true]:bg-surface-2 data-[selected=true]:before:bg-accent data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-40\",\n separator: \"-mx-2 my-1 h-px bg-border-subtle\",\n shortcut: \"ml-auto text-xs tracking-widest text-fg-secondary\",\n },\n});\nconst s = command();\n\nfunction SearchIcon({ className }: { className?: string }) {\n return (\n <svg viewBox=\"0 0 24 24\" className={className} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m20 20-3.5-3.5\" />\n </svg>\n );\n}\n\nexport type CommandRootProps = React.ComponentProps<typeof CommandPrimitive> & {\n /** The accessible name of the palette — passed translated (\"Search\", \"Commands\"). */\n label: string;\n};\n\n/** The palette: `value` / `onValueChange` (the active item), `shouldFilter`, `filter`, `loop`. */\nexport function CommandRoot({ className, ...props }: CommandRootProps) {\n return <CommandPrimitive className={s.root({ className })} {...props} />;\n}\n\nexport type CommandInputProps = Omit<React.ComponentProps<typeof CommandPrimitive.Input>, \"placeholder\"> & {\n /** What to type — passed translated. The input is named by the palette's `label`. */\n placeholder: string;\n /** After the input, inside the bar: a spinner, a clear button. */\n trailing?: React.ReactNode;\n};\n\n/** The search field (`value` / `onValueChange` for the query), with a magnifier. */\nexport function CommandInput({ className, placeholder, trailing, ...props }: CommandInputProps) {\n return (\n <div className={s.inputWrap()} cmdk-input-wrapper=\"\">\n <SearchIcon className={s.icon()} />\n <CommandPrimitive.Input placeholder={placeholder} className={s.input({ className })} {...props} />\n {trailing}\n </div>\n );\n}\n\nexport type CommandListProps = React.ComponentProps<typeof CommandPrimitive.List> & {\n /** The accessible name of the list of results — passed translated (\"Suggestions\", \"Results\"). */\n label: string;\n};\n\nexport function CommandList({ className, ...props }: CommandListProps) {\n return <CommandPrimitive.List className={s.list({ className })} {...props} />;\n}\n\nexport type CommandEmptyProps = Omit<React.ComponentProps<typeof CommandPrimitive.Empty>, \"children\"> & {\n /** Shown when nothing matches — passed translated. */\n emptyLabel: React.ReactNode;\n};\n\n/** Shown by cmdk only when no item matches. */\nexport function CommandEmpty({ className, emptyLabel, ...props }: CommandEmptyProps) {\n return (\n <CommandPrimitive.Empty className={s.empty({ className })} {...props}>\n {emptyLabel}\n </CommandPrimitive.Empty>\n );\n}\n\n/** While results load (`label` is announced). */\nexport function CommandLoading({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Loading>) {\n return <CommandPrimitive.Loading className={s.loading({ className })} {...props} />;\n}\n\n/** A group of items under a `heading`. */\nexport function CommandGroup({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Group>) {\n return <CommandPrimitive.Group className={s.group({ className })} {...props} />;\n}\n\n/** An option: `onSelect` on Enter or click, `value` and `keywords` for the filter, `disabled`. */\nexport function CommandItem({ className, ...props }: React.ComponentProps<typeof CommandPrimitive.Item>) {\n return <CommandPrimitive.Item className={s.item({ className })} {...props} />;\n}\n\n/** A line between groups. Decorative (`role=\"none\"`): cmdk's own separator is a role a listbox may not hold (axe). */\nexport function CommandSeparator({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div role=\"none\" className={s.separator({ className })} {...props} />;\n}\n\n/** A keyboard hint at the end of an item. */\nexport function CommandShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return <span className={s.shortcut({ className })} {...props} />;\n}\n\n/** `Command.Root/Input/List/Empty/Loading/Group/Item/Separator/Shortcut`. */\nexport const Command = {\n Root: CommandRoot,\n Input: CommandInput,\n List: CommandList,\n Empty: CommandEmpty,\n Loading: CommandLoading,\n Group: CommandGroup,\n Item: CommandItem,\n Separator: CommandSeparator,\n Shortcut: CommandShortcut,\n};\n\nexport type CommandDialogProps = Omit<CommandRootProps, \"title\"> & {\n /** Open or closed (controlled), with `onOpenChange`. */\n open?: boolean;\n /** Open at first, uncontrolled. */\n defaultOpen?: boolean;\n /** Called when it opens or closes (Escape, a click outside, a shortcut of the product). */\n onOpenChange?: (open: boolean) => void;\n /** Under the palette, inside the dialog: shortcuts, a \"see all results\" link. */\n footer?: React.ReactNode;\n /** Passed to `Dialog.Content`: `size`, `container`, `className` of the dialog. */\n contentProps?: Omit<Extract<DialogContentProps, { hideClose: true }>, \"hideClose\" | \"children\" | \"placement\">;\n};\n\n/**\n * The palette in a dialog (Radix: focus trap, Escape, focus return). The dialog is named by `label`; it has no close\n * button — Escape and a click outside close it — and the input takes the focus when it opens.\n */\nexport function CommandDialog({ open, defaultOpen, onOpenChange, footer, contentProps, label, className, children, ...props }: CommandDialogProps) {\n const { className: contentClassName, ...content } = contentProps ?? {};\n return (\n <DialogPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>\n <DialogContent\n hideClose\n size=\"lg\"\n aria-describedby={undefined}\n className={cn(\"top-[12dvh] translate-y-0 sm:top-[14dvh]\", contentClassName)}\n {...content}\n >\n <DialogPrimitive.Title className=\"sr-only\">{label}</DialogPrimitive.Title>\n <CommandRoot label={label} className={className} {...props}>\n {children}\n </CommandRoot>\n {footer}\n </DialogContent>\n </DialogPrimitive.Root>\n );\n}\n"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"path": "command/index.ts",
|
|
12
|
+
"content": "/** @krizaka/ui/command — `Command.*` (cmdk) and `CommandDialog`, `command` (the slots). Client (\"use client\"). */\nexport {\n Command,\n command,\n CommandDialog,\n type CommandDialogProps,\n CommandEmpty,\n type CommandEmptyProps,\n CommandGroup,\n CommandInput,\n type CommandInputProps,\n CommandItem,\n CommandList,\n type CommandListProps,\n CommandLoading,\n CommandRoot,\n type CommandRootProps,\n CommandSeparator,\n CommandShortcut,\n} from \"./command\";\n"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"dependencies": [
|
|
16
|
+
"cmdk@^1.1.1",
|
|
17
|
+
"radix-ui@^1.7.0",
|
|
18
|
+
"tailwind-variants@^3.3.1"
|
|
19
|
+
],
|
|
20
|
+
"registryDependencies": [
|
|
21
|
+
"cn",
|
|
22
|
+
"dialog"
|
|
23
|
+
],
|
|
24
|
+
"demo": {
|
|
25
|
+
"path": "demos/command.tsx",
|
|
26
|
+
"content": "import { Command } from \"@krizaka/ui/command\";\n\nexport default function CommandDemo() {\n return (\n <Command.Root label=\"Commands\" className=\"w-[28rem] max-w-full rounded-xl border border-border-default shadow-lg\">\n <Command.Input placeholder=\"Type a command or search\" />\n <Command.List label=\"Suggestions\">\n <Command.Empty emptyLabel=\"Nothing matches.\" />\n <Command.Group heading=\"Pages\">\n <Command.Item>\n Home\n <Command.Shortcut>G H</Command.Shortcut>\n </Command.Item>\n <Command.Item>\n Wallet\n <Command.Shortcut>G W</Command.Shortcut>\n </Command.Item>\n <Command.Item disabled>Studio</Command.Item>\n </Command.Group>\n <Command.Separator />\n <Command.Group heading=\"Actions\">\n <Command.Item keywords={[\"video\", \"new\"]}>Upload a video</Command.Item>\n <Command.Item>Switch the theme</Command.Item>\n </Command.Group>\n </Command.List>\n </Command.Root>\n );\n}\n"
|
|
27
|
+
},
|
|
28
|
+
"props": [
|
|
29
|
+
{
|
|
30
|
+
"component": "CommandRoot",
|
|
31
|
+
"description": "The palette: `value` / `onValueChange` (the active item), `shouldFilter`, `filter`, `loop`.",
|
|
32
|
+
"props": [
|
|
33
|
+
{
|
|
34
|
+
"name": "label",
|
|
35
|
+
"type": "string",
|
|
36
|
+
"default": null,
|
|
37
|
+
"description": "Accessible label for this command menu. Not shown visibly.\nThe accessible name of the palette — passed translated (\"Search\", \"Commands\").",
|
|
38
|
+
"required": false
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"component": "CommandInput",
|
|
44
|
+
"description": "The search field (`value` / `onValueChange` for the query), with a magnifier.",
|
|
45
|
+
"props": [
|
|
46
|
+
{
|
|
47
|
+
"name": "placeholder",
|
|
48
|
+
"type": "string",
|
|
49
|
+
"default": null,
|
|
50
|
+
"description": "What to type — passed translated. The input is named by the palette's `label`.",
|
|
51
|
+
"required": true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "trailing",
|
|
55
|
+
"type": "ReactNode",
|
|
56
|
+
"default": null,
|
|
57
|
+
"description": "After the input, inside the bar: a spinner, a clear button.",
|
|
58
|
+
"required": false
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"component": "CommandList",
|
|
64
|
+
"description": "",
|
|
65
|
+
"props": [
|
|
66
|
+
{
|
|
67
|
+
"name": "label",
|
|
68
|
+
"type": "string",
|
|
69
|
+
"default": null,
|
|
70
|
+
"description": "Accessible label for this List of suggestions. Not shown visibly.\nThe accessible name of the list of results — passed translated (\"Suggestions\", \"Results\").",
|
|
71
|
+
"required": false
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"component": "CommandEmpty",
|
|
77
|
+
"description": "Shown by cmdk only when no item matches.",
|
|
78
|
+
"props": [
|
|
79
|
+
{
|
|
80
|
+
"name": "emptyLabel",
|
|
81
|
+
"type": "ReactNode",
|
|
82
|
+
"default": null,
|
|
83
|
+
"description": "Shown when nothing matches — passed translated.",
|
|
84
|
+
"required": true
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"component": "CommandLoading",
|
|
90
|
+
"description": "While results load (`label` is announced).",
|
|
91
|
+
"props": []
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"component": "CommandGroup",
|
|
95
|
+
"description": "A group of items under a `heading`.",
|
|
96
|
+
"props": []
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"component": "CommandItem",
|
|
100
|
+
"description": "An option: `onSelect` on Enter or click, `value` and `keywords` for the filter, `disabled`.",
|
|
101
|
+
"props": []
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"component": "CommandSeparator",
|
|
105
|
+
"description": "A line between groups. Decorative (`role=\"none\"`): cmdk's own separator is a role a listbox may not hold (axe).",
|
|
106
|
+
"props": []
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"component": "CommandShortcut",
|
|
110
|
+
"description": "A keyboard hint at the end of an item.",
|
|
111
|
+
"props": []
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"component": "CommandDialog",
|
|
115
|
+
"description": "The palette in a dialog (Radix: focus trap, Escape, focus return). The dialog is named by `label`; it has no close\nbutton — Escape and a click outside close it — and the input takes the focus when it opens.",
|
|
116
|
+
"props": [
|
|
117
|
+
{
|
|
118
|
+
"name": "contentProps",
|
|
119
|
+
"type": "Omit<DialogContentProps & RefAttributes<HTMLDivElement> & DialogVariants & { closeLabel?: string; hideClose: true; } & { ...; }, \"children\" | ... 1 more ... | \"hideClose\">",
|
|
120
|
+
"default": null,
|
|
121
|
+
"description": "Passed to `Dialog.Content`: `size`, `container`, `className` of the dialog.",
|
|
122
|
+
"required": false
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "defaultOpen",
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"default": null,
|
|
128
|
+
"description": "Open at first, uncontrolled.",
|
|
129
|
+
"required": false
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"name": "footer",
|
|
133
|
+
"type": "ReactNode",
|
|
134
|
+
"default": null,
|
|
135
|
+
"description": "Under the palette, inside the dialog: shortcuts, a \"see all results\" link.",
|
|
136
|
+
"required": false
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "label",
|
|
140
|
+
"type": "string",
|
|
141
|
+
"default": null,
|
|
142
|
+
"description": "Accessible label for this command menu. Not shown visibly.\nThe accessible name of the palette — passed translated (\"Search\", \"Commands\").",
|
|
143
|
+
"required": false
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "onOpenChange",
|
|
147
|
+
"type": "((open: boolean) => void)",
|
|
148
|
+
"default": null,
|
|
149
|
+
"description": "Called when it opens or closes (Escape, a click outside, a shortcut of the product).",
|
|
150
|
+
"required": false
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "open",
|
|
154
|
+
"type": "boolean",
|
|
155
|
+
"default": null,
|
|
156
|
+
"description": "Open or closed (controlled), with `onOpenChange`.",
|
|
157
|
+
"required": false
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|