svelora 3.0.8 → 3.0.11
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/LocaleButton/LocaleButton.svelte +77 -67
- package/dist/LocaleButton/locale-button.types.d.ts +17 -0
- package/dist/LocaleButton/locale-button.variants.d.ts +72 -3
- package/dist/LocaleButton/locale-button.variants.js +24 -1
- package/dist/NavigationMenu/NavigationMenu.svelte +170 -95
- package/dist/NavigationMenu/navigation-menu.types.d.ts +17 -0
- package/dist/NavigationMenu/navigation-menu.variants.d.ts +36 -0
- package/dist/NavigationMenu/navigation-menu.variants.js +24 -1
- package/dist/Sidebar/Sidebar.svelte +5 -5
- package/dist/Sidebar/sidebar.types.d.ts +10 -4
- package/dist/Sidebar/sidebar.variants.d.ts +18 -0
- package/dist/Sidebar/sidebar.variants.js +16 -6
- package/dist/Table/TableBulkActionBar.svelte +38 -0
- package/dist/Table/TableBulkActionBar.svelte.d.ts +5 -0
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.js +1 -0
- package/dist/Table/table-bulk-action-bar.types.d.ts +31 -0
- package/dist/Table/table-bulk-action-bar.types.js +1 -0
- package/dist/Table/table-bulk-action-bar.variants.d.ts +52 -0
- package/dist/Table/table-bulk-action-bar.variants.js +10 -0
- package/dist/docs/navigation.d.ts +1 -0
- package/dist/docs/navigation.js +13 -5
- package/dist/mcp/svelora-docs.data.json +92 -92
- package/package.json +1 -1
|
@@ -9,28 +9,30 @@ import Link from "../Link/Link.svelte";
|
|
|
9
9
|
import { localeButtonDefaults, localeButtonVariants } from "./locale-button.variants.js";
|
|
10
10
|
const config = getComponentConfig("localeButton", localeButtonDefaults);
|
|
11
11
|
const icons = getComponentConfig("icons", iconsDefaults);
|
|
12
|
-
let { locales = [], locale, variant = config.defaultVariants.variant ?? "outline", color = config.defaultVariants.color ?? "surface", size = config.defaultVariants.size ?? "md", loading = false, disabled = false, block = false, square = false, placeholder = "Select language", ariaLabel = "Change language", menuLabel = "Language", icon = "lucide:languages", chevronIcon = icons.chevronDown, showIcon = true, showChevron = true, showCode = true, showIndicator = true, disableCurrentLocale = true, closeOnSelect = true, getLocaleHref, onLocaleChange, open = $bindable(false), onOpenChange, side = "bottom", sideOffset = 4, align = "start", alignOffset = 0, avoidCollisions = true, collisionBoundary, collisionPadding = 8, sticky = "partial", hideWhenDetached = false, onEscapeKeydown, onInteractOutside, onCloseAutoFocus, forceMount, loop = true, portal = true, arrow = false, ui, children: triggerContent, class: className, ...restProps } = $props();
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
12
|
+
let { locales = [], locale, variant = config.defaultVariants.variant ?? "outline", color = config.defaultVariants.color ?? "surface", size = config.defaultVariants.size ?? "md", loading = false, disabled = false, block = false, square = false, placeholder = "Select language", ariaLabel = "Change language", menuLabel = "Language", icon = "lucide:languages", chevronIcon = icons.chevronDown, showIcon = true, showChevron = true, showCode = true, showIndicator = true, disableCurrentLocale = true, closeOnSelect = true, getLocaleHref, onLocaleChange, open = $bindable(false), onOpenChange, side = "bottom", sideOffset = 4, align = "start", alignOffset = 0, avoidCollisions = true, collisionBoundary, collisionPadding = 8, sticky = "partial", hideWhenDetached = false, onEscapeKeydown, onInteractOutside, onCloseAutoFocus, forceMount, loop = true, portal = true, arrow = false, ui, children: triggerContent, item: itemSnippet, menu: menuSnippet, class: className, ...restProps } = $props();
|
|
13
|
+
const classes = $derived.by(() => {
|
|
14
|
+
const slots = localeButtonVariants({ size });
|
|
15
|
+
return {
|
|
16
|
+
base: slots.base({ class: [
|
|
17
|
+
config.slots.base,
|
|
18
|
+
className,
|
|
19
|
+
ui?.base
|
|
20
|
+
] }),
|
|
21
|
+
triggerLabel: slots.triggerLabel({ class: [config.slots.triggerLabel, ui?.triggerLabel] }),
|
|
22
|
+
triggerIcon: slots.triggerIcon({ class: [config.slots.triggerIcon, ui?.triggerIcon] }),
|
|
23
|
+
triggerChevron: slots.triggerChevron({ class: [config.slots.triggerChevron, ui?.triggerChevron] }),
|
|
24
|
+
menu: slots.menu({ class: [config.slots.menu, ui?.menu] }),
|
|
25
|
+
menuLabel: slots.menuLabel({ class: [config.slots.menuLabel, ui?.menuLabel] }),
|
|
26
|
+
item: slots.item({ class: [config.slots.item, ui?.item] }),
|
|
27
|
+
itemIdle: slots.itemIdle({ class: [config.slots.itemIdle, ui?.itemIdle] }),
|
|
28
|
+
itemCurrent: slots.itemCurrent({ class: [config.slots.itemCurrent, ui?.itemCurrent] }),
|
|
29
|
+
itemLeading: slots.itemLeading({ class: [config.slots.itemLeading, ui?.itemLeading] }),
|
|
30
|
+
itemText: slots.itemText({ class: [config.slots.itemText, ui?.itemText] }),
|
|
31
|
+
itemLabel: slots.itemLabel({ class: [config.slots.itemLabel, ui?.itemLabel] }),
|
|
32
|
+
itemDescription: slots.itemDescription({ class: [config.slots.itemDescription, ui?.itemDescription] }),
|
|
33
|
+
itemCode: slots.itemCode({ class: [config.slots.itemCode, ui?.itemCode] }),
|
|
34
|
+
itemIndicator: slots.itemIndicator({ class: [config.slots.itemIndicator, ui?.itemIndicator] })
|
|
35
|
+
};
|
|
34
36
|
});
|
|
35
37
|
const currentLocale = $derived(locales.find((item) => item.code === locale) ?? locales[0]);
|
|
36
38
|
const triggerLabel = $derived(currentLocale?.shortLabel ?? currentLocale?.label ?? placeholder);
|
|
@@ -112,54 +114,62 @@ async function handleItemClick(event, item, close) {
|
|
|
112
114
|
{/snippet}
|
|
113
115
|
|
|
114
116
|
{#snippet content({ close })}
|
|
115
|
-
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
{
|
|
117
|
+
{#if menuSnippet}
|
|
118
|
+
{@render menuSnippet({ close })}
|
|
119
|
+
{:else}
|
|
120
|
+
<div class={classes.menu}>
|
|
121
|
+
{#if menuLabel}
|
|
122
|
+
<p class={classes.menuLabel}>{menuLabel}</p>
|
|
123
|
+
{/if}
|
|
119
124
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
{#each locales as item (item.code)}
|
|
126
|
+
{@const current = isCurrentLocale(item)}
|
|
127
|
+
{@const href = getResolvedHref(item)}
|
|
128
|
+
{@const itemDisabled = isItemDisabled(item)}
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
<span class={classes.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
130
|
+
{#if itemSnippet}
|
|
131
|
+
{@render itemSnippet({ item, current, disabled: itemDisabled, close })}
|
|
132
|
+
{:else}
|
|
133
|
+
<Link
|
|
134
|
+
href={href}
|
|
135
|
+
hreflang={item.hreflang ?? item.code}
|
|
136
|
+
raw
|
|
137
|
+
role="menuitemradio"
|
|
138
|
+
aria-checked={current}
|
|
139
|
+
disabled={itemDisabled}
|
|
140
|
+
class={[
|
|
141
|
+
classes.item,
|
|
142
|
+
current ? classes.itemCurrent : classes.itemIdle,
|
|
143
|
+
item.class
|
|
144
|
+
]}
|
|
145
|
+
onclick={(event) => handleItemClick(event, item, close)}
|
|
146
|
+
>
|
|
147
|
+
<span class={classes.itemLeading}>
|
|
148
|
+
{#if item.icon}
|
|
149
|
+
<Icon name={item.icon} />
|
|
150
|
+
{/if}
|
|
151
|
+
<span class={classes.itemText}>
|
|
152
|
+
<span class={classes.itemLabel}>{item.label}</span>
|
|
153
|
+
{#if item.description}
|
|
154
|
+
<span class={classes.itemDescription}>{item.description}</span>
|
|
155
|
+
{/if}
|
|
156
|
+
</span>
|
|
157
|
+
</span>
|
|
150
158
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
<span class="flex items-center gap-2">
|
|
160
|
+
{#if showCode}
|
|
161
|
+
<span class={classes.itemCode}>{item.shortLabel ?? item.code}</span>
|
|
162
|
+
{/if}
|
|
163
|
+
{#if showIndicator && current}
|
|
164
|
+
<span class={classes.itemIndicator}>
|
|
165
|
+
<Icon name={icons.check} />
|
|
166
|
+
</span>
|
|
167
|
+
{/if}
|
|
158
168
|
</span>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
</Link>
|
|
170
|
+
{/if}
|
|
171
|
+
{/each}
|
|
172
|
+
</div>
|
|
173
|
+
{/if}
|
|
164
174
|
{/snippet}
|
|
165
175
|
</DropdownMenu>
|
|
@@ -160,6 +160,23 @@ export type LocaleButtonProps = Omit<HTMLAttributes<HTMLElement>, 'children' | '
|
|
|
160
160
|
open: boolean;
|
|
161
161
|
currentLocale: LocaleButtonLocale | undefined;
|
|
162
162
|
}]>;
|
|
163
|
+
/**
|
|
164
|
+
* Render custom content for each locale item in the menu.
|
|
165
|
+
*/
|
|
166
|
+
item?: Snippet<[
|
|
167
|
+
{
|
|
168
|
+
item: LocaleButtonLocale;
|
|
169
|
+
current: boolean;
|
|
170
|
+
disabled: boolean;
|
|
171
|
+
close: () => void;
|
|
172
|
+
}
|
|
173
|
+
]>;
|
|
174
|
+
/**
|
|
175
|
+
* Render a custom menu layout entirely.
|
|
176
|
+
*/
|
|
177
|
+
menu?: Snippet<[{
|
|
178
|
+
close: () => void;
|
|
179
|
+
}]>;
|
|
163
180
|
/**
|
|
164
181
|
* Whether to render the menu in a portal.
|
|
165
182
|
* @default true
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import { type VariantProps } from 'tailwind-variants';
|
|
2
2
|
import type { ButtonVariantProps } from '../Button/button.variants.js';
|
|
3
|
-
export declare const localeButtonVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
export declare const localeButtonVariants: import("tailwind-variants").TVReturnType<{
|
|
4
|
+
size: {
|
|
5
|
+
xs: {
|
|
6
|
+
triggerIcon: string;
|
|
7
|
+
triggerChevron: string;
|
|
8
|
+
};
|
|
9
|
+
sm: {
|
|
10
|
+
triggerIcon: string;
|
|
11
|
+
triggerChevron: string;
|
|
12
|
+
};
|
|
13
|
+
md: {
|
|
14
|
+
triggerIcon: string;
|
|
15
|
+
triggerChevron: string;
|
|
16
|
+
};
|
|
17
|
+
lg: {
|
|
18
|
+
triggerIcon: string;
|
|
19
|
+
triggerChevron: string;
|
|
20
|
+
};
|
|
21
|
+
xl: {
|
|
22
|
+
triggerIcon: string;
|
|
23
|
+
triggerChevron: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}, {
|
|
4
27
|
base: string;
|
|
5
28
|
triggerLabel: string;
|
|
6
29
|
triggerIcon: string;
|
|
@@ -16,7 +39,30 @@ export declare const localeButtonVariants: import("tailwind-variants").TVReturnT
|
|
|
16
39
|
itemDescription: string;
|
|
17
40
|
itemCode: string;
|
|
18
41
|
itemIndicator: string;
|
|
19
|
-
}, undefined, {
|
|
42
|
+
}, undefined, {
|
|
43
|
+
size: {
|
|
44
|
+
xs: {
|
|
45
|
+
triggerIcon: string;
|
|
46
|
+
triggerChevron: string;
|
|
47
|
+
};
|
|
48
|
+
sm: {
|
|
49
|
+
triggerIcon: string;
|
|
50
|
+
triggerChevron: string;
|
|
51
|
+
};
|
|
52
|
+
md: {
|
|
53
|
+
triggerIcon: string;
|
|
54
|
+
triggerChevron: string;
|
|
55
|
+
};
|
|
56
|
+
lg: {
|
|
57
|
+
triggerIcon: string;
|
|
58
|
+
triggerChevron: string;
|
|
59
|
+
};
|
|
60
|
+
xl: {
|
|
61
|
+
triggerIcon: string;
|
|
62
|
+
triggerChevron: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}, {
|
|
20
66
|
base: string;
|
|
21
67
|
triggerLabel: string;
|
|
22
68
|
triggerIcon: string;
|
|
@@ -32,7 +78,30 @@ export declare const localeButtonVariants: import("tailwind-variants").TVReturnT
|
|
|
32
78
|
itemDescription: string;
|
|
33
79
|
itemCode: string;
|
|
34
80
|
itemIndicator: string;
|
|
35
|
-
}, import("tailwind-variants").TVReturnType<{
|
|
81
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
82
|
+
size: {
|
|
83
|
+
xs: {
|
|
84
|
+
triggerIcon: string;
|
|
85
|
+
triggerChevron: string;
|
|
86
|
+
};
|
|
87
|
+
sm: {
|
|
88
|
+
triggerIcon: string;
|
|
89
|
+
triggerChevron: string;
|
|
90
|
+
};
|
|
91
|
+
md: {
|
|
92
|
+
triggerIcon: string;
|
|
93
|
+
triggerChevron: string;
|
|
94
|
+
};
|
|
95
|
+
lg: {
|
|
96
|
+
triggerIcon: string;
|
|
97
|
+
triggerChevron: string;
|
|
98
|
+
};
|
|
99
|
+
xl: {
|
|
100
|
+
triggerIcon: string;
|
|
101
|
+
triggerChevron: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}, {
|
|
36
105
|
base: string;
|
|
37
106
|
triggerLabel: string;
|
|
38
107
|
triggerIcon: string;
|
|
@@ -20,7 +20,30 @@ export const localeButtonVariants = tv({
|
|
|
20
20
|
itemCode: 'shrink-0 rounded-md bg-surface-container px-1.5 py-0.5 text-[11px] font-medium text-on-surface-variant uppercase',
|
|
21
21
|
itemIndicator: 'shrink-0 text-primary'
|
|
22
22
|
},
|
|
23
|
-
variants: {
|
|
23
|
+
variants: {
|
|
24
|
+
size: {
|
|
25
|
+
xs: {
|
|
26
|
+
triggerIcon: 'size-3.5',
|
|
27
|
+
triggerChevron: 'size-3.5'
|
|
28
|
+
},
|
|
29
|
+
sm: {
|
|
30
|
+
triggerIcon: 'size-4',
|
|
31
|
+
triggerChevron: 'size-4'
|
|
32
|
+
},
|
|
33
|
+
md: {
|
|
34
|
+
triggerIcon: 'size-5',
|
|
35
|
+
triggerChevron: 'size-5'
|
|
36
|
+
},
|
|
37
|
+
lg: {
|
|
38
|
+
triggerIcon: 'size-5',
|
|
39
|
+
triggerChevron: 'size-5'
|
|
40
|
+
},
|
|
41
|
+
xl: {
|
|
42
|
+
triggerIcon: 'size-6',
|
|
43
|
+
triggerChevron: 'size-6'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
24
47
|
defaultVariants: {}
|
|
25
48
|
});
|
|
26
49
|
export const localeButtonDefaults = {
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
<script lang="ts">import { untrack } from "svelte";
|
|
1
|
+
<script lang="ts">import { untrack, getContext } from "svelte";
|
|
2
2
|
import { twMerge } from "tailwind-merge";
|
|
3
|
-
import Icon from "../Icon/
|
|
3
|
+
import { Icon } from "../Icon/index.js";
|
|
4
4
|
import Badge from "../Badge/Badge.svelte";
|
|
5
5
|
import Kbd from "../Kbd/Kbd.svelte";
|
|
6
|
-
import DropdownMenu from "../DropdownMenu/
|
|
6
|
+
import { DropdownMenu } from "../DropdownMenu/index.js";
|
|
7
|
+
import { Tooltip } from "../Tooltip/index.js";
|
|
7
8
|
import { navigationMenuVariants } from "./navigation-menu.variants.js";
|
|
8
|
-
let { items = [], variant = "default", orientation = "horizontal", accordion = false, class: className, ui, children } = $props();
|
|
9
|
+
let { items = [], variant = "default", orientation = "horizontal", accordion = false, tree = false, class: className, ui, children } = $props();
|
|
10
|
+
const sidebarCollapsedFn = getContext("sidebar-collapsed");
|
|
11
|
+
let isCollapsed = $derived(sidebarCollapsedFn ? sidebarCollapsedFn() : false);
|
|
12
|
+
const sidebarPositionFn = getContext("sidebar-position");
|
|
13
|
+
let sidebarPosition = $derived(sidebarPositionFn ? sidebarPositionFn() : "left");
|
|
9
14
|
let styles = $derived(navigationMenuVariants({
|
|
10
15
|
variant,
|
|
11
|
-
orientation
|
|
16
|
+
orientation,
|
|
17
|
+
collapsed: isCollapsed,
|
|
18
|
+
tree
|
|
12
19
|
}));
|
|
13
20
|
let normalizedItems = $derived.by(() => {
|
|
14
21
|
if (!items || items.length === 0) return [];
|
|
@@ -46,22 +53,53 @@ function isGroupOpen(item) {
|
|
|
46
53
|
if (!item.label) return false;
|
|
47
54
|
return openGroups.includes(item.label);
|
|
48
55
|
}
|
|
56
|
+
import { LINK_LOCATION_CONTEXT_KEY } from "../Link/location-context.js";
|
|
57
|
+
const linkLocation = getContext(LINK_LOCATION_CONTEXT_KEY);
|
|
49
58
|
// --- Auto Active State ---
|
|
50
|
-
let
|
|
59
|
+
let windowPath = $state("");
|
|
51
60
|
$effect(() => {
|
|
52
|
-
if (typeof window !== "undefined") {
|
|
53
|
-
|
|
61
|
+
if (!linkLocation && typeof window !== "undefined") {
|
|
62
|
+
windowPath = window.location.pathname;
|
|
54
63
|
const onPopState = () => {
|
|
55
|
-
|
|
64
|
+
windowPath = window.location.pathname;
|
|
56
65
|
};
|
|
57
66
|
window.addEventListener("popstate", onPopState);
|
|
58
67
|
return () => window.removeEventListener("popstate", onPopState);
|
|
59
68
|
}
|
|
60
69
|
});
|
|
70
|
+
const currentPath = $derived(linkLocation ? linkLocation.currentUrl().pathname : windowPath);
|
|
71
|
+
const bestMatchHref = $derived.by(() => {
|
|
72
|
+
let best = "";
|
|
73
|
+
const search = (menuItems) => {
|
|
74
|
+
for (const item of menuItems) {
|
|
75
|
+
const target = item.matchPattern || item.href;
|
|
76
|
+
if (target) {
|
|
77
|
+
const isExact = item.exact !== false;
|
|
78
|
+
if (isExact && currentPath === target) {
|
|
79
|
+
return target;
|
|
80
|
+
} else if (!isExact && (currentPath === target || currentPath.startsWith(target + "/"))) {
|
|
81
|
+
if (target.length > best.length) {
|
|
82
|
+
best = target;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const children = item.items || item.children || [];
|
|
87
|
+
if (children.length > 0) {
|
|
88
|
+
const childBest = search(children);
|
|
89
|
+
if (childBest && childBest.length > best.length) best = childBest;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return best;
|
|
93
|
+
};
|
|
94
|
+
return search(normalizedItems.flat());
|
|
95
|
+
});
|
|
61
96
|
function isActive(item) {
|
|
62
97
|
if (item.active) return true;
|
|
63
|
-
|
|
64
|
-
|
|
98
|
+
const target = item.matchPattern || item.href;
|
|
99
|
+
if (target && target === bestMatchHref) return true;
|
|
100
|
+
const children = item.items || item.children || [];
|
|
101
|
+
if (children.some((child) => isActive(child))) {
|
|
102
|
+
return true;
|
|
65
103
|
}
|
|
66
104
|
return false;
|
|
67
105
|
}
|
|
@@ -89,67 +127,94 @@ function getKbds(item) {
|
|
|
89
127
|
>
|
|
90
128
|
{#snippet children({ props, open })}
|
|
91
129
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
{#snippet buttonContent()}
|
|
131
|
+
<button
|
|
132
|
+
type="button"
|
|
133
|
+
{...props}
|
|
134
|
+
class={twMerge(styles.item({ active: isActive(item), collapsed: isCollapsed }), item.class)}
|
|
135
|
+
disabled={item.disabled}
|
|
136
|
+
>
|
|
137
|
+
{#if item.icon}
|
|
138
|
+
<Icon name={item.icon} class={styles.icon({ active: isActive(item), collapsed: isCollapsed })} />
|
|
139
|
+
{/if}
|
|
140
|
+
{#if !isCollapsed}
|
|
141
|
+
<span>{item.label}</span>
|
|
142
|
+
{#if item.badge !== undefined}
|
|
143
|
+
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" class="ml-1" />
|
|
144
|
+
{/if}
|
|
145
|
+
<span class="ml-auto flex items-center">
|
|
146
|
+
<Icon
|
|
147
|
+
name="lucide:chevron-down"
|
|
148
|
+
class={twMerge(styles.chevron(), open ? 'rotate-180' : '')}
|
|
149
|
+
/>
|
|
150
|
+
</span>
|
|
151
|
+
{/if}
|
|
152
|
+
</button>
|
|
153
|
+
{/snippet}
|
|
154
|
+
|
|
155
|
+
{#if isCollapsed}
|
|
156
|
+
<Tooltip text={item.label} side={sidebarPosition === 'right' ? 'left' : 'right'}>
|
|
157
|
+
{@render buttonContent()}
|
|
158
|
+
</Tooltip>
|
|
159
|
+
{:else}
|
|
160
|
+
{@render buttonContent()}
|
|
161
|
+
{/if}
|
|
112
162
|
{/snippet}
|
|
113
163
|
</DropdownMenu>
|
|
114
164
|
{:else}
|
|
115
165
|
<!-- VERTICAL: Accordion -->
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{#if getKbds(item).length > 0}
|
|
139
|
-
<span class="flex items-center gap-
|
|
140
|
-
{#
|
|
141
|
-
<
|
|
142
|
-
{/
|
|
166
|
+
{#snippet accordionButton()}
|
|
167
|
+
<button
|
|
168
|
+
type="button"
|
|
169
|
+
class={twMerge(
|
|
170
|
+
styles.accordionTrigger(),
|
|
171
|
+
isGroupOpen(item) ? styles.item({ active: true, collapsed: isCollapsed }) : '',
|
|
172
|
+
isCollapsed ? styles.item({ collapsed: true }) : '',
|
|
173
|
+
item.class
|
|
174
|
+
)}
|
|
175
|
+
disabled={item.disabled}
|
|
176
|
+
onclick={() => handleGroupToggle(item)}
|
|
177
|
+
>
|
|
178
|
+
<div class="flex flex-1 items-center truncate">
|
|
179
|
+
{#if item.icon}
|
|
180
|
+
<Icon name={item.icon} class={styles.icon({ active: isActive(item), collapsed: isCollapsed })} />
|
|
181
|
+
{/if}
|
|
182
|
+
{#if !isCollapsed}
|
|
183
|
+
<span class="truncate ml-1.5">{item.label}</span>
|
|
184
|
+
{/if}
|
|
185
|
+
</div>
|
|
186
|
+
{#if !isCollapsed}
|
|
187
|
+
<div class="flex items-center gap-2">
|
|
188
|
+
{#if item.badge !== undefined || getKbds(item).length > 0}
|
|
189
|
+
<span class="flex items-center gap-2">
|
|
190
|
+
{#if item.badge !== undefined}
|
|
191
|
+
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" />
|
|
192
|
+
{/if}
|
|
193
|
+
{#if getKbds(item).length > 0}
|
|
194
|
+
<span class="flex items-center gap-0.5">
|
|
195
|
+
{#each getKbds(item) as key}
|
|
196
|
+
<Kbd value={typeof key === 'string' ? key : key.value} size="sm" />
|
|
197
|
+
{/each}
|
|
198
|
+
</span>
|
|
199
|
+
{/if}
|
|
143
200
|
</span>
|
|
144
201
|
{/if}
|
|
145
|
-
|
|
202
|
+
<Icon
|
|
203
|
+
name="lucide:chevron-down"
|
|
204
|
+
class={twMerge(styles.chevron(), isGroupOpen(item) ? 'rotate-180' : '')}
|
|
205
|
+
/>
|
|
206
|
+
</div>
|
|
146
207
|
{/if}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
208
|
+
</button>
|
|
209
|
+
{/snippet}
|
|
210
|
+
|
|
211
|
+
{#if isCollapsed}
|
|
212
|
+
<Tooltip text={item.label} side={sidebarPosition === 'right' ? 'left' : 'right'}>
|
|
213
|
+
{@render accordionButton()}
|
|
214
|
+
</Tooltip>
|
|
215
|
+
{:else}
|
|
216
|
+
{@render accordionButton()}
|
|
217
|
+
{/if}
|
|
153
218
|
{#if isGroupOpen(item)}
|
|
154
219
|
<ul class={styles.accordionGroupContent()}>
|
|
155
220
|
{#each subItems as subItem}
|
|
@@ -161,38 +226,48 @@ function getKbds(item) {
|
|
|
161
226
|
{:else}
|
|
162
227
|
<!-- STANDARD LINK -->
|
|
163
228
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
{#if item.label}
|
|
178
|
-
<span class="truncate">{item.label}</span>
|
|
179
|
-
{/if}
|
|
180
|
-
</div>
|
|
181
|
-
{#if item.badge !== undefined || getKbds(item).length > 0}
|
|
182
|
-
<span class="ml-auto flex items-center gap-2">
|
|
183
|
-
{#if item.badge !== undefined}
|
|
184
|
-
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" />
|
|
229
|
+
{#snippet linkContent()}
|
|
230
|
+
<svelte:element
|
|
231
|
+
this={item.href ? 'a' : 'button'}
|
|
232
|
+
href={item.href}
|
|
233
|
+
target={item.target}
|
|
234
|
+
type={!item.href ? 'button' : undefined}
|
|
235
|
+
class={twMerge(styles.item({ active: isActive(item), collapsed: isCollapsed }), item.class)}
|
|
236
|
+
disabled={item.disabled}
|
|
237
|
+
onclick={item.onClick}
|
|
238
|
+
>
|
|
239
|
+
<div class="flex flex-1 items-center truncate gap-1.5">
|
|
240
|
+
{#if item.icon}
|
|
241
|
+
<Icon name={item.icon} class={styles.icon({ active: isActive(item), collapsed: isCollapsed })} />
|
|
185
242
|
{/if}
|
|
186
|
-
{#if
|
|
187
|
-
<span class="
|
|
188
|
-
{#each getKbds(item) as key}
|
|
189
|
-
<Kbd value={typeof key === 'string' ? key : key.value} size="sm" />
|
|
190
|
-
{/each}
|
|
191
|
-
</span>
|
|
243
|
+
{#if !isCollapsed && item.label}
|
|
244
|
+
<span class="truncate">{item.label}</span>
|
|
192
245
|
{/if}
|
|
193
|
-
</
|
|
194
|
-
|
|
195
|
-
|
|
246
|
+
</div>
|
|
247
|
+
{#if !isCollapsed && (item.badge !== undefined || getKbds(item).length > 0)}
|
|
248
|
+
<span class="ml-auto flex items-center gap-2">
|
|
249
|
+
{#if item.badge !== undefined}
|
|
250
|
+
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" />
|
|
251
|
+
{/if}
|
|
252
|
+
{#if getKbds(item).length > 0}
|
|
253
|
+
<span class="flex items-center gap-0.5">
|
|
254
|
+
{#each getKbds(item) as key}
|
|
255
|
+
<Kbd value={typeof key === 'string' ? key : key.value} size="sm" />
|
|
256
|
+
{/each}
|
|
257
|
+
</span>
|
|
258
|
+
{/if}
|
|
259
|
+
</span>
|
|
260
|
+
{/if}
|
|
261
|
+
</svelte:element>
|
|
262
|
+
{/snippet}
|
|
263
|
+
|
|
264
|
+
{#if isCollapsed}
|
|
265
|
+
<Tooltip text={item.label} side={sidebarPosition === 'right' ? 'left' : 'right'}>
|
|
266
|
+
{@render linkContent()}
|
|
267
|
+
</Tooltip>
|
|
268
|
+
{:else}
|
|
269
|
+
{@render linkContent()}
|
|
270
|
+
{/if}
|
|
196
271
|
{/if}
|
|
197
272
|
</li>
|
|
198
273
|
{/snippet}
|