@nikala-ui/core 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/package.json +25 -0
- package/registry/accordion.json +18 -0
- package/registry/alert.json +18 -0
- package/registry/avatar.json +17 -0
- package/registry/badge.json +18 -0
- package/registry/banner.json +19 -0
- package/registry/breadcrumb.json +17 -0
- package/registry/button.json +18 -0
- package/registry/card.json +17 -0
- package/registry/checkbox.json +17 -0
- package/registry/command.json +25 -0
- package/registry/dialog.json +18 -0
- package/registry/dropdown-menu.json +18 -0
- package/registry/index.json +297 -0
- package/registry/input-group.json +21 -0
- package/registry/input.json +17 -0
- package/registry/kbd.json +18 -0
- package/registry/label.json +18 -0
- package/registry/list.json +20 -0
- package/registry/radio-group.json +18 -0
- package/registry/select.json +18 -0
- package/registry/separator.json +17 -0
- package/registry/sheet.json +19 -0
- package/registry/skeleton.json +17 -0
- package/registry/switch.json +17 -0
- package/registry/tabs.json +17 -0
- package/registry/textarea.json +17 -0
- package/registry/theme-manager.json +38 -0
- package/src/index.css +11 -0
- package/src/lib/cn.ts +9 -0
- package/src/registry/components/ui/accordion.tsx +128 -0
- package/src/registry/components/ui/alert.tsx +155 -0
- package/src/registry/components/ui/avatar.tsx +114 -0
- package/src/registry/components/ui/badge.tsx +50 -0
- package/src/registry/components/ui/banner.tsx +189 -0
- package/src/registry/components/ui/breadcrumb.tsx +136 -0
- package/src/registry/components/ui/button.tsx +63 -0
- package/src/registry/components/ui/card.tsx +113 -0
- package/src/registry/components/ui/checkbox.tsx +85 -0
- package/src/registry/components/ui/command.tsx +286 -0
- package/src/registry/components/ui/dialog.tsx +195 -0
- package/src/registry/components/ui/dropdown-menu.tsx +250 -0
- package/src/registry/components/ui/input-group.tsx +80 -0
- package/src/registry/components/ui/input.tsx +28 -0
- package/src/registry/components/ui/kbd.tsx +73 -0
- package/src/registry/components/ui/label.tsx +30 -0
- package/src/registry/components/ui/list.tsx +222 -0
- package/src/registry/components/ui/radio-group.tsx +95 -0
- package/src/registry/components/ui/select.tsx +138 -0
- package/src/registry/components/ui/separator.tsx +37 -0
- package/src/registry/components/ui/sheet.tsx +216 -0
- package/src/registry/components/ui/skeleton.tsx +24 -0
- package/src/registry/components/ui/switch.tsx +79 -0
- package/src/registry/components/ui/tabs.tsx +212 -0
- package/src/registry/components/ui/textarea.tsx +82 -0
- package/src/registry/components/ui/theme-toggle.tsx +195 -0
- package/src/registry/index.ts +50 -0
- package/src/registry/metadata.ts +152 -0
- package/src/registry/providers/theme-provider.tsx +208 -0
- package/src/registry/providers/theme-script.tsx +58 -0
- package/src/registry/providers/theme-transitions.ts +108 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
createSignal,
|
|
5
|
+
onMount,
|
|
6
|
+
onCleanup,
|
|
7
|
+
Show,
|
|
8
|
+
splitProps,
|
|
9
|
+
type Component,
|
|
10
|
+
type JSX,
|
|
11
|
+
type Accessor,
|
|
12
|
+
} from "solid-js";
|
|
13
|
+
import { Dialog } from "@kobalte/core/dialog";
|
|
14
|
+
import { Search, ArrowUp, ArrowDown, CornerDownLeft } from "lucide-solid";
|
|
15
|
+
import { cn } from "@/lib/cn";
|
|
16
|
+
import { Kbd, KbdGroup } from "../ui/kbd";
|
|
17
|
+
import { InputGroup, InputGroupInput, InputGroupAddon } from "../ui/input-group";
|
|
18
|
+
import { List, ListGroup, ListHeader, ListItem, type ListItemProps } from "../ui/list";
|
|
19
|
+
|
|
20
|
+
/* --- Command Context State --- */
|
|
21
|
+
interface CommandContextValue {
|
|
22
|
+
search: Accessor<string>;
|
|
23
|
+
setSearch: (value: string) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const CommandContext = createContext<CommandContextValue>();
|
|
27
|
+
|
|
28
|
+
export const useCommand = () => {
|
|
29
|
+
const ctx = useContext(CommandContext);
|
|
30
|
+
if (!ctx) {
|
|
31
|
+
throw new Error("useCommand must be used within a <Command />");
|
|
32
|
+
}
|
|
33
|
+
return ctx;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/* --- Root Command Container --- */
|
|
37
|
+
export interface CommandProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
38
|
+
class?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const Command: Component<CommandProps> = (props) => {
|
|
42
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
43
|
+
const [search, setSearch] = createSignal("");
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<CommandContext.Provider value={{ search, setSearch }}>
|
|
47
|
+
<div
|
|
48
|
+
class={cn(
|
|
49
|
+
"flex flex-col w-full h-full rounded-xl bg-popover text-popover-foreground overflow-hidden border border-border shadow-md",
|
|
50
|
+
local.class
|
|
51
|
+
)}
|
|
52
|
+
{...rest}
|
|
53
|
+
>
|
|
54
|
+
{local.children}
|
|
55
|
+
</div>
|
|
56
|
+
</CommandContext.Provider>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/* --- Command Dialog (Modal) --- */
|
|
61
|
+
export interface CommandDialogProps {
|
|
62
|
+
open?: boolean;
|
|
63
|
+
onOpenChange?: (open: boolean) => void;
|
|
64
|
+
enableHotkey?: boolean;
|
|
65
|
+
children?: JSX.Element;
|
|
66
|
+
class?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const CommandDialog: Component<CommandDialogProps> = (props) => {
|
|
70
|
+
const [internalOpen, setInternalOpen] = createSignal(false);
|
|
71
|
+
|
|
72
|
+
const isOpen = () => (props.open !== undefined ? props.open : internalOpen());
|
|
73
|
+
const setOpen = (val: boolean) => {
|
|
74
|
+
if (props.open === undefined) setInternalOpen(val);
|
|
75
|
+
if (typeof props.onOpenChange === "function") props.onOpenChange(val);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/* Listen for global Ctrl+K / Cmd+K hotkeys */
|
|
79
|
+
onMount(() => {
|
|
80
|
+
if (props.enableHotkey !== false) {
|
|
81
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
82
|
+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
setOpen(!isOpen());
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
88
|
+
onCleanup(() => window.removeEventListener("keydown", handleKeyDown));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<Dialog open={isOpen()} onOpenChange={setOpen}>
|
|
94
|
+
<Dialog.Portal>
|
|
95
|
+
<Dialog.Overlay class="fixed inset-0 z-50 bg-black/60 backdrop-blur-xs data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0" />
|
|
96
|
+
<div class="fixed inset-0 z-50 flex items-start justify-center pt-16 sm:pt-24 px-4">
|
|
97
|
+
<Dialog.Content class="w-full max-w-xl rounded-xl border border-border bg-popover text-popover-foreground shadow-2xl outline-none data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95">
|
|
98
|
+
<Command class={props.class}>{props.children}</Command>
|
|
99
|
+
</Dialog.Content>
|
|
100
|
+
</div>
|
|
101
|
+
</Dialog.Portal>
|
|
102
|
+
</Dialog>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/* --- Command Input --- */
|
|
107
|
+
export interface CommandInputProps
|
|
108
|
+
extends JSX.InputHTMLAttributes<HTMLInputElement> {
|
|
109
|
+
class?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const CommandInput: Component<CommandInputProps> = (props) => {
|
|
113
|
+
const [local, rest] = splitProps(props, ["class", "value", "onInput"]);
|
|
114
|
+
const { search, setSearch } = useCommand();
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<InputGroup class="border-0 border-b border-border rounded-none bg-transparent px-3 py-1 shadow-none focus-within:ring-0 focus-within:border-border">
|
|
118
|
+
<InputGroupAddon align="inline-start">
|
|
119
|
+
<Search class="w-4 h-4 text-muted-foreground" />
|
|
120
|
+
</InputGroupAddon>
|
|
121
|
+
|
|
122
|
+
<InputGroupInput
|
|
123
|
+
value={search()}
|
|
124
|
+
onInput={(e) => {
|
|
125
|
+
setSearch(e.currentTarget.value);
|
|
126
|
+
if (typeof local.onInput === "function") {
|
|
127
|
+
local.onInput(e);
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
130
|
+
placeholder="Type a command or search..."
|
|
131
|
+
class="h-11 text-base sm:text-sm font-medium"
|
|
132
|
+
{...rest}
|
|
133
|
+
/>
|
|
134
|
+
</InputGroup>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/* --- Command List Container --- */
|
|
139
|
+
export interface CommandListProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
140
|
+
class?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const CommandList: Component<CommandListProps> = (props) => {
|
|
144
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div
|
|
148
|
+
class={cn("max-h-[330px] overflow-y-auto p-1.5 scrollbar-thin", local.class)}
|
|
149
|
+
{...rest}
|
|
150
|
+
>
|
|
151
|
+
<List>{local.children}</List>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/* --- Command Empty Block --- */
|
|
157
|
+
export interface CommandEmptyProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
158
|
+
class?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const CommandEmpty: Component<CommandEmptyProps> = (props) => {
|
|
162
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
163
|
+
const { search } = useCommand();
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Show when={search().trim().length > 0}>
|
|
167
|
+
<div
|
|
168
|
+
class={cn(
|
|
169
|
+
"py-8 text-center text-sm text-muted-foreground font-medium select-none",
|
|
170
|
+
local.class
|
|
171
|
+
)}
|
|
172
|
+
{...rest}
|
|
173
|
+
>
|
|
174
|
+
{local.children || `No results found for "${search()}".`}
|
|
175
|
+
</div>
|
|
176
|
+
</Show>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/* --- Command Group --- */
|
|
181
|
+
export interface CommandGroupProps {
|
|
182
|
+
heading: string;
|
|
183
|
+
children?: JSX.Element;
|
|
184
|
+
class?: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const CommandGroup: Component<CommandGroupProps> = (props) => {
|
|
188
|
+
return (
|
|
189
|
+
<ListGroup class={props.class}>
|
|
190
|
+
<ListHeader title={props.heading} />
|
|
191
|
+
{props.children}
|
|
192
|
+
</ListGroup>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/* --- Command Item --- */
|
|
197
|
+
export interface CommandItemProps extends ListItemProps {
|
|
198
|
+
keywords?: string[];
|
|
199
|
+
onSelect?: () => void;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export const CommandItem: Component<CommandItemProps> = (props) => {
|
|
203
|
+
const [local, rest] = splitProps(props, [
|
|
204
|
+
"title",
|
|
205
|
+
"subtitle",
|
|
206
|
+
"keywords",
|
|
207
|
+
"onSelect",
|
|
208
|
+
"onClick",
|
|
209
|
+
"class",
|
|
210
|
+
]);
|
|
211
|
+
const { search } = useCommand();
|
|
212
|
+
|
|
213
|
+
/* Auto fuzzy-filter item based on search query */
|
|
214
|
+
const matchesSearch = () => {
|
|
215
|
+
const query = search().toLowerCase().trim();
|
|
216
|
+
if (!query) return true;
|
|
217
|
+
|
|
218
|
+
const titleMatch = local.title?.toLowerCase().includes(query);
|
|
219
|
+
const subtitleMatch = local.subtitle?.toLowerCase().includes(query);
|
|
220
|
+
const keywordMatch = local.keywords?.some((k) =>
|
|
221
|
+
k.toLowerCase().includes(query)
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
return Boolean(titleMatch || subtitleMatch || keywordMatch);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const handleClick = (e: MouseEvent) => {
|
|
228
|
+
if (typeof local.onSelect === "function") {
|
|
229
|
+
local.onSelect();
|
|
230
|
+
}
|
|
231
|
+
if (typeof local.onClick === "function") {
|
|
232
|
+
local.onClick(e as any);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<Show when={matchesSearch()}>
|
|
238
|
+
<ListItem
|
|
239
|
+
title={local.title}
|
|
240
|
+
subtitle={local.subtitle}
|
|
241
|
+
onClick={handleClick}
|
|
242
|
+
class={local.class}
|
|
243
|
+
{...rest}
|
|
244
|
+
/>
|
|
245
|
+
</Show>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/* --- Command Footer --- */
|
|
250
|
+
export interface CommandFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
251
|
+
class?: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const CommandFooter: Component<CommandFooterProps> = (props) => {
|
|
255
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div
|
|
259
|
+
class={cn(
|
|
260
|
+
"flex items-center justify-between border-t border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground select-none",
|
|
261
|
+
local.class
|
|
262
|
+
)}
|
|
263
|
+
{...rest}
|
|
264
|
+
>
|
|
265
|
+
<div class="flex items-center gap-3">
|
|
266
|
+
<span class="flex items-center gap-1">
|
|
267
|
+
<KbdGroup>
|
|
268
|
+
<Kbd size="sm"><ArrowUp class="w-2.5 h-2.5" /></Kbd>
|
|
269
|
+
<Kbd size="sm"><ArrowDown class="w-2.5 h-2.5" /></Kbd>
|
|
270
|
+
</KbdGroup>
|
|
271
|
+
<span>Navigate</span>
|
|
272
|
+
</span>
|
|
273
|
+
|
|
274
|
+
<span class="flex items-center gap-1">
|
|
275
|
+
<Kbd size="sm"><CornerDownLeft class="w-2.5 h-2.5" /></Kbd>
|
|
276
|
+
<span>Select</span>
|
|
277
|
+
</span>
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
<span class="flex items-center gap-1">
|
|
281
|
+
<Kbd size="sm">Esc</Kbd>
|
|
282
|
+
<span>Close</span>
|
|
283
|
+
</span>
|
|
284
|
+
</div>
|
|
285
|
+
);
|
|
286
|
+
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { splitProps, type Component, type JSX, type ValidComponent, Show } from "solid-js";
|
|
2
|
+
import * as DialogPrimitive from "@kobalte/core/dialog";
|
|
3
|
+
import type { PolymorphicProps } from "@kobalte/core/polymorphic";
|
|
4
|
+
import { cn } from "@/lib/cn";
|
|
5
|
+
|
|
6
|
+
export type DialogRootProps = DialogPrimitive.DialogRootProps;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Root Dialog component managing modal open state.
|
|
10
|
+
*/
|
|
11
|
+
export const Dialog: Component<DialogRootProps> = (props) => {
|
|
12
|
+
return <DialogPrimitive.Root {...props} />;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const DialogTrigger = DialogPrimitive.Trigger;
|
|
16
|
+
|
|
17
|
+
export type DialogOverlayProps<T extends ValidComponent = "div"> =
|
|
18
|
+
DialogPrimitive.DialogOverlayProps<T> & {
|
|
19
|
+
/** Whether to apply background blur effect (default: true) */
|
|
20
|
+
blur?: boolean;
|
|
21
|
+
class?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Backdrop overlay wrapper with optional backdrop blur styling.
|
|
26
|
+
*/
|
|
27
|
+
export const DialogOverlay = <T extends ValidComponent = "div">(
|
|
28
|
+
props: PolymorphicProps<T, DialogOverlayProps<T>>
|
|
29
|
+
) => {
|
|
30
|
+
const [local, rest] = splitProps(props as DialogOverlayProps, ["class", "blur"]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<DialogPrimitive.Overlay
|
|
34
|
+
class={cn(
|
|
35
|
+
"fixed inset-0 z-50 bg-black/80 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
|
|
36
|
+
local.blur !== false && "backdrop-blur-sm",
|
|
37
|
+
local.class
|
|
38
|
+
)}
|
|
39
|
+
{...(rest as any)}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type DialogContentProps<T extends ValidComponent = "div"> =
|
|
45
|
+
DialogPrimitive.DialogContentProps<T> & {
|
|
46
|
+
/** Whether to show the top-right close (X) button (default: true) */
|
|
47
|
+
showCloseButton?: boolean;
|
|
48
|
+
/** Whether clicking outside closes the dialog (default: true) */
|
|
49
|
+
closeOnOutsideClick?: boolean;
|
|
50
|
+
/** Whether to apply background backdrop blur (default: true) */
|
|
51
|
+
blur?: boolean;
|
|
52
|
+
class?: string;
|
|
53
|
+
children?: JSX.Element;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Main dialog popup window housing header, content, and footer.
|
|
58
|
+
*/
|
|
59
|
+
export const DialogContent = <T extends ValidComponent = "div">(
|
|
60
|
+
props: PolymorphicProps<T, DialogContentProps<T>>
|
|
61
|
+
) => {
|
|
62
|
+
const [local, rest] = splitProps(props as DialogContentProps, [
|
|
63
|
+
"class",
|
|
64
|
+
"children",
|
|
65
|
+
"showCloseButton",
|
|
66
|
+
"closeOnOutsideClick",
|
|
67
|
+
"blur",
|
|
68
|
+
"onPointerDownOutside",
|
|
69
|
+
"onInteractOutside",
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
const handlePointerDownOutside = (e: Event) => {
|
|
73
|
+
if (local.closeOnOutsideClick === false) {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
}
|
|
76
|
+
if (typeof local.onPointerDownOutside === "function") {
|
|
77
|
+
local.onPointerDownOutside(e as any);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const handleInteractOutside = (e: Event) => {
|
|
82
|
+
if (local.closeOnOutsideClick === false) {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
}
|
|
85
|
+
if (typeof local.onInteractOutside === "function") {
|
|
86
|
+
local.onInteractOutside(e as any);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<DialogPrimitive.Portal>
|
|
92
|
+
{/* Forward blur prop to DialogOverlay */}
|
|
93
|
+
<DialogOverlay blur={local.blur} />
|
|
94
|
+
<DialogPrimitive.Content
|
|
95
|
+
onPointerDownOutside={handlePointerDownOutside}
|
|
96
|
+
onInteractOutside={handleInteractOutside}
|
|
97
|
+
class={cn(
|
|
98
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-card p-6 shadow-lg duration-200 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg text-card-foreground",
|
|
99
|
+
local.class
|
|
100
|
+
)}
|
|
101
|
+
{...(rest as any)}
|
|
102
|
+
>
|
|
103
|
+
{local.children}
|
|
104
|
+
<Show when={local.showCloseButton !== false}>
|
|
105
|
+
<DialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[expanded]:bg-accent data-[expanded]:text-muted-foreground cursor-pointer">
|
|
106
|
+
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
107
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
108
|
+
</svg>
|
|
109
|
+
<span class="sr-only">Close</span>
|
|
110
|
+
</DialogPrimitive.CloseButton>
|
|
111
|
+
</Show>
|
|
112
|
+
</DialogPrimitive.Content>
|
|
113
|
+
</DialogPrimitive.Portal>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export interface DialogHeaderProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
118
|
+
class?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Dialog top header container.
|
|
123
|
+
*/
|
|
124
|
+
export const DialogHeader: Component<DialogHeaderProps> = (props) => {
|
|
125
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
class={cn("flex flex-col space-y-1.5 text-center sm:text-left", local.class)}
|
|
130
|
+
{...rest}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export interface DialogFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
136
|
+
class?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Dialog bottom action buttons area.
|
|
141
|
+
*/
|
|
142
|
+
export const DialogFooter: Component<DialogFooterProps> = (props) => {
|
|
143
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div
|
|
147
|
+
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", local.class)}
|
|
148
|
+
{...rest}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export type DialogTitleProps<T extends ValidComponent = "h2"> =
|
|
154
|
+
DialogPrimitive.DialogTitleProps<T> & {
|
|
155
|
+
class?: string;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Dialog title heading.
|
|
160
|
+
*/
|
|
161
|
+
export const DialogTitle = <T extends ValidComponent = "h2">(
|
|
162
|
+
props: PolymorphicProps<T, DialogTitleProps<T>>
|
|
163
|
+
) => {
|
|
164
|
+
const [local, rest] = splitProps(props as DialogTitleProps, ["class"]);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<DialogPrimitive.Title
|
|
168
|
+
class={cn("text-lg font-semibold leading-none tracking-tight text-foreground", local.class)}
|
|
169
|
+
{...(rest as any)}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export type DialogDescriptionProps<T extends ValidComponent = "p"> =
|
|
175
|
+
DialogPrimitive.DialogDescriptionProps<T> & {
|
|
176
|
+
class?: string;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Dialog subtitle or description text.
|
|
181
|
+
*/
|
|
182
|
+
export const DialogDescription = <T extends ValidComponent = "p">(
|
|
183
|
+
props: PolymorphicProps<T, DialogDescriptionProps<T>>
|
|
184
|
+
) => {
|
|
185
|
+
const [local, rest] = splitProps(props as DialogDescriptionProps, ["class"]);
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<DialogPrimitive.Description
|
|
189
|
+
class={cn("text-sm text-muted-foreground", local.class)}
|
|
190
|
+
{...(rest as any)}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const DialogClose = DialogPrimitive.CloseButton;
|