@nikala-ui/core 0.11.0 → 0.12.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/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
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSignal,
|
|
3
|
+
splitProps,
|
|
4
|
+
Show,
|
|
5
|
+
type JSX,
|
|
6
|
+
type ParentComponent,
|
|
7
|
+
} from "solid-js";
|
|
8
|
+
import { Portal } from "solid-js/web";
|
|
9
|
+
import {
|
|
10
|
+
Monitor,
|
|
11
|
+
Tablet,
|
|
12
|
+
Smartphone,
|
|
13
|
+
Sparkles,
|
|
14
|
+
RotateCcw,
|
|
15
|
+
Terminal,
|
|
16
|
+
Check,
|
|
17
|
+
Maximize2,
|
|
18
|
+
Minimize2,
|
|
19
|
+
Grid,
|
|
20
|
+
} from "lucide-solid";
|
|
21
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
|
22
|
+
import { Button } from "@/components/ui/button";
|
|
23
|
+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
|
24
|
+
import { CodeBlock } from "@/components/ui/code-block";
|
|
25
|
+
import { createClipboard } from "@/hooks/create-clipboard";
|
|
26
|
+
import { cn } from "@/lib/cn";
|
|
27
|
+
|
|
28
|
+
export type ComponentViewerViewport = "desktop" | "tablet" | "mobile";
|
|
29
|
+
|
|
30
|
+
export interface ComponentViewerProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
31
|
+
/** Name of the component or UI primitive (e.g. "button", "dialog") */
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Title label displayed in the viewer header */
|
|
34
|
+
title?: string;
|
|
35
|
+
/** Raw TSX/JSX source code to display in the Code tab and copy */
|
|
36
|
+
code: string;
|
|
37
|
+
/** Programming language for syntax highlighting (defaults to "tsx") */
|
|
38
|
+
lang?: string;
|
|
39
|
+
/** Alignment of the preview canvas: "center" (default), "start", or "end" */
|
|
40
|
+
align?: "center" | "start" | "end";
|
|
41
|
+
/** Optional CLI command to install this component (e.g. "bunx @nikala-ui/cli add button") */
|
|
42
|
+
command?: string;
|
|
43
|
+
/** Initial responsive viewport (default: "desktop") */
|
|
44
|
+
defaultViewport?: ComponentViewerViewport;
|
|
45
|
+
/** Whether to allow overflowing elements in the canvas (e.g. popovers, dropdowns) */
|
|
46
|
+
allowOverflow?: boolean;
|
|
47
|
+
/** Enables responsive screen size toggle controls (Desktop, Tablet, Mobile) */
|
|
48
|
+
responsive?: boolean;
|
|
49
|
+
/** Enables canvas grid pattern toggle */
|
|
50
|
+
showGridToggle?: boolean;
|
|
51
|
+
/** Enables a reset button to re-mount the preview canvas */
|
|
52
|
+
reRenderable?: boolean;
|
|
53
|
+
class?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Advanced component inspection and preview container with responsive viewport emulation,
|
|
58
|
+
* background grid patterns, live re-mounting, AI context generation, and syntax-highlighted code.
|
|
59
|
+
*/
|
|
60
|
+
export const ComponentViewer: ParentComponent<ComponentViewerProps> = (props) => {
|
|
61
|
+
const [local, rest] = splitProps(props, [
|
|
62
|
+
"name",
|
|
63
|
+
"title",
|
|
64
|
+
"code",
|
|
65
|
+
"lang",
|
|
66
|
+
"align",
|
|
67
|
+
"command",
|
|
68
|
+
"defaultViewport",
|
|
69
|
+
"allowOverflow",
|
|
70
|
+
"responsive",
|
|
71
|
+
"showGridToggle",
|
|
72
|
+
"reRenderable",
|
|
73
|
+
"class",
|
|
74
|
+
"children",
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
const [viewport, setViewport] = createSignal<ComponentViewerViewport>(
|
|
78
|
+
local.defaultViewport || "desktop"
|
|
79
|
+
);
|
|
80
|
+
const [showGrid, setShowGrid] = createSignal(true);
|
|
81
|
+
const [isFullscreen, setIsFullscreen] = createSignal(false);
|
|
82
|
+
const aiClipboard = createClipboard();
|
|
83
|
+
const cmdClipboard = createClipboard();
|
|
84
|
+
const [mounted, setMounted] = createSignal(true);
|
|
85
|
+
|
|
86
|
+
const cliCommand = () => local.command;
|
|
87
|
+
|
|
88
|
+
const handleCopyAi = async () => {
|
|
89
|
+
const cmd = cliCommand();
|
|
90
|
+
const prompt = `// ${local.title || local.name || "Component"}
|
|
91
|
+
${cmd ? `// Installation: ${cmd}\n` : ""}// Usage Code:
|
|
92
|
+
${local.code}`;
|
|
93
|
+
await aiClipboard.copy(prompt);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleCopyCmd = async () => {
|
|
97
|
+
const cmd = cliCommand();
|
|
98
|
+
if (!cmd) return;
|
|
99
|
+
await cmdClipboard.copy(cmd);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleReset = () => {
|
|
103
|
+
setMounted(false);
|
|
104
|
+
setTimeout(() => setMounted(true), 20);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const alignmentClass = () => {
|
|
108
|
+
if (local.align === "start") return "items-start justify-start";
|
|
109
|
+
if (local.align === "end") return "items-end justify-end";
|
|
110
|
+
return "items-center justify-center";
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const viewportWidthClass = () => {
|
|
114
|
+
switch (viewport()) {
|
|
115
|
+
case "mobile":
|
|
116
|
+
return "max-w-[375px]";
|
|
117
|
+
case "tablet":
|
|
118
|
+
return "max-w-[768px]";
|
|
119
|
+
default:
|
|
120
|
+
return "max-w-full";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const renderViewerContent = (isModal = false) => (
|
|
125
|
+
<div
|
|
126
|
+
class={cn(
|
|
127
|
+
"group relative flex flex-col rounded-lg border border-border bg-card/60 shadow-2xs transition-all w-full",
|
|
128
|
+
isModal && "h-full flex-1 bg-background shadow-2xl",
|
|
129
|
+
!isModal && "my-6",
|
|
130
|
+
local.class
|
|
131
|
+
)}
|
|
132
|
+
{...rest}
|
|
133
|
+
>
|
|
134
|
+
<Tabs defaultValue="preview" class="relative w-full flex flex-col h-full flex-1 gap-0">
|
|
135
|
+
{/* Top Header & Toolbar */}
|
|
136
|
+
<div class="flex flex-wrap items-center justify-between border-b border-border/70 px-3 py-2 gap-2 bg-muted/30 shrink-0">
|
|
137
|
+
<div class="flex items-center gap-3">
|
|
138
|
+
<TabsList class="h-8 rounded-md bg-muted/60 p-0.5 gap-1">
|
|
139
|
+
<TabsTrigger value="preview" class="h-7 px-3 text-xs cursor-pointer data-[state=active]:bg-background data-[state=active]:shadow-2xs">
|
|
140
|
+
Preview
|
|
141
|
+
</TabsTrigger>
|
|
142
|
+
<TabsTrigger value="code" class="h-7 px-3 text-xs cursor-pointer data-[state=active]:bg-background data-[state=active]:shadow-2xs">
|
|
143
|
+
Code
|
|
144
|
+
</TabsTrigger>
|
|
145
|
+
</TabsList>
|
|
146
|
+
|
|
147
|
+
<Show when={local.title}>
|
|
148
|
+
<span class="text-xs font-semibold text-foreground tracking-tight hidden sm:inline-block">
|
|
149
|
+
{local.title}
|
|
150
|
+
</span>
|
|
151
|
+
</Show>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{/* Right Action Tools */}
|
|
155
|
+
<div class="flex flex-wrap items-center gap-1.5 ml-auto">
|
|
156
|
+
{/* Responsive Viewport Switcher */}
|
|
157
|
+
<Show when={local.responsive ?? true}>
|
|
158
|
+
<div class="flex items-center rounded-md border border-border/60 bg-muted/40 p-0.5 gap-0.5 mr-1">
|
|
159
|
+
<Tooltip>
|
|
160
|
+
<TooltipTrigger
|
|
161
|
+
as={Button}
|
|
162
|
+
variant="ghost"
|
|
163
|
+
size="icon"
|
|
164
|
+
onClick={() => setViewport("desktop")}
|
|
165
|
+
aria-label="Desktop viewport"
|
|
166
|
+
class={cn(
|
|
167
|
+
"size-6.5 p-0 rounded-sm cursor-pointer text-muted-foreground",
|
|
168
|
+
viewport() === "desktop" && "bg-background text-foreground shadow-2xs font-semibold"
|
|
169
|
+
)}
|
|
170
|
+
>
|
|
171
|
+
<Monitor class="size-3.5" />
|
|
172
|
+
</TooltipTrigger>
|
|
173
|
+
<TooltipContent class="text-[10px] py-1 px-2">Desktop (100%)</TooltipContent>
|
|
174
|
+
</Tooltip>
|
|
175
|
+
|
|
176
|
+
<Tooltip>
|
|
177
|
+
<TooltipTrigger
|
|
178
|
+
as={Button}
|
|
179
|
+
variant="ghost"
|
|
180
|
+
size="icon"
|
|
181
|
+
onClick={() => setViewport("tablet")}
|
|
182
|
+
aria-label="Tablet viewport"
|
|
183
|
+
class={cn(
|
|
184
|
+
"size-6.5 p-0 rounded-sm cursor-pointer text-muted-foreground",
|
|
185
|
+
viewport() === "tablet" && "bg-background text-foreground shadow-2xs font-semibold"
|
|
186
|
+
)}
|
|
187
|
+
>
|
|
188
|
+
<Tablet class="size-3.5" />
|
|
189
|
+
</TooltipTrigger>
|
|
190
|
+
<TooltipContent class="text-[10px] py-1 px-2">Tablet (768px)</TooltipContent>
|
|
191
|
+
</Tooltip>
|
|
192
|
+
|
|
193
|
+
<Tooltip>
|
|
194
|
+
<TooltipTrigger
|
|
195
|
+
as={Button}
|
|
196
|
+
variant="ghost"
|
|
197
|
+
size="icon"
|
|
198
|
+
onClick={() => setViewport("mobile")}
|
|
199
|
+
aria-label="Mobile viewport"
|
|
200
|
+
class={cn(
|
|
201
|
+
"size-6.5 p-0 rounded-sm cursor-pointer text-muted-foreground",
|
|
202
|
+
viewport() === "mobile" && "bg-background text-foreground shadow-2xs font-semibold"
|
|
203
|
+
)}
|
|
204
|
+
>
|
|
205
|
+
<Smartphone class="size-3.5" />
|
|
206
|
+
</TooltipTrigger>
|
|
207
|
+
<TooltipContent class="text-[10px] py-1 px-2">Mobile (375px)</TooltipContent>
|
|
208
|
+
</Tooltip>
|
|
209
|
+
</div>
|
|
210
|
+
</Show>
|
|
211
|
+
|
|
212
|
+
{/* Grid Pattern Toggle */}
|
|
213
|
+
<Show when={local.showGridToggle ?? true}>
|
|
214
|
+
<Tooltip>
|
|
215
|
+
<TooltipTrigger
|
|
216
|
+
as={Button}
|
|
217
|
+
variant="ghost"
|
|
218
|
+
size="icon"
|
|
219
|
+
onClick={() => setShowGrid(!showGrid())}
|
|
220
|
+
aria-label="Toggle background grid"
|
|
221
|
+
class={cn(
|
|
222
|
+
"size-7 rounded-md border border-border/50 text-muted-foreground hover:text-foreground cursor-pointer",
|
|
223
|
+
showGrid() && "text-foreground bg-muted/60"
|
|
224
|
+
)}
|
|
225
|
+
>
|
|
226
|
+
<Grid class="size-3.5" />
|
|
227
|
+
</TooltipTrigger>
|
|
228
|
+
<TooltipContent class="text-[10px] py-1 px-2">Toggle Canvas Grid</TooltipContent>
|
|
229
|
+
</Tooltip>
|
|
230
|
+
</Show>
|
|
231
|
+
|
|
232
|
+
{/* Re-render Reset Canvas */}
|
|
233
|
+
<Show when={local.reRenderable ?? true}>
|
|
234
|
+
<Tooltip>
|
|
235
|
+
<TooltipTrigger
|
|
236
|
+
as={Button}
|
|
237
|
+
variant="ghost"
|
|
238
|
+
size="icon"
|
|
239
|
+
onClick={handleReset}
|
|
240
|
+
aria-label="Reset preview canvas"
|
|
241
|
+
class="size-7 rounded-md border border-border/50 text-muted-foreground hover:text-foreground cursor-pointer"
|
|
242
|
+
>
|
|
243
|
+
<RotateCcw class="size-3.5" />
|
|
244
|
+
</TooltipTrigger>
|
|
245
|
+
<TooltipContent class="text-[10px] py-1 px-2">Reset canvas</TooltipContent>
|
|
246
|
+
</Tooltip>
|
|
247
|
+
</Show>
|
|
248
|
+
|
|
249
|
+
{/* Copy for AI */}
|
|
250
|
+
<Tooltip>
|
|
251
|
+
<TooltipTrigger
|
|
252
|
+
as={Button}
|
|
253
|
+
variant="ghost"
|
|
254
|
+
size="sm"
|
|
255
|
+
onClick={handleCopyAi}
|
|
256
|
+
aria-label="Copy context formatted for AI assistants"
|
|
257
|
+
class="h-7 px-2 text-xs text-muted-foreground hover:text-foreground border border-border/50 rounded-md cursor-pointer flex items-center gap-1.5"
|
|
258
|
+
>
|
|
259
|
+
<Show when={aiClipboard.copied()} fallback={<Sparkles class="size-3" />}>
|
|
260
|
+
<Check class="size-3 text-emerald-500" />
|
|
261
|
+
</Show>
|
|
262
|
+
<span class="hidden sm:inline">{aiClipboard.copied() ? "Copied!" : "Prompt"}</span>
|
|
263
|
+
</TooltipTrigger>
|
|
264
|
+
<TooltipContent class="text-[10px] py-1 px-2">Copy code formatted for AI</TooltipContent>
|
|
265
|
+
</Tooltip>
|
|
266
|
+
|
|
267
|
+
{/* Copy CLI Installation Command (only when command prop is provided) */}
|
|
268
|
+
<Show when={cliCommand()}>
|
|
269
|
+
<Tooltip>
|
|
270
|
+
<TooltipTrigger
|
|
271
|
+
as={Button}
|
|
272
|
+
variant="ghost"
|
|
273
|
+
size="sm"
|
|
274
|
+
onClick={handleCopyCmd}
|
|
275
|
+
aria-label="Copy CLI installation command"
|
|
276
|
+
class="h-7 px-2 text-xs font-mono text-muted-foreground hover:text-foreground border border-border/50 rounded-md cursor-pointer flex items-center gap-1.5"
|
|
277
|
+
>
|
|
278
|
+
<Show when={cmdClipboard.copied()} fallback={<Terminal class="size-3" />}>
|
|
279
|
+
<Check class="size-3 text-emerald-500" />
|
|
280
|
+
</Show>
|
|
281
|
+
<span class="hidden md:inline">{cmdClipboard.copied() ? "Copied!" : cliCommand()}</span>
|
|
282
|
+
</TooltipTrigger>
|
|
283
|
+
<TooltipContent class="text-[10px] py-1 px-2">Copy CLI command</TooltipContent>
|
|
284
|
+
</Tooltip>
|
|
285
|
+
</Show>
|
|
286
|
+
|
|
287
|
+
{/* Fullscreen Expand Button */}
|
|
288
|
+
<Tooltip>
|
|
289
|
+
<TooltipTrigger
|
|
290
|
+
as={Button}
|
|
291
|
+
variant="ghost"
|
|
292
|
+
size="icon"
|
|
293
|
+
onClick={() => setIsFullscreen(!isFullscreen())}
|
|
294
|
+
aria-label={isFullscreen() ? "Exit fullscreen" : "Expand to fullscreen"}
|
|
295
|
+
class="size-7 rounded-md border border-border/50 text-muted-foreground hover:text-foreground cursor-pointer"
|
|
296
|
+
>
|
|
297
|
+
<Show when={isFullscreen()} fallback={<Maximize2 class="size-3.5" />}>
|
|
298
|
+
<Minimize2 class="size-3.5 text-primary" />
|
|
299
|
+
</Show>
|
|
300
|
+
</TooltipTrigger>
|
|
301
|
+
<TooltipContent class="text-[10px] py-1 px-2">
|
|
302
|
+
{isFullscreen() ? "Exit Fullscreen" : "Fullscreen Preview"}
|
|
303
|
+
</TooltipContent>
|
|
304
|
+
</Tooltip>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
{/* Live Preview Canvas Content */}
|
|
309
|
+
<TabsContent
|
|
310
|
+
value="preview"
|
|
311
|
+
class={cn(
|
|
312
|
+
"relative flex-1 p-4 sm:p-6 md:p-10 flex flex-col items-center justify-center transition-all",
|
|
313
|
+
isModal ? "min-h-[450px] flex-1" : "min-h-[280px]",
|
|
314
|
+
showGrid() &&
|
|
315
|
+
"bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] dark:bg-[radial-gradient(#27272a_1px,transparent_1px)] [background-size:16px_16px]",
|
|
316
|
+
local.allowOverflow ? "overflow-visible" : "overflow-x-auto"
|
|
317
|
+
)}
|
|
318
|
+
>
|
|
319
|
+
<div
|
|
320
|
+
class={cn(
|
|
321
|
+
"w-full transition-all duration-300 flex",
|
|
322
|
+
viewportWidthClass(),
|
|
323
|
+
alignmentClass(),
|
|
324
|
+
viewport() !== "desktop" &&
|
|
325
|
+
"rounded-lg border border-dashed border-border/80 p-4 bg-background/80 shadow-xs"
|
|
326
|
+
)}
|
|
327
|
+
>
|
|
328
|
+
<Show when={mounted()}>
|
|
329
|
+
{local.children}
|
|
330
|
+
</Show>
|
|
331
|
+
</div>
|
|
332
|
+
</TabsContent>
|
|
333
|
+
|
|
334
|
+
{/* Source Code View Content */}
|
|
335
|
+
<TabsContent value="code" class={cn("p-0 m-0", isModal && "flex-1 overflow-auto")}>
|
|
336
|
+
<CodeBlock
|
|
337
|
+
code={local.code}
|
|
338
|
+
lang={local.lang || "tsx"}
|
|
339
|
+
filename={local.name ? `${local.name}.tsx` : undefined}
|
|
340
|
+
class="my-0 border-0 rounded-t-none"
|
|
341
|
+
/>
|
|
342
|
+
</TabsContent>
|
|
343
|
+
</Tabs>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<>
|
|
349
|
+
<Show when={!isFullscreen()}>
|
|
350
|
+
{renderViewerContent(false)}
|
|
351
|
+
</Show>
|
|
352
|
+
|
|
353
|
+
{/* Fullscreen Portal Modal */}
|
|
354
|
+
<Show when={isFullscreen()}>
|
|
355
|
+
<Portal mount={typeof document !== "undefined" ? document.body : undefined}>
|
|
356
|
+
<div class="fixed inset-0 z-[9999] flex flex-col bg-background/95 backdrop-blur-md p-4 sm:p-8 animate-in fade-in duration-200">
|
|
357
|
+
{renderViewerContent(true)}
|
|
358
|
+
</div>
|
|
359
|
+
</Portal>
|
|
360
|
+
</Show>
|
|
361
|
+
</>
|
|
362
|
+
);
|
|
363
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { splitProps, type JSX, type ValidComponent } from "solid-js";
|
|
2
|
+
import { Dynamic } from "solid-js/web";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@/lib/cn";
|
|
5
|
+
|
|
6
|
+
export const containerVariants = cva("w-full mx-auto px-4 sm:px-6 lg:px-8", {
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
sm: "max-w-screen-sm",
|
|
10
|
+
md: "max-w-screen-md",
|
|
11
|
+
lg: "max-w-screen-lg",
|
|
12
|
+
xl: "max-w-screen-xl",
|
|
13
|
+
"2xl": "max-w-screen-2xl",
|
|
14
|
+
full: "max-w-full",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
size: "2xl",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export interface ContainerProps<T extends ValidComponent = "div">
|
|
23
|
+
extends JSX.HTMLAttributes<HTMLDivElement>,
|
|
24
|
+
VariantProps<typeof containerVariants> {
|
|
25
|
+
/** Underlying HTML element or Solid component tag (defaults to "div") */
|
|
26
|
+
as?: T;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Responsive container primitive constraining max-width with semantic padding tokens.
|
|
32
|
+
*/
|
|
33
|
+
export const Container = <T extends ValidComponent = "div">(props: ContainerProps<T>) => {
|
|
34
|
+
const [local, rest] = splitProps(props as ContainerProps, ["as", "size", "class", "children"]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Dynamic
|
|
38
|
+
component={local.as || "div"}
|
|
39
|
+
class={cn(containerVariants({ size: local.size }), local.class)}
|
|
40
|
+
{...rest}
|
|
41
|
+
>
|
|
42
|
+
{local.children}
|
|
43
|
+
</Dynamic>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { splitProps, type Component, type JSX, type ValidComponent } from "solid-js";
|
|
2
2
|
import * as ContextMenuPrimitive from "@kobalte/core/context-menu";
|
|
3
3
|
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
|
|
4
|
-
import { createClickOutside } from "
|
|
5
|
-
import { ScrollArea } from "
|
|
6
|
-
import { Separator } from "
|
|
7
|
-
import { Kbd } from "
|
|
4
|
+
import { createClickOutside } from "@/hooks/create-click-outside";
|
|
5
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
6
|
+
import { Separator } from "@/components/ui/separator";
|
|
7
|
+
import { Kbd } from "@/components/ui/kbd";
|
|
8
8
|
import { cn } from "@/lib/cn";
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
export const ContextMenu: Component<ContextMenuRootProps> = (props) => {
|
|
13
|
-
return <ContextMenuPrimitive.Root {...props} />;
|
|
14
|
-
};
|
|
15
|
-
|
|
10
|
+
export const ContextMenu = ContextMenuPrimitive.Root;
|
|
16
11
|
export const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
|
17
12
|
export const ContextMenuGroup = ContextMenuPrimitive.Group;
|
|
13
|
+
export const ContextMenuPortal = ContextMenuPrimitive.Portal;
|
|
18
14
|
export const ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
15
|
+
export const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
19
16
|
|
|
20
17
|
export type ContextMenuSubTriggerProps<T extends ValidComponent = "div"> =
|
|
21
18
|
ContextMenuPrimitive.ContextMenuSubTriggerProps<T> & {
|
|
@@ -32,7 +29,7 @@ export const ContextMenuSubTrigger = <T extends ValidComponent = "div">(
|
|
|
32
29
|
return (
|
|
33
30
|
<ContextMenuPrimitive.SubTrigger
|
|
34
31
|
class={cn(
|
|
35
|
-
"flex cursor-
|
|
32
|
+
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent text-foreground",
|
|
36
33
|
local.inset && "pl-8",
|
|
37
34
|
local.class
|
|
38
35
|
)}
|
|
@@ -126,7 +123,7 @@ export const ContextMenuItem = <T extends ValidComponent = "div">(
|
|
|
126
123
|
return (
|
|
127
124
|
<ContextMenuPrimitive.Item
|
|
128
125
|
class={cn(
|
|
129
|
-
"relative flex cursor-
|
|
126
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
130
127
|
local.inset && "pl-8",
|
|
131
128
|
local.class
|
|
132
129
|
)}
|
|
@@ -151,7 +148,7 @@ export const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(
|
|
|
151
148
|
<ContextMenuPrimitive.CheckboxItem
|
|
152
149
|
checked={local.checked}
|
|
153
150
|
class={cn(
|
|
154
|
-
"relative flex cursor-
|
|
151
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
155
152
|
local.class
|
|
156
153
|
)}
|
|
157
154
|
{...(rest as any)}
|
|
@@ -168,25 +165,73 @@ export const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(
|
|
|
168
165
|
);
|
|
169
166
|
};
|
|
170
167
|
|
|
171
|
-
export
|
|
168
|
+
export type ContextMenuRadioItemProps<T extends ValidComponent = "div"> =
|
|
169
|
+
ContextMenuPrimitive.ContextMenuRadioItemProps<T> & {
|
|
170
|
+
class?: string;
|
|
171
|
+
children?: JSX.Element;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const ContextMenuRadioItem = <T extends ValidComponent = "div">(
|
|
175
|
+
props: PolymorphicProps<T, ContextMenuRadioItemProps<T>>
|
|
176
|
+
) => {
|
|
177
|
+
const [local, rest] = splitProps(props as ContextMenuRadioItemProps, ["class", "children"]);
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<ContextMenuPrimitive.RadioItem
|
|
181
|
+
class={cn(
|
|
182
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
183
|
+
local.class
|
|
184
|
+
)}
|
|
185
|
+
{...(rest as any)}
|
|
186
|
+
>
|
|
187
|
+
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
188
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
189
|
+
<svg class="h-2 w-2 fill-current" viewBox="0 0 8 8">
|
|
190
|
+
<circle cx="4" cy="4" r="3" />
|
|
191
|
+
</svg>
|
|
192
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
193
|
+
</span>
|
|
194
|
+
{local.children}
|
|
195
|
+
</ContextMenuPrimitive.RadioItem>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export interface ContextMenuLabelProps {
|
|
172
200
|
class?: string;
|
|
201
|
+
inset?: boolean;
|
|
173
202
|
children?: JSX.Element;
|
|
174
203
|
}
|
|
175
204
|
|
|
176
|
-
export const
|
|
177
|
-
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
205
|
+
export const ContextMenuLabel: Component<ContextMenuLabelProps> = (props) => {
|
|
206
|
+
const [local, rest] = splitProps(props, ["class", "inset", "children"]);
|
|
178
207
|
|
|
179
208
|
return (
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
<ContextMenuPrimitive.GroupLabel
|
|
210
|
+
class={cn("px-2 py-1.5 text-sm font-semibold text-foreground", local.inset && "pl-8", local.class)}
|
|
211
|
+
{...rest}
|
|
212
|
+
>
|
|
213
|
+
{local.children}
|
|
214
|
+
</ContextMenuPrimitive.GroupLabel>
|
|
183
215
|
);
|
|
184
216
|
};
|
|
185
217
|
|
|
186
|
-
export
|
|
218
|
+
export const ContextMenuSeparator: Component<{ class?: string }> = (props) => {
|
|
219
|
+
return <Separator class={cn("-mx-1 my-1", props.class)} />;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export interface ContextMenuShortcutProps {
|
|
187
223
|
class?: string;
|
|
224
|
+
children?: JSX.Element;
|
|
188
225
|
}
|
|
189
226
|
|
|
190
|
-
export const
|
|
191
|
-
|
|
227
|
+
export const ContextMenuShortcut: Component<ContextMenuShortcutProps> = (props) => {
|
|
228
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<span class={cn("ml-auto pl-4 text-xs tracking-widest text-muted-foreground", local.class)} {...rest}>
|
|
232
|
+
<Kbd size="sm" variant="outline">
|
|
233
|
+
{local.children}
|
|
234
|
+
</Kbd>
|
|
235
|
+
</span>
|
|
236
|
+
);
|
|
192
237
|
};
|