@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.
Files changed (109) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +97 -20
  3. package/package.json +43 -2
  4. package/registry/api-table.json +21 -0
  5. package/registry/banner.json +1 -1
  6. package/registry/button.json +1 -1
  7. package/registry/callout.json +19 -0
  8. package/registry/checkbox.json +4 -1
  9. package/registry/code-block.json +26 -0
  10. package/registry/code-group.json +20 -0
  11. package/registry/combobox.json +1 -1
  12. package/registry/command.json +3 -2
  13. package/registry/component-viewer.json +25 -0
  14. package/registry/container.json +18 -0
  15. package/registry/context-menu.json +1 -1
  16. package/registry/create-app-updater.json +13 -0
  17. package/registry/create-document-tabs.json +13 -0
  18. package/registry/create-global-shortcut.json +13 -0
  19. package/registry/create-tauri-window.json +13 -0
  20. package/registry/create-tiptap-editor.json +34 -0
  21. package/registry/dialog.json +1 -1
  22. package/registry/dropdown-menu.json +3 -2
  23. package/registry/field.json +1 -1
  24. package/registry/file-tree.json +21 -0
  25. package/registry/footer.json +1 -1
  26. package/registry/form-message.json +3 -2
  27. package/registry/icon-button.json +1 -1
  28. package/registry/index.json +336 -5
  29. package/registry/navbar.json +1 -1
  30. package/registry/navigation-menu.json +1 -1
  31. package/registry/number-input.json +1 -1
  32. package/registry/package-manager-tabs.json +24 -0
  33. package/registry/pager.json +21 -0
  34. package/registry/popover.json +4 -1
  35. package/registry/resizable.json +1 -1
  36. package/registry/rich-text-editor.json +95 -0
  37. package/registry/scroll-area.json +1 -1
  38. package/registry/section-heading.json +21 -0
  39. package/registry/select.json +3 -2
  40. package/registry/sheet.json +1 -1
  41. package/registry/sidebar.json +1 -1
  42. package/registry/status.json +1 -1
  43. package/registry/steps.json +20 -0
  44. package/registry/switch.json +4 -1
  45. package/registry/table-of-contents.json +23 -0
  46. package/registry/tabs.json +4 -1
  47. package/registry/theme-manager.json +6 -5
  48. package/registry/titlebar-tabs.json +24 -0
  49. package/registry/titlebar.json +26 -0
  50. package/registry/toggle-group.json +1 -1
  51. package/registry/updater-modal.json +26 -0
  52. package/src/index.ts +48 -0
  53. package/src/registry/components/ui/api-table.tsx +117 -0
  54. package/src/registry/components/ui/banner.tsx +0 -2
  55. package/src/registry/components/ui/button.tsx +1 -1
  56. package/src/registry/components/ui/callout.tsx +137 -0
  57. package/src/registry/components/ui/checkbox.tsx +1 -1
  58. package/src/registry/components/ui/code-block.tsx +326 -0
  59. package/src/registry/components/ui/code-group.tsx +105 -0
  60. package/src/registry/components/ui/combobox.tsx +83 -18
  61. package/src/registry/components/ui/command.tsx +110 -106
  62. package/src/registry/components/ui/component-viewer.tsx +363 -0
  63. package/src/registry/components/ui/container.tsx +45 -0
  64. package/src/registry/components/ui/context-menu.tsx +67 -22
  65. package/src/registry/components/ui/dialog.tsx +42 -32
  66. package/src/registry/components/ui/dropdown-menu.tsx +69 -31
  67. package/src/registry/components/ui/field.tsx +7 -3
  68. package/src/registry/components/ui/file-tree.tsx +181 -0
  69. package/src/registry/components/ui/footer.tsx +1 -1
  70. package/src/registry/components/ui/form-message.tsx +2 -2
  71. package/src/registry/components/ui/icon-button.tsx +1 -1
  72. package/src/registry/components/ui/navbar.tsx +18 -11
  73. package/src/registry/components/ui/navigation-menu.tsx +2 -2
  74. package/src/registry/components/ui/number-input.tsx +3 -3
  75. package/src/registry/components/ui/package-manager-tabs.tsx +226 -0
  76. package/src/registry/components/ui/pager.tsx +102 -0
  77. package/src/registry/components/ui/popover.tsx +1 -1
  78. package/src/registry/components/ui/resizable.tsx +3 -1
  79. package/src/registry/components/ui/rich-text-editor/bubble-menu.tsx +236 -0
  80. package/src/registry/components/ui/rich-text-editor/editor.tsx +308 -0
  81. package/src/registry/components/ui/rich-text-editor/extensions.ts +116 -0
  82. package/src/registry/components/ui/rich-text-editor/footer.tsx +36 -0
  83. package/src/registry/components/ui/rich-text-editor/image-dialog.tsx +75 -0
  84. package/src/registry/components/ui/rich-text-editor/index.ts +11 -0
  85. package/src/registry/components/ui/rich-text-editor/link-dialog.tsx +48 -0
  86. package/src/registry/components/ui/rich-text-editor/markdown.ts +170 -0
  87. package/src/registry/components/ui/rich-text-editor/slash-command.tsx +301 -0
  88. package/src/registry/components/ui/rich-text-editor/table-controls.tsx +68 -0
  89. package/src/registry/components/ui/rich-text-editor/toolbar.tsx +465 -0
  90. package/src/registry/components/ui/rich-text-editor/use-editor.ts +231 -0
  91. package/src/registry/components/ui/scroll-area.tsx +12 -2
  92. package/src/registry/components/ui/section-heading.tsx +108 -0
  93. package/src/registry/components/ui/select.tsx +16 -19
  94. package/src/registry/components/ui/sheet.tsx +58 -53
  95. package/src/registry/components/ui/sidebar.tsx +4 -4
  96. package/src/registry/components/ui/status.tsx +7 -5
  97. package/src/registry/components/ui/steps.tsx +198 -0
  98. package/src/registry/components/ui/switch.tsx +1 -1
  99. package/src/registry/components/ui/table-of-contents.tsx +131 -0
  100. package/src/registry/components/ui/tabs.tsx +1 -1
  101. package/src/registry/components/ui/theme-toggle.tsx +175 -139
  102. package/src/registry/components/ui/titlebar-tabs.tsx +773 -0
  103. package/src/registry/components/ui/titlebar.tsx +468 -0
  104. package/src/registry/components/ui/toggle-group.tsx +1 -1
  105. package/src/registry/components/ui/updater-modal.tsx +254 -0
  106. package/src/registry/metadata.ts +167 -6
  107. package/src/registry/providers/theme-provider.tsx +16 -8
  108. package/src/registry/providers/theme-script.tsx +29 -9
  109. package/src/registry/providers/theme-transitions.ts +81 -50
@@ -1,4 +1,4 @@
1
- import { splitProps, type Component, For, Show } from "solid-js";
1
+ import { splitProps, type Component, For, Show, type JSX } from "solid-js";
2
2
  import { Sun, Moon, Monitor } from "lucide-solid";
3
3
  import {
4
4
  useTheme,
@@ -10,35 +10,38 @@ import {
10
10
  runThemeTransition,
11
11
  type ThemeEffect,
12
12
  } from "../../providers/theme-transitions";
13
- import { Button } from "./button";
13
+ import { Button, type ButtonProps } from "./button";
14
14
  import {
15
15
  DropdownMenu,
16
16
  DropdownMenuTrigger,
17
17
  DropdownMenuContent,
18
18
  DropdownMenuItem,
19
+ DropdownMenuGroup,
19
20
  DropdownMenuLabel,
20
21
  DropdownMenuSeparator,
21
22
  } from "./dropdown-menu";
22
23
  import { cn } from "@/lib/cn";
23
24
 
24
- import { createColorMode } from "@nikala-ui/hooks";
25
-
26
- export interface ThemeToggleProps {
27
- /** Display mode: "mini" for compact dropdown, "max" for full customizer panel (default: "mini") */
28
- mode?: "mini" | "max";
25
+ export interface ThemeToggleProps
26
+ extends Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, "onClick"> {
27
+ /** Display mode: "button" (direct 1-click toggle), "mini" (compact dropdown), "max" (full customizer panel). Defaults to "mini". */
28
+ mode?: "button" | "mini" | "max";
29
29
  /** Transition animation effect when changing themes ("none", "circular", "fade") */
30
30
  effect?: ThemeEffect;
31
+ /** Button visual style variant when rendered as a button or dropdown trigger */
32
+ variant?: ButtonProps["variant"];
33
+ /** Button size */
34
+ size?: ButtonProps["size"];
31
35
  class?: string;
32
36
  }
33
37
 
34
38
  const ACCENT_OPTIONS: { name: AccentColor; label: string; color: string }[] = [
35
- { name: "wine", label: "Wine", color: "bg-[#722f37]" },
36
- { name: "violet", label: "Violet", color: "bg-[#7c3aed]" },
37
- { name: "sky", label: "Sky", color: "bg-[#0284c7]" },
38
- { name: "emerald", label: "Emerald", color: "bg-[#059669]" },
39
- { name: "rose", label: "Rose", color: "bg-[#e11d48]" },
40
- { name: "amber", label: "Amber", color: "bg-[#d97706]" },
41
- { name: "zinc", label: "Zinc", color: "bg-[#18181b]" },
39
+ { name: "yellow", label: "Yellow", color: "bg-yellow-500" },
40
+ { name: "red", label: "Red", color: "bg-red-500" },
41
+ { name: "violet", label: "Violet", color: "bg-violet-500" },
42
+ { name: "sky", label: "Sky", color: "bg-sky-500" },
43
+ { name: "emerald", label: "Emerald", color: "bg-emerald-500" },
44
+ { name: "zinc", label: "Zinc", color: "bg-zinc-800 dark:bg-zinc-200" },
42
45
  ];
43
46
 
44
47
  const RADIUS_OPTIONS: { value: Radius; label: string }[] = [
@@ -50,153 +53,186 @@ const RADIUS_OPTIONS: { value: Radius; label: string }[] = [
50
53
  ];
51
54
 
52
55
  /**
53
- * Interactive UI theme switcher supporting mini/max modes and View Transition animations.
56
+ * Interactive UI theme switcher supporting button, mini (dropdown), and max (customizer panel) modes with View Transition animations.
54
57
  */
55
58
  export const ThemeToggle: Component<ThemeToggleProps> = (props) => {
56
- const [local] = splitProps(props, ["mode", "effect", "class"]);
59
+ const [local, rest] = splitProps(props, ["mode", "effect", "variant", "size", "class"]);
57
60
  const { theme, setTheme, accent, setAccent, radius, setRadius } = useTheme();
58
61
 
59
- const colorMode = createColorMode({
60
- initialValue: theme(),
61
- storageKey: "nikala-theme-mode",
62
- });
63
-
64
62
  const mode = () => local.mode || "mini";
65
63
  const effect = () => local.effect || "none";
66
64
 
67
- /* Reactive accessor determining whether dark mode is active via createColorMode hook */
65
+ /* Reactive accessor determining whether dark mode is active */
68
66
  const isDarkMode = () => {
69
67
  const currentTheme = theme();
70
68
  if (currentTheme === "dark") return true;
71
69
  if (currentTheme === "light") return false;
72
- return colorMode.isDark();
70
+ if (typeof window !== "undefined") {
71
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
72
+ }
73
+ return false;
73
74
  };
74
75
 
75
76
  const changeThemeWithEffect = (newTheme: Theme, e: MouseEvent) => {
76
77
  runThemeTransition(effect(), e, () => {
77
78
  setTheme(newTheme);
78
- colorMode.setMode(newTheme);
79
79
  });
80
80
  };
81
81
 
82
+ const handleToggleClick = (e: MouseEvent) => {
83
+ const nextTheme: Theme = isDarkMode() ? "light" : "dark";
84
+ changeThemeWithEffect(nextTheme, e);
85
+ };
86
+
82
87
  return (
83
- <DropdownMenu placement="bottom-end">
84
- <DropdownMenuTrigger
85
- as={Button}
86
- variant="ghost"
87
- size="icon"
88
- class={cn("relative h-9 w-9 cursor-pointer", local.class)}
89
- >
90
- {/* Reactive Sun / Moon Icon Toggle */}
91
- <Show
92
- when={isDarkMode()}
93
- fallback={<Sun class="h-4 w-4 text-foreground transition-transform" />}
88
+ <Show
89
+ when={mode() !== "button"}
90
+ fallback={
91
+ /* Button Mode: Direct 1-click Dark/Light switcher without dropdown */
92
+ <Button
93
+ variant={local.variant || "ghost"}
94
+ size={local.size || "icon"}
95
+ onClick={handleToggleClick}
96
+ class={cn("relative cursor-pointer", local.class)}
97
+ aria-label="Toggle theme"
98
+ {...rest}
94
99
  >
95
- <Moon class="h-4 w-4 text-foreground transition-transform" />
96
- </Show>
100
+ <Show
101
+ when={isDarkMode()}
102
+ fallback={<Sun class="h-4 w-4 text-foreground transition-transform" />}
103
+ >
104
+ <Moon class="h-4 w-4 text-foreground transition-transform" />
105
+ </Show>
106
+ <span class="sr-only">Toggle theme</span>
107
+ </Button>
108
+ }
109
+ >
110
+ <DropdownMenu placement="bottom-end">
111
+ <DropdownMenuTrigger
112
+ as={Button}
113
+ variant={local.variant || "ghost"}
114
+ size={local.size || "icon"}
115
+ class={cn("relative cursor-pointer", local.class)}
116
+ {...rest}
117
+ >
118
+ {/* Reactive Sun / Moon Icon Toggle */}
119
+ <Show
120
+ when={isDarkMode()}
121
+ fallback={<Sun class="h-4 w-4 text-foreground transition-transform" />}
122
+ >
123
+ <Moon class="h-4 w-4 text-foreground transition-transform" />
124
+ </Show>
125
+ <span class="sr-only">Toggle theme</span>
126
+ </DropdownMenuTrigger>
97
127
 
98
- <span class="sr-only">Toggle theme</span>
99
- </DropdownMenuTrigger>
100
-
101
- <Show
102
- when={mode() === "max"}
103
- fallback={
104
- /* Mini Mode: Compact Dropdown */
105
- <DropdownMenuContent>
106
- <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect("light", e)}>
107
- <Sun class="mr-2 h-4 w-4 text-muted-foreground" />
108
- Light
109
- </DropdownMenuItem>
110
-
111
- <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect("dark", e)}>
112
- <Moon class="mr-2 h-4 w-4 text-muted-foreground" />
113
- Dark
114
- </DropdownMenuItem>
115
-
116
- <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect("system", e)}>
117
- <Monitor class="mr-2 h-4 w-4 text-muted-foreground" />
118
- System
119
- </DropdownMenuItem>
128
+ <Show
129
+ when={mode() === "max"}
130
+ fallback={
131
+ /* Mini Mode: Compact Dropdown */
132
+ <DropdownMenuContent>
133
+ <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect("light", e)}>
134
+ <Sun class="mr-2 h-4 w-4 text-muted-foreground" />
135
+ Light
136
+ </DropdownMenuItem>
137
+
138
+ <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect("dark", e)}>
139
+ <Moon class="mr-2 h-4 w-4 text-muted-foreground" />
140
+ Dark
141
+ </DropdownMenuItem>
142
+
143
+ <DropdownMenuItem onClick={(e: MouseEvent) => changeThemeWithEffect("system", e)}>
144
+ <Monitor class="mr-2 h-4 w-4 text-muted-foreground" />
145
+ System
146
+ </DropdownMenuItem>
147
+ </DropdownMenuContent>
148
+ }
149
+ >
150
+ {/* Max Mode: Full Theme Customizer Panel */}
151
+ <DropdownMenuContent class="w-64">
152
+ <div class="p-2 space-y-2">
153
+ <DropdownMenuGroup>
154
+ <DropdownMenuLabel class="px-0 pt-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
155
+ Theme Mode
156
+ </DropdownMenuLabel>
157
+ <div class="grid grid-cols-3 gap-1 my-1.5">
158
+ <Button
159
+ variant={theme() === "light" ? "default" : "outline"}
160
+ size="sm"
161
+ onClick={(e: MouseEvent) => changeThemeWithEffect("light", e)}
162
+ class="h-8 text-xs cursor-pointer"
163
+ >
164
+ Light
165
+ </Button>
166
+ <Button
167
+ variant={theme() === "dark" ? "default" : "outline"}
168
+ size="sm"
169
+ onClick={(e: MouseEvent) => changeThemeWithEffect("dark", e)}
170
+ class="h-8 text-xs cursor-pointer"
171
+ >
172
+ Dark
173
+ </Button>
174
+ <Button
175
+ variant={theme() === "system" ? "default" : "outline"}
176
+ size="sm"
177
+ onClick={(e: MouseEvent) => changeThemeWithEffect("system", e)}
178
+ class="h-8 text-xs cursor-pointer"
179
+ >
180
+ System
181
+ </Button>
182
+ </div>
183
+ </DropdownMenuGroup>
184
+
185
+ <DropdownMenuSeparator class="my-2" />
186
+
187
+ <DropdownMenuGroup>
188
+ <DropdownMenuLabel class="px-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
189
+ Brand Accent Color
190
+ </DropdownMenuLabel>
191
+ <div class="flex flex-wrap gap-1.5 my-1.5">
192
+ <For each={ACCENT_OPTIONS}>
193
+ {(opt) => (
194
+ <button
195
+ type="button"
196
+ title={opt.label}
197
+ onClick={() => setAccent(opt.name)}
198
+ class={cn(
199
+ "h-6 w-6 rounded-md transition-all cursor-pointer border border-border flex items-center justify-center",
200
+ opt.color,
201
+ accent() === opt.name
202
+ ? "ring-2 ring-primary ring-offset-2 ring-offset-background scale-110"
203
+ : "hover:scale-105"
204
+ )}
205
+ />
206
+ )}
207
+ </For>
208
+ </div>
209
+ </DropdownMenuGroup>
210
+
211
+ <DropdownMenuSeparator class="my-2" />
212
+
213
+ <DropdownMenuGroup>
214
+ <DropdownMenuLabel class="px-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
215
+ Border Radius
216
+ </DropdownMenuLabel>
217
+ <div class="grid grid-cols-5 gap-1 my-1.5">
218
+ <For each={RADIUS_OPTIONS}>
219
+ {(r) => (
220
+ <Button
221
+ variant={radius() === r.value ? "default" : "outline"}
222
+ size="sm"
223
+ onClick={() => setRadius(r.value)}
224
+ class="h-7 text-xs px-1 cursor-pointer"
225
+ >
226
+ {r.label}
227
+ </Button>
228
+ )}
229
+ </For>
230
+ </div>
231
+ </DropdownMenuGroup>
232
+ </div>
120
233
  </DropdownMenuContent>
121
- }
122
- >
123
- {/* Max Mode: Full Theme Customizer Panel */}
124
- <DropdownMenuContent class="w-64">
125
- <div class="p-2 space-y-2">
126
- <DropdownMenuLabel class="px-0 pt-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
127
- Theme Mode
128
- </DropdownMenuLabel>
129
- <div class="grid grid-cols-3 gap-1 my-1.5">
130
- <Button
131
- variant={theme() === "light" ? "default" : "outline"}
132
- size="sm"
133
- onClick={(e: MouseEvent) => changeThemeWithEffect("light", e)}
134
- class="h-8 text-xs cursor-pointer"
135
- >
136
- Light
137
- </Button>
138
- <Button
139
- variant={theme() === "dark" ? "default" : "outline"}
140
- size="sm"
141
- onClick={(e: MouseEvent) => changeThemeWithEffect("dark", e)}
142
- class="h-8 text-xs cursor-pointer"
143
- >
144
- Dark
145
- </Button>
146
- <Button
147
- variant={theme() === "system" ? "default" : "outline"}
148
- size="sm"
149
- onClick={(e: MouseEvent) => changeThemeWithEffect("system", e)}
150
- class="h-8 text-xs cursor-pointer"
151
- >
152
- System
153
- </Button>
154
- </div>
155
-
156
- <DropdownMenuSeparator class="my-2" />
157
-
158
- <DropdownMenuLabel class="px-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
159
- Brand Accent Color
160
- </DropdownMenuLabel>
161
- <div class="flex flex-wrap gap-1.5 my-1.5">
162
- <For each={ACCENT_OPTIONS}>
163
- {(opt) => (
164
- <button
165
- type="button"
166
- title={opt.label}
167
- onClick={() => setAccent(opt.name)}
168
- class={cn(
169
- "h-6 w-6 rounded-md transition-all cursor-pointer border border-border flex items-center justify-center",
170
- opt.color,
171
- accent() === opt.name ? "ring-2 ring-primary ring-offset-2 ring-offset-background scale-110" : "hover:scale-105"
172
- )}
173
- />
174
- )}
175
- </For>
176
- </div>
177
-
178
- <DropdownMenuSeparator class="my-2" />
179
-
180
- <DropdownMenuLabel class="px-0 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
181
- Border Radius
182
- </DropdownMenuLabel>
183
- <div class="grid grid-cols-5 gap-1 my-1.5">
184
- <For each={RADIUS_OPTIONS}>
185
- {(r) => (
186
- <Button
187
- variant={radius() === r.value ? "default" : "outline"}
188
- size="sm"
189
- onClick={() => setRadius(r.value)}
190
- class="h-7 text-xs px-1 cursor-pointer"
191
- >
192
- {r.label}
193
- </Button>
194
- )}
195
- </For>
196
- </div>
197
- </div>
198
- </DropdownMenuContent>
199
- </Show>
200
- </DropdownMenu>
234
+ </Show>
235
+ </DropdownMenu>
236
+ </Show>
201
237
  );
202
238
  };