@nikala-ui/core 0.11.0 → 0.12.1
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/LICENSE +20 -0
- package/README.md +97 -20
- package/package.json +43 -2
- package/registry/api-table.json +21 -0
- package/registry/banner.json +1 -1
- package/registry/button.json +1 -1
- package/registry/callout.json +19 -0
- package/registry/checkbox.json +4 -1
- package/registry/code-block.json +26 -0
- package/registry/code-group.json +20 -0
- package/registry/combobox.json +1 -1
- package/registry/command.json +3 -2
- package/registry/component-viewer.json +25 -0
- package/registry/container.json +18 -0
- package/registry/context-menu.json +1 -1
- package/registry/create-app-updater.json +13 -0
- package/registry/create-document-tabs.json +13 -0
- package/registry/create-global-shortcut.json +13 -0
- package/registry/create-tauri-window.json +13 -0
- package/registry/create-tiptap-editor.json +34 -0
- package/registry/dialog.json +1 -1
- package/registry/dropdown-menu.json +3 -2
- package/registry/field.json +1 -1
- package/registry/file-tree.json +21 -0
- package/registry/footer.json +1 -1
- package/registry/form-message.json +3 -2
- package/registry/icon-button.json +1 -1
- package/registry/index.json +336 -5
- package/registry/navbar.json +1 -1
- package/registry/navigation-menu.json +1 -1
- package/registry/number-input.json +1 -1
- package/registry/package-manager-tabs.json +24 -0
- package/registry/pager.json +21 -0
- package/registry/popover.json +4 -1
- package/registry/resizable.json +1 -1
- package/registry/rich-text-editor.json +95 -0
- package/registry/scroll-area.json +1 -1
- package/registry/section-heading.json +21 -0
- package/registry/select.json +3 -2
- package/registry/sheet.json +1 -1
- package/registry/sidebar.json +1 -1
- package/registry/status.json +1 -1
- package/registry/steps.json +20 -0
- package/registry/switch.json +4 -1
- package/registry/table-of-contents.json +23 -0
- package/registry/tabs.json +4 -1
- package/registry/theme-manager.json +6 -5
- package/registry/titlebar-tabs.json +24 -0
- package/registry/titlebar.json +26 -0
- package/registry/toggle-group.json +1 -1
- package/registry/updater-modal.json +26 -0
- package/src/index.ts +48 -0
- package/src/registry/components/ui/api-table.tsx +117 -0
- package/src/registry/components/ui/banner.tsx +0 -2
- package/src/registry/components/ui/button.tsx +1 -1
- package/src/registry/components/ui/callout.tsx +137 -0
- package/src/registry/components/ui/checkbox.tsx +1 -1
- package/src/registry/components/ui/code-block.tsx +326 -0
- package/src/registry/components/ui/code-group.tsx +105 -0
- package/src/registry/components/ui/combobox.tsx +83 -18
- package/src/registry/components/ui/command.tsx +110 -106
- package/src/registry/components/ui/component-viewer.tsx +363 -0
- package/src/registry/components/ui/container.tsx +45 -0
- package/src/registry/components/ui/context-menu.tsx +67 -22
- package/src/registry/components/ui/dialog.tsx +42 -32
- package/src/registry/components/ui/dropdown-menu.tsx +69 -31
- package/src/registry/components/ui/field.tsx +7 -3
- package/src/registry/components/ui/file-tree.tsx +181 -0
- package/src/registry/components/ui/footer.tsx +1 -1
- package/src/registry/components/ui/form-message.tsx +2 -2
- package/src/registry/components/ui/icon-button.tsx +1 -1
- package/src/registry/components/ui/navbar.tsx +18 -11
- package/src/registry/components/ui/navigation-menu.tsx +2 -2
- package/src/registry/components/ui/number-input.tsx +3 -3
- package/src/registry/components/ui/package-manager-tabs.tsx +226 -0
- package/src/registry/components/ui/pager.tsx +102 -0
- package/src/registry/components/ui/popover.tsx +1 -1
- package/src/registry/components/ui/resizable.tsx +3 -1
- package/src/registry/components/ui/rich-text-editor/bubble-menu.tsx +236 -0
- package/src/registry/components/ui/rich-text-editor/editor.tsx +308 -0
- package/src/registry/components/ui/rich-text-editor/extensions.ts +116 -0
- package/src/registry/components/ui/rich-text-editor/footer.tsx +36 -0
- package/src/registry/components/ui/rich-text-editor/image-dialog.tsx +75 -0
- package/src/registry/components/ui/rich-text-editor/index.ts +11 -0
- package/src/registry/components/ui/rich-text-editor/link-dialog.tsx +48 -0
- package/src/registry/components/ui/rich-text-editor/markdown.ts +170 -0
- package/src/registry/components/ui/rich-text-editor/slash-command.tsx +301 -0
- package/src/registry/components/ui/rich-text-editor/table-controls.tsx +68 -0
- package/src/registry/components/ui/rich-text-editor/toolbar.tsx +465 -0
- package/src/registry/components/ui/rich-text-editor/use-editor.ts +231 -0
- package/src/registry/components/ui/scroll-area.tsx +12 -2
- package/src/registry/components/ui/section-heading.tsx +108 -0
- package/src/registry/components/ui/select.tsx +16 -19
- package/src/registry/components/ui/sheet.tsx +58 -53
- package/src/registry/components/ui/sidebar.tsx +4 -4
- package/src/registry/components/ui/status.tsx +7 -5
- package/src/registry/components/ui/steps.tsx +198 -0
- package/src/registry/components/ui/switch.tsx +1 -1
- package/src/registry/components/ui/table-of-contents.tsx +131 -0
- package/src/registry/components/ui/tabs.tsx +1 -1
- package/src/registry/components/ui/theme-toggle.tsx +175 -139
- package/src/registry/components/ui/titlebar-tabs.tsx +773 -0
- package/src/registry/components/ui/titlebar.tsx +468 -0
- package/src/registry/components/ui/toggle-group.tsx +1 -1
- package/src/registry/components/ui/updater-modal.tsx +254 -0
- package/src/registry/metadata.ts +167 -6
- package/src/registry/providers/theme-provider.tsx +16 -8
- package/src/registry/providers/theme-script.tsx +29 -9
- package/src/registry/providers/theme-transitions.ts +81 -50
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
"description": "Validation message helper connected to createForm errors and touched state.",
|
|
5
5
|
"type": "registry:ui",
|
|
6
6
|
"registryDependencies": [
|
|
7
|
-
"field"
|
|
7
|
+
"field",
|
|
8
|
+
"create-form"
|
|
8
9
|
],
|
|
9
10
|
"files": [
|
|
10
11
|
{
|
|
11
12
|
"path": "ui/form-message.tsx",
|
|
12
|
-
"content": "import { Show, splitProps, type JSX } from \"solid-js\";\nimport type { CreateFormReturn } from \"
|
|
13
|
+
"content": "import { Show, splitProps, type JSX } from \"solid-js\";\nimport type { CreateFormReturn } from \"@/hooks/create-form\";\nimport { FieldError, type FieldErrorProps } from \"@/components/ui/field\";\n\ntype FormState<T extends Record<string, any>> = Pick<\n CreateFormReturn<T>,\n \"errors\" | \"touched\"\n>;\n\nexport interface FormMessageProps<\n T extends Record<string, any>,\n K extends keyof T,\n> extends Omit<FieldErrorProps, \"children\"> {\n form: FormState<T>;\n name: K;\n /** Show the message even before the field has been touched. */\n showUntouched?: boolean;\n}\n\n/** Displays a field's validation error from createForm when it should be visible. */\nexport function FormMessage<\n T extends Record<string, any>,\n K extends keyof T,\n>(props: FormMessageProps<T, K>): JSX.Element {\n const [local, rest] = splitProps(props, [\"form\", \"name\", \"showUntouched\", \"class\"]);\n const error = () => local.form.errors()[local.name] as string | undefined;\n const touched = () => local.form.touched()[local.name] === true;\n\n return (\n <Show when={error() && (local.showUntouched || touched())}>\n <FieldError class={local.class} {...rest}>\n {error()}\n </FieldError>\n </Show>\n );\n}\n",
|
|
13
14
|
"type": "registry:ui"
|
|
14
15
|
}
|
|
15
16
|
]
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
{
|
|
15
15
|
"path": "ui/icon-button.tsx",
|
|
16
|
-
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { Button, type ButtonProps } from \"
|
|
16
|
+
"content": "import { splitProps, type Component, type JSX } from \"solid-js\";\nimport { Button, type ButtonProps } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface IconButtonProps\n extends Omit<ButtonProps, \"size\" | \"children\"> {\n /** Accessible label for the icon-only button. */\n label: string;\n size?: \"sm\" | \"default\" | \"lg\";\n children?: JSX.Element;\n}\n\n/** An accessible square button intended for a single icon action. */\nexport const IconButton: Component<IconButtonProps> = (props) => {\n const [local, rest] = splitProps(props, [\"label\", \"size\", \"class\", \"children\"]);\n\n return (\n <Button\n size=\"icon\"\n aria-label={local.label}\n class={cn(\n {\n \"size-8\": local.size === \"sm\",\n \"size-9\": !local.size || local.size === \"default\",\n \"size-10\": local.size === \"lg\",\n },\n local.class\n )}\n {...rest}\n >\n {local.children}\n </Button>\n );\n};\n",
|
|
17
17
|
"type": "registry:ui"
|
|
18
18
|
}
|
|
19
19
|
]
|
package/registry/index.json
CHANGED
|
@@ -21,6 +21,20 @@
|
|
|
21
21
|
"class-variance-authority"
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
|
+
{
|
|
25
|
+
"name": "api-table",
|
|
26
|
+
"title": "API Table",
|
|
27
|
+
"description": "A structured, clean API reference table component for documenting props, options, and events.",
|
|
28
|
+
"type": "registry:ui",
|
|
29
|
+
"dependencies": [
|
|
30
|
+
"clsx",
|
|
31
|
+
"tailwind-merge"
|
|
32
|
+
],
|
|
33
|
+
"registryDependencies": [
|
|
34
|
+
"table",
|
|
35
|
+
"section-heading"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
24
38
|
{
|
|
25
39
|
"name": "aspect-ratio",
|
|
26
40
|
"title": "Aspect Ratio",
|
|
@@ -112,6 +126,18 @@
|
|
|
112
126
|
"spinner"
|
|
113
127
|
]
|
|
114
128
|
},
|
|
129
|
+
{
|
|
130
|
+
"name": "callout",
|
|
131
|
+
"title": "Callout",
|
|
132
|
+
"description": "A semantic callout and alert block with status variants, icons, and titles for documentation and notices.",
|
|
133
|
+
"type": "registry:ui",
|
|
134
|
+
"dependencies": [
|
|
135
|
+
"clsx",
|
|
136
|
+
"tailwind-merge",
|
|
137
|
+
"class-variance-authority",
|
|
138
|
+
"lucide-solid"
|
|
139
|
+
]
|
|
140
|
+
},
|
|
115
141
|
{
|
|
116
142
|
"name": "card",
|
|
117
143
|
"title": "Card",
|
|
@@ -130,6 +156,41 @@
|
|
|
130
156
|
"dependencies": [
|
|
131
157
|
"clsx",
|
|
132
158
|
"tailwind-merge"
|
|
159
|
+
],
|
|
160
|
+
"registryDependencies": [
|
|
161
|
+
"create-controllable-signal"
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "code-block",
|
|
166
|
+
"title": "Code Block",
|
|
167
|
+
"description": "A code block container with filename header, language indicator, and interactive copy to clipboard button.",
|
|
168
|
+
"type": "registry:ui",
|
|
169
|
+
"dependencies": [
|
|
170
|
+
"clsx",
|
|
171
|
+
"tailwind-merge",
|
|
172
|
+
"lucide-solid",
|
|
173
|
+
"shiki"
|
|
174
|
+
],
|
|
175
|
+
"registryDependencies": [
|
|
176
|
+
"button",
|
|
177
|
+
"badge",
|
|
178
|
+
"tooltip",
|
|
179
|
+
"tabs",
|
|
180
|
+
"create-clipboard"
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "code-group",
|
|
185
|
+
"title": "Code Group",
|
|
186
|
+
"description": "A compound tabbed code container for organizing multiple files, languages, and snippets built on Tabs.",
|
|
187
|
+
"type": "registry:ui",
|
|
188
|
+
"dependencies": [
|
|
189
|
+
"clsx",
|
|
190
|
+
"tailwind-merge"
|
|
191
|
+
],
|
|
192
|
+
"registryDependencies": [
|
|
193
|
+
"tabs"
|
|
133
194
|
]
|
|
134
195
|
},
|
|
135
196
|
{
|
|
@@ -173,7 +234,37 @@
|
|
|
173
234
|
"kbd",
|
|
174
235
|
"input-group",
|
|
175
236
|
"list",
|
|
176
|
-
"scroll-area"
|
|
237
|
+
"scroll-area",
|
|
238
|
+
"create-keybindings"
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"name": "component-viewer",
|
|
243
|
+
"title": "Component Viewer",
|
|
244
|
+
"description": "An interactive preview canvas and code viewer container with responsive viewports, canvas grid, and AI prompts.",
|
|
245
|
+
"type": "registry:ui",
|
|
246
|
+
"dependencies": [
|
|
247
|
+
"clsx",
|
|
248
|
+
"tailwind-merge",
|
|
249
|
+
"lucide-solid"
|
|
250
|
+
],
|
|
251
|
+
"registryDependencies": [
|
|
252
|
+
"tabs",
|
|
253
|
+
"button",
|
|
254
|
+
"tooltip",
|
|
255
|
+
"code-block",
|
|
256
|
+
"create-clipboard"
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"name": "container",
|
|
261
|
+
"title": "Container",
|
|
262
|
+
"description": "A responsive layout container constraining maximum width with semantic padding tokens.",
|
|
263
|
+
"type": "registry:ui",
|
|
264
|
+
"dependencies": [
|
|
265
|
+
"clsx",
|
|
266
|
+
"tailwind-merge",
|
|
267
|
+
"class-variance-authority"
|
|
177
268
|
]
|
|
178
269
|
},
|
|
179
270
|
{
|
|
@@ -218,7 +309,8 @@
|
|
|
218
309
|
"@kobalte/core"
|
|
219
310
|
],
|
|
220
311
|
"registryDependencies": [
|
|
221
|
-
"scroll-area"
|
|
312
|
+
"scroll-area",
|
|
313
|
+
"create-click-outside"
|
|
222
314
|
]
|
|
223
315
|
},
|
|
224
316
|
{
|
|
@@ -258,6 +350,20 @@
|
|
|
258
350
|
"label"
|
|
259
351
|
]
|
|
260
352
|
},
|
|
353
|
+
{
|
|
354
|
+
"name": "file-tree",
|
|
355
|
+
"title": "File Tree",
|
|
356
|
+
"description": "An interactive hierarchical directory file tree component built on Collapsible.",
|
|
357
|
+
"type": "registry:ui",
|
|
358
|
+
"dependencies": [
|
|
359
|
+
"clsx",
|
|
360
|
+
"tailwind-merge",
|
|
361
|
+
"lucide-solid"
|
|
362
|
+
],
|
|
363
|
+
"registryDependencies": [
|
|
364
|
+
"collapsible"
|
|
365
|
+
]
|
|
366
|
+
},
|
|
261
367
|
{
|
|
262
368
|
"name": "footer",
|
|
263
369
|
"title": "Footer",
|
|
@@ -275,7 +381,8 @@
|
|
|
275
381
|
"description": "Validation message helper connected to createForm errors and touched state.",
|
|
276
382
|
"type": "registry:ui",
|
|
277
383
|
"registryDependencies": [
|
|
278
|
-
"field"
|
|
384
|
+
"field",
|
|
385
|
+
"create-form"
|
|
279
386
|
]
|
|
280
387
|
},
|
|
281
388
|
{
|
|
@@ -455,6 +562,37 @@
|
|
|
455
562
|
"create-long-press"
|
|
456
563
|
]
|
|
457
564
|
},
|
|
565
|
+
{
|
|
566
|
+
"name": "package-manager-tabs",
|
|
567
|
+
"title": "Package Manager Tabs",
|
|
568
|
+
"description": "An interactive command tab bar supporting customizable package managers (bun, pnpm, npm, yarn, deno, bunx, npx) with copy functionality.",
|
|
569
|
+
"type": "registry:ui",
|
|
570
|
+
"dependencies": [
|
|
571
|
+
"clsx",
|
|
572
|
+
"tailwind-merge",
|
|
573
|
+
"lucide-solid"
|
|
574
|
+
],
|
|
575
|
+
"registryDependencies": [
|
|
576
|
+
"button",
|
|
577
|
+
"tooltip",
|
|
578
|
+
"tabs",
|
|
579
|
+
"create-clipboard"
|
|
580
|
+
]
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
"name": "pager",
|
|
584
|
+
"title": "Pager",
|
|
585
|
+
"description": "Previous and next article navigation links with card previews for documentation and blog layouts.",
|
|
586
|
+
"type": "registry:ui",
|
|
587
|
+
"dependencies": [
|
|
588
|
+
"clsx",
|
|
589
|
+
"tailwind-merge",
|
|
590
|
+
"lucide-solid"
|
|
591
|
+
],
|
|
592
|
+
"registryDependencies": [
|
|
593
|
+
"card"
|
|
594
|
+
]
|
|
595
|
+
},
|
|
458
596
|
{
|
|
459
597
|
"name": "pagination",
|
|
460
598
|
"title": "Pagination",
|
|
@@ -490,6 +628,9 @@
|
|
|
490
628
|
"tailwind-merge",
|
|
491
629
|
"@kobalte/core",
|
|
492
630
|
"lucide-solid"
|
|
631
|
+
],
|
|
632
|
+
"registryDependencies": [
|
|
633
|
+
"create-click-outside"
|
|
493
634
|
]
|
|
494
635
|
},
|
|
495
636
|
{
|
|
@@ -556,6 +697,39 @@
|
|
|
556
697
|
"rating"
|
|
557
698
|
]
|
|
558
699
|
},
|
|
700
|
+
{
|
|
701
|
+
"name": "rich-text-editor",
|
|
702
|
+
"title": "Rich Text Editor",
|
|
703
|
+
"description": "A full-featured WYSIWYG rich text editor with toolbar, bubble formatting, tables, task lists, and image uploads built on Tiptap.",
|
|
704
|
+
"type": "registry:ui",
|
|
705
|
+
"dependencies": [
|
|
706
|
+
"@tiptap/core",
|
|
707
|
+
"@tiptap/starter-kit",
|
|
708
|
+
"@tiptap/extension-link",
|
|
709
|
+
"@tiptap/extension-image",
|
|
710
|
+
"@tiptap/extension-placeholder",
|
|
711
|
+
"@tiptap/extension-task-list",
|
|
712
|
+
"@tiptap/extension-task-item",
|
|
713
|
+
"@tiptap/extension-table",
|
|
714
|
+
"@tiptap/extension-table-row",
|
|
715
|
+
"@tiptap/extension-table-cell",
|
|
716
|
+
"@tiptap/extension-table-header",
|
|
717
|
+
"@tiptap/extension-underline",
|
|
718
|
+
"@tiptap/extension-text-align",
|
|
719
|
+
"@tiptap/extension-highlight",
|
|
720
|
+
"@tiptap/extension-character-count",
|
|
721
|
+
"@tiptap/extension-subscript",
|
|
722
|
+
"@tiptap/extension-superscript",
|
|
723
|
+
"@tiptap/extension-typography",
|
|
724
|
+
"tiptap-markdown",
|
|
725
|
+
"clsx",
|
|
726
|
+
"tailwind-merge",
|
|
727
|
+
"lucide-solid"
|
|
728
|
+
],
|
|
729
|
+
"registryDependencies": [
|
|
730
|
+
"create-tiptap-editor"
|
|
731
|
+
]
|
|
732
|
+
},
|
|
559
733
|
{
|
|
560
734
|
"name": "scroll-area",
|
|
561
735
|
"title": "Scroll Area",
|
|
@@ -570,6 +744,20 @@
|
|
|
570
744
|
"create-resize-observer"
|
|
571
745
|
]
|
|
572
746
|
},
|
|
747
|
+
{
|
|
748
|
+
"name": "section-heading",
|
|
749
|
+
"title": "Section Heading",
|
|
750
|
+
"description": "A reusable heading block with title, optional badge, and description. Supports page and section variants.",
|
|
751
|
+
"type": "registry:ui",
|
|
752
|
+
"dependencies": [
|
|
753
|
+
"clsx",
|
|
754
|
+
"tailwind-merge",
|
|
755
|
+
"class-variance-authority"
|
|
756
|
+
],
|
|
757
|
+
"registryDependencies": [
|
|
758
|
+
"badge"
|
|
759
|
+
]
|
|
760
|
+
},
|
|
573
761
|
{
|
|
574
762
|
"name": "select",
|
|
575
763
|
"title": "Select",
|
|
@@ -581,7 +769,8 @@
|
|
|
581
769
|
"@kobalte/core"
|
|
582
770
|
],
|
|
583
771
|
"registryDependencies": [
|
|
584
|
-
"scroll-area"
|
|
772
|
+
"scroll-area",
|
|
773
|
+
"create-click-outside"
|
|
585
774
|
]
|
|
586
775
|
},
|
|
587
776
|
{
|
|
@@ -681,6 +870,19 @@
|
|
|
681
870
|
"class-variance-authority"
|
|
682
871
|
]
|
|
683
872
|
},
|
|
873
|
+
{
|
|
874
|
+
"name": "steps",
|
|
875
|
+
"title": "Steps",
|
|
876
|
+
"description": "A vertical multi-step instruction container with connecting progress line and numbered step indicators.",
|
|
877
|
+
"type": "registry:ui",
|
|
878
|
+
"dependencies": [
|
|
879
|
+
"clsx",
|
|
880
|
+
"tailwind-merge"
|
|
881
|
+
],
|
|
882
|
+
"registryDependencies": [
|
|
883
|
+
"badge"
|
|
884
|
+
]
|
|
885
|
+
},
|
|
684
886
|
{
|
|
685
887
|
"name": "switch",
|
|
686
888
|
"title": "Switch",
|
|
@@ -689,6 +891,25 @@
|
|
|
689
891
|
"dependencies": [
|
|
690
892
|
"clsx",
|
|
691
893
|
"tailwind-merge"
|
|
894
|
+
],
|
|
895
|
+
"registryDependencies": [
|
|
896
|
+
"create-controllable-signal"
|
|
897
|
+
]
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
"name": "table-of-contents",
|
|
901
|
+
"title": "Table of Contents",
|
|
902
|
+
"description": "An accessible heading navigation tree with ScrollSpy tracking and smooth scrolling, composed of ScrollArea and List.",
|
|
903
|
+
"type": "registry:ui",
|
|
904
|
+
"dependencies": [
|
|
905
|
+
"clsx",
|
|
906
|
+
"tailwind-merge",
|
|
907
|
+
"class-variance-authority"
|
|
908
|
+
],
|
|
909
|
+
"registryDependencies": [
|
|
910
|
+
"scroll-area",
|
|
911
|
+
"list",
|
|
912
|
+
"create-scroll-position"
|
|
692
913
|
]
|
|
693
914
|
},
|
|
694
915
|
{
|
|
@@ -709,6 +930,9 @@
|
|
|
709
930
|
"dependencies": [
|
|
710
931
|
"clsx",
|
|
711
932
|
"tailwind-merge"
|
|
933
|
+
],
|
|
934
|
+
"registryDependencies": [
|
|
935
|
+
"create-controllable-signal"
|
|
712
936
|
]
|
|
713
937
|
},
|
|
714
938
|
{
|
|
@@ -734,7 +958,8 @@
|
|
|
734
958
|
],
|
|
735
959
|
"registryDependencies": [
|
|
736
960
|
"button",
|
|
737
|
-
"dropdown-menu"
|
|
961
|
+
"dropdown-menu",
|
|
962
|
+
"create-color-mode"
|
|
738
963
|
]
|
|
739
964
|
},
|
|
740
965
|
{
|
|
@@ -748,6 +973,42 @@
|
|
|
748
973
|
"class-variance-authority"
|
|
749
974
|
]
|
|
750
975
|
},
|
|
976
|
+
{
|
|
977
|
+
"name": "titlebar-tabs",
|
|
978
|
+
"title": "Titlebar Tabs",
|
|
979
|
+
"description": "Native draggable tab bar integrated directly inside desktop titlebars for multi-document applications.",
|
|
980
|
+
"type": "registry:ui",
|
|
981
|
+
"dependencies": [
|
|
982
|
+
"clsx",
|
|
983
|
+
"tailwind-merge",
|
|
984
|
+
"class-variance-authority",
|
|
985
|
+
"lucide-solid"
|
|
986
|
+
],
|
|
987
|
+
"registryDependencies": [
|
|
988
|
+
"button",
|
|
989
|
+
"tooltip",
|
|
990
|
+
"create-document-tabs"
|
|
991
|
+
]
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
"name": "titlebar",
|
|
995
|
+
"title": "Titlebar",
|
|
996
|
+
"description": "A native-feeling custom titlebar for frameless desktop windows supporting macOS Traffic Lights and Windows 11 controls.",
|
|
997
|
+
"type": "registry:ui",
|
|
998
|
+
"dependencies": [
|
|
999
|
+
"clsx",
|
|
1000
|
+
"tailwind-merge",
|
|
1001
|
+
"class-variance-authority",
|
|
1002
|
+
"lucide-solid"
|
|
1003
|
+
],
|
|
1004
|
+
"registryDependencies": [
|
|
1005
|
+
"titlebar-tabs",
|
|
1006
|
+
"button",
|
|
1007
|
+
"tooltip",
|
|
1008
|
+
"create-tauri-window",
|
|
1009
|
+
"create-document-tabs"
|
|
1010
|
+
]
|
|
1011
|
+
},
|
|
751
1012
|
{
|
|
752
1013
|
"name": "toast",
|
|
753
1014
|
"title": "Toast / Sonner",
|
|
@@ -797,6 +1058,25 @@
|
|
|
797
1058
|
"@kobalte/core"
|
|
798
1059
|
]
|
|
799
1060
|
},
|
|
1061
|
+
{
|
|
1062
|
+
"name": "updater-modal",
|
|
1063
|
+
"title": "Updater Modal",
|
|
1064
|
+
"description": "An automated application auto-updater dialog for Tauri v2 with release notes preview, download progress bar, and relaunch actions.",
|
|
1065
|
+
"type": "registry:ui",
|
|
1066
|
+
"dependencies": [
|
|
1067
|
+
"clsx",
|
|
1068
|
+
"tailwind-merge",
|
|
1069
|
+
"lucide-solid"
|
|
1070
|
+
],
|
|
1071
|
+
"registryDependencies": [
|
|
1072
|
+
"dialog",
|
|
1073
|
+
"button",
|
|
1074
|
+
"badge",
|
|
1075
|
+
"progress",
|
|
1076
|
+
"alert",
|
|
1077
|
+
"create-app-updater"
|
|
1078
|
+
]
|
|
1079
|
+
},
|
|
800
1080
|
{
|
|
801
1081
|
"name": "forgot-password-01",
|
|
802
1082
|
"title": "Forgot Password 01 — Account Recovery Flow",
|
|
@@ -1154,5 +1434,56 @@
|
|
|
1154
1434
|
"title": "createChatScroll",
|
|
1155
1435
|
"description": "SolidJS reactive primitive for automated chat container scrolling with manual scroll-up detection",
|
|
1156
1436
|
"type": "registry:hook"
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
"name": "create-tauri-window",
|
|
1440
|
+
"title": "createTauriWindow",
|
|
1441
|
+
"description": "SolidJS reactive primitive for interacting with and controlling Tauri v2 application window states",
|
|
1442
|
+
"type": "registry:hook"
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
"name": "create-global-shortcut",
|
|
1446
|
+
"title": "createGlobalShortcut",
|
|
1447
|
+
"description": "SolidJS reactive primitive for registering OS-level global keyboard shortcuts via Tauri v2 plugin",
|
|
1448
|
+
"type": "registry:hook"
|
|
1449
|
+
},
|
|
1450
|
+
{
|
|
1451
|
+
"name": "create-app-updater",
|
|
1452
|
+
"title": "createAppUpdater",
|
|
1453
|
+
"description": "SolidJS reactive primitive for controlling and observing Tauri v2 application updates",
|
|
1454
|
+
"type": "registry:hook"
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
"name": "create-document-tabs",
|
|
1458
|
+
"title": "createDocumentTabs",
|
|
1459
|
+
"description": "SolidJS reactive primitive for managing multi-document tabs, editor buffers, and browser tab stacks with adjacent activation and pinned state",
|
|
1460
|
+
"type": "registry:hook"
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
"name": "create-tiptap-editor",
|
|
1464
|
+
"title": "createTiptapEditor",
|
|
1465
|
+
"description": "Fine-grained reactive SolidJS primitive for managing Tiptap rich text editor instances, signals, and formatting commands.",
|
|
1466
|
+
"type": "registry:hook",
|
|
1467
|
+
"dependencies": [
|
|
1468
|
+
"@tiptap/core",
|
|
1469
|
+
"@tiptap/starter-kit",
|
|
1470
|
+
"@tiptap/extension-link",
|
|
1471
|
+
"@tiptap/extension-image",
|
|
1472
|
+
"@tiptap/extension-placeholder",
|
|
1473
|
+
"@tiptap/extension-task-list",
|
|
1474
|
+
"@tiptap/extension-task-item",
|
|
1475
|
+
"@tiptap/extension-table",
|
|
1476
|
+
"@tiptap/extension-table-row",
|
|
1477
|
+
"@tiptap/extension-table-cell",
|
|
1478
|
+
"@tiptap/extension-table-header",
|
|
1479
|
+
"@tiptap/extension-underline",
|
|
1480
|
+
"@tiptap/extension-text-align",
|
|
1481
|
+
"@tiptap/extension-highlight",
|
|
1482
|
+
"@tiptap/extension-character-count",
|
|
1483
|
+
"@tiptap/extension-subscript",
|
|
1484
|
+
"@tiptap/extension-superscript",
|
|
1485
|
+
"@tiptap/extension-typography",
|
|
1486
|
+
"tiptap-markdown"
|
|
1487
|
+
]
|
|
1157
1488
|
}
|
|
1158
1489
|
]
|
package/registry/navbar.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
{
|
|
14
14
|
"path": "ui/navbar.tsx",
|
|
15
|
-
"content": "import {\n createContext,\n createSignal,\n useContext,\n splitProps,\n Show,\n type JSX,\n type ParentComponent,\n type Component,\n type Accessor,\n} from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Menu, X } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\n/* --- Navbar Context --- */\ninterface NavbarContextValue {\n isOpen: Accessor<boolean>;\n setIsOpen: (open: boolean) => void;\n toggleMobileMenu: () => void;\n}\n\nconst NavbarContext = createContext<NavbarContextValue>();\n\nexport function useNavbar() {\n const context = useContext(NavbarContext);\n if (!context) {\n throw new Error(\"useNavbar must be used within a <Navbar /> component\");\n }\n return context;\n}\n\n/* --- Root Navbar --- */\nexport const navbarVariants = cva(\n \"w-full transition-all duration-200 z-40\",\n {\n variants: {\n variant: {\n default: \"border-b border-border/60 bg-background/80 backdrop-blur-md\",\n floating: \"my-3 rounded-lg border border-border/80 bg-background/90 backdrop-blur-md shadow-sm\",\n bordered: \"border-b border-border bg-background\",\n transparent: \"bg-transparent border-transparent\",\n },\n isSticky: {\n true: \"sticky top-0\",\n false: \"relative\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n isSticky: true,\n },\n }\n);\n\nexport interface NavbarProps\n extends JSX.HTMLAttributes<HTMLElement>,\n VariantProps<typeof navbarVariants> {\n maxWidth?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"full\";\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport const Navbar: ParentComponent<NavbarProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"class\",\n \"variant\",\n \"isSticky\",\n \"maxWidth\",\n \"open\",\n \"onOpenChange\",\n \"children\",\n ]);\n\n const [internalOpen, setInternalOpen] = createSignal(false);\n const isOpen = () => (local.open !== undefined ? local.open : internalOpen());\n\n const setIsOpen = (open: boolean) => {\n if (local.open === undefined) {\n setInternalOpen(open);\n }\n local.onOpenChange?.(open);\n };\n\n const toggleMobileMenu = () => setIsOpen(!isOpen());\n\n const maxWidthClass = () => {\n switch (local.maxWidth) {\n case \"sm\":\n return \"max-w-screen-sm\";\n case \"md\":\n return \"max-w-screen-md\";\n case \"lg\":\n return \"max-w-screen-lg\";\n case \"xl\":\n return \"max-w-screen-xl\";\n case \"full\":\n return \"max-w-full\";\n case \"2xl\":\n default:\n return \"max-w-screen-2xl\";\n }\n };\n\n return (\n <NavbarContext.Provider value={{ isOpen, setIsOpen, toggleMobileMenu }}>\n <header\n class={cn(\n navbarVariants({\n variant: local.variant,\n isSticky: local.isSticky,\n }),\n local.variant === \"floating\" && cn(\"mx-auto\", maxWidthClass()),\n local.class\n )}\n {...rest}\n >\n <div class={cn(\"w-full\", local.variant !== \"floating\" && cn(\"mx-auto\", maxWidthClass()))}>\n {local.children}\n </div>\n </header>\n </NavbarContext.Provider>\n );\n};\n\n/* --- Navbar Container (Top Flex Row) --- */\nexport interface NavbarContainerProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavbarContainer: ParentComponent<NavbarContainerProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div\n class={cn(\n \"flex h-14 md:h-16 items-center justify-between px-4 sm:px-6 lg:px-8 gap-3 w-full\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\n/* --- Navbar Brand / Logo Container --- */\nexport interface NavbarBrandProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n}\n\nexport const NavbarBrand: ParentComponent<NavbarBrandProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <a\n class={cn(\n \"flex items-center gap-2 font-bold text-foreground transition-opacity hover:opacity-90 cursor-pointer shrink-0\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </a>\n );\n};\n\n/* --- Navbar Content / Section --- */\nexport const navbarContentVariants = cva(\"flex items-center gap-1 sm:gap-2 h-full\", {\n variants: {\n justify: {\n start: \"justify-start flex-1\",\n center: \"justify-center flex-1\",\n end: \"justify-end flex-1\",\n },\n },\n defaultVariants: {\n justify: \"center\",\n },\n});\n\nexport interface NavbarContentProps\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof navbarContentVariants> {\n class?: string;\n hideOnMobile?: boolean;\n}\n\nexport const NavbarContent: ParentComponent<NavbarContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"justify\", \"hideOnMobile\", \"children\"]);\n\n return (\n <div\n class={cn(\n navbarContentVariants({ justify: local.justify }),\n local.hideOnMobile !== false && \"hidden md:flex\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\n/* --- Navbar Item --- */\nexport interface NavbarItemProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n isActive?: boolean;\n}\n\nexport const NavbarItem: ParentComponent<NavbarItemProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"isActive\", \"children\"]);\n\n return (\n <div\n class={cn(\n \"relative flex items-center h-full text-sm font-medium\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n <Show when={local.isActive}>\n <div class=\"absolute -bottom-px left-0 right-0 h-0.5 bg-primary rounded-lg z-10\" />\n </Show>\n </div>\n );\n};\n\n/* --- Navbar Link --- */\nexport const navbarLinkVariants = cva(\n \"inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium transition-colors cursor-pointer select-none focus-visible:outline-hidden\",\n {\n variants: {\n variant: {\n default:\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n active:\n \"text-foreground font-semibold bg-muted/80\",\n ghost:\n \"text-foreground hover:text-primary\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nexport interface NavbarLinkProps\n extends JSX.AnchorHTMLAttributes<HTMLAnchorElement>,\n VariantProps<typeof navbarLinkVariants> {\n class?: string;\n isActive?: boolean;\n}\n\nexport const NavbarLink: ParentComponent<NavbarLinkProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"variant\", \"isActive\", \"children\"]);\n\n return (\n <a\n class={cn(\n navbarLinkVariants({\n variant: local.isActive ? \"active\" : local.variant,\n }),\n local.class\n )}\n {...rest}\n >\n {local.children}\n </a>\n );\n};\n\n/* --- Navbar Actions Container --- */\nexport interface NavbarActionsProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavbarActions: ParentComponent<NavbarActionsProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div class={cn(\"flex items-center gap-2 shrink-0\", local.class)} {...rest}>\n {local.children}\n </div>\n );\n};\n\n/* --- Navbar Mobile Toggle Button --- */\nexport interface NavbarMobileToggleProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {\n class?: string;\n}\n\nexport const NavbarMobileToggle: Component<NavbarMobileToggleProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n const { isOpen, toggleMobileMenu } = useNavbar();\n\n return (\n <button\n type=\"button\"\n onClick={toggleMobileMenu}\n aria-label=\"Toggle navigation menu\"\n aria-expanded={isOpen()}\n class={cn(\n \"inline-flex md:hidden size-8 items-center justify-center rounded-md border border-border/60 bg-muted/30 text-muted-foreground hover:bg-muted hover:text-foreground transition-colors cursor-pointer focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring shrink-0\",\n local.class\n )}\n {...rest}\n >\n <Show when={isOpen()} fallback={<Menu class=\"size-4\" />}>\n <X class=\"size-4\" />\n </Show>\n </button>\n );\n};\n\n/* --- Navbar Mobile Menu Container --- */\nexport interface NavbarMobileMenuProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavbarMobileMenu: ParentComponent<NavbarMobileMenuProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const { isOpen } = useNavbar();\n\n return (\n <Show when={isOpen()}>\n <div\n class={cn(\n \"md:hidden w-full border-t border-border/60 bg-background/95 backdrop-blur-lg px-4 py-3 space-y-1 shadow-md transition-all\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </Show>\n );\n};\n\n/* --- Navbar Mobile Item & Link --- */\nexport interface NavbarMobileLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n isActive?: boolean;\n}\n\nexport const NavbarMobileLink: ParentComponent<NavbarMobileLinkProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"isActive\", \"children\"]);\n const { setIsOpen } = useNavbar();\n\n return (\n <a\n onClick={() => setIsOpen(false)}\n class={cn(\n \"flex w-full items-center justify-between rounded-md px-3 py-2 text-sm font-medium transition-colors cursor-pointer\",\n local.isActive\n ? \"bg-primary text-primary-foreground font-semibold shadow-2xs\"\n : \"text-muted-foreground hover:bg-muted/80 hover:text-foreground\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </a>\n );\n};\n",
|
|
15
|
+
"content": "import {\n createContext,\n createSignal,\n useContext,\n splitProps,\n Show,\n type JSX,\n type ParentComponent,\n type Component,\n type Accessor,\n type ValidComponent,\n} from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Menu, X } from \"lucide-solid\";\nimport { cn } from \"@/lib/cn\";\n\n/* --- Navbar Context --- */\ninterface NavbarContextValue {\n isOpen: Accessor<boolean>;\n setIsOpen: (open: boolean) => void;\n toggleMobileMenu: () => void;\n}\n\nconst NavbarContext = createContext<NavbarContextValue>();\n\nexport function useNavbar() {\n const context = useContext(NavbarContext);\n if (!context) {\n throw new Error(\"useNavbar must be used within a <Navbar /> component\");\n }\n return context;\n}\n\n/* --- Root Navbar --- */\nexport const navbarVariants = cva(\n \"w-full transition-all duration-200 z-40\",\n {\n variants: {\n variant: {\n default: \"border-b border-border/60 bg-background/80 backdrop-blur-md\",\n floating: \"my-3 rounded-lg border border-border/80 bg-background/90 backdrop-blur-md shadow-sm\",\n bordered: \"border-b border-border bg-background\",\n transparent: \"bg-transparent border-transparent\",\n },\n isSticky: {\n true: \"sticky top-0\",\n false: \"relative\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n isSticky: true,\n },\n }\n);\n\nexport interface NavbarProps\n extends JSX.HTMLAttributes<HTMLElement>,\n VariantProps<typeof navbarVariants> {\n maxWidth?: \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"full\";\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\nexport const Navbar: ParentComponent<NavbarProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"class\",\n \"variant\",\n \"isSticky\",\n \"maxWidth\",\n \"open\",\n \"onOpenChange\",\n \"children\",\n ]);\n\n const [internalOpen, setInternalOpen] = createSignal(false);\n const isOpen = () => (local.open !== undefined ? local.open : internalOpen());\n\n const setIsOpen = (open: boolean) => {\n if (local.open === undefined) {\n setInternalOpen(open);\n }\n local.onOpenChange?.(open);\n };\n\n const toggleMobileMenu = () => setIsOpen(!isOpen());\n\n const maxWidthClass = () => {\n switch (local.maxWidth) {\n case \"sm\":\n return \"max-w-screen-sm\";\n case \"md\":\n return \"max-w-screen-md\";\n case \"lg\":\n return \"max-w-screen-lg\";\n case \"xl\":\n return \"max-w-screen-xl\";\n case \"full\":\n return \"max-w-full\";\n case \"2xl\":\n default:\n return \"max-w-screen-2xl\";\n }\n };\n\n return (\n <NavbarContext.Provider value={{ isOpen, setIsOpen, toggleMobileMenu }}>\n <header\n class={cn(\n navbarVariants({\n variant: local.variant,\n isSticky: local.isSticky,\n }),\n local.variant === \"floating\" && cn(\"mx-auto\", maxWidthClass()),\n local.class\n )}\n {...rest}\n >\n <div class={cn(\"w-full\", local.variant !== \"floating\" && cn(\"mx-auto\", maxWidthClass()))}>\n {local.children}\n </div>\n </header>\n </NavbarContext.Provider>\n );\n};\n\n/* --- Navbar Container (Top Flex Row) --- */\nexport interface NavbarContainerProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavbarContainer: ParentComponent<NavbarContainerProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div\n class={cn(\n \"flex h-14 md:h-16 items-center justify-between px-4 sm:px-6 lg:px-8 gap-3 w-full\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\n/* --- Navbar Brand / Logo Container --- */\nexport interface NavbarBrandProps<T extends ValidComponent = \"a\"> extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n as?: T;\n href?: string;\n}\n\nexport const NavbarBrand = <T extends ValidComponent = \"a\">(props: NavbarBrandProps<T>) => {\n const [local, rest] = splitProps(props as NavbarBrandProps, [\"class\", \"as\", \"children\"]);\n\n return (\n <Dynamic\n component={local.as || \"a\"}\n class={cn(\n \"flex items-center gap-2 font-bold text-foreground transition-opacity hover:opacity-90 cursor-pointer shrink-0\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </Dynamic>\n );\n};\n\n/* --- Navbar Content / Section --- */\nexport const navbarContentVariants = cva(\"flex items-center gap-1 sm:gap-2 h-full\", {\n variants: {\n justify: {\n start: \"justify-start flex-1\",\n center: \"justify-center flex-1\",\n end: \"justify-end flex-1\",\n },\n },\n defaultVariants: {\n justify: \"center\",\n },\n});\n\nexport interface NavbarContentProps\n extends JSX.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof navbarContentVariants> {\n class?: string;\n hideOnMobile?: boolean;\n}\n\nexport const NavbarContent: ParentComponent<NavbarContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"justify\", \"hideOnMobile\", \"children\"]);\n\n return (\n <div\n class={cn(\n navbarContentVariants({ justify: local.justify }),\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n );\n};\n\n/* --- Navbar Item --- */\nexport interface NavbarItemProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n isActive?: boolean;\n}\n\nexport const NavbarItem: ParentComponent<NavbarItemProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"isActive\", \"children\"]);\n\n return (\n <div\n class={cn(\n \"relative flex items-center h-full text-sm font-medium\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n <Show when={local.isActive}>\n <div class=\"absolute -bottom-px left-0 right-0 h-0.5 bg-primary rounded-lg z-10\" />\n </Show>\n </div>\n );\n};\n\n/* --- Navbar Link --- */\nexport const navbarLinkVariants = cva(\n \"inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium transition-colors cursor-pointer select-none focus-visible:outline-hidden\",\n {\n variants: {\n variant: {\n default:\n \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n active:\n \"text-foreground font-semibold bg-muted/80\",\n ghost:\n \"text-foreground hover:text-primary\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nexport interface NavbarLinkProps<T extends ValidComponent = \"a\">\n extends JSX.AnchorHTMLAttributes<HTMLAnchorElement>,\n VariantProps<typeof navbarLinkVariants> {\n class?: string;\n isActive?: boolean;\n as?: T;\n href?: string;\n}\n\nexport const NavbarLink = <T extends ValidComponent = \"a\">(props: NavbarLinkProps<T>) => {\n const [local, rest] = splitProps(props as NavbarLinkProps, [\"class\", \"variant\", \"isActive\", \"as\", \"children\"]);\n\n return (\n <Dynamic\n component={local.as || \"a\"}\n class={cn(\n navbarLinkVariants({\n variant: local.isActive ? \"active\" : local.variant,\n }),\n local.class\n )}\n {...rest}\n >\n {local.children}\n </Dynamic>\n );\n};\n\n/* --- Navbar Actions Container --- */\nexport interface NavbarActionsProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavbarActions: ParentComponent<NavbarActionsProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <div class={cn(\"flex items-center gap-2 shrink-0\", local.class)} {...rest}>\n {local.children}\n </div>\n );\n};\n\n/* --- Navbar Mobile Toggle Button --- */\nexport interface NavbarMobileToggleProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {\n class?: string;\n}\n\nexport const NavbarMobileToggle: Component<NavbarMobileToggleProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n const { isOpen, toggleMobileMenu } = useNavbar();\n\n return (\n <button\n type=\"button\"\n onClick={toggleMobileMenu}\n aria-label=\"Toggle navigation menu\"\n aria-expanded={isOpen()}\n class={cn(\n \"inline-flex md:hidden size-8 items-center justify-center rounded-md border border-border/60 bg-muted/30 text-muted-foreground hover:bg-muted hover:text-foreground transition-colors cursor-pointer focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring shrink-0\",\n local.class\n )}\n {...rest}\n >\n <Show when={isOpen()} fallback={<Menu class=\"size-4\" />}>\n <X class=\"size-4\" />\n </Show>\n </button>\n );\n};\n\n/* --- Navbar Mobile Menu Container --- */\nexport interface NavbarMobileMenuProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavbarMobileMenu: ParentComponent<NavbarMobileMenuProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const { isOpen } = useNavbar();\n\n return (\n <Show when={isOpen()}>\n <div\n class={cn(\n \"md:hidden w-full border-t border-border/60 bg-background/95 backdrop-blur-lg px-4 py-3 space-y-1 shadow-md transition-all\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </Show>\n );\n};\n\n/* --- Navbar Mobile Item & Link --- */\nexport interface NavbarMobileLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n isActive?: boolean;\n}\n\nexport const NavbarMobileLink: ParentComponent<NavbarMobileLinkProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"isActive\", \"children\"]);\n const { setIsOpen } = useNavbar();\n\n return (\n <a\n onClick={() => setIsOpen(false)}\n class={cn(\n \"flex w-full items-center justify-between rounded-md px-3 py-2 text-sm font-medium transition-colors cursor-pointer\",\n local.isActive\n ? \"bg-primary text-primary-foreground font-semibold shadow-2xs\"\n : \"text-muted-foreground hover:bg-muted/80 hover:text-foreground\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </a>\n );\n};\n",
|
|
16
16
|
"type": "registry:ui"
|
|
17
17
|
}
|
|
18
18
|
]
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
{
|
|
17
17
|
"path": "ui/navigation-menu.tsx",
|
|
18
|
-
"content": "import {\n createContext,\n createSignal,\n useContext,\n splitProps,\n onCleanup,\n type Component,\n type JSX,\n type ParentComponent,\n type Accessor,\n Show,\n} from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { createClickOutside } from \"@nikala-ui/hooks\";\nimport { cn } from \"@/lib/cn\";\nimport { ChevronDown } from \"lucide-solid\";\n\n/* --- Context Definition --- */\ninterface NavigationMenuContextValue {\n activeValue: Accessor<string | null>;\n setActiveValue: (value: string | null) => void;\n onItemMouseEnter: (value: string) => void;\n onItemMouseLeave: () => void;\n delayMs: Accessor<number>;\n}\n\nconst NavigationMenuContext = createContext<NavigationMenuContextValue>();\n\nexport function useNavigationMenu() {\n const context = useContext(NavigationMenuContext);\n if (!context) {\n throw new Error(\"useNavigationMenu must be used within a <NavigationMenu />\");\n }\n return context;\n}\n\n/* --- 1. NavigationMenu Root --- */\nexport interface NavigationMenuProps extends JSX.HTMLAttributes<HTMLElement> {\n class?: string;\n delayMs?: number;\n value?: string | null;\n onValueChange?: (value: string | null) => void;\n}\n\nexport const NavigationMenu: ParentComponent<NavigationMenuProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"delayMs\", \"value\", \"onValueChange\", \"children\"]);\n\n const [internalValue, setInternalValue] = createSignal<string | null>(local.value ?? null);\n const activeValue = () => (local.value !== undefined ? local.value : internalValue());\n\n const setActiveValue = (val: string | null) => {\n if (local.value === undefined) {\n setInternalValue(val);\n }\n local.onValueChange?.(val);\n };\n\n let rootRef!: HTMLElement;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const delay = () => local.delayMs ?? 150;\n\n const onItemMouseEnter = (val: string) => {\n clearTimeout(timer);\n const ms = delay();\n if (ms <= 0) {\n setActiveValue(val);\n } else {\n timer = setTimeout(() => {\n setActiveValue(val);\n }, ms);\n }\n };\n\n const onItemMouseLeave = () => {\n clearTimeout(timer);\n const ms = delay();\n timer = setTimeout(() => {\n setActiveValue(null);\n }, Math.max(50, ms));\n };\n\n createClickOutside({\n target: () => rootRef,\n onInteractOutside: () => {\n setActiveValue(null);\n },\n });\n\n onCleanup(() => {\n clearTimeout(timer);\n });\n\n const contextValue: NavigationMenuContextValue = {\n activeValue,\n setActiveValue,\n onItemMouseEnter,\n onItemMouseLeave,\n delayMs: delay,\n };\n\n return (\n <NavigationMenuContext.Provider value={contextValue}>\n <nav\n ref={rootRef}\n aria-label=\"Main Navigation\"\n class={cn(\"relative z-10 flex max-w-max flex-1 items-center justify-center\", local.class)}\n {...rest}\n >\n {local.children}\n </nav>\n </NavigationMenuContext.Provider>\n );\n};\n\n/* --- 2. NavigationMenuList --- */\nexport interface NavigationMenuListProps extends JSX.HTMLAttributes<HTMLUListElement> {\n class?: string;\n}\n\nexport const NavigationMenuList: ParentComponent<NavigationMenuListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <ul\n class={cn(\n \"group flex flex-1 list-none items-center justify-center space-x-1 p-1\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </ul>\n );\n};\n\n/* --- 3. NavigationMenuItem Context & Component --- */\ninterface NavigationMenuItemContextValue {\n value: string;\n isOpen: Accessor<boolean>;\n}\n\nconst NavigationMenuItemContext = createContext<NavigationMenuItemContextValue>();\n\nfunction useNavigationMenuItem() {\n const context = useContext(NavigationMenuItemContext);\n if (!context) {\n throw new Error(\"useNavigationMenuItem must be used within a <NavigationMenuItem />\");\n }\n return context;\n}\n\nexport interface NavigationMenuItemProps extends JSX.HTMLAttributes<HTMLLIElement> {\n value?: string;\n class?: string;\n}\n\nlet itemCounter = 0;\n\nexport const NavigationMenuItem: ParentComponent<NavigationMenuItemProps> = (props) => {\n const itemId = props.value ?? `nav-item-${++itemCounter}`;\n const [local, rest] = splitProps(props, [\"class\", \"value\", \"children\"]);\n const rootContext = useNavigationMenu();\n\n const isOpen = () => rootContext.activeValue() === itemId;\n\n const itemContext: NavigationMenuItemContextValue = {\n value: itemId,\n isOpen,\n };\n\n return (\n <NavigationMenuItemContext.Provider value={itemContext}>\n <li\n class={cn(\"relative\", local.class)}\n onMouseEnter={() => rootContext.onItemMouseEnter(itemId)}\n onMouseLeave={() => rootContext.onItemMouseLeave()}\n {...rest}\n >\n {local.children}\n </li>\n </NavigationMenuItemContext.Provider>\n );\n};\n\n/* --- 4. Trigger Style Helper --- */\nexport const navigationMenuTriggerVariants = cva(\n \"group inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium transition-colors focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none gap-1\",\n {\n variants: {\n variant: {\n default:\n \"bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground data-[state=open]:bg-muted data-[state=open]:text-foreground\",\n filled:\n \"bg-muted/60 text-foreground hover:bg-muted border border-border/40 data-[state=open]:bg-muted\",\n ghost:\n \"bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent/60\",\n outline:\n \"border border-border bg-background hover:bg-accent hover:text-accent-foreground shadow-2xs data-[state=open]:bg-accent\",\n },\n size: {\n default: \"h-9 px-3 text-sm\",\n sm: \"h-8 px-2.5 text-xs\",\n lg: \"h-10 px-4 text-base\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nexport function navigationMenuTriggerStyle(\n variants?: VariantProps<typeof navigationMenuTriggerVariants>\n) {\n return navigationMenuTriggerVariants(variants);\n}\n\n/* --- 5. NavigationMenuTrigger --- */\nexport interface NavigationMenuTriggerProps\n extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof navigationMenuTriggerVariants> {\n class?: string;\n hideChevron?: boolean;\n}\n\nexport const NavigationMenuTrigger: ParentComponent<NavigationMenuTriggerProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"variant\", \"size\", \"hideChevron\", \"children\"]);\n const rootContext = useNavigationMenu();\n const itemContext = useNavigationMenuItem();\n\n const handleClick = (e: MouseEvent) => {\n e.preventDefault();\n if (itemContext.isOpen()) {\n rootContext.setActiveValue(null);\n } else {\n rootContext.setActiveValue(itemContext.value);\n }\n };\n\n return (\n <button\n type=\"button\"\n aria-expanded={itemContext.isOpen()}\n data-state={itemContext.isOpen() ? \"open\" : \"closed\"}\n onClick={handleClick}\n class={cn(\n navigationMenuTriggerVariants({ variant: local.variant, size: local.size }),\n \"gap-1.5\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n <Show when={!local.hideChevron}>\n <ChevronDown\n class={cn(\n \"size-3.5 text-muted-foreground transition-transform duration-200\",\n itemContext.isOpen() && \"rotate-180 text-foreground\"\n )}\n aria-hidden=\"true\"\n />\n </Show>\n </button>\n );\n};\n\n/* --- 6. NavigationMenuContent (Mega Menu Flyout) --- */\nexport interface NavigationMenuContentProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavigationMenuContent: ParentComponent<NavigationMenuContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const itemContext = useNavigationMenuItem();\n\n return (\n <Show when={itemContext.isOpen()}>\n <div\n data-state={itemContext.isOpen() ? \"open\" : \"closed\"}\n class={cn(\n \"absolute left-0 top-full mt-1.5 w-auto rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-lg outline-hidden z-50\",\n \"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 duration-200\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </Show>\n );\n};\n\n/* --- 7. NavigationMenuLink --- */\nexport interface NavigationMenuLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n active?: boolean;\n}\n\nexport const NavigationMenuLink: ParentComponent<NavigationMenuLinkProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"active\", \"children\"]);\n\n return (\n <a\n data-active={local.active ? \"\" : undefined}\n class={cn(\n \"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground\",\n local.active && \"bg-accent/60 text-accent-foreground\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </a>\n );\n};\n\n/* --- 8. NavigationMenuViewport --- */\nexport interface NavigationMenuViewportProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavigationMenuViewport: Component<NavigationMenuViewportProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div class=\"absolute left-0 top-full flex justify-center\">\n <div\n class={cn(\n \"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg transition-all\",\n local.class\n )}\n {...rest}\n />\n </div>\n );\n};\n\n/* --- 9. NavigationMenuIndicator --- */\nexport interface NavigationMenuIndicatorProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavigationMenuIndicator: Component<NavigationMenuIndicatorProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\n \"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden transition-[width,transform] duration-200\",\n local.class\n )}\n {...rest}\n >\n <div class=\"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-xs bg-border shadow-md\" />\n </div>\n );\n};\n",
|
|
18
|
+
"content": "import {\n createContext,\n createSignal,\n useContext,\n splitProps,\n onCleanup,\n type Component,\n type JSX,\n type ParentComponent,\n type Accessor,\n Show,\n} from \"solid-js\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { createClickOutside } from \"@/hooks/create-click-outside\";\nimport { cn } from \"@/lib/cn\";\nimport { ChevronDown } from \"lucide-solid\";\n\n/* --- Context Definition --- */\ninterface NavigationMenuContextValue {\n activeValue: Accessor<string | null>;\n setActiveValue: (value: string | null) => void;\n onItemMouseEnter: (value: string) => void;\n onItemMouseLeave: () => void;\n delayMs: Accessor<number>;\n}\n\nconst NavigationMenuContext = createContext<NavigationMenuContextValue>();\n\nfunction useNavigationMenu() {\n const context = useContext(NavigationMenuContext);\n if (!context) {\n throw new Error(\"useNavigationMenu must be used within a <NavigationMenu />\");\n }\n return context;\n}\n\n/* --- 1. NavigationMenu Root --- */\nexport interface NavigationMenuProps extends JSX.HTMLAttributes<HTMLElement> {\n class?: string;\n delayMs?: number;\n value?: string | null;\n onValueChange?: (value: string | null) => void;\n}\n\nexport const NavigationMenu: ParentComponent<NavigationMenuProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"delayMs\", \"value\", \"onValueChange\", \"children\"]);\n\n const [internalValue, setInternalValue] = createSignal<string | null>(local.value ?? null);\n const activeValue = () => (local.value !== undefined ? local.value : internalValue());\n\n const setActiveValue = (val: string | null) => {\n if (local.value === undefined) {\n setInternalValue(val);\n }\n local.onValueChange?.(val);\n };\n\n let rootRef!: HTMLElement;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const delay = () => local.delayMs ?? 150;\n\n const onItemMouseEnter = (val: string) => {\n clearTimeout(timer);\n const ms = delay();\n if (ms <= 0) {\n setActiveValue(val);\n } else {\n timer = setTimeout(() => {\n setActiveValue(val);\n }, ms);\n }\n };\n\n const onItemMouseLeave = () => {\n clearTimeout(timer);\n const ms = delay();\n timer = setTimeout(() => {\n setActiveValue(null);\n }, Math.max(50, ms));\n };\n\n createClickOutside({\n target: () => rootRef,\n onInteractOutside: () => {\n setActiveValue(null);\n },\n });\n\n onCleanup(() => {\n clearTimeout(timer);\n });\n\n const contextValue: NavigationMenuContextValue = {\n activeValue,\n setActiveValue,\n onItemMouseEnter,\n onItemMouseLeave,\n delayMs: delay,\n };\n\n return (\n <NavigationMenuContext.Provider value={contextValue}>\n <nav\n ref={rootRef}\n aria-label=\"Main Navigation\"\n class={cn(\"relative z-10 flex max-w-max flex-1 items-center justify-center\", local.class)}\n {...rest}\n >\n {local.children}\n </nav>\n </NavigationMenuContext.Provider>\n );\n};\n\n/* --- 2. NavigationMenuList --- */\nexport interface NavigationMenuListProps extends JSX.HTMLAttributes<HTMLUListElement> {\n class?: string;\n}\n\nexport const NavigationMenuList: ParentComponent<NavigationMenuListProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n\n return (\n <ul\n class={cn(\n \"group flex flex-1 list-none items-center justify-center space-x-1 p-1\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </ul>\n );\n};\n\n/* --- 3. NavigationMenuItem Context & Component --- */\ninterface NavigationMenuItemContextValue {\n value: string;\n isOpen: Accessor<boolean>;\n}\n\nconst NavigationMenuItemContext = createContext<NavigationMenuItemContextValue>();\n\nfunction useNavigationMenuItem() {\n const context = useContext(NavigationMenuItemContext);\n if (!context) {\n throw new Error(\"useNavigationMenuItem must be used within a <NavigationMenuItem />\");\n }\n return context;\n}\n\nexport interface NavigationMenuItemProps extends JSX.HTMLAttributes<HTMLLIElement> {\n value?: string;\n class?: string;\n}\n\nlet itemCounter = 0;\n\nexport const NavigationMenuItem: ParentComponent<NavigationMenuItemProps> = (props) => {\n const itemId = props.value ?? `nav-item-${++itemCounter}`;\n const [local, rest] = splitProps(props, [\"class\", \"value\", \"children\"]);\n const rootContext = useNavigationMenu();\n\n const isOpen = () => rootContext.activeValue() === itemId;\n\n const itemContext: NavigationMenuItemContextValue = {\n value: itemId,\n isOpen,\n };\n\n return (\n <NavigationMenuItemContext.Provider value={itemContext}>\n <li\n class={cn(\"relative\", local.class)}\n onMouseEnter={() => rootContext.onItemMouseEnter(itemId)}\n onMouseLeave={() => rootContext.onItemMouseLeave()}\n {...rest}\n >\n {local.children}\n </li>\n </NavigationMenuItemContext.Provider>\n );\n};\n\n/* --- 4. Trigger Style Helper --- */\nexport const navigationMenuTriggerVariants = cva(\n \"group inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium transition-colors focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none gap-1\",\n {\n variants: {\n variant: {\n default:\n \"bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground data-[state=open]:bg-muted data-[state=open]:text-foreground\",\n filled:\n \"bg-muted/60 text-foreground hover:bg-muted border border-border/40 data-[state=open]:bg-muted\",\n ghost:\n \"bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent/60\",\n outline:\n \"border border-border bg-background hover:bg-accent hover:text-accent-foreground shadow-2xs data-[state=open]:bg-accent\",\n },\n size: {\n default: \"h-9 px-3 text-sm\",\n sm: \"h-8 px-2.5 text-xs\",\n lg: \"h-10 px-4 text-base\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nexport function navigationMenuTriggerStyle(\n variants?: VariantProps<typeof navigationMenuTriggerVariants>\n) {\n return navigationMenuTriggerVariants(variants);\n}\n\n/* --- 5. NavigationMenuTrigger --- */\nexport interface NavigationMenuTriggerProps\n extends JSX.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof navigationMenuTriggerVariants> {\n class?: string;\n hideChevron?: boolean;\n}\n\nexport const NavigationMenuTrigger: ParentComponent<NavigationMenuTriggerProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"variant\", \"size\", \"hideChevron\", \"children\"]);\n const rootContext = useNavigationMenu();\n const itemContext = useNavigationMenuItem();\n\n const handleClick = (e: MouseEvent) => {\n e.preventDefault();\n if (itemContext.isOpen()) {\n rootContext.setActiveValue(null);\n } else {\n rootContext.setActiveValue(itemContext.value);\n }\n };\n\n return (\n <button\n type=\"button\"\n aria-expanded={itemContext.isOpen()}\n data-state={itemContext.isOpen() ? \"open\" : \"closed\"}\n onClick={handleClick}\n class={cn(\n navigationMenuTriggerVariants({ variant: local.variant, size: local.size }),\n \"gap-1.5\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n <Show when={!local.hideChevron}>\n <ChevronDown\n class={cn(\n \"size-3.5 text-muted-foreground transition-transform duration-200\",\n itemContext.isOpen() && \"rotate-180 text-foreground\"\n )}\n aria-hidden=\"true\"\n />\n </Show>\n </button>\n );\n};\n\n/* --- 6. NavigationMenuContent (Mega Menu Flyout) --- */\nexport interface NavigationMenuContentProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavigationMenuContent: ParentComponent<NavigationMenuContentProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"children\"]);\n const itemContext = useNavigationMenuItem();\n\n return (\n <Show when={itemContext.isOpen()}>\n <div\n data-state={itemContext.isOpen() ? \"open\" : \"closed\"}\n class={cn(\n \"absolute left-0 top-full mt-1.5 w-auto rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-lg outline-hidden z-50\",\n \"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 duration-200\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n </Show>\n );\n};\n\n/* --- 7. NavigationMenuLink --- */\nexport interface NavigationMenuLinkProps extends JSX.AnchorHTMLAttributes<HTMLAnchorElement> {\n class?: string;\n active?: boolean;\n}\n\nexport const NavigationMenuLink: ParentComponent<NavigationMenuLinkProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\", \"active\", \"children\"]);\n\n return (\n <a\n data-active={local.active ? \"\" : undefined}\n class={cn(\n \"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground\",\n local.active && \"bg-accent/60 text-accent-foreground\",\n local.class\n )}\n {...rest}\n >\n {local.children}\n </a>\n );\n};\n\n/* --- 8. NavigationMenuViewport --- */\nexport interface NavigationMenuViewportProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavigationMenuViewport: Component<NavigationMenuViewportProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div class=\"absolute left-0 top-full flex justify-center\">\n <div\n class={cn(\n \"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg transition-all\",\n local.class\n )}\n {...rest}\n />\n </div>\n );\n};\n\n/* --- 9. NavigationMenuIndicator --- */\nexport interface NavigationMenuIndicatorProps extends JSX.HTMLAttributes<HTMLDivElement> {\n class?: string;\n}\n\nexport const NavigationMenuIndicator: Component<NavigationMenuIndicatorProps> = (props) => {\n const [local, rest] = splitProps(props, [\"class\"]);\n\n return (\n <div\n class={cn(\n \"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden transition-[width,transform] duration-200\",\n local.class\n )}\n {...rest}\n >\n <div class=\"relative top-[60%] h-2 w-2 rotate-45 rounded-tl-xs bg-border shadow-md\" />\n </div>\n );\n};\n",
|
|
19
19
|
"type": "registry:ui"
|
|
20
20
|
}
|
|
21
21
|
]
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
{
|
|
19
19
|
"path": "ui/number-input.tsx",
|
|
20
|
-
"content": "import { createSignal, onCleanup, splitProps, type Component, type JSX } from \"solid-js\";\nimport { NumberField as KobalteNumberField } from \"@kobalte/core/number-field\";\nimport { Plus, Minus } from \"lucide-solid\";\nimport { Input } from \"
|
|
20
|
+
"content": "import { createSignal, onCleanup, splitProps, type Component, type JSX } from \"solid-js\";\nimport { NumberField as KobalteNumberField } from \"@kobalte/core/number-field\";\nimport { Plus, Minus } from \"lucide-solid\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { createLongPress } from \"@/hooks/create-long-press\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface NumberInputProps {\n value?: number;\n defaultValue?: number;\n onValueChange?: (value: number) => void;\n minValue?: number;\n maxValue?: number;\n step?: number;\n allowNegative?: boolean;\n disabled?: boolean;\n readOnly?: boolean;\n class?: string;\n id?: string;\n}\n\nexport const NumberInput: Component<NumberInputProps> = (props) => {\n const [local, rest] = splitProps(props, [\n \"value\",\n \"defaultValue\",\n \"onValueChange\",\n \"minValue\",\n \"maxValue\",\n \"step\",\n \"allowNegative\",\n \"disabled\",\n \"readOnly\",\n \"class\",\n \"id\",\n ]);\n\n const [internalVal, setInternalVal] = createSignal<number>(\n local.value !== undefined\n ? local.value\n : local.defaultValue !== undefined\n ? local.defaultValue\n : 0\n );\n\n let autoRepeatInterval: ReturnType<typeof setInterval> | undefined;\n\n const currentVal = () => (local.value !== undefined ? local.value : internalVal());\n\n const effectiveMin = () => {\n if (local.minValue !== undefined) return local.minValue;\n return local.allowNegative ? -Infinity : 0;\n };\n\n const effectiveMax = () => {\n if (local.maxValue !== undefined) return local.maxValue;\n return Infinity;\n };\n\n const handleValueChange = (val: number) => {\n if (isNaN(val)) return;\n const clamped = Math.max(effectiveMin(), Math.min(effectiveMax(), val));\n setInternalVal(clamped);\n local.onValueChange?.(clamped);\n };\n\n const stopAutoRepeat = () => {\n if (autoRepeatInterval) {\n clearInterval(autoRepeatInterval);\n autoRepeatInterval = undefined;\n }\n };\n\n const startAutoRepeat = (direction: 1 | -1) => {\n if (local.disabled || local.readOnly) return;\n stopAutoRepeat();\n const stepAmount = local.step || 1;\n autoRepeatInterval = setInterval(() => {\n handleValueChange(currentVal() + direction * stepAmount);\n }, 75);\n };\n\n /* Nikala UI createLongPress hook for Increment Trigger */\n const incrementLongPress = createLongPress(\n () => {\n startAutoRepeat(1);\n },\n {\n threshold: 300,\n onCancel: stopAutoRepeat,\n }\n );\n\n /* Nikala UI createLongPress hook for Decrement Trigger */\n const decrementLongPress = createLongPress(\n () => {\n startAutoRepeat(-1);\n },\n {\n threshold: 300,\n onCancel: stopAutoRepeat,\n }\n );\n\n onCleanup(() => stopAutoRepeat());\n\n return (\n <KobalteNumberField\n value={currentVal()}\n onRawValueChange={handleValueChange}\n minValue={effectiveMin()}\n maxValue={effectiveMax()}\n step={local.step || 1}\n disabled={local.disabled}\n readOnly={local.readOnly}\n id={local.id}\n class={cn(\"relative flex items-center max-w-[160px]\", local.class)}\n {...rest}\n >\n <KobalteNumberField.Input\n as={Input}\n class=\"pr-16 text-center font-mono focus-visible:ring-1\"\n />\n\n <div class=\"absolute right-1 flex items-center gap-0.5\">\n <KobalteNumberField.DecrementTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n class=\"h-7 w-7 rounded-sm p-0 text-muted-foreground hover:text-foreground select-none\"\n aria-label=\"Decrement value\"\n {...decrementLongPress.props}\n >\n <Minus class=\"h-3.5 w-3.5\" />\n </KobalteNumberField.DecrementTrigger>\n\n <KobalteNumberField.IncrementTrigger\n as={Button}\n variant=\"ghost\"\n size=\"icon\"\n class=\"h-7 w-7 rounded-sm p-0 text-muted-foreground hover:text-foreground select-none\"\n aria-label=\"Increment value\"\n {...incrementLongPress.props}\n >\n <Plus class=\"h-3.5 w-3.5\" />\n </KobalteNumberField.IncrementTrigger>\n </div>\n </KobalteNumberField>\n );\n};\n",
|
|
21
21
|
"type": "registry:ui"
|
|
22
22
|
}
|
|
23
23
|
]
|