@nikala-ui/core 0.6.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/README.md +42 -0
- package/package.json +25 -0
- package/registry/accordion.json +18 -0
- package/registry/alert.json +18 -0
- package/registry/avatar.json +17 -0
- package/registry/badge.json +18 -0
- package/registry/banner.json +19 -0
- package/registry/breadcrumb.json +17 -0
- package/registry/button.json +18 -0
- package/registry/card.json +17 -0
- package/registry/checkbox.json +17 -0
- package/registry/command.json +25 -0
- package/registry/dialog.json +18 -0
- package/registry/dropdown-menu.json +18 -0
- package/registry/index.json +297 -0
- package/registry/input-group.json +21 -0
- package/registry/input.json +17 -0
- package/registry/kbd.json +18 -0
- package/registry/label.json +18 -0
- package/registry/list.json +20 -0
- package/registry/radio-group.json +18 -0
- package/registry/select.json +18 -0
- package/registry/separator.json +17 -0
- package/registry/sheet.json +19 -0
- package/registry/skeleton.json +17 -0
- package/registry/switch.json +17 -0
- package/registry/tabs.json +17 -0
- package/registry/textarea.json +17 -0
- package/registry/theme-manager.json +38 -0
- package/src/index.css +11 -0
- package/src/lib/cn.ts +9 -0
- package/src/registry/components/ui/accordion.tsx +128 -0
- package/src/registry/components/ui/alert.tsx +155 -0
- package/src/registry/components/ui/avatar.tsx +114 -0
- package/src/registry/components/ui/badge.tsx +50 -0
- package/src/registry/components/ui/banner.tsx +189 -0
- package/src/registry/components/ui/breadcrumb.tsx +136 -0
- package/src/registry/components/ui/button.tsx +63 -0
- package/src/registry/components/ui/card.tsx +113 -0
- package/src/registry/components/ui/checkbox.tsx +85 -0
- package/src/registry/components/ui/command.tsx +286 -0
- package/src/registry/components/ui/dialog.tsx +195 -0
- package/src/registry/components/ui/dropdown-menu.tsx +250 -0
- package/src/registry/components/ui/input-group.tsx +80 -0
- package/src/registry/components/ui/input.tsx +28 -0
- package/src/registry/components/ui/kbd.tsx +73 -0
- package/src/registry/components/ui/label.tsx +30 -0
- package/src/registry/components/ui/list.tsx +222 -0
- package/src/registry/components/ui/radio-group.tsx +95 -0
- package/src/registry/components/ui/select.tsx +138 -0
- package/src/registry/components/ui/separator.tsx +37 -0
- package/src/registry/components/ui/sheet.tsx +216 -0
- package/src/registry/components/ui/skeleton.tsx +24 -0
- package/src/registry/components/ui/switch.tsx +79 -0
- package/src/registry/components/ui/tabs.tsx +212 -0
- package/src/registry/components/ui/textarea.tsx +82 -0
- package/src/registry/components/ui/theme-toggle.tsx +195 -0
- package/src/registry/index.ts +50 -0
- package/src/registry/metadata.ts +152 -0
- package/src/registry/providers/theme-provider.tsx +208 -0
- package/src/registry/providers/theme-script.tsx +58 -0
- package/src/registry/providers/theme-transitions.ts +108 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "accordion",
|
|
4
|
+
"title": "Accordion",
|
|
5
|
+
"description": "A vertically stacked set of interactive headings built on Kobalte primitives.",
|
|
6
|
+
"type": "registry:ui",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"clsx",
|
|
9
|
+
"tailwind-merge",
|
|
10
|
+
"@kobalte/core"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "alert",
|
|
15
|
+
"title": "Alert",
|
|
16
|
+
"description": "Displays a callout banner for user feedback with variants, dismiss button, and timer.",
|
|
17
|
+
"type": "registry:ui",
|
|
18
|
+
"dependencies": [
|
|
19
|
+
"clsx",
|
|
20
|
+
"tailwind-merge",
|
|
21
|
+
"class-variance-authority"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "avatar",
|
|
26
|
+
"title": "Avatar",
|
|
27
|
+
"description": "An image element with fallback representation for representing users.",
|
|
28
|
+
"type": "registry:ui",
|
|
29
|
+
"dependencies": [
|
|
30
|
+
"clsx",
|
|
31
|
+
"tailwind-merge"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "badge",
|
|
36
|
+
"title": "Badge",
|
|
37
|
+
"description": "A small badge component for status indicators and tags.",
|
|
38
|
+
"type": "registry:ui",
|
|
39
|
+
"dependencies": [
|
|
40
|
+
"clsx",
|
|
41
|
+
"tailwind-merge",
|
|
42
|
+
"class-variance-authority"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "banner",
|
|
47
|
+
"title": "Banner",
|
|
48
|
+
"description": "An announcement banner with sticky positioning, dismissal persistence, auto-hide timer, Lucide icons, and variant styles.",
|
|
49
|
+
"type": "registry:ui",
|
|
50
|
+
"dependencies": [
|
|
51
|
+
"clsx",
|
|
52
|
+
"tailwind-merge",
|
|
53
|
+
"class-variance-authority",
|
|
54
|
+
"lucide-solid"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "breadcrumb",
|
|
59
|
+
"title": "Breadcrumb",
|
|
60
|
+
"description": "Displays the path to the current resource using a hierarchy of links.",
|
|
61
|
+
"type": "registry:ui",
|
|
62
|
+
"dependencies": [
|
|
63
|
+
"clsx",
|
|
64
|
+
"tailwind-merge"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "button",
|
|
69
|
+
"title": "Button",
|
|
70
|
+
"description": "An interactive button component with variant and size options.",
|
|
71
|
+
"type": "registry:ui",
|
|
72
|
+
"dependencies": [
|
|
73
|
+
"clsx",
|
|
74
|
+
"tailwind-merge",
|
|
75
|
+
"class-variance-authority"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "card",
|
|
80
|
+
"title": "Card",
|
|
81
|
+
"description": "A versatile container component with header, content, and footer sections.",
|
|
82
|
+
"type": "registry:ui",
|
|
83
|
+
"dependencies": [
|
|
84
|
+
"clsx",
|
|
85
|
+
"tailwind-merge"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "checkbox",
|
|
90
|
+
"title": "Checkbox",
|
|
91
|
+
"description": "A control that allows the user to toggle between checked and unchecked options.",
|
|
92
|
+
"type": "registry:ui",
|
|
93
|
+
"dependencies": [
|
|
94
|
+
"clsx",
|
|
95
|
+
"tailwind-merge"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "command",
|
|
100
|
+
"title": "Command / Command Palette",
|
|
101
|
+
"description": "Fast, accessible command palette and search modal built on Kobalte Dialog primitives with auto-filtering.",
|
|
102
|
+
"type": "registry:ui",
|
|
103
|
+
"dependencies": [
|
|
104
|
+
"clsx",
|
|
105
|
+
"tailwind-merge",
|
|
106
|
+
"class-variance-authority",
|
|
107
|
+
"lucide-solid",
|
|
108
|
+
"@kobalte/core"
|
|
109
|
+
],
|
|
110
|
+
"registryDependencies": [
|
|
111
|
+
"kbd",
|
|
112
|
+
"input-group",
|
|
113
|
+
"list"
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"name": "dialog",
|
|
118
|
+
"title": "Dialog",
|
|
119
|
+
"description": "A modal window overlaying the main content, built on Kobalte primitives with blur and outside-click options.",
|
|
120
|
+
"type": "registry:ui",
|
|
121
|
+
"dependencies": [
|
|
122
|
+
"clsx",
|
|
123
|
+
"tailwind-merge",
|
|
124
|
+
"@kobalte/core"
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "dropdown-menu",
|
|
129
|
+
"title": "Dropdown Menu",
|
|
130
|
+
"description": "Displays a menu to the user—such as a set of actions or functions—triggered by a button or avatar.",
|
|
131
|
+
"type": "registry:ui",
|
|
132
|
+
"dependencies": [
|
|
133
|
+
"clsx",
|
|
134
|
+
"tailwind-merge",
|
|
135
|
+
"@kobalte/core"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "input-group",
|
|
140
|
+
"title": "Input Group",
|
|
141
|
+
"description": "Compound input wrapper for combining text inputs with prefix and suffix addons.",
|
|
142
|
+
"type": "registry:ui",
|
|
143
|
+
"dependencies": [
|
|
144
|
+
"clsx",
|
|
145
|
+
"tailwind-merge",
|
|
146
|
+
"class-variance-authority"
|
|
147
|
+
],
|
|
148
|
+
"registryDependencies": [
|
|
149
|
+
"kbd"
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "input",
|
|
154
|
+
"title": "Input",
|
|
155
|
+
"description": "A standard text input field with styling variants.",
|
|
156
|
+
"type": "registry:ui",
|
|
157
|
+
"dependencies": [
|
|
158
|
+
"clsx",
|
|
159
|
+
"tailwind-merge"
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "kbd",
|
|
164
|
+
"title": "Kbd (Keyboard Key)",
|
|
165
|
+
"description": "Keyboard key and shortcut group indicators for displaying hotkeys.",
|
|
166
|
+
"type": "registry:ui",
|
|
167
|
+
"dependencies": [
|
|
168
|
+
"clsx",
|
|
169
|
+
"tailwind-merge",
|
|
170
|
+
"class-variance-authority"
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "label",
|
|
175
|
+
"title": "Label",
|
|
176
|
+
"description": "Accessible caption label for form controls and inputs.",
|
|
177
|
+
"type": "registry:ui",
|
|
178
|
+
"dependencies": [
|
|
179
|
+
"clsx",
|
|
180
|
+
"tailwind-merge",
|
|
181
|
+
"class-variance-authority"
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "list",
|
|
186
|
+
"title": "List / List Item",
|
|
187
|
+
"description": "Compound list components supporting icons, avatars, titles, subtitles, hotkey badges, chevron indicators, and interactive links.",
|
|
188
|
+
"type": "registry:ui",
|
|
189
|
+
"dependencies": [
|
|
190
|
+
"clsx",
|
|
191
|
+
"tailwind-merge",
|
|
192
|
+
"class-variance-authority",
|
|
193
|
+
"lucide-solid",
|
|
194
|
+
"@kobalte/core"
|
|
195
|
+
]
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"name": "radio-group",
|
|
199
|
+
"title": "Radio Group",
|
|
200
|
+
"description": "A set of checkable buttons built on Kobalte primitives where only one button can be checked at a time.",
|
|
201
|
+
"type": "registry:ui",
|
|
202
|
+
"dependencies": [
|
|
203
|
+
"clsx",
|
|
204
|
+
"tailwind-merge",
|
|
205
|
+
"@kobalte/core"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "select",
|
|
210
|
+
"title": "Select",
|
|
211
|
+
"description": "Displays a list of options for the user to pick from, built on Kobalte primitives.",
|
|
212
|
+
"type": "registry:ui",
|
|
213
|
+
"dependencies": [
|
|
214
|
+
"clsx",
|
|
215
|
+
"tailwind-merge",
|
|
216
|
+
"@kobalte/core"
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "separator",
|
|
221
|
+
"title": "Separator",
|
|
222
|
+
"description": "Visually or semantically separates content horizontally or vertically.",
|
|
223
|
+
"type": "registry:ui",
|
|
224
|
+
"dependencies": [
|
|
225
|
+
"clsx",
|
|
226
|
+
"tailwind-merge"
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"name": "sheet",
|
|
231
|
+
"title": "Sheet / Drawer",
|
|
232
|
+
"description": "Extends the dialog component to display content that slides in from screen edges.",
|
|
233
|
+
"type": "registry:ui",
|
|
234
|
+
"dependencies": [
|
|
235
|
+
"clsx",
|
|
236
|
+
"tailwind-merge",
|
|
237
|
+
"class-variance-authority",
|
|
238
|
+
"@kobalte/core"
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"name": "skeleton",
|
|
243
|
+
"title": "Skeleton",
|
|
244
|
+
"description": "Renders an animated pulse loading placeholder for content loading states.",
|
|
245
|
+
"type": "registry:ui",
|
|
246
|
+
"dependencies": [
|
|
247
|
+
"clsx",
|
|
248
|
+
"tailwind-merge"
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"name": "switch",
|
|
253
|
+
"title": "Switch",
|
|
254
|
+
"description": "A control that allows the user to toggle between checked and unchecked states.",
|
|
255
|
+
"type": "registry:ui",
|
|
256
|
+
"dependencies": [
|
|
257
|
+
"clsx",
|
|
258
|
+
"tailwind-merge"
|
|
259
|
+
]
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "tabs",
|
|
263
|
+
"title": "Tabs",
|
|
264
|
+
"description": "A set of layered sections of content displayed one at a time.",
|
|
265
|
+
"type": "registry:ui",
|
|
266
|
+
"dependencies": [
|
|
267
|
+
"clsx",
|
|
268
|
+
"tailwind-merge"
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"name": "textarea",
|
|
273
|
+
"title": "Textarea",
|
|
274
|
+
"description": "A multi-line text input field with responsive focus styles.",
|
|
275
|
+
"type": "registry:ui",
|
|
276
|
+
"dependencies": [
|
|
277
|
+
"clsx",
|
|
278
|
+
"tailwind-merge"
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"name": "theme-manager",
|
|
283
|
+
"title": "Theme Manager",
|
|
284
|
+
"description": "Zero-dependency ThemeProvider and ThemeToggle component for switching light, dark, and system themes.",
|
|
285
|
+
"type": "registry:ui",
|
|
286
|
+
"dependencies": [
|
|
287
|
+
"clsx",
|
|
288
|
+
"tailwind-merge",
|
|
289
|
+
"@kobalte/core",
|
|
290
|
+
"lucide-solid"
|
|
291
|
+
],
|
|
292
|
+
"registryDependencies": [
|
|
293
|
+
"button",
|
|
294
|
+
"dropdown-menu"
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "input-group",
|
|
3
|
+
"title": "Input Group",
|
|
4
|
+
"description": "Compound input wrapper for combining text inputs with prefix and suffix addons.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"class-variance-authority"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"kbd"
|
|
13
|
+
],
|
|
14
|
+
"files": [
|
|
15
|
+
{
|
|
16
|
+
"path": "ui/input-group.tsx",
|
|
17
|
+
"content": "// src/components/ui/input-group.tsx\nimport { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\n\n/* --- Main InputGroup Wrapper --- */\nexport interface InputGroupProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const InputGroup: Component<InputGroupProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div\n class={cn(\n \"relative flex w-full items-center rounded-md border border-input bg-background text-sm shadow-2xs transition-colors focus-within:border-primary focus-within:ring-1 focus-within:ring-primary\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\n/* --- InputGroup Addon (Prefix / Suffix) --- */\nexport const addonVariants = cva(\n \"flex items-center shrink-0 text-muted-foreground select-none\",\n {\n variants: {\n align: {\n \"inline-start\": \"order-first pl-3 pr-1\",\n \"inline-end\": \"order-last pr-3 pl-1 gap-1\",\n },\n },\n defaultVariants: {\n align: \"inline-start\",\n },\n }\n);\n\nexport interface InputGroupAddonProps\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof addonVariants> {\n class?: string;\n}\n\nexport const InputGroupAddon: Component<InputGroupAddonProps> = (props) => {\n const [local, rest] = splitProps(props, [\"align\", \"class\", \"children\"]);\n\n return (\n <div\n class={cn(addonVariants({ align: local.align }), local.class)}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\n/* --- InputGroup Native Input --- */\nexport interface InputGroupInputProps\n extends JSX.InputHTMLAttributes<HTMLInputElement> {\n class?: string;\n}\n\nexport const InputGroupInput: Component<InputGroupInputProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <input\n class={cn(\n \"flex h-9 w-full flex-1 border-0 bg-transparent px-2.5 py-1 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n local.class\n )}\n {...rest}\n />\n );\n};",
|
|
18
|
+
"type": "registry:ui"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "input",
|
|
3
|
+
"title": "Input",
|
|
4
|
+
"description": "A standard text input field with styling variants.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "ui/input.tsx",
|
|
13
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Props interface for the Input component extending standard HTML input attributes.\n */\nexport interface InputProps extends JSX.InputHTMLAttributes<HTMLInputElement> {\n class?: string;\n}\n\n/**\n * Nikala UI Input component built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Input: Component<InputProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity for destructured props\n const [local, rest] = splitProps(props, [\"class\", \"type\"]);\n\n return (\n <input\n type={local.type || \"text\"}\n class={cn(\n \"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n local.class\n )}\n {...rest}\n />\n );\n};",
|
|
14
|
+
"type": "registry:ui"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kbd",
|
|
3
|
+
"title": "Kbd (Keyboard Key)",
|
|
4
|
+
"description": "Keyboard key and shortcut group indicators for displaying hotkeys.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"class-variance-authority"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "ui/kbd.tsx",
|
|
14
|
+
"content": "// src/components/ui/kbd.tsx\nimport { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\n\nexport const kbdVariants = cva(\n \"pointer-events-none inline-flex select-none items-center justify-center gap-1 rounded border border-border bg-muted font-mono font-medium text-muted-foreground shadow-2xs transition-colors\",\n {\n variants: {\n variant: {\n default: \"border-border bg-muted text-muted-foreground\",\n outline: \"border-border bg-transparent text-foreground\",\n },\n size: {\n sm: \"h-4 min-w-[16px] px-1 text-[9px]\",\n md: \"h-5 min-w-[20px] px-1.5 text-[10px]\",\n lg: \"h-6 min-w-[24px] px-2 text-xs\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"md\",\n },\n }\n);\n\nexport interface KbdProps\n extends JSX.HTMLAttributes<HTMLElement>,\n VariantProps<typeof kbdVariants> {\n class?: string;\n}\n\n/**\n * Nikala UI Kbd (Keyboard Key) Component.\n */\nexport const Kbd: Component<KbdProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"variant\",\n \"size\",\n \"class\",\n \"children\",\n ]);\n\n return (\n <kbd\n class={cn(\n kbdVariants({ variant: local.variant, size: local.size }),\n local.class\n )}\n {...rest}\n >\n {local.children}\n </kbd>\n );\n};\n\n/* --- Kbd Group Container --- */\nexport interface KbdGroupProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const KbdGroup: Component<KbdGroupProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div\n class={cn(\"inline-flex items-center gap-1\", local.class)}\n {...rest}\n >\n {local.children}\n </div>\n );\n};",
|
|
15
|
+
"type": "registry:ui"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "label",
|
|
3
|
+
"title": "Label",
|
|
4
|
+
"description": "Accessible caption label for form controls and inputs.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"class-variance-authority"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "ui/label.tsx",
|
|
14
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\n\nexport const labelVariants = cva(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-foreground select-none\"\n);\n\nexport interface LabelProps\n extends JSX.LabelHTMLAttributes<HTMLLabelElement>,\n VariantProps<typeof labelVariants> {\n class?: string;\n}\n\n/**\n * Nikala UI Label component for form controls built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Label: Component<LabelProps> = (props) => {\n /* Use splitProps to preserve SolidJS signal reactivity */\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <label\n class={cn(labelVariants(), local.class)}\n {...rest}\n >\n {local.children}\n </label>\n );\n};",
|
|
15
|
+
"type": "registry:ui"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "list",
|
|
3
|
+
"title": "List / List Item",
|
|
4
|
+
"description": "Compound list components supporting icons, avatars, titles, subtitles, hotkey badges, chevron indicators, and interactive links.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"class-variance-authority",
|
|
10
|
+
"lucide-solid",
|
|
11
|
+
"@kobalte/core"
|
|
12
|
+
],
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "ui/list.tsx",
|
|
16
|
+
"content": "// src/components/ui/list.tsx\nimport {\n splitProps,\n Show,\n type Component,\n type JSX,\n} from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { ChevronRight } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\n/* --- List Container --- */\nexport interface ListProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const List: Component<ListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n return (\n <div role=\"list\" class={cn(\"flex flex-col gap-1 w-full p-1\", local.class)} {...rest}>\n {local.children}\n </div>\n );\n};\n\n/* --- List Group Container --- */\nexport interface ListGroupProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const ListGroup: Component<ListGroupProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n return (\n <div class={cn(\"flex flex-col gap-1 w-full my-1\", local.class)} {...rest}>\n {local.children}\n </div>\n );\n};\n\n/* --- List Group Header --- */\nexport interface ListHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {\n title: string;\n class?: string;\n}\n\nexport const ListHeader: Component<ListHeaderProps> = (props) => {\n const [local, rest] = splitProps(props, [\"title\", \"class\"]);\n return (\n <div\n class={cn(\n \"px-2 py-1.5 text-xs font-semibold uppercase tracking-wider text-muted-foreground select-none\",\n local.class\n )}\n {...rest}\n >\n {local.title}\n </div>\n );\n};\n\n/* --- List Item Variants --- */\nexport const listItemVariants = cva(\n \"group relative flex w-full items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors outline-none cursor-pointer select-none disabled:pointer-events-none disabled:opacity-50 transform duration-150 ease-in-out\",\n {\n variants: {\n hoverVariant: {\n default:\n \"hover:bg-accent hover:text-accent-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground\",\n accent:\n \"hover:bg-accent/80 hover:text-accent-foreground data-highlighted:bg-accent/80\",\n primary:\n \"hover:bg-primary/10 hover:text-primary data-highlighted:bg-primary/10 data-highlighted:text-primary\",\n muted:\n \"hover:bg-muted hover:text-foreground data-highlighted:bg-muted\",\n },\n size: {\n sm: \"px-2.5 py-1.5 text-xs\",\n md: \"px-3 py-2 text-sm\",\n lg: \"px-4 py-3 text-base\",\n },\n active: {\n true: \"bg-accent text-accent-foreground font-semibold\",\n false: \"\",\n },\n },\n defaultVariants: {\n hoverVariant: \"default\",\n size: \"md\",\n active: false,\n },\n }\n);\n\nexport interface ListItemProps\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof listItemVariants> {\n title?: string;\n subtitle?: string;\n avatar?: string;\n avatarFallback?: string;\n icon?: Component<{ class?: string }>;\n shortcut?: string;\n showChevron?: boolean;\n href?: string;\n disabled?: boolean;\n active?: boolean;\n class?: string;\n}\n\n/**\n * Nikala UI ListItem Component.\n */\nexport const ListItem: Component<ListItemProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"title\",\n \"subtitle\",\n \"avatar\",\n \"avatarFallback\",\n \"icon\",\n \"shortcut\",\n \"showChevron\",\n \"href\",\n \"disabled\",\n \"active\",\n \"hoverVariant\",\n \"size\",\n \"class\",\n \"children\",\n ]);\n\n const content = () => (\n <>\n {/* Left Visual: Icon, Avatar, or Image */}\n <div class=\"flex items-center gap-2.5 min-w-0\">\n <Show when={local.avatar}>\n <img\n src={local.avatar}\n alt={local.title || \"Avatar\"}\n class=\"w-6 h-6 rounded-full object-cover shrink-0 border border-border\"\n />\n </Show>\n\n <Show when={!local.avatar && local.avatarFallback}>\n <span class=\"w-6 h-6 rounded-full bg-primary/10 text-primary text-[10px] font-bold flex items-center justify-center shrink-0 uppercase\">\n {local.avatarFallback}\n </span>\n </Show>\n\n <Show when={!local.avatar && !local.avatarFallback && local.icon}>\n <span class=\"shrink-0 text-muted-foreground group-hover:text-foreground transition-colors\">\n <Dynamic component={local.icon} class=\"w-4 h-4\" />\n </span>\n </Show>\n\n {/* Main Text Content */}\n <div class=\"flex flex-col min-w-0 text-left\">\n <Show when={local.title}>\n <span class=\"truncate font-medium leading-none\">\n {local.title}\n </span>\n </Show>\n <Show when={local.subtitle}>\n <span class=\"truncate text-xs text-muted-foreground font-normal mt-1\">\n {local.subtitle}\n </span>\n </Show>\n {local.children}\n </div>\n </div>\n\n {/* Right Visual: Shortcut Badge & Trailing Chevron */}\n <div class=\"flex items-center gap-2 shrink-0\">\n <Show when={local.shortcut}>\n <kbd class=\"pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground\">\n {local.shortcut}\n </kbd>\n </Show>\n\n <Show when={local.showChevron}>\n <ChevronRight class=\"w-4 h-4 text-muted-foreground group-hover:text-foreground transition-colors\" />\n </Show>\n </div>\n </>\n );\n\n const itemClasses = () =>\n cn(\n listItemVariants({\n hoverVariant: local.hoverVariant,\n size: local.size,\n active: local.active,\n }),\n local.class\n );\n\n return (\n <Show\n when={local.href}\n fallback={\n <div\n role=\"listitem\"\n class={itemClasses()}\n aria-disabled={local.disabled}\n {...rest}\n >\n {content()}\n </div>\n }\n >\n <a\n role=\"listitem\"\n href={local.href}\n class={itemClasses()}\n aria-disabled={local.disabled}\n {...(rest as JSX.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n {content()}\n </a>\n </Show>\n );\n};",
|
|
17
|
+
"type": "registry:ui"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "radio-group",
|
|
3
|
+
"title": "Radio Group",
|
|
4
|
+
"description": "A set of checkable buttons built on Kobalte primitives where only one button can be checked at a time.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"@kobalte/core"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "ui/radio-group.tsx",
|
|
14
|
+
"content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport * as RadioGroupPrimitive from \"@kobalte/core/radio-group\";\nimport type { PolymorphicProps } from \"@kobalte/core/polymorphic\";\nimport { cn } from \"@/lib/cn\";\n\nexport type RadioGroupRootProps<T extends ValidComponent = \"div\"> =\n RadioGroupPrimitive.RadioGroupRootProps<T> & {\n class?: string;\n };\n\n/**\n * Root RadioGroup component built on top of Kobalte headless primitives.\n * Supports both \"vertical\" (default) and \"horizontal\" layout orientations.\n */\nexport const RadioGroup = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, RadioGroupRootProps<T>>\n) => {\n const [local, rest] = splitProps(props as RadioGroupRootProps, [\"class\", \"orientation\"]);\n\n const isHorizontal = () => local.orientation === \"horizontal\";\n\n return (\n <RadioGroupPrimitive.Root\n orientation={local.orientation || \"vertical\"}\n class={cn(\n isHorizontal()\n ? \"flex flex-row items-center gap-4\"\n : \"grid gap-2\",\n local.class\n )}\n {...(rest as any)}\n />\n );\n};\n\nexport type RadioGroupItemProps<T extends ValidComponent = \"div\"> =\n RadioGroupPrimitive.RadioGroupItemProps<T> & {\n class?: string;\n children?: JSX.Element;\n id?: string;\n value: string;\n };\n\n/**\n * Accessible RadioGroup item choice wrapping Kobalte primitives.\n */\nexport const RadioGroupItem = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, RadioGroupItemProps<T>>\n) => {\n const [local, rest] = splitProps(props as RadioGroupItemProps, [\"class\", \"children\"]);\n\n return (\n <RadioGroupPrimitive.Item\n class={cn(\"flex items-center space-x-2 cursor-pointer\", local.class)}\n {...rest}\n >\n <RadioGroupPrimitive.ItemInput />\n <RadioGroupPrimitive.ItemControl\n class={cn(\n \"aspect-square h-4 w-4 rounded-lg border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 flex items-center justify-center transition-colors data-[checked]:bg-primary data-[checked]:text-primary-foreground\"\n )}\n >\n <RadioGroupPrimitive.ItemIndicator class=\"flex items-center justify-center\">\n <span class=\"h-2 w-2 rounded-lg bg-primary-foreground\" />\n </RadioGroupPrimitive.ItemIndicator>\n </RadioGroupPrimitive.ItemControl>\n {local.children}\n </RadioGroupPrimitive.Item>\n );\n};\n\nexport type RadioGroupItemLabelProps<T extends ValidComponent = \"label\"> =\n RadioGroupPrimitive.RadioGroupItemLabelProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Accessible text label for a RadioGroup item.\n */\nexport const RadioGroupItemLabel = <T extends ValidComponent = \"label\">(\n props: PolymorphicProps<T, RadioGroupItemLabelProps<T>>\n) => {\n const [local, rest] = splitProps(props as RadioGroupItemLabelProps, [\"class\"]);\n\n return (\n <RadioGroupPrimitive.ItemLabel\n class={cn(\n \"text-sm font-medium leading-none select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-foreground\",\n local.class\n )}\n {...rest}\n />\n );\n};",
|
|
15
|
+
"type": "registry:ui"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "select",
|
|
3
|
+
"title": "Select",
|
|
4
|
+
"description": "Displays a list of options for the user to pick from, built on Kobalte primitives.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"@kobalte/core"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "ui/select.tsx",
|
|
14
|
+
"content": "import { splitProps, type JSX, type ValidComponent } from \"solid-js\";\nimport * as SelectPrimitive from \"@kobalte/core/select\";\nimport type { PolymorphicProps } from \"@kobalte/core/polymorphic\";\nimport { cn } from \"@/lib/cn\";\n\nexport type SelectRootProps<Option = any, OptGroup = any, T extends ValidComponent = \"div\"> =\n SelectPrimitive.SelectRootProps<Option, OptGroup, T> & {\n class?: string;\n };\n\n/**\n * Root Select component built on top of Kobalte headless primitives.\n */\nexport const Select = <Option = any, OptGroup = any, T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SelectRootProps<Option, OptGroup, T>>\n) => {\n const [local, rest] = splitProps(props as SelectRootProps, [\"class\"]);\n\n return <SelectPrimitive.Root class={cn(\"relative w-full\", local.class)} {...(rest as any)} />;\n};\n\nexport type SelectTriggerProps<T extends ValidComponent = \"button\"> =\n SelectPrimitive.SelectTriggerProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Trigger button opening the Select options list.\n */\nexport const SelectTrigger = <T extends ValidComponent = \"button\">(\n props: PolymorphicProps<T, SelectTriggerProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectTriggerProps, [\"class\", \"children\"]);\n\n return (\n <SelectPrimitive.Trigger\n class={cn(\n \"flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer text-foreground\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n <SelectPrimitive.Icon\n as=\"svg\"\n class=\"h-4 w-4 opacity-50 transition-transform\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n};\n\nexport type SelectValueProps<Option, T extends ValidComponent = \"span\"> =\n SelectPrimitive.SelectValueProps<Option, T> & {\n class?: string;\n };\n\n/**\n * Renders the currently selected option text or placeholder.\n */\nexport const SelectValue = <Option = any, T extends ValidComponent = \"span\">(\n props: PolymorphicProps<T, SelectValueProps<Option, T>>\n) => {\n const [local, rest] = splitProps(props as SelectValueProps<Option>, [\"class\"]);\n\n return (\n <SelectPrimitive.Value\n class={cn(\"block truncate\", local.class)}\n {...(rest as any)}\n />\n );\n};\n\nexport type SelectContentProps<T extends ValidComponent = \"div\"> =\n SelectPrimitive.SelectContentProps<T> & {\n class?: string;\n };\n\n/**\n * Portaled overlay container rendering the listbox options.\n */\nexport const SelectContent = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SelectContentProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectContentProps, [\"class\"]);\n\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n class={cn(\n \"relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md animate-in fade-in-80\",\n local.class\n )}\n {...rest}\n >\n <SelectPrimitive.Listbox class=\"p-1 outline-none\" />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n};\n\nexport type SelectItemProps<T extends ValidComponent = \"li\"> =\n SelectPrimitive.SelectItemProps<T> & {\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Individual option item choice inside SelectContent.\n */\nexport const SelectItem = <T extends ValidComponent = \"li\">(\n props: PolymorphicProps<T, SelectItemProps<T>>\n) => {\n const [local, rest] = splitProps(props as SelectItemProps, [\"class\", \"children\"]);\n\n return (\n <SelectPrimitive.Item\n class={cn(\n \"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground\",\n local.class\n )}\n {...rest}\n >\n <SelectPrimitive.ItemIndicator class=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <svg class=\"h-4 w-4 fill-none stroke-current stroke-2\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n </SelectPrimitive.ItemIndicator>\n <SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel>\n </SelectPrimitive.Item>\n );\n};",
|
|
15
|
+
"type": "registry:ui"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "separator",
|
|
3
|
+
"title": "Separator",
|
|
4
|
+
"description": "Visually or semantically separates content horizontally or vertically.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "ui/separator.tsx",
|
|
13
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SeparatorProps extends JSX.HTMLAttributes<HTMLDivElement> {\n /** Orientation of the separator line */\n orientation?: \"horizontal\" | \"vertical\";\n /** Whether the element is purely visual or semantic for screen readers */\n decorative?: boolean;\n class?: string;\n}\n\n/**\n * Nikala UI Separator component used to divide content visually or semantically.\n */\nexport const Separator: Component<SeparatorProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"orientation\",\n \"decorative\",\n \"class\",\n ]);\n\n const orientation = () => local.orientation || \"horizontal\";\n const isDecorative = () => local.decorative ?? true;\n\n return (\n <div\n role={isDecorative() ? \"none\" : \"separator\"}\n aria-orientation={isDecorative() ? undefined : orientation()}\n class={cn(\n \"shrink-0 bg-border\",\n orientation() === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",\n local.class\n )}\n {...rest}\n />\n );\n};",
|
|
14
|
+
"type": "registry:ui"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sheet",
|
|
3
|
+
"title": "Sheet / Drawer",
|
|
4
|
+
"description": "Extends the dialog component to display content that slides in from screen edges.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge",
|
|
9
|
+
"class-variance-authority",
|
|
10
|
+
"@kobalte/core"
|
|
11
|
+
],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "ui/sheet.tsx",
|
|
15
|
+
"content": "import { splitProps, type Component, type JSX, type ValidComponent, Show } from \"solid-js\";\nimport * as DialogPrimitive from \"@kobalte/core/dialog\";\nimport type { PolymorphicProps } from \"@kobalte/core/polymorphic\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/cn\";\n\n// Global CSS Keyframe Animations specifically designed for Kobalte animationend DOM events\nconst sheetStyles = `\n@keyframes sheet-slide-in-left { from { transform: translateX(-100%); } to { transform: translateX(0); } }\n@keyframes sheet-slide-out-left { from { transform: translateX(0); } to { transform: translateX(-100%); } }\n@keyframes sheet-slide-in-right { from { transform: translateX(100%); } to { transform: translateX(0); } }\n@keyframes sheet-slide-out-right { from { transform: translateX(0); } to { transform: translateX(100%); } }\n@keyframes sheet-slide-in-top { from { transform: translateY(-100%); } to { transform: translateY(0); } }\n@keyframes sheet-slide-out-top { from { transform: translateY(0); } to { transform: translateY(-100%); } }\n@keyframes sheet-slide-in-bottom { from { transform: translateY(100%); } to { transform: translateY(0); } }\n@keyframes sheet-slide-out-bottom { from { transform: translateY(0); } to { transform: translateY(100%); } }\n@keyframes sheet-fade-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes sheet-fade-out { from { opacity: 1; } to { opacity: 0; } }\n`;\n\n/**\n * CVA variants for CSS keyframe slide animations across 4 edges.\n */\nexport const sheetVariants = cva(\n \"fixed z-50 gap-4 bg-card p-6 text-card-foreground shadow-lg\",\n {\n variants: {\n side: {\n left: \"fixed top-0 bottom-0 left-0 w-3/4 border-r border-border sm:max-w-sm data-[expanded]:animate-[sheet-slide-in-left_300ms_ease-in-out] data-[closed]:animate-[sheet-slide-out-left_300ms_ease-in-out]\",\n right: \"fixed top-0 bottom-0 right-0 w-3/4 border-l border-border sm:max-w-sm data-[expanded]:animate-[sheet-slide-in-right_300ms_ease-in-out] data-[closed]:animate-[sheet-slide-out-right_300ms_ease-in-out]\",\n top: \"fixed top-0 left-0 right-0 border-b border-border data-[expanded]:animate-[sheet-slide-in-top_300ms_ease-in-out] data-[closed]:animate-[sheet-slide-out-top_300ms_ease-in-out]\",\n bottom: \"fixed bottom-0 left-0 right-0 border-t border-border data-[expanded]:animate-[sheet-slide-in-bottom_300ms_ease-in-out] data-[closed]:animate-[sheet-slide-out-bottom_300ms_ease-in-out]\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n }\n);\n\nexport type SheetRootProps = DialogPrimitive.DialogRootProps;\n\nexport const Sheet: Component<SheetRootProps> = (props) => {\n return <DialogPrimitive.Root {...props} />;\n};\n\nexport const SheetTrigger = DialogPrimitive.Trigger;\nexport const SheetClose = DialogPrimitive.CloseButton;\n\nexport interface SheetOverlayProps<T extends ValidComponent = \"div\"> {\n /** Whether to apply background blur effect or keep transparent (default: true) */\n blur?: boolean;\n class?: string;\n}\n\n/**\n * Backdrop overlay wrapper with fade-in and fade-out animations.\n */\nexport const SheetOverlay = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SheetOverlayProps<T>>\n) => {\n const [local, rest] = splitProps(props as SheetOverlayProps, [\"class\", \"blur\"]);\n\n return (\n <DialogPrimitive.Overlay\n class={cn(\n \"fixed inset-0 z-50 data-[expanded]:animate-[sheet-fade-in_300ms_ease-in-out] data-[closed]:animate-[sheet-fade-out_300ms_ease-in-out]\",\n local.blur !== false ? \"bg-black/80 backdrop-blur-sm\" : \"bg-transparent\",\n local.class\n )}\n {...(rest as any)}\n />\n );\n};\n\nexport type SheetContentProps<T extends ValidComponent = \"div\"> =\n DialogPrimitive.DialogContentProps<T> &\n VariantProps<typeof sheetVariants> & {\n /** Direction from which the sheet slides out: top, bottom, left, right */\n side?: \"top\" | \"bottom\" | \"left\" | \"right\";\n /** Whether to display the top-right close (X) button (default: true) */\n showCloseButton?: boolean;\n /** Whether clicking outside closes the sheet (default: true) */\n closeOnOutsideClick?: boolean;\n /** Whether to apply background backdrop blur (default: true) */\n blur?: boolean;\n class?: string;\n children?: JSX.Element;\n };\n\n/**\n * Main sheet container with smooth CSS keyframe slide animations.\n */\nexport const SheetContent = <T extends ValidComponent = \"div\">(\n props: PolymorphicProps<T, SheetContentProps<T>>\n) => {\n const [local, rest] = splitProps(props as SheetContentProps, [\n \"side\",\n \"showCloseButton\",\n \"closeOnOutsideClick\",\n \"blur\",\n \"class\",\n \"children\",\n \"onPointerDownOutside\",\n \"onInteractOutside\",\n ]);\n\n const side = () => local.side || \"right\";\n\n const handlePointerDownOutside = (e: Event) => {\n if (local.closeOnOutsideClick === false) {\n e.preventDefault();\n }\n if (typeof local.onPointerDownOutside === \"function\") {\n local.onPointerDownOutside(e as any);\n }\n };\n\n const handleInteractOutside = (e: Event) => {\n if (local.closeOnOutsideClick === false) {\n e.preventDefault();\n }\n if (typeof local.onInteractOutside === \"function\") {\n local.onInteractOutside(e as any);\n }\n };\n\n return (\n <DialogPrimitive.Portal>\n <style>{sheetStyles}</style>\n <SheetOverlay blur={local.blur} />\n <DialogPrimitive.Content\n onPointerDownOutside={handlePointerDownOutside}\n onInteractOutside={handleInteractOutside}\n class={cn(sheetVariants({ side: side() }), local.class)}\n {...(rest as any)}\n >\n {local.children}\n <Show when={local.showCloseButton !== false}>\n <DialogPrimitive.CloseButton class=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none cursor-pointer\">\n <svg class=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n <span class=\"sr-only\">Close</span>\n </DialogPrimitive.CloseButton>\n </Show>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n};\n\nexport interface SheetHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const SheetHeader: Component<SheetHeaderProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\"flex flex-col space-y-2 text-center sm:text-left\", local.class)}\n {...rest}\n />\n );\n};\n\nexport interface SheetFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const SheetFooter: Component<SheetFooterProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\", local.class)}\n {...rest}\n />\n );\n};\n\nexport type SheetTitleProps<T extends ValidComponent = \"h2\"> =\n DialogPrimitive.DialogTitleProps<T> & {\n class?: string;\n };\n\nexport const SheetTitle = <T extends ValidComponent = \"h2\">(\n props: PolymorphicProps<T, SheetTitleProps<T>>\n) => {\n const [local, rest] = splitProps(props as SheetTitleProps, [\"class\"]);\n\n return (\n <DialogPrimitive.Title\n class={cn(\"text-lg font-semibold text-foreground\", local.class)}\n {...(rest as any)}\n />\n );\n};\n\nexport type SheetDescriptionProps<T extends ValidComponent = \"p\"> =\n DialogPrimitive.DialogDescriptionProps<T> & {\n class?: string;\n };\n\nexport const SheetDescription = <T extends ValidComponent = \"p\">(\n props: PolymorphicProps<T, SheetDescriptionProps<T>>\n) => {\n const [local, rest] = splitProps(props as SheetDescriptionProps, [\"class\"]);\n\n return (\n <DialogPrimitive.Description\n class={cn(\"text-sm text-muted-foreground\", local.class)}\n {...(rest as any)}\n />\n );\n};",
|
|
16
|
+
"type": "registry:ui"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skeleton",
|
|
3
|
+
"title": "Skeleton",
|
|
4
|
+
"description": "Renders an animated pulse loading placeholder for content loading states.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "ui/skeleton.tsx",
|
|
13
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SkeletonProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Nikala UI Skeleton component for rendering animated pulse loading placeholders.\n */\nexport const Skeleton: Component<SkeletonProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\n \"animate-pulse rounded-md bg-muted\",\n local.class\n )}\n {...rest}\n />\n );\n};",
|
|
14
|
+
"type": "registry:ui"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "switch",
|
|
3
|
+
"title": "Switch",
|
|
4
|
+
"description": "A control that allows the user to toggle between checked and unchecked states.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "ui/switch.tsx",
|
|
13
|
+
"content": "import { createSignal, splitProps, type Component, type JSX } from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SwitchProps\n extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, \"onChange\"> {\n /** Controlled checked state */\n checked?: boolean;\n /** Uncontrolled default checked state */\n defaultChecked?: boolean;\n /** Event handler triggered when toggle state changes */\n onChange?: (checked: boolean) => void;\n class?: string;\n}\n\n/**\n * Nikala UI Switch component for boolean toggle inputs built for SolidJS with Tailwind CSS v4 styling.\n */\nexport const Switch: Component<SwitchProps> = (props) => {\n // Use splitProps to preserve SolidJS reactivity\n const [local, rest] = splitProps(props, [\n \"checked\",\n \"defaultChecked\",\n \"onChange\",\n \"disabled\",\n \"class\",\n \"onClick\",\n ]);\n\n // Internal state for uncontrolled mode\n const [internalChecked, setInternalChecked] = createSignal(\n local.defaultChecked ?? false\n );\n\n // Computed checked state supporting both controlled and uncontrolled modes\n const isChecked = () =>\n local.checked !== undefined ? local.checked : internalChecked();\n\n const toggle = (\n e: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }\n ) => {\n if (local.disabled) return;\n\n const nextState = !isChecked();\n if (local.checked === undefined) {\n setInternalChecked(nextState);\n }\n\n if (typeof local.onChange === \"function\") {\n local.onChange(nextState);\n }\n\n if (typeof local.onClick === \"function\") {\n local.onClick(e);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isChecked()}\n data-state={isChecked() ? \"checked\" : \"unchecked\"}\n disabled={local.disabled}\n onClick={toggle}\n class={cn(\n \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-lg border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n local.class\n )}\n {...rest}\n >\n <span\n data-state={isChecked() ? \"checked\" : \"unchecked\"}\n class={cn(\n \"pointer-events-none block h-4 w-4 rounded-sm bg-primary-foreground shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0\"\n )}\n />\n </button>\n );\n};",
|
|
14
|
+
"type": "registry:ui"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tabs",
|
|
3
|
+
"title": "Tabs",
|
|
4
|
+
"description": "A set of layered sections of content displayed one at a time.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "ui/tabs.tsx",
|
|
13
|
+
"content": "import {\n createContext,\n useContext,\n createSignal,\n splitProps,\n Show,\n type Component,\n type JSX,\n type Accessor,\n} from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\ninterface TabsContextValue {\n value: Accessor<string | undefined>;\n setValue: (value: string) => void;\n orientation: Accessor<\"horizontal\" | \"vertical\">;\n}\n\nconst TabsContext = createContext<TabsContextValue>();\n\nexport interface TabsProps\n extends Omit<JSX.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** Controlled active tab value */\n value?: string;\n /** Uncontrolled default active tab value */\n defaultValue?: string;\n /** Callback fired when active tab changes */\n onChange?: (value: string) => void;\n /** Layout orientation of tab triggers and content */\n orientation?: \"horizontal\" | \"vertical\";\n class?: string;\n}\n\n/**\n * Root Tabs container component managing active tab context state and orientation.\n */\nexport const Tabs: Component<TabsProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"defaultValue\",\n \"onChange\",\n \"orientation\",\n \"class\",\n \"children\",\n ]);\n\n const [internalValue, setInternalValue] = createSignal(local.defaultValue);\n\n const currentValue = () =>\n local.value !== undefined ? local.value : internalValue();\n\n const orientation = () => local.orientation || \"horizontal\";\n\n const handleSelect = (val: string) => {\n if (local.value === undefined) {\n setInternalValue(val);\n }\n if (typeof local.onChange === \"function\") {\n local.onChange(val);\n }\n };\n\n const contextValue: TabsContextValue = {\n value: currentValue,\n setValue: handleSelect,\n orientation,\n };\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n data-orientation={orientation()}\n class={cn(\n \"w-full\",\n orientation() === \"vertical\" ? \"flex flex-row gap-4\" : \"flex flex-col gap-2\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </TabsContext.Provider>\n );\n};\n\nexport interface TabsListProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\n/**\n * Container wrapper for Tab triggers.\n */\nexport const TabsList: Component<TabsListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const context = useContext(TabsContext);\n\n const isVertical = () => context?.orientation() === \"vertical\";\n\n return (\n <div\n role=\"tablist\"\n aria-orientation={context?.orientation() || \"horizontal\"}\n class={cn(\n \"inline-flex rounded-lg bg-muted p-1 text-muted-foreground\",\n isVertical()\n ? \"flex-col h-auto w-auto items-stretch justify-start\"\n : \"h-9 items-center justify-center\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\nexport interface TabsTriggerProps\n extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, \"onChange\"> {\n /** Unique value identifier for this tab */\n value: string;\n class?: string;\n}\n\n/**\n * Tab button trigger to activate a specific tab panel.\n */\nexport const TabsTrigger: Component<TabsTriggerProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"disabled\",\n \"class\",\n \"children\",\n \"onClick\",\n ]);\n const context = useContext(TabsContext);\n\n if (!context) {\n throw new Error(\"TabsTrigger must be used within a Tabs component\");\n }\n\n const isSelected = () => context.value() === local.value;\n const isVertical = () => context.orientation() === \"vertical\";\n\n const handleClick = (\n e: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }\n ) => {\n if (local.disabled) return;\n context.setValue(local.value);\n if (typeof local.onClick === \"function\") {\n local.onClick(e);\n }\n };\n\n return (\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={isSelected()}\n data-state={isSelected() ? \"active\" : \"inactive\"}\n data-orientation={context.orientation()}\n disabled={local.disabled}\n onClick={handleClick}\n class={cn(\n \"inline-flex items-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm cursor-pointer\",\n isVertical() ? \"justify-start py-1.5\" : \"justify-center\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </button>\n );\n};\n\nexport interface TabsContentProps extends JSX.HTMLAttributes<HTMLDivElement> {\n /** Value matching the corresponding tab trigger */\n value: string;\n class?: string;\n}\n\n/**\n * Content panel revealed when the associated tab is active.\n */\nexport const TabsContent: Component<TabsContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"value\", \"class\", \"children\"]);\n const context = useContext(TabsContext);\n\n if (!context) {\n throw new Error(\"TabsContent must be used within a Tabs component\");\n }\n\n const isSelected = () => context.value() === local.value;\n const isVertical = () => context.orientation() === \"vertical\";\n\n return (\n <Show when={isSelected()}>\n <div\n role=\"tabpanel\"\n data-state={isSelected() ? \"active\" : \"inactive\"}\n data-orientation={context.orientation()}\n class={cn(\n \"ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n isVertical() ? \"flex-1 mt-0\" : \"mt-2\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </Show>\n );\n};",
|
|
14
|
+
"type": "registry:ui"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "textarea",
|
|
3
|
+
"title": "Textarea",
|
|
4
|
+
"description": "A multi-line text input field with responsive focus styles.",
|
|
5
|
+
"type": "registry:ui",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
"clsx",
|
|
8
|
+
"tailwind-merge"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
{
|
|
12
|
+
"path": "ui/textarea.tsx",
|
|
13
|
+
"content": "// src/components/ui/textarea.tsx\nimport {\n createSignal,\n splitProps,\n Show,\n type Component,\n type JSX,\n} from \"solid-js\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface TextareaProps\n extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {\n /** Uncontrolled initial default value */\n defaultValue?: string | number;\n /** Maximum allowed character limit */\n maxLength?: number;\n /** Whether to show live character count badge */\n showCount?: boolean;\n class?: string;\n}\n\n/**\n * Nikala UI Textarea component with optional live character counter and limit indicators.\n */\nexport const Textarea: Component<TextareaProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"maxLength\",\n \"showCount\",\n \"value\",\n \"defaultValue\",\n \"onInput\",\n \"class\",\n ]);\n\n /* Internal reactive signal for character count tracking */\n const [currentValue, setCurrentValue] = createSignal<string>(\n String(local.value || local.defaultValue || \"\")\n );\n\n const handleInput: JSX.EventHandlerUnion<HTMLTextAreaElement, InputEvent> = (\n e\n ) => {\n setCurrentValue(e.currentTarget.value);\n if (typeof local.onInput === \"function\") {\n (local.onInput as (e: InputEvent) => void)(e);\n }\n };\n\n const count = () => currentValue().length;\n const isAtLimit = () =>\n local.maxLength !== undefined && count() >= local.maxLength;\n\n return (\n <div class=\"relative flex flex-col w-full\">\n <textarea\n value={local.value !== undefined ? String(local.value) : currentValue()}\n maxLength={local.maxLength}\n onInput={handleInput}\n class={cn(\n \"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-2xs transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n local.class\n )}\n {...rest}\n />\n\n {/* Dynamic Character Counter */}\n <Show when={local.showCount ?? Boolean(local.maxLength)}>\n <div\n class={cn(\n \"mt-1 text-right text-[11px] font-mono transition-colors select-none\",\n isAtLimit() ? \"text-rose-500 font-bold\" : \"text-muted-foreground\"\n )}\n >\n <span>{count()}</span>\n <Show when={local.maxLength}>\n <span> / {local.maxLength}</span>\n </Show>\n </div>\n </Show>\n </div>\n );\n};",
|
|
14
|
+
"type": "registry:ui"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|