kosui 0.0.7
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 +7 -0
- package/README.md +3 -0
- package/dist/alert/alert.svelte +28 -0
- package/dist/alert/alert.svelte.d.ts +5 -0
- package/dist/alert/index.d.ts +1 -0
- package/dist/alert/index.js +1 -0
- package/dist/autocomplete/autocomplete-content.svelte +47 -0
- package/dist/autocomplete/autocomplete-content.svelte.d.ts +10 -0
- package/dist/autocomplete/autocomplete-context.svelte.d.ts +12 -0
- package/dist/autocomplete/autocomplete-context.svelte.js +34 -0
- package/dist/autocomplete/autocomplete-input.svelte +43 -0
- package/dist/autocomplete/autocomplete-input.svelte.d.ts +7 -0
- package/dist/autocomplete/autocomplete-item.svelte +68 -0
- package/dist/autocomplete/autocomplete-item.svelte.d.ts +11 -0
- package/dist/autocomplete/autocomplete.svelte +20 -0
- package/dist/autocomplete/autocomplete.svelte.d.ts +8 -0
- package/dist/autocomplete/index.d.ts +4 -0
- package/dist/autocomplete/index.js +4 -0
- package/dist/avatar/avatar.svelte +47 -0
- package/dist/avatar/avatar.svelte.d.ts +8 -0
- package/dist/avatar/index.d.ts +1 -0
- package/dist/avatar/index.js +1 -0
- package/dist/badge/badge.svelte +43 -0
- package/dist/badge/badge.svelte.d.ts +9 -0
- package/dist/badge/index.d.ts +1 -0
- package/dist/badge/index.js +1 -0
- package/dist/base.d.ts +21 -0
- package/dist/base.js +70 -0
- package/dist/bindable.svelte.d.ts +7 -0
- package/dist/bindable.svelte.js +25 -0
- package/dist/box/box.svelte +42 -0
- package/dist/box/box.svelte.d.ts +12 -0
- package/dist/box/index.d.ts +1 -0
- package/dist/box/index.js +1 -0
- package/dist/breadcrumbs/breadcrumbs.svelte +39 -0
- package/dist/breadcrumbs/breadcrumbs.svelte.d.ts +13 -0
- package/dist/breadcrumbs/index.d.ts +1 -0
- package/dist/breadcrumbs/index.js +1 -0
- package/dist/button/button.svelte +70 -0
- package/dist/button/button.svelte.d.ts +13 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +1 -0
- package/dist/chip/chip.svelte +57 -0
- package/dist/chip/chip.svelte.d.ts +11 -0
- package/dist/chip/index.d.ts +1 -0
- package/dist/chip/index.js +1 -0
- package/dist/command/action.svelte.d.ts +35 -0
- package/dist/command/action.svelte.js +76 -0
- package/dist/command/command-content.svelte +15 -0
- package/dist/command/command-content.svelte.d.ts +8 -0
- package/dist/command/command-context.svelte.d.ts +5 -0
- package/dist/command/command-context.svelte.js +13 -0
- package/dist/command/command-input.svelte +44 -0
- package/dist/command/command-input.svelte.d.ts +7 -0
- package/dist/command/command-item.svelte +66 -0
- package/dist/command/command-item.svelte.d.ts +11 -0
- package/dist/command/command-search.svelte +29 -0
- package/dist/command/command-search.svelte.d.ts +5 -0
- package/dist/command/command.svelte +19 -0
- package/dist/command/command.svelte.d.ts +8 -0
- package/dist/command/commander.svelte +107 -0
- package/dist/command/commander.svelte.d.ts +9 -0
- package/dist/command/index.d.ts +8 -0
- package/dist/command/index.js +8 -0
- package/dist/common/divider.svelte +13 -0
- package/dist/common/divider.svelte.d.ts +6 -0
- package/dist/common/header.svelte +35 -0
- package/dist/common/header.svelte.d.ts +10 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +3 -0
- package/dist/common/item-context.svelte.d.ts +12 -0
- package/dist/common/item-context.svelte.js +77 -0
- package/dist/dialog/dialog-button.svelte +25 -0
- package/dist/dialog/dialog-button.svelte.d.ts +32 -0
- package/dist/dialog/dialog.svelte +69 -0
- package/dist/dialog/dialog.svelte.d.ts +37 -0
- package/dist/dialog/dialoguer-context.svelte.d.ts +53 -0
- package/dist/dialog/dialoguer-context.svelte.js +85 -0
- package/dist/dialog/dialoguer.svelte +65 -0
- package/dist/dialog/dialoguer.svelte.d.ts +7 -0
- package/dist/dialog/index.d.ts +4 -0
- package/dist/dialog/index.js +4 -0
- package/dist/event-manager.d.ts +15 -0
- package/dist/event-manager.js +47 -0
- package/dist/fab/fab.svelte +28 -0
- package/dist/fab/fab.svelte.d.ts +6 -0
- package/dist/fab/index.d.ts +1 -0
- package/dist/fab/index.js +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +39 -0
- package/dist/input/index.d.ts +1 -0
- package/dist/input/index.js +1 -0
- package/dist/input/input.svelte +41 -0
- package/dist/input/input.svelte.d.ts +7 -0
- package/dist/link/index.d.ts +1 -0
- package/dist/link/index.js +1 -0
- package/dist/link/link.svelte +51 -0
- package/dist/link/link.svelte.d.ts +10 -0
- package/dist/markdown/index.d.ts +25 -0
- package/dist/markdown/index.js +25 -0
- package/dist/markdown/markdown-blockquote.svelte +14 -0
- package/dist/markdown/markdown-blockquote.svelte.d.ts +7 -0
- package/dist/markdown/markdown-br.svelte +12 -0
- package/dist/markdown/markdown-br.svelte.d.ts +7 -0
- package/dist/markdown/markdown-code.svelte +14 -0
- package/dist/markdown/markdown-code.svelte.d.ts +7 -0
- package/dist/markdown/markdown-codespan.svelte +15 -0
- package/dist/markdown/markdown-codespan.svelte.d.ts +7 -0
- package/dist/markdown/markdown-context.svelte.d.ts +26 -0
- package/dist/markdown/markdown-context.svelte.js +30 -0
- package/dist/markdown/markdown-def.svelte +15 -0
- package/dist/markdown/markdown-def.svelte.d.ts +7 -0
- package/dist/markdown/markdown-del.svelte +14 -0
- package/dist/markdown/markdown-del.svelte.d.ts +7 -0
- package/dist/markdown/markdown-em.svelte +14 -0
- package/dist/markdown/markdown-em.svelte.d.ts +7 -0
- package/dist/markdown/markdown-escape.svelte +8 -0
- package/dist/markdown/markdown-escape.svelte.d.ts +5 -0
- package/dist/markdown/markdown-heading.svelte +16 -0
- package/dist/markdown/markdown-heading.svelte.d.ts +7 -0
- package/dist/markdown/markdown-hr.svelte +12 -0
- package/dist/markdown/markdown-hr.svelte.d.ts +7 -0
- package/dist/markdown/markdown-html.svelte +9 -0
- package/dist/markdown/markdown-html.svelte.d.ts +5 -0
- package/dist/markdown/markdown-image.svelte +12 -0
- package/dist/markdown/markdown-image.svelte.d.ts +7 -0
- package/dist/markdown/markdown-link.svelte +15 -0
- package/dist/markdown/markdown-link.svelte.d.ts +7 -0
- package/dist/markdown/markdown-list-item.svelte +14 -0
- package/dist/markdown/markdown-list-item.svelte.d.ts +7 -0
- package/dist/markdown/markdown-list.svelte +19 -0
- package/dist/markdown/markdown-list.svelte.d.ts +7 -0
- package/dist/markdown/markdown-paragraph.svelte +16 -0
- package/dist/markdown/markdown-paragraph.svelte.d.ts +7 -0
- package/dist/markdown/markdown-space.svelte +8 -0
- package/dist/markdown/markdown-space.svelte.d.ts +5 -0
- package/dist/markdown/markdown-strong.svelte +15 -0
- package/dist/markdown/markdown-strong.svelte.d.ts +7 -0
- package/dist/markdown/markdown-table-cell.svelte +16 -0
- package/dist/markdown/markdown-table-cell.svelte.d.ts +7 -0
- package/dist/markdown/markdown-table.svelte +34 -0
- package/dist/markdown/markdown-table.svelte.d.ts +7 -0
- package/dist/markdown/markdown-text.svelte +8 -0
- package/dist/markdown/markdown-text.svelte.d.ts +5 -0
- package/dist/markdown/markdown-token.svelte +25 -0
- package/dist/markdown/markdown-token.svelte.d.ts +7 -0
- package/dist/markdown/markdown-tokens.svelte +13 -0
- package/dist/markdown/markdown-tokens.svelte.d.ts +7 -0
- package/dist/markdown/markdown.svelte +186 -0
- package/dist/markdown/markdown.svelte.d.ts +31 -0
- package/dist/menu/index.d.ts +7 -0
- package/dist/menu/index.js +7 -0
- package/dist/menu/menu-actions.svelte +51 -0
- package/dist/menu/menu-actions.svelte.d.ts +7 -0
- package/dist/menu/menu-content.svelte +47 -0
- package/dist/menu/menu-content.svelte.d.ts +10 -0
- package/dist/menu/menu-context.svelte.d.ts +18 -0
- package/dist/menu/menu-context.svelte.js +118 -0
- package/dist/menu/menu-item.svelte +85 -0
- package/dist/menu/menu-item.svelte.d.ts +11 -0
- package/dist/menu/menu-trigger-button.svelte +31 -0
- package/dist/menu/menu-trigger-button.svelte.d.ts +5 -0
- package/dist/menu/menu-trigger.svelte +42 -0
- package/dist/menu/menu-trigger.svelte.d.ts +10 -0
- package/dist/menu/menu.svelte +29 -0
- package/dist/menu/menu.svelte.d.ts +9 -0
- package/dist/merge-props.d.ts +13 -0
- package/dist/merge-props.js +84 -0
- package/dist/modal/index.d.ts +1 -0
- package/dist/modal/index.js +1 -0
- package/dist/modal/modal.svelte +123 -0
- package/dist/modal/modal.svelte.d.ts +11 -0
- package/dist/ordered-html-elements.d.ts +7 -0
- package/dist/ordered-html-elements.js +29 -0
- package/dist/popover/index.d.ts +1 -0
- package/dist/popover/index.js +1 -0
- package/dist/popover/popover.svelte +184 -0
- package/dist/popover/popover.svelte.d.ts +18 -0
- package/dist/portal/index.d.ts +1 -0
- package/dist/portal/index.js +1 -0
- package/dist/portal/portal.svelte +12 -0
- package/dist/portal/portal.svelte.d.ts +8 -0
- package/dist/portal/proxy.svelte +10 -0
- package/dist/portal/proxy.svelte.d.ts +7 -0
- package/dist/progress/circular-progress.svelte +86 -0
- package/dist/progress/circular-progress.svelte.d.ts +13 -0
- package/dist/progress/index.d.ts +1 -0
- package/dist/progress/index.js +1 -0
- package/dist/shortcut/index.d.ts +2 -0
- package/dist/shortcut/index.js +2 -0
- package/dist/shortcut/shortcut-badge.svelte +41 -0
- package/dist/shortcut/shortcut-badge.svelte.d.ts +11 -0
- package/dist/shortcut/shortcut.d.ts +29 -0
- package/dist/shortcut/shortcut.js +95 -0
- package/dist/toggle/index.d.ts +2 -0
- package/dist/toggle/index.js +2 -0
- package/dist/toggle/toggle-button.svelte +60 -0
- package/dist/toggle/toggle-button.svelte.d.ts +32 -0
- package/dist/toggle/toggle-context.svelte.d.ts +9 -0
- package/dist/toggle/toggle-context.svelte.js +32 -0
- package/dist/toggle/toggle-group.svelte +41 -0
- package/dist/toggle/toggle-group.svelte.d.ts +32 -0
- package/dist/tooltip/index.d.ts +1 -0
- package/dist/tooltip/index.js +1 -0
- package/dist/tooltip/tooltip.svelte +94 -0
- package/dist/tooltip/tooltip.svelte.d.ts +25 -0
- package/dist/utils.d.ts +43 -0
- package/dist/utils.js +72 -0
- package/package.json +76 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Check } from "@lucide/svelte";
|
|
3
|
+
import { MenuDivider, MenuHeader, MenuItem } from ".";
|
|
4
|
+
import type { Action } from "../command";
|
|
5
|
+
|
|
6
|
+
export type MenuActionsProps = {
|
|
7
|
+
actions: Action[];
|
|
8
|
+
};
|
|
9
|
+
let { actions }: MenuActionsProps = $props();
|
|
10
|
+
|
|
11
|
+
let categoryHasSelected = $derived(
|
|
12
|
+
actions.reduce(
|
|
13
|
+
(acc, { category, selected }) => ({
|
|
14
|
+
...acc,
|
|
15
|
+
[category]: acc[category] || selected !== undefined,
|
|
16
|
+
}),
|
|
17
|
+
{} as Record<string, boolean>,
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
{#each actions as action, index (action)}
|
|
23
|
+
{@const Icon = action.icon}
|
|
24
|
+
{#if index === 0 || actions[index - 1].category !== action.category}
|
|
25
|
+
{#if index !== 0}
|
|
26
|
+
<MenuDivider />
|
|
27
|
+
{/if}
|
|
28
|
+
<MenuHeader>{action.category}</MenuHeader>
|
|
29
|
+
{/if}
|
|
30
|
+
<MenuItem
|
|
31
|
+
class="gap-2"
|
|
32
|
+
onSelect={action.callback}
|
|
33
|
+
title={action.description}
|
|
34
|
+
disabled={!action.enabled()}
|
|
35
|
+
>
|
|
36
|
+
{#if categoryHasSelected[action.category]}
|
|
37
|
+
{#if action.selected?.()}
|
|
38
|
+
<Check class="text-m3-primary" size={16} />
|
|
39
|
+
{:else}
|
|
40
|
+
<div class="size-4"></div>
|
|
41
|
+
{/if}
|
|
42
|
+
{/if}
|
|
43
|
+
<Icon size={16} />
|
|
44
|
+
{action.name}
|
|
45
|
+
{#if action.shortcut}
|
|
46
|
+
<div class="ml-auto pl-2 text-xs">
|
|
47
|
+
{action.shortcut.toString()}
|
|
48
|
+
</div>
|
|
49
|
+
{/if}
|
|
50
|
+
</MenuItem>
|
|
51
|
+
{/each}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Action } from "../command";
|
|
2
|
+
export type MenuActionsProps = {
|
|
3
|
+
actions: Action[];
|
|
4
|
+
};
|
|
5
|
+
declare const MenuActions: import("svelte").Component<MenuActionsProps, {}, "">;
|
|
6
|
+
type MenuActions = ReturnType<typeof MenuActions>;
|
|
7
|
+
export default MenuActions;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
import { baseClasses, type Variants } from "../base";
|
|
5
|
+
import { mergeComponentProps } from "../merge-props";
|
|
6
|
+
import { Popover, type PopoverProps } from "../popover";
|
|
7
|
+
import { type ClassName } from "../utils";
|
|
8
|
+
import { getMenuContext } from "./menu-context.svelte";
|
|
9
|
+
|
|
10
|
+
export type MenuContentProps = {
|
|
11
|
+
children: Snippet;
|
|
12
|
+
} & ClassName &
|
|
13
|
+
Variants &
|
|
14
|
+
Omit<PopoverProps, "children" | "open">;
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
let {
|
|
19
|
+
children,
|
|
20
|
+
class: className,
|
|
21
|
+
variant = "elevated",
|
|
22
|
+
color = "primary",
|
|
23
|
+
shape = "rounded",
|
|
24
|
+
placement = "bottom",
|
|
25
|
+
...restProps
|
|
26
|
+
}: MenuContentProps = $props();
|
|
27
|
+
|
|
28
|
+
const ctx = getMenuContext();
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<Popover
|
|
32
|
+
bind:open={ctx.open}
|
|
33
|
+
role="menu"
|
|
34
|
+
class={twMerge(
|
|
35
|
+
baseClasses({ variant, color, shape }),
|
|
36
|
+
"max-h-[60%] border bg-m3-surface-container-highest p-1 shadow",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{placement}
|
|
40
|
+
anchorEl={ctx.anchorEl}
|
|
41
|
+
{...mergeComponentProps(Popover, restProps, {
|
|
42
|
+
onmouseleave: () => ctx.blur(),
|
|
43
|
+
onKeydownWhileOpen: (event) => ctx.handleKeyDown(event),
|
|
44
|
+
})}
|
|
45
|
+
>
|
|
46
|
+
{@render children()}
|
|
47
|
+
</Popover>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import { type Variants } from "../base";
|
|
3
|
+
import { type PopoverProps } from "../popover";
|
|
4
|
+
import { type ClassName } from "../utils";
|
|
5
|
+
export type MenuContentProps = {
|
|
6
|
+
children: Snippet;
|
|
7
|
+
} & ClassName & Variants & Omit<PopoverProps, "children" | "open">;
|
|
8
|
+
declare const MenuContent: import("svelte").Component<MenuContentProps, {}, "">;
|
|
9
|
+
type MenuContent = ReturnType<typeof MenuContent>;
|
|
10
|
+
export default MenuContent;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class MenuContext {
|
|
2
|
+
#private;
|
|
3
|
+
focused: HTMLElement | undefined;
|
|
4
|
+
constructor(getOpen: () => boolean, setOpen: (val: boolean) => void, getAnchorEl: () => HTMLElement | undefined, setAnchorEl: (anchorEl: HTMLElement | undefined) => void);
|
|
5
|
+
get open(): boolean;
|
|
6
|
+
set open(value: boolean);
|
|
7
|
+
get anchorEl(): HTMLElement | undefined;
|
|
8
|
+
set anchorEl(value: HTMLElement | undefined);
|
|
9
|
+
close(): void;
|
|
10
|
+
get items(): HTMLElement[];
|
|
11
|
+
add(item: HTMLElement): () => void;
|
|
12
|
+
delete(item: HTMLElement): void;
|
|
13
|
+
focus(item?: HTMLElement): void;
|
|
14
|
+
blur(): void;
|
|
15
|
+
handleKeyDown(event: KeyboardEvent): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function setMenuContext(state: MenuContext): MenuContext;
|
|
18
|
+
export declare function getMenuContext(): MenuContext;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { getContext, setContext } from "svelte";
|
|
2
|
+
import { Bindable } from "../bindable.svelte";
|
|
3
|
+
import { OrderedHTMLElements } from "../ordered-html-elements";
|
|
4
|
+
import { Shortcut } from "../shortcut";
|
|
5
|
+
import { TypingBuffer } from "../utils";
|
|
6
|
+
export class MenuContext {
|
|
7
|
+
#open;
|
|
8
|
+
#anchorEl;
|
|
9
|
+
#items = new OrderedHTMLElements();
|
|
10
|
+
focused = $state();
|
|
11
|
+
#buffer = new TypingBuffer();
|
|
12
|
+
constructor(getOpen, setOpen, getAnchorEl, setAnchorEl) {
|
|
13
|
+
this.#open = new Bindable(getOpen());
|
|
14
|
+
this.#open.bind(getOpen, setOpen);
|
|
15
|
+
this.#anchorEl = new Bindable(getAnchorEl());
|
|
16
|
+
this.#anchorEl.bind(getAnchorEl, setAnchorEl);
|
|
17
|
+
}
|
|
18
|
+
get open() {
|
|
19
|
+
return this.#open.value;
|
|
20
|
+
}
|
|
21
|
+
set open(value) {
|
|
22
|
+
this.#open.value = value;
|
|
23
|
+
}
|
|
24
|
+
get anchorEl() {
|
|
25
|
+
return this.#anchorEl.value;
|
|
26
|
+
}
|
|
27
|
+
set anchorEl(value) {
|
|
28
|
+
this.#anchorEl.value = value;
|
|
29
|
+
}
|
|
30
|
+
close() {
|
|
31
|
+
this.open = false;
|
|
32
|
+
this.anchorEl?.focus();
|
|
33
|
+
}
|
|
34
|
+
get items() {
|
|
35
|
+
return this.#items.items;
|
|
36
|
+
}
|
|
37
|
+
add(item) {
|
|
38
|
+
this.#items.add(item);
|
|
39
|
+
return () => this.delete(item);
|
|
40
|
+
}
|
|
41
|
+
delete(item) {
|
|
42
|
+
this.#items.delete(item);
|
|
43
|
+
}
|
|
44
|
+
focus(item) {
|
|
45
|
+
if (!item)
|
|
46
|
+
return;
|
|
47
|
+
this.focused = item;
|
|
48
|
+
this.focused.focus();
|
|
49
|
+
}
|
|
50
|
+
blur() {
|
|
51
|
+
this.focused?.blur();
|
|
52
|
+
this.focused = undefined;
|
|
53
|
+
}
|
|
54
|
+
handleKeyDown(event) {
|
|
55
|
+
if (!this.items)
|
|
56
|
+
return;
|
|
57
|
+
if (Shortcut.ARROW_UP.matches(event) ||
|
|
58
|
+
Shortcut.TAB_BACKWARD.matches(event)) {
|
|
59
|
+
if (!this.focused) {
|
|
60
|
+
this.focus(this.items[this.items.length - 1]);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
let activeIndex = this.items.indexOf(this.focused);
|
|
64
|
+
do {
|
|
65
|
+
activeIndex =
|
|
66
|
+
(activeIndex - 1 + this.items.length) % this.items.length;
|
|
67
|
+
} while (this.items[activeIndex].disabled);
|
|
68
|
+
this.focus(this.items[activeIndex]);
|
|
69
|
+
}
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
event.stopImmediatePropagation();
|
|
72
|
+
}
|
|
73
|
+
else if (Shortcut.ARROW_DOWN.matches(event) ||
|
|
74
|
+
Shortcut.TAB_FORWARD.matches(event)) {
|
|
75
|
+
if (!this.focused) {
|
|
76
|
+
this.focus(this.items[0]);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
let activeIndex = this.items.indexOf(this.focused);
|
|
80
|
+
do {
|
|
81
|
+
activeIndex = (activeIndex + 1) % this.items.length;
|
|
82
|
+
} while (this.items[activeIndex].disabled);
|
|
83
|
+
this.focus(this.items[activeIndex]);
|
|
84
|
+
}
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
event.stopImmediatePropagation();
|
|
87
|
+
}
|
|
88
|
+
else if (Shortcut.HOME.matches(event)) {
|
|
89
|
+
this.focus(this.items[0]);
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
event.stopImmediatePropagation();
|
|
92
|
+
}
|
|
93
|
+
else if (Shortcut.END.matches(event)) {
|
|
94
|
+
this.focus(this.items[this.items.length - 1]);
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
event.stopImmediatePropagation();
|
|
97
|
+
}
|
|
98
|
+
else if (Shortcut.ENTER.matches(event)) {
|
|
99
|
+
event.stopImmediatePropagation();
|
|
100
|
+
}
|
|
101
|
+
else if (Shortcut.isChar(event)) {
|
|
102
|
+
const prefix = this.#buffer.append(event.key);
|
|
103
|
+
const matchedItem = this.items.find((menuItem) => (menuItem.textContent?.trim().toLowerCase() ?? "").startsWith(prefix.toLowerCase()));
|
|
104
|
+
this.focus(matchedItem);
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
event.stopImmediatePropagation();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function setMenuContext(state) {
|
|
111
|
+
return setContext(MenuContext, state);
|
|
112
|
+
}
|
|
113
|
+
export function getMenuContext() {
|
|
114
|
+
const ctx = getContext(MenuContext);
|
|
115
|
+
if (!ctx)
|
|
116
|
+
throw new Error("MenuContext is undefined");
|
|
117
|
+
return ctx;
|
|
118
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { type Snippet } from "svelte";
|
|
3
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
import { baseClasses, type Variants } from "../base";
|
|
6
|
+
import { mergeProps } from "../merge-props";
|
|
7
|
+
import { Shortcut } from "../shortcut";
|
|
8
|
+
import { type ClassName, type ElementRef } from "../utils";
|
|
9
|
+
import { getMenuContext } from "./menu-context.svelte";
|
|
10
|
+
|
|
11
|
+
export type MenuItemProps = {
|
|
12
|
+
onSelect?: (el: HTMLElement) => void;
|
|
13
|
+
children: Snippet<[]>;
|
|
14
|
+
} & ElementRef &
|
|
15
|
+
ClassName &
|
|
16
|
+
Variants &
|
|
17
|
+
Omit<HTMLButtonAttributes, "type" | "value">;
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
let {
|
|
22
|
+
onSelect,
|
|
23
|
+
children,
|
|
24
|
+
el = $bindable(),
|
|
25
|
+
class: className,
|
|
26
|
+
variant = "plain",
|
|
27
|
+
color = "secondary",
|
|
28
|
+
shape = "rounded",
|
|
29
|
+
...restProps
|
|
30
|
+
}: MenuItemProps = $props();
|
|
31
|
+
|
|
32
|
+
const ctx = getMenuContext();
|
|
33
|
+
|
|
34
|
+
function handleClick() {
|
|
35
|
+
if (el) {
|
|
36
|
+
onSelect?.(el);
|
|
37
|
+
}
|
|
38
|
+
ctx.close();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handleMouseEnter(event: MouseEvent) {
|
|
42
|
+
if (event.target instanceof HTMLElement) {
|
|
43
|
+
ctx.focus(event.target);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function handleFocus(event: FocusEvent) {
|
|
48
|
+
if (event.target instanceof HTMLElement) {
|
|
49
|
+
ctx.focus(event.target);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
54
|
+
if (Shortcut.ENTER.matches(event) || Shortcut.SPACE.matches(event)) {
|
|
55
|
+
event.stopImmediatePropagation();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
$effect(() => {
|
|
60
|
+
if (el) {
|
|
61
|
+
return ctx.add(el);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<button
|
|
67
|
+
bind:this={el}
|
|
68
|
+
role="menuitem"
|
|
69
|
+
class={twMerge(
|
|
70
|
+
baseClasses({ variant, color, shape, focus: true }),
|
|
71
|
+
"flex w-full items-center gap-1 px-2 py-1 text-left text-sm focus:ring-0",
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
{...mergeProps(
|
|
75
|
+
{
|
|
76
|
+
onclick: handleClick,
|
|
77
|
+
onmouseenter: handleMouseEnter,
|
|
78
|
+
onfocus: handleFocus,
|
|
79
|
+
onkeydown: handleKeyDown,
|
|
80
|
+
},
|
|
81
|
+
restProps,
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
{@render children()}
|
|
85
|
+
</button>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
import { type Variants } from "../base";
|
|
4
|
+
import { type ClassName, type ElementRef } from "../utils";
|
|
5
|
+
export type MenuItemProps = {
|
|
6
|
+
onSelect?: (el: HTMLElement) => void;
|
|
7
|
+
children: Snippet<[]>;
|
|
8
|
+
} & ElementRef & ClassName & Variants & Omit<HTMLButtonAttributes, "type" | "value">;
|
|
9
|
+
declare const MenuItem: import("svelte").Component<MenuItemProps, {}, "el">;
|
|
10
|
+
type MenuItem = ReturnType<typeof MenuItem>;
|
|
11
|
+
export default MenuItem;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { Button, type ButtonProps } from "../button";
|
|
3
|
+
import { mergeProps } from "../merge-props";
|
|
4
|
+
import { Shortcut } from "../shortcut";
|
|
5
|
+
import { getMenuContext } from "./menu-context.svelte";
|
|
6
|
+
|
|
7
|
+
export type MenuTriggerButtonProps = {} & ButtonProps;
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
let { ...restProps }: MenuTriggerButtonProps = $props();
|
|
12
|
+
|
|
13
|
+
const ctx = getMenuContext();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<Button
|
|
17
|
+
bind:el={ctx.anchorEl}
|
|
18
|
+
{...mergeProps(
|
|
19
|
+
{
|
|
20
|
+
onclick: () => (ctx.open = !ctx.open),
|
|
21
|
+
onkeydown: (event: KeyboardEvent) => {
|
|
22
|
+
if (Shortcut.ENTER.matches(event) || Shortcut.SPACE.matches(event)) {
|
|
23
|
+
event.stopImmediatePropagation();
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"aria-haspopup": "menu",
|
|
27
|
+
"aria-expanded": ctx.open,
|
|
28
|
+
},
|
|
29
|
+
restProps,
|
|
30
|
+
)}
|
|
31
|
+
/>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ButtonProps } from "../button";
|
|
2
|
+
export type MenuTriggerButtonProps = {} & ButtonProps;
|
|
3
|
+
declare const MenuTriggerButton: import("svelte").Component<MenuTriggerButtonProps, {}, "">;
|
|
4
|
+
type MenuTriggerButton = ReturnType<typeof MenuTriggerButton>;
|
|
5
|
+
export default MenuTriggerButton;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { type Snippet } from "svelte";
|
|
3
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
import type { Variants } from "../base";
|
|
6
|
+
import { mergeProps } from "../merge-props";
|
|
7
|
+
import { Shortcut } from "../shortcut";
|
|
8
|
+
import { type ClassName } from "../utils";
|
|
9
|
+
import { getMenuContext } from "./menu-context.svelte";
|
|
10
|
+
|
|
11
|
+
export type MenuTriggerProps = {
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
} & ClassName &
|
|
14
|
+
Variants &
|
|
15
|
+
HTMLButtonAttributes;
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
let { children, class: className, ...restProps }: MenuTriggerProps = $props();
|
|
20
|
+
|
|
21
|
+
const ctx = getMenuContext();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<button
|
|
25
|
+
bind:this={ctx.anchorEl}
|
|
26
|
+
class={twMerge(className)}
|
|
27
|
+
{...mergeProps(
|
|
28
|
+
{
|
|
29
|
+
onclick: () => (ctx.open = !ctx.open),
|
|
30
|
+
onkeydown: (event: KeyboardEvent) => {
|
|
31
|
+
if (Shortcut.ENTER.matches(event) || Shortcut.SPACE.matches(event)) {
|
|
32
|
+
event.stopImmediatePropagation();
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"aria-haspopup": "menu",
|
|
36
|
+
"aria-expanded": ctx.open,
|
|
37
|
+
},
|
|
38
|
+
restProps,
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
{@render children?.()}
|
|
42
|
+
</button>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Snippet } from "svelte";
|
|
2
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
3
|
+
import type { Variants } from "../base";
|
|
4
|
+
import { type ClassName } from "../utils";
|
|
5
|
+
export type MenuTriggerProps = {
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
} & ClassName & Variants & HTMLButtonAttributes;
|
|
8
|
+
declare const MenuTrigger: import("svelte").Component<MenuTriggerProps, {}, "">;
|
|
9
|
+
type MenuTrigger = ReturnType<typeof MenuTrigger>;
|
|
10
|
+
export default MenuTrigger;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { ElementRef } from "../utils";
|
|
4
|
+
import { MenuContext, setMenuContext } from "./menu-context.svelte";
|
|
5
|
+
|
|
6
|
+
export type MenuProps = {
|
|
7
|
+
open?: boolean;
|
|
8
|
+
children: Snippet;
|
|
9
|
+
} & ElementRef;
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
let {
|
|
14
|
+
open = $bindable(false),
|
|
15
|
+
children,
|
|
16
|
+
el = $bindable(),
|
|
17
|
+
}: MenuProps = $props();
|
|
18
|
+
|
|
19
|
+
setMenuContext(
|
|
20
|
+
new MenuContext(
|
|
21
|
+
() => open,
|
|
22
|
+
(value) => (open = value),
|
|
23
|
+
() => el,
|
|
24
|
+
(value) => (el = value),
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
{@render children()}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { ElementRef } from "../utils";
|
|
3
|
+
export type MenuProps = {
|
|
4
|
+
open?: boolean;
|
|
5
|
+
children: Snippet;
|
|
6
|
+
} & ElementRef;
|
|
7
|
+
declare const Menu: import("svelte").Component<MenuProps, {}, "open" | "el">;
|
|
8
|
+
type Menu = ReturnType<typeof Menu>;
|
|
9
|
+
export default Menu;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Component, ComponentProps } from "svelte";
|
|
2
|
+
import type { EventHandler } from "svelte/elements";
|
|
3
|
+
export declare function interceptStopImmediatePropagation(event: Event, callback: () => void): void;
|
|
4
|
+
export declare function runEventHandlers(target: EventTarget, event: Event & {
|
|
5
|
+
currentTarget: EventTarget & Element;
|
|
6
|
+
}, ...handlers: EventHandler[]): void;
|
|
7
|
+
type Props = Record<string, unknown>;
|
|
8
|
+
type PropsUnion<T extends Props[]> = (T extends (infer U)[] ? U extends Props ? (k: {
|
|
9
|
+
[K in keyof U]: U[K];
|
|
10
|
+
}) => void : never : never) extends (k: infer I) => void ? I : never;
|
|
11
|
+
export declare function mergeProps<T extends Props[]>(...args: [...T]): PropsUnion<T>;
|
|
12
|
+
export declare function mergeComponentProps<C extends Component<any>>(_component: C, ...args: Partial<ComponentProps<C>>[]): Partial<ComponentProps<C>>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { twMerge } from "tailwind-merge";
|
|
2
|
+
function isClass(value) {
|
|
3
|
+
if (value === null || value === undefined) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
if (["string", "number", "bigint", "boolean"].includes(typeof value)) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
if (Array.isArray(value)) {
|
|
10
|
+
return value.every((item) => isClass(item));
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
function isHandlerKey(key) {
|
|
15
|
+
return key.startsWith("on") && key[2].toLowerCase() === key[2];
|
|
16
|
+
}
|
|
17
|
+
function isHandler(value) {
|
|
18
|
+
return typeof value === "function";
|
|
19
|
+
}
|
|
20
|
+
export function interceptStopImmediatePropagation(event, callback) {
|
|
21
|
+
const original = event.stopImmediatePropagation;
|
|
22
|
+
event.stopImmediatePropagation = function () {
|
|
23
|
+
callback();
|
|
24
|
+
original.call(event);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function runEventHandlers(target, event, ...handlers) {
|
|
28
|
+
let stopped = false;
|
|
29
|
+
interceptStopImmediatePropagation(event, () => (stopped = true));
|
|
30
|
+
for (const handler of handlers) {
|
|
31
|
+
if (stopped)
|
|
32
|
+
break;
|
|
33
|
+
handler.call(target, event);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function chainHandlers(...handlers) {
|
|
37
|
+
return function (event) {
|
|
38
|
+
runEventHandlers(this, event, ...handlers);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function isCallback(value) {
|
|
42
|
+
return typeof value === "function";
|
|
43
|
+
}
|
|
44
|
+
function chainCallbacks(...callbacks) {
|
|
45
|
+
return function (...args) {
|
|
46
|
+
for (const callback of callbacks) {
|
|
47
|
+
callback(...args);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function mergeProps(...args) {
|
|
52
|
+
const result = {};
|
|
53
|
+
for (const props of args) {
|
|
54
|
+
for (const key in props) {
|
|
55
|
+
const prevVal = result[key];
|
|
56
|
+
const currVal = props[key];
|
|
57
|
+
if (currVal === undefined) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (prevVal === undefined) {
|
|
61
|
+
result[key] = currVal;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (isHandlerKey(key) && isHandler(prevVal) && isHandler(currVal)) {
|
|
65
|
+
result[key] = chainHandlers(prevVal, currVal);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (isCallback(prevVal) && isCallback(currVal)) {
|
|
69
|
+
result[key] = chainCallbacks(prevVal, currVal);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (key === "class" && isClass(prevVal) && isClass(currVal)) {
|
|
73
|
+
result[key] = twMerge(prevVal, currVal);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
result[key] = currVal;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
+
export function mergeComponentProps(_component, ...args) {
|
|
83
|
+
return mergeProps(...args);
|
|
84
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Modal, type ModalProps } from "./modal.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Modal } from "./modal.svelte";
|