@mrintel/villain-ui 0.2.2 → 0.3.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/dist/components/buttons/Button.svelte +33 -0
- package/dist/components/buttons/Button.svelte.d.ts +11 -0
- package/dist/components/buttons/ButtonGroup.svelte +30 -0
- package/dist/components/buttons/ButtonGroup.svelte.d.ts +8 -0
- package/dist/components/buttons/FloatingActionButton.svelte +44 -0
- package/dist/components/buttons/FloatingActionButton.svelte.d.ts +11 -0
- package/dist/components/buttons/IconButton.svelte +53 -0
- package/dist/components/buttons/IconButton.svelte.d.ts +13 -0
- package/dist/components/buttons/LinkButton.svelte +37 -0
- package/dist/components/buttons/LinkButton.svelte.d.ts +12 -0
- package/dist/components/buttons/buttonClasses.d.ts +10 -0
- package/dist/components/buttons/buttonClasses.js +10 -0
- package/dist/components/buttons/index.d.ts +5 -0
- package/dist/components/buttons/index.js +5 -0
- package/dist/components/cards/Card.svelte +46 -0
- package/dist/components/cards/Card.svelte.d.ts +11 -0
- package/dist/components/cards/Container.svelte +33 -0
- package/dist/components/cards/Container.svelte.d.ts +10 -0
- package/dist/components/cards/Divider.svelte +52 -0
- package/dist/components/cards/Divider.svelte.d.ts +9 -0
- package/dist/components/cards/Grid.svelte +44 -0
- package/dist/components/cards/Grid.svelte.d.ts +10 -0
- package/dist/components/cards/Panel.svelte +32 -0
- package/dist/components/cards/Panel.svelte.d.ts +10 -0
- package/dist/components/cards/SectionHeader.svelte +38 -0
- package/dist/components/cards/SectionHeader.svelte.d.ts +11 -0
- package/dist/components/cards/index.d.ts +6 -0
- package/dist/components/cards/index.js +6 -0
- package/dist/components/data/Avatar.svelte +67 -0
- package/dist/components/data/Avatar.svelte.d.ts +10 -0
- package/dist/components/data/Badge.svelte +32 -0
- package/dist/components/data/Badge.svelte.d.ts +8 -0
- package/dist/components/data/CodeBlock.svelte +121 -0
- package/dist/components/data/CodeBlock.svelte.d.ts +32 -0
- package/dist/components/data/List.svelte +64 -0
- package/dist/components/data/List.svelte.d.ts +8 -0
- package/dist/components/data/Pagination.svelte +123 -0
- package/dist/components/data/Pagination.svelte.d.ts +9 -0
- package/dist/components/data/Stat.svelte +103 -0
- package/dist/components/data/Stat.svelte.d.ts +11 -0
- package/dist/components/data/Table.svelte +76 -0
- package/dist/components/data/Table.svelte.d.ts +9 -0
- package/dist/components/data/Tag.svelte +53 -0
- package/dist/components/data/Tag.svelte.d.ts +9 -0
- package/dist/components/data/index.d.ts +8 -0
- package/dist/components/data/index.js +8 -0
- package/dist/components/forms/Checkbox.svelte +51 -0
- package/dist/components/forms/Checkbox.svelte.d.ts +10 -0
- package/dist/components/forms/FileUpload.svelte +164 -0
- package/dist/components/forms/FileUpload.svelte.d.ts +22 -0
- package/dist/components/forms/Input.svelte +57 -0
- package/dist/components/forms/Input.svelte.d.ts +13 -0
- package/dist/components/forms/InputGroup.svelte +7 -0
- package/dist/components/forms/InputGroup.svelte.d.ts +20 -0
- package/dist/components/forms/RadioGroup.svelte +87 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts +15 -0
- package/dist/components/forms/RangeSlider.svelte +116 -0
- package/dist/components/forms/RangeSlider.svelte.d.ts +14 -0
- package/dist/components/forms/Select.svelte +71 -0
- package/dist/components/forms/Select.svelte.d.ts +16 -0
- package/dist/components/forms/Switch.svelte +56 -0
- package/dist/components/forms/Switch.svelte.d.ts +10 -0
- package/dist/components/forms/Textarea.svelte +57 -0
- package/dist/components/forms/Textarea.svelte.d.ts +13 -0
- package/dist/components/forms/index.d.ts +9 -0
- package/dist/components/forms/index.js +9 -0
- package/dist/components/navigation/Breadcrumbs.svelte +59 -0
- package/dist/components/navigation/Breadcrumbs.svelte.d.ts +14 -0
- package/dist/components/navigation/ContextMenu.svelte +83 -0
- package/dist/components/navigation/ContextMenu.svelte.d.ts +11 -0
- package/dist/components/navigation/DropdownMenu.svelte +80 -0
- package/dist/components/navigation/DropdownMenu.svelte.d.ts +10 -0
- package/dist/components/navigation/Menu.svelte +48 -0
- package/dist/components/navigation/Menu.svelte.d.ts +15 -0
- package/dist/components/navigation/Navbar.svelte +32 -0
- package/dist/components/navigation/Navbar.svelte.d.ts +9 -0
- package/dist/components/navigation/Sidebar.svelte +35 -0
- package/dist/components/navigation/Sidebar.svelte.d.ts +10 -0
- package/dist/components/navigation/Tabs.svelte +54 -0
- package/dist/components/navigation/Tabs.svelte.d.ts +15 -0
- package/dist/components/navigation/index.d.ts +7 -0
- package/dist/components/navigation/index.js +7 -0
- package/dist/components/overlays/Alert.svelte +99 -0
- package/dist/components/overlays/Alert.svelte.d.ts +11 -0
- package/dist/components/overlays/CommandPalette.svelte +217 -0
- package/dist/components/overlays/CommandPalette.svelte.d.ts +16 -0
- package/dist/components/overlays/Drawer.svelte +167 -0
- package/dist/components/overlays/Drawer.svelte.d.ts +14 -0
- package/dist/components/overlays/Dropdown.svelte +30 -0
- package/dist/components/overlays/Dropdown.svelte.d.ts +9 -0
- package/dist/components/overlays/Modal.svelte +130 -0
- package/dist/components/overlays/Modal.svelte.d.ts +13 -0
- package/dist/components/overlays/Popover.svelte +131 -0
- package/dist/components/overlays/Popover.svelte.d.ts +11 -0
- package/dist/components/overlays/ProgressBar.svelte +45 -0
- package/dist/components/overlays/ProgressBar.svelte.d.ts +10 -0
- package/dist/components/overlays/SkeletonLoader.svelte +82 -0
- package/dist/components/overlays/SkeletonLoader.svelte.d.ts +9 -0
- package/dist/components/overlays/Spinner.svelte +43 -0
- package/dist/components/overlays/Spinner.svelte.d.ts +7 -0
- package/dist/components/overlays/Toast.svelte +140 -0
- package/dist/components/overlays/Toast.svelte.d.ts +13 -0
- package/dist/components/overlays/Tooltip.svelte +115 -0
- package/dist/components/overlays/Tooltip.svelte.d.ts +10 -0
- package/dist/components/overlays/index.d.ts +11 -0
- package/dist/components/overlays/index.js +11 -0
- package/dist/components/typography/Code.svelte +14 -0
- package/dist/components/typography/Code.svelte.d.ts +6 -0
- package/dist/components/typography/Heading.svelte +22 -0
- package/dist/components/typography/Heading.svelte.d.ts +9 -0
- package/dist/components/typography/Text.svelte +24 -0
- package/dist/components/typography/Text.svelte.d.ts +9 -0
- package/dist/components/typography/index.d.ts +3 -0
- package/dist/components/typography/index.js +3 -0
- package/dist/components/utilities/Accordion.svelte +67 -0
- package/dist/components/utilities/Accordion.svelte.d.ts +14 -0
- package/dist/components/utilities/Carousel.svelte +152 -0
- package/dist/components/utilities/Carousel.svelte.d.ts +16 -0
- package/dist/components/utilities/Collapse.svelte +60 -0
- package/dist/components/utilities/Collapse.svelte.d.ts +10 -0
- package/dist/components/utilities/Portal.svelte +72 -0
- package/dist/components/utilities/Portal.svelte.d.ts +21 -0
- package/dist/components/utilities/ScrollArea.svelte +41 -0
- package/dist/components/utilities/ScrollArea.svelte.d.ts +8 -0
- package/dist/components/utilities/index.d.ts +5 -0
- package/dist/components/utilities/index.js +5 -0
- package/dist/index.d.ts +15 -175
- package/dist/index.js +24 -4560
- package/dist/lib/internal/id.d.ts +12 -0
- package/dist/lib/internal/id.js +15 -0
- package/dist/theme.css +218 -0
- package/package.json +6 -5
- package/dist/index.css +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createId } from '../../lib/internal/id.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
value?: string;
|
|
6
|
+
options: Array<{ value: string; label: string }>;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
error?: boolean;
|
|
10
|
+
label?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
onchange?: (event: Event) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
value = $bindable(''),
|
|
17
|
+
options,
|
|
18
|
+
placeholder,
|
|
19
|
+
disabled = false,
|
|
20
|
+
error = false,
|
|
21
|
+
label,
|
|
22
|
+
id = createId('select'),
|
|
23
|
+
onchange
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
const chevronIcon = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%23ADADAD' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E";
|
|
27
|
+
|
|
28
|
+
const baseClasses = `glass-panel rounded-lg px-4 py-3 pr-10 font-body text-text transition-all duration-300 ease-luxe w-full appearance-none bg-no-repeat bg-[right_1rem_center] bg-[length:12px]`;
|
|
29
|
+
const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
|
|
30
|
+
const errorClasses = error ? 'border-error' : '';
|
|
31
|
+
const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#if label}
|
|
35
|
+
<div>
|
|
36
|
+
<label for={id} class="text-text-soft text-sm mb-2 block">
|
|
37
|
+
{label}
|
|
38
|
+
</label>
|
|
39
|
+
<select
|
|
40
|
+
{id}
|
|
41
|
+
{disabled}
|
|
42
|
+
bind:value
|
|
43
|
+
onchange={onchange}
|
|
44
|
+
class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
|
|
45
|
+
style={`background-image: url('${chevronIcon}')`}
|
|
46
|
+
>
|
|
47
|
+
{#if placeholder}
|
|
48
|
+
<option disabled value="">{placeholder}</option>
|
|
49
|
+
{/if}
|
|
50
|
+
{#each options as option}
|
|
51
|
+
<option value={option.value}>{option.label}</option>
|
|
52
|
+
{/each}
|
|
53
|
+
</select>
|
|
54
|
+
</div>
|
|
55
|
+
{:else}
|
|
56
|
+
<select
|
|
57
|
+
{id}
|
|
58
|
+
{disabled}
|
|
59
|
+
bind:value
|
|
60
|
+
onchange={onchange}
|
|
61
|
+
class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
|
|
62
|
+
style={`background-image: url('${chevronIcon}')`}
|
|
63
|
+
>
|
|
64
|
+
{#if placeholder}
|
|
65
|
+
<option disabled value="">{placeholder}</option>
|
|
66
|
+
{/if}
|
|
67
|
+
{#each options as option}
|
|
68
|
+
<option value={option.value}>{option.label}</option>
|
|
69
|
+
{/each}
|
|
70
|
+
</select>
|
|
71
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
value?: string;
|
|
3
|
+
options: Array<{
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}>;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
error?: boolean;
|
|
10
|
+
label?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
onchange?: (event: Event) => void;
|
|
13
|
+
}
|
|
14
|
+
declare const Select: import("svelte").Component<Props, {}, "value">;
|
|
15
|
+
type Select = ReturnType<typeof Select>;
|
|
16
|
+
export default Select;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createId } from '../../lib/internal/id.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
checked?: boolean;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
label?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
onchange?: (event: Event) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
checked = $bindable(false),
|
|
14
|
+
disabled = false,
|
|
15
|
+
label,
|
|
16
|
+
id = createId('switch'),
|
|
17
|
+
onchange
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<label for={id} class="flex items-center gap-2 cursor-pointer {disabled ? 'opacity-50 cursor-not-allowed' : ''}">
|
|
22
|
+
<input
|
|
23
|
+
type="checkbox"
|
|
24
|
+
role="switch"
|
|
25
|
+
aria-checked={checked}
|
|
26
|
+
{id}
|
|
27
|
+
{disabled}
|
|
28
|
+
bind:checked
|
|
29
|
+
onchange={onchange}
|
|
30
|
+
class="w-11 h-6 rounded-pill bg-base-3 border border-border appearance-none transition-all duration-300 ease-luxe cursor-pointer checked:bg-accent checked:accent-glow focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {disabled ? 'cursor-not-allowed' : ''}"
|
|
31
|
+
/>
|
|
32
|
+
{#if label}
|
|
33
|
+
<span class="text-text text-sm select-none">
|
|
34
|
+
{label}
|
|
35
|
+
</span>
|
|
36
|
+
{/if}
|
|
37
|
+
</label>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
input[type="checkbox"]::after {
|
|
41
|
+
content: '';
|
|
42
|
+
position: absolute;
|
|
43
|
+
left: 0.25rem;
|
|
44
|
+
top: 50%;
|
|
45
|
+
transform: translateY(-50%);
|
|
46
|
+
width: 1rem;
|
|
47
|
+
height: 1rem;
|
|
48
|
+
border-radius: var(--radius-pill);
|
|
49
|
+
background: var(--color-text);
|
|
50
|
+
transition: all 0.3s var(--ease-luxe);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
input[type="checkbox"]:checked::after {
|
|
54
|
+
transform: translate(1.25rem, -50%);
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
checked?: boolean;
|
|
3
|
+
disabled?: boolean;
|
|
4
|
+
label?: string;
|
|
5
|
+
id?: string;
|
|
6
|
+
onchange?: (event: Event) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const Switch: import("svelte").Component<Props, {}, "checked">;
|
|
9
|
+
type Switch = ReturnType<typeof Switch>;
|
|
10
|
+
export default Switch;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createId } from '../../lib/internal/id.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
value?: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
rows?: number;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
error?: boolean;
|
|
10
|
+
label?: string;
|
|
11
|
+
id?: string;
|
|
12
|
+
oninput?: (event: Event) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
value = $bindable(''),
|
|
17
|
+
placeholder,
|
|
18
|
+
rows = 4,
|
|
19
|
+
disabled = false,
|
|
20
|
+
error = false,
|
|
21
|
+
label,
|
|
22
|
+
id = createId('textarea'),
|
|
23
|
+
oninput
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
const baseClasses = 'glass-panel rounded-lg px-4 py-3 font-body text-text placeholder:text-text-muted transition-all duration-300 ease-luxe w-full resize-y min-h-[100px]';
|
|
27
|
+
const focusClasses = 'focus:outline-none focus:border-accent focus:accent-glow';
|
|
28
|
+
const errorClasses = error ? 'border-error' : '';
|
|
29
|
+
const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
{#if label}
|
|
33
|
+
<div>
|
|
34
|
+
<label for={id} class="text-text-soft text-sm mb-2 block">
|
|
35
|
+
{label}
|
|
36
|
+
</label>
|
|
37
|
+
<textarea
|
|
38
|
+
{id}
|
|
39
|
+
{placeholder}
|
|
40
|
+
{disabled}
|
|
41
|
+
{rows}
|
|
42
|
+
bind:value
|
|
43
|
+
oninput={oninput}
|
|
44
|
+
class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
|
|
45
|
+
></textarea>
|
|
46
|
+
</div>
|
|
47
|
+
{:else}
|
|
48
|
+
<textarea
|
|
49
|
+
{id}
|
|
50
|
+
{placeholder}
|
|
51
|
+
{disabled}
|
|
52
|
+
{rows}
|
|
53
|
+
bind:value
|
|
54
|
+
oninput={oninput}
|
|
55
|
+
class="{baseClasses} {focusClasses} {errorClasses} {disabledClasses}"
|
|
56
|
+
></textarea>
|
|
57
|
+
{/if}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
value?: string;
|
|
3
|
+
placeholder?: string;
|
|
4
|
+
rows?: number;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
error?: boolean;
|
|
7
|
+
label?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
oninput?: (event: Event) => void;
|
|
10
|
+
}
|
|
11
|
+
declare const Textarea: import("svelte").Component<Props, {}, "value">;
|
|
12
|
+
type Textarea = ReturnType<typeof Textarea>;
|
|
13
|
+
export default Textarea;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as Input } from './Input.svelte';
|
|
2
|
+
export { default as Textarea } from './Textarea.svelte';
|
|
3
|
+
export { default as Select } from './Select.svelte';
|
|
4
|
+
export { default as Checkbox } from './Checkbox.svelte';
|
|
5
|
+
export { default as Switch } from './Switch.svelte';
|
|
6
|
+
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
7
|
+
export { default as RangeSlider } from './RangeSlider.svelte';
|
|
8
|
+
export { default as FileUpload } from './FileUpload.svelte';
|
|
9
|
+
export { default as InputGroup } from './InputGroup.svelte';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { default as Input } from './Input.svelte';
|
|
2
|
+
export { default as Textarea } from './Textarea.svelte';
|
|
3
|
+
export { default as Select } from './Select.svelte';
|
|
4
|
+
export { default as Checkbox } from './Checkbox.svelte';
|
|
5
|
+
export { default as Switch } from './Switch.svelte';
|
|
6
|
+
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
7
|
+
export { default as RangeSlider } from './RangeSlider.svelte';
|
|
8
|
+
export { default as FileUpload } from './FileUpload.svelte';
|
|
9
|
+
export { default as InputGroup } from './InputGroup.svelte';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface BreadcrumbItem {
|
|
5
|
+
label: string;
|
|
6
|
+
href?: string;
|
|
7
|
+
onclick?: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
items: BreadcrumbItem[];
|
|
12
|
+
separator?: string;
|
|
13
|
+
separatorIcon?: Snippet;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
items,
|
|
18
|
+
separator = '/',
|
|
19
|
+
separatorIcon
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<nav role="navigation" aria-label="Breadcrumb">
|
|
24
|
+
<ol class="flex items-center gap-2">
|
|
25
|
+
{#each items as item, index}
|
|
26
|
+
<li class="flex items-center gap-2">
|
|
27
|
+
{#if item.href}
|
|
28
|
+
<a
|
|
29
|
+
href={item.href}
|
|
30
|
+
class="text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)]"
|
|
31
|
+
>
|
|
32
|
+
{item.label}
|
|
33
|
+
</a>
|
|
34
|
+
{:else if item.onclick}
|
|
35
|
+
<button
|
|
36
|
+
onclick={item.onclick}
|
|
37
|
+
class="text-sm font-[var(--font-body)] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)] cursor-pointer"
|
|
38
|
+
>
|
|
39
|
+
{item.label}
|
|
40
|
+
</button>
|
|
41
|
+
{:else}
|
|
42
|
+
<span class="text-sm font-[var(--font-body)] {index === items.length - 1 ? 'text-[var(--color-text)]' : 'text-[var(--color-text-soft)]'}">
|
|
43
|
+
{item.label}
|
|
44
|
+
</span>
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
{#if index < items.length - 1}
|
|
48
|
+
<span class="text-[var(--color-text-muted)] text-sm" aria-hidden="true">
|
|
49
|
+
{#if separatorIcon}
|
|
50
|
+
{@render separatorIcon()}
|
|
51
|
+
{:else}
|
|
52
|
+
{separator}
|
|
53
|
+
{/if}
|
|
54
|
+
</span>
|
|
55
|
+
{/if}
|
|
56
|
+
</li>
|
|
57
|
+
{/each}
|
|
58
|
+
</ol>
|
|
59
|
+
</nav>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface BreadcrumbItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
onclick?: () => void;
|
|
6
|
+
}
|
|
7
|
+
interface Props {
|
|
8
|
+
items: BreadcrumbItem[];
|
|
9
|
+
separator?: string;
|
|
10
|
+
separatorIcon?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
declare const Breadcrumbs: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Breadcrumbs = ReturnType<typeof Breadcrumbs>;
|
|
14
|
+
export default Breadcrumbs;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
open?: boolean;
|
|
6
|
+
x?: number;
|
|
7
|
+
y?: number;
|
|
8
|
+
trigger?: Snippet;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
open = $bindable(false),
|
|
14
|
+
x = $bindable(0),
|
|
15
|
+
y = $bindable(0),
|
|
16
|
+
trigger,
|
|
17
|
+
children
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
|
|
20
|
+
let menuElement = $state<HTMLDivElement>();
|
|
21
|
+
|
|
22
|
+
function handleContextMenu(event: MouseEvent) {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
x = event.clientX;
|
|
25
|
+
y = event.clientY;
|
|
26
|
+
open = true;
|
|
27
|
+
|
|
28
|
+
// Adjust position if menu would overflow viewport
|
|
29
|
+
requestAnimationFrame(() => {
|
|
30
|
+
if (menuElement) {
|
|
31
|
+
const menuRect = menuElement.getBoundingClientRect();
|
|
32
|
+
const viewportWidth = window.innerWidth;
|
|
33
|
+
const viewportHeight = window.innerHeight;
|
|
34
|
+
|
|
35
|
+
if (x + menuRect.width > viewportWidth) {
|
|
36
|
+
x = viewportWidth - menuRect.width - 10;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (y + menuRect.height > viewportHeight) {
|
|
40
|
+
y = viewportHeight - menuRect.height - 10;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleClickOutside(event: MouseEvent) {
|
|
47
|
+
if (menuElement && !menuElement.contains(event.target as Node)) {
|
|
48
|
+
open = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function handleEscape(event: KeyboardEvent) {
|
|
53
|
+
if (event.key === 'Escape') {
|
|
54
|
+
open = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
$effect(() => {
|
|
59
|
+
if (open) {
|
|
60
|
+
document.addEventListener('click', handleClickOutside);
|
|
61
|
+
document.addEventListener('keydown', handleEscape);
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
document.removeEventListener('click', handleClickOutside);
|
|
65
|
+
document.removeEventListener('keydown', handleEscape);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<div oncontextmenu={handleContextMenu} role="presentation" class="contents">
|
|
72
|
+
{@render trigger?.()}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{#if open}
|
|
76
|
+
<div
|
|
77
|
+
bind:this={menuElement}
|
|
78
|
+
class="fixed z-50 glass-panel rounded-[var(--radius-lg)] shadow-[var(--shadow-deep)] min-w-[12rem] animate-[fade-in_0.15s_var(--ease-luxe)]"
|
|
79
|
+
style="left: {x}px; top: {y}px;"
|
|
80
|
+
>
|
|
81
|
+
{@render children?.()}
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
x?: number;
|
|
5
|
+
y?: number;
|
|
6
|
+
trigger?: Snippet;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
declare const ContextMenu: import("svelte").Component<Props, {}, "open" | "x" | "y">;
|
|
10
|
+
type ContextMenu = ReturnType<typeof ContextMenu>;
|
|
11
|
+
export default ContextMenu;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
open?: boolean;
|
|
6
|
+
placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
7
|
+
trigger?: Snippet;
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
open = $bindable(false),
|
|
13
|
+
placement = 'bottom-start',
|
|
14
|
+
trigger,
|
|
15
|
+
children
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
|
|
18
|
+
let menuElement: HTMLDivElement;
|
|
19
|
+
let wrapperElement: HTMLDivElement;
|
|
20
|
+
|
|
21
|
+
const placementClasses = {
|
|
22
|
+
'bottom-start': 'top-full left-0 mt-2',
|
|
23
|
+
'bottom-end': 'top-full right-0 mt-2',
|
|
24
|
+
'top-start': 'bottom-full left-0 mb-2',
|
|
25
|
+
'top-end': 'bottom-full right-0 mb-2'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function toggleMenu() {
|
|
29
|
+
open = !open;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function handleClickOutside(event: MouseEvent) {
|
|
33
|
+
if (menuElement && !wrapperElement.contains(event.target as Node)) {
|
|
34
|
+
open = false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function handleEscape(event: KeyboardEvent) {
|
|
39
|
+
if (event.key === 'Escape') {
|
|
40
|
+
open = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
$effect(() => {
|
|
45
|
+
if (open) {
|
|
46
|
+
document.addEventListener('click', handleClickOutside);
|
|
47
|
+
document.addEventListener('keydown', handleEscape);
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
document.removeEventListener('click', handleClickOutside);
|
|
51
|
+
document.removeEventListener('keydown', handleEscape);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div bind:this={wrapperElement} class="relative">
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
onclick={toggleMenu}
|
|
61
|
+
onkeydown={(e) => {
|
|
62
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
toggleMenu();
|
|
65
|
+
}
|
|
66
|
+
}}
|
|
67
|
+
class="bg-transparent border-none p-0 cursor-pointer"
|
|
68
|
+
>
|
|
69
|
+
{@render trigger?.()}
|
|
70
|
+
</button>
|
|
71
|
+
|
|
72
|
+
{#if open}
|
|
73
|
+
<div
|
|
74
|
+
bind:this={menuElement}
|
|
75
|
+
class="absolute {placementClasses[placement]} z-50 glass-panel rounded-[var(--radius-lg)] shadow-[var(--shadow-deep)] min-w-[12rem] animate-[fade-up_0.2s_var(--ease-luxe)]"
|
|
76
|
+
>
|
|
77
|
+
{@render children?.()}
|
|
78
|
+
</div>
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
5
|
+
trigger?: Snippet;
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const DropdownMenu: import("svelte").Component<Props, {}, "open">;
|
|
9
|
+
type DropdownMenu = ReturnType<typeof DropdownMenu>;
|
|
10
|
+
export default DropdownMenu;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface MenuItem {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
icon?: Snippet;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onclick?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
items?: MenuItem[];
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
items,
|
|
19
|
+
children
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
function handleItemClick(item: MenuItem) {
|
|
23
|
+
if (!item.disabled && item.onclick) {
|
|
24
|
+
item.onclick();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<div role="menu" class="glass-panel rounded-[var(--radius-lg)] p-2 shadow-[var(--shadow-deep)]">
|
|
30
|
+
{#if children}
|
|
31
|
+
{@render children()}
|
|
32
|
+
{:else if items}
|
|
33
|
+
{#each items as item}
|
|
34
|
+
<button
|
|
35
|
+
role="menuitem"
|
|
36
|
+
aria-disabled={item.disabled}
|
|
37
|
+
onclick={() => handleItemClick(item)}
|
|
38
|
+
disabled={item.disabled}
|
|
39
|
+
class="flex items-center gap-2 w-full px-3 py-2 rounded-[var(--radius-md)] text-[var(--color-text)] text-sm font-[var(--font-body)] hover:bg-[var(--color-base-3)] transition-all duration-200 ease-[var(--ease-luxe)] {item.disabled ? 'opacity-50 pointer-events-none' : 'cursor-pointer'}"
|
|
40
|
+
>
|
|
41
|
+
{#if item.icon}
|
|
42
|
+
{@render item.icon()}
|
|
43
|
+
{/if}
|
|
44
|
+
<span>{item.label}</span>
|
|
45
|
+
</button>
|
|
46
|
+
{/each}
|
|
47
|
+
{/if}
|
|
48
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface MenuItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: Snippet;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
onclick?: () => void;
|
|
8
|
+
}
|
|
9
|
+
interface Props {
|
|
10
|
+
items?: MenuItem[];
|
|
11
|
+
children?: Snippet;
|
|
12
|
+
}
|
|
13
|
+
declare const Menu: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type Menu = ReturnType<typeof Menu>;
|
|
15
|
+
export default Menu;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
position?: 'sticky' | 'fixed';
|
|
6
|
+
height?: 'sm' | 'md' | 'lg';
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
position = 'sticky',
|
|
12
|
+
height = 'md',
|
|
13
|
+
children
|
|
14
|
+
}: Props = $props();
|
|
15
|
+
|
|
16
|
+
const positionClasses = {
|
|
17
|
+
sticky: 'sticky top-0',
|
|
18
|
+
fixed: 'fixed top-0 left-0 right-0'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const heightClasses = {
|
|
22
|
+
sm: 'h-14',
|
|
23
|
+
md: 'h-16',
|
|
24
|
+
lg: 'h-20'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const baseClasses = 'z-40 glass-panel flex items-center justify-between px-4 md:px-6 lg:px-8 transition-all duration-300 ease-[var(--ease-luxe)]';
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<nav class="{baseClasses} {positionClasses[position]} {heightClasses[height]}">
|
|
31
|
+
{@render children?.()}
|
|
32
|
+
</nav>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
position?: 'sticky' | 'fixed';
|
|
4
|
+
height?: 'sm' | 'md' | 'lg';
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
}
|
|
7
|
+
declare const Navbar: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type Navbar = ReturnType<typeof Navbar>;
|
|
9
|
+
export default Navbar;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
open?: boolean;
|
|
6
|
+
side?: 'left' | 'right';
|
|
7
|
+
width?: 'sm' | 'md' | 'lg';
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let {
|
|
12
|
+
open = $bindable(true),
|
|
13
|
+
side = 'left',
|
|
14
|
+
width = 'md',
|
|
15
|
+
children
|
|
16
|
+
}: Props = $props();
|
|
17
|
+
|
|
18
|
+
const widthClasses = {
|
|
19
|
+
sm: { open: 'w-56', collapsed: 'w-14' },
|
|
20
|
+
md: { open: 'w-64', collapsed: 'w-16' },
|
|
21
|
+
lg: { open: 'w-80', collapsed: 'w-20' }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const sideClasses = {
|
|
25
|
+
left: 'left-0',
|
|
26
|
+
right: 'right-0'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const currentWidth = $derived(open ? widthClasses[width].open : widthClasses[width].collapsed);
|
|
30
|
+
const baseClasses = 'fixed top-0 bottom-0 z-30 glass-panel overflow-y-auto overflow-x-hidden transition-all duration-300 ease-[var(--ease-luxe)]';
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<aside class="{baseClasses} {sideClasses[side]} {currentWidth}">
|
|
34
|
+
{@render children?.()}
|
|
35
|
+
</aside>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
side?: 'left' | 'right';
|
|
5
|
+
width?: 'sm' | 'md' | 'lg';
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
}
|
|
8
|
+
declare const Sidebar: import("svelte").Component<Props, {}, "open">;
|
|
9
|
+
type Sidebar = ReturnType<typeof Sidebar>;
|
|
10
|
+
export default Sidebar;
|