@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
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
type JSX,
|
|
10
10
|
type Accessor,
|
|
11
11
|
} from "solid-js";
|
|
12
|
-
import { createKeybindings } from "
|
|
12
|
+
import { createKeybindings } from "@/hooks/create-keybindings";
|
|
13
|
+
import { Dialog } from "@kobalte/core/dialog";
|
|
13
14
|
import { Search, ArrowUp, ArrowDown, CornerDownLeft } from "lucide-solid";
|
|
14
15
|
import { cn } from "@/lib/cn";
|
|
15
|
-
import { Kbd, KbdGroup } from "
|
|
16
|
-
import { InputGroup, InputGroupInput, InputGroupAddon } from "
|
|
17
|
-
import { List, ListGroup, ListHeader, ListItem, type ListItemProps } from "
|
|
18
|
-
import {
|
|
19
|
-
import { ScrollArea } from "./scroll-area";
|
|
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
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
20
20
|
|
|
21
21
|
/* --- Command Context State --- */
|
|
22
22
|
interface CommandContextValue {
|
|
@@ -24,8 +24,8 @@ interface CommandContextValue {
|
|
|
24
24
|
setSearch: (value: string) => void;
|
|
25
25
|
activeIndex: Accessor<number>;
|
|
26
26
|
setActiveIndex: (fn: (prev: number) => number) => void;
|
|
27
|
-
registerItemIndex: () => number;
|
|
28
|
-
onItemSelect: (
|
|
27
|
+
registerItemIndex: (element: HTMLElement) => number;
|
|
28
|
+
onItemSelect: (index: number, action?: () => void) => void;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const CommandContext = createContext<CommandContextValue>();
|
|
@@ -33,12 +33,12 @@ const CommandContext = createContext<CommandContextValue>();
|
|
|
33
33
|
export const useCommand = () => {
|
|
34
34
|
const ctx = useContext(CommandContext);
|
|
35
35
|
if (!ctx) {
|
|
36
|
-
throw new Error("useCommand must be used within a <Command />
|
|
36
|
+
throw new Error("useCommand must be used within a <Command />");
|
|
37
37
|
}
|
|
38
38
|
return ctx;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
/* --- Command
|
|
41
|
+
/* --- Root Command Container --- */
|
|
42
42
|
export interface CommandProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
43
43
|
class?: string;
|
|
44
44
|
children?: JSX.Element | ((ctx: CommandContextValue) => JSX.Element);
|
|
@@ -48,32 +48,56 @@ export const Command: Component<CommandProps> = (props) => {
|
|
|
48
48
|
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
49
49
|
const [search, setSearch] = createSignal("");
|
|
50
50
|
const [activeIndex, setActiveIndex] = createSignal(0);
|
|
51
|
-
|
|
52
|
-
const itemCallbacks: Record<number, () => void> = {};
|
|
51
|
+
|
|
53
52
|
let containerRef: HTMLDivElement | undefined;
|
|
53
|
+
let itemsList: HTMLElement[] = [];
|
|
54
|
+
|
|
55
|
+
const registerItemIndex = (element: HTMLElement) => {
|
|
56
|
+
if (!itemsList.includes(element)) {
|
|
57
|
+
itemsList.push(element);
|
|
58
|
+
}
|
|
59
|
+
return itemsList.indexOf(element);
|
|
60
|
+
};
|
|
54
61
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return idx;
|
|
62
|
+
const onItemSelect = (index: number, action?: () => void) => {
|
|
63
|
+
setActiveIndex(index);
|
|
64
|
+
if (action) action();
|
|
59
65
|
};
|
|
60
66
|
|
|
61
|
-
const
|
|
62
|
-
|
|
67
|
+
const getVisibleItems = () => {
|
|
68
|
+
if (!containerRef) return [];
|
|
69
|
+
return Array.from(containerRef.querySelectorAll<HTMLElement>("[data-command-item]:not(.hidden)"));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const updateActiveAttribute = (index: number) => {
|
|
73
|
+
const visible = getVisibleItems();
|
|
74
|
+
visible.forEach((el, idx) => {
|
|
75
|
+
if (idx === index) {
|
|
76
|
+
el.setAttribute("aria-selected", "true");
|
|
77
|
+
el.scrollIntoView({ block: "nearest" });
|
|
78
|
+
} else {
|
|
79
|
+
el.removeAttribute("aria-selected");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
63
82
|
};
|
|
64
83
|
|
|
65
84
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
85
|
+
const visible = getVisibleItems();
|
|
86
|
+
if (visible.length === 0) return;
|
|
87
|
+
|
|
66
88
|
if (e.key === "ArrowDown") {
|
|
67
89
|
e.preventDefault();
|
|
68
|
-
|
|
90
|
+
const next = (activeIndex() + 1) % visible.length;
|
|
91
|
+
setActiveIndex(next);
|
|
92
|
+
updateActiveAttribute(next);
|
|
69
93
|
} else if (e.key === "ArrowUp") {
|
|
70
94
|
e.preventDefault();
|
|
71
|
-
|
|
95
|
+
const prev = (activeIndex() - 1 + visible.length) % visible.length;
|
|
96
|
+
setActiveIndex(prev);
|
|
97
|
+
updateActiveAttribute(prev);
|
|
72
98
|
} else if (e.key === "Enter") {
|
|
73
99
|
e.preventDefault();
|
|
74
|
-
const
|
|
75
|
-
if (fn) fn();
|
|
76
|
-
const current = containerRef?.querySelector('[data-command-active="true"]') as HTMLElement;
|
|
100
|
+
const current = visible[activeIndex()];
|
|
77
101
|
if (current) {
|
|
78
102
|
current.click();
|
|
79
103
|
}
|
|
@@ -144,12 +168,14 @@ export const CommandDialog: Component<CommandDialogProps> = (props) => {
|
|
|
144
168
|
|
|
145
169
|
return (
|
|
146
170
|
<Dialog open={isOpen()} onOpenChange={setOpen}>
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
<
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
<Dialog.Portal>
|
|
172
|
+
<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" />
|
|
173
|
+
<div class="fixed inset-0 z-50 flex items-start justify-center pt-16 sm:pt-24 px-4">
|
|
174
|
+
<Dialog.Content class="w-full max-w-xl rounded-lg 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">
|
|
175
|
+
<Command class={props.class}>{props.children}</Command>
|
|
176
|
+
</Dialog.Content>
|
|
177
|
+
</div>
|
|
178
|
+
</Dialog.Portal>
|
|
153
179
|
</Dialog>
|
|
154
180
|
);
|
|
155
181
|
};
|
|
@@ -171,7 +197,6 @@ export const CommandInput: Component<CommandInputProps> = (props) => {
|
|
|
171
197
|
</InputGroupAddon>
|
|
172
198
|
|
|
173
199
|
<InputGroupInput
|
|
174
|
-
ref={(el) => setTimeout(() => el.focus(), 50)}
|
|
175
200
|
value={search()}
|
|
176
201
|
onInput={(e) => {
|
|
177
202
|
setSearch(e.currentTarget.value);
|
|
@@ -248,7 +273,9 @@ export const CommandGroup: Component<CommandGroupProps> = (props) => {
|
|
|
248
273
|
/* --- Command Item --- */
|
|
249
274
|
export interface CommandItemProps extends ListItemProps {
|
|
250
275
|
keywords?: string[];
|
|
276
|
+
description?: string;
|
|
251
277
|
onSelect?: () => void;
|
|
278
|
+
shouldFilter?: boolean;
|
|
252
279
|
}
|
|
253
280
|
|
|
254
281
|
export const CommandItem: Component<CommandItemProps> = (props) => {
|
|
@@ -256,77 +283,62 @@ export const CommandItem: Component<CommandItemProps> = (props) => {
|
|
|
256
283
|
const [local, rest] = splitProps(props, [
|
|
257
284
|
"title",
|
|
258
285
|
"subtitle",
|
|
286
|
+
"description",
|
|
259
287
|
"keywords",
|
|
260
|
-
"disabled",
|
|
261
288
|
"onSelect",
|
|
289
|
+
"onClick",
|
|
290
|
+
"shouldFilter",
|
|
262
291
|
"class",
|
|
263
|
-
"children",
|
|
264
292
|
]);
|
|
293
|
+
const { search } = useCommand();
|
|
265
294
|
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
/* Automatic scroll into view when navigating with Arrow keys */
|
|
272
|
-
const scrollIntoViewIfNeeded = () => {
|
|
273
|
-
if (isActive() && itemRef) {
|
|
274
|
-
itemRef.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const isVisible = () => {
|
|
279
|
-
const query = ctx.search().toLowerCase().trim();
|
|
295
|
+
/* Auto filter item based on search query */
|
|
296
|
+
const matchesSearch = () => {
|
|
297
|
+
if (local.shouldFilter === false) return true;
|
|
298
|
+
const query = search().toLowerCase().trim();
|
|
280
299
|
if (!query) return true;
|
|
281
300
|
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return local.keywords.some((k) => k.toLowerCase().includes(query));
|
|
289
|
-
}
|
|
301
|
+
const titleMatch = local.title?.toLowerCase().includes(query);
|
|
302
|
+
const subtitleMatch = local.subtitle?.toLowerCase().includes(query);
|
|
303
|
+
const descMatch = local.description?.toLowerCase().includes(query);
|
|
304
|
+
const keywordMatch = local.keywords?.some((k) =>
|
|
305
|
+
k.toLowerCase().includes(query)
|
|
306
|
+
);
|
|
290
307
|
|
|
291
|
-
return
|
|
308
|
+
return Boolean(titleMatch || subtitleMatch || descMatch || keywordMatch);
|
|
292
309
|
};
|
|
293
310
|
|
|
294
|
-
const
|
|
295
|
-
if (local.
|
|
296
|
-
|
|
311
|
+
const handleClick = (e: MouseEvent) => {
|
|
312
|
+
if (typeof local.onSelect === "function") {
|
|
313
|
+
local.onSelect();
|
|
314
|
+
}
|
|
315
|
+
if (typeof local.onClick === "function") {
|
|
316
|
+
local.onClick(e as any);
|
|
317
|
+
}
|
|
297
318
|
};
|
|
298
319
|
|
|
299
320
|
return (
|
|
300
|
-
<Show when={
|
|
321
|
+
<Show when={matchesSearch()}>
|
|
301
322
|
<ListItem
|
|
302
|
-
ref={
|
|
303
|
-
|
|
304
|
-
scrollIntoViewIfNeeded();
|
|
305
|
-
}}
|
|
323
|
+
ref={itemRef}
|
|
324
|
+
data-command-item="true"
|
|
306
325
|
title={local.title}
|
|
307
|
-
subtitle={local.subtitle}
|
|
308
|
-
|
|
326
|
+
subtitle={local.subtitle || local.description}
|
|
327
|
+
onClick={handleClick}
|
|
309
328
|
class={cn(
|
|
310
|
-
"
|
|
311
|
-
isActive()
|
|
312
|
-
? "bg-accent text-accent-foreground font-medium"
|
|
313
|
-
: "text-popover-foreground hover:bg-muted/50",
|
|
314
|
-
local.disabled && "pointer-events-none opacity-50",
|
|
329
|
+
"aria-selected:bg-accent aria-selected:text-accent-foreground cursor-pointer transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
315
330
|
local.class
|
|
316
331
|
)}
|
|
317
|
-
onClick={handleSelect}
|
|
318
|
-
onMouseEnter={() => ctx.setActiveIndex(() => index)}
|
|
319
332
|
{...rest}
|
|
320
|
-
|
|
321
|
-
{local.children}
|
|
322
|
-
</ListItem>
|
|
333
|
+
/>
|
|
323
334
|
</Show>
|
|
324
335
|
);
|
|
325
336
|
};
|
|
326
337
|
|
|
327
|
-
/* --- Command Footer
|
|
338
|
+
/* --- Command Footer --- */
|
|
328
339
|
export interface CommandFooterProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
329
340
|
class?: string;
|
|
341
|
+
children?: JSX.Element;
|
|
330
342
|
}
|
|
331
343
|
|
|
332
344
|
export const CommandFooter: Component<CommandFooterProps> = (props) => {
|
|
@@ -335,40 +347,32 @@ export const CommandFooter: Component<CommandFooterProps> = (props) => {
|
|
|
335
347
|
return (
|
|
336
348
|
<div
|
|
337
349
|
class={cn(
|
|
338
|
-
"flex items-center justify-between border-t border-border px-3 py-2 text-xs text-muted-foreground
|
|
350
|
+
"flex items-center justify-between border-t border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground select-none",
|
|
339
351
|
local.class
|
|
340
352
|
)}
|
|
341
353
|
{...rest}
|
|
342
354
|
>
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
<
|
|
346
|
-
<
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
</div>
|
|
365
|
-
|
|
366
|
-
<span class="inline-flex items-center gap-1">
|
|
367
|
-
<Kbd size="sm">ESC</Kbd>
|
|
368
|
-
<span>Close</span>
|
|
369
|
-
</span>
|
|
370
|
-
</>
|
|
371
|
-
)}
|
|
355
|
+
<div class="flex items-center gap-3">
|
|
356
|
+
<span class="flex items-center gap-1">
|
|
357
|
+
<KbdGroup>
|
|
358
|
+
<Kbd size="sm"><ArrowUp class="w-2.5 h-2.5" /></Kbd>
|
|
359
|
+
<Kbd size="sm"><ArrowDown class="w-2.5 h-2.5" /></Kbd>
|
|
360
|
+
</KbdGroup>
|
|
361
|
+
<span>Navigate</span>
|
|
362
|
+
</span>
|
|
363
|
+
|
|
364
|
+
<span class="flex items-center gap-1">
|
|
365
|
+
<Kbd size="sm"><CornerDownLeft class="w-2.5 h-2.5" /></Kbd>
|
|
366
|
+
<span>Select</span>
|
|
367
|
+
</span>
|
|
368
|
+
|
|
369
|
+
<span class="flex items-center gap-1">
|
|
370
|
+
<Kbd size="sm">Esc</Kbd>
|
|
371
|
+
<span>Close</span>
|
|
372
|
+
</span>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
{local.children}
|
|
372
376
|
</div>
|
|
373
377
|
);
|
|
374
378
|
};
|