ui-svelte 0.1.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/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/charts/ArcChart.svelte +320 -0
- package/dist/charts/ArcChart.svelte.d.ts +26 -0
- package/dist/charts/AreaChart.svelte +495 -0
- package/dist/charts/AreaChart.svelte.d.ts +32 -0
- package/dist/charts/BarChart.svelte +504 -0
- package/dist/charts/BarChart.svelte.d.ts +38 -0
- package/dist/charts/Candlestick.svelte +527 -0
- package/dist/charts/Candlestick.svelte.d.ts +38 -0
- package/dist/charts/LineChart.svelte +365 -0
- package/dist/charts/LineChart.svelte.d.ts +36 -0
- package/dist/charts/PieChart.svelte +311 -0
- package/dist/charts/PieChart.svelte.d.ts +28 -0
- package/dist/charts/css/arc-chart.css +237 -0
- package/dist/charts/css/area-chart.css +289 -0
- package/dist/charts/css/bar-chart.css +167 -0
- package/dist/charts/css/candlestick.css +197 -0
- package/dist/charts/css/line-chart.css +202 -0
- package/dist/charts/css/pie-chart.css +199 -0
- package/dist/control/Audio.svelte +212 -0
- package/dist/control/Audio.svelte.d.ts +8 -0
- package/dist/control/Button.svelte +116 -0
- package/dist/control/Button.svelte.d.ts +22 -0
- package/dist/control/IconButton.svelte +104 -0
- package/dist/control/IconButton.svelte.d.ts +17 -0
- package/dist/control/Record.svelte +430 -0
- package/dist/control/Record.svelte.d.ts +11 -0
- package/dist/control/ToggleTheme.svelte +21 -0
- package/dist/control/ToggleTheme.svelte.d.ts +8 -0
- package/dist/control/Video.svelte +222 -0
- package/dist/control/Video.svelte.d.ts +10 -0
- package/dist/control/css/btn.css +206 -0
- package/dist/control/css/media.css +78 -0
- package/dist/control/css/video.css +58 -0
- package/dist/css/animations.css +27 -0
- package/dist/css/base.css +192 -0
- package/dist/css/utilities.css +136 -0
- package/dist/display/Accordion.svelte +98 -0
- package/dist/display/Accordion.svelte.d.ts +20 -0
- package/dist/display/Alert.svelte +65 -0
- package/dist/display/Alert.svelte.d.ts +15 -0
- package/dist/display/Avatar.svelte +80 -0
- package/dist/display/Avatar.svelte.d.ts +13 -0
- package/dist/display/Badge.svelte +46 -0
- package/dist/display/Badge.svelte.d.ts +11 -0
- package/dist/display/Card.svelte +94 -0
- package/dist/display/Card.svelte.d.ts +21 -0
- package/dist/display/Carousel.svelte +359 -0
- package/dist/display/Carousel.svelte.d.ts +25 -0
- package/dist/display/ChatBox.svelte +249 -0
- package/dist/display/ChatBox.svelte.d.ts +18 -0
- package/dist/display/Chip.svelte +67 -0
- package/dist/display/Chip.svelte.d.ts +17 -0
- package/dist/display/Code.svelte +56 -0
- package/dist/display/Code.svelte.d.ts +9 -0
- package/dist/display/Collapsible.svelte +71 -0
- package/dist/display/Collapsible.svelte.d.ts +15 -0
- package/dist/display/Divider.svelte +32 -0
- package/dist/display/Divider.svelte.d.ts +10 -0
- package/dist/display/Empty.svelte +462 -0
- package/dist/display/Empty.svelte.d.ts +11 -0
- package/dist/display/Icon.svelte +20 -0
- package/dist/display/Icon.svelte.d.ts +11 -0
- package/dist/display/Item.svelte +119 -0
- package/dist/display/Item.svelte.d.ts +24 -0
- package/dist/display/Loading.svelte +8 -0
- package/dist/display/Loading.svelte.d.ts +26 -0
- package/dist/display/Marquee.svelte +164 -0
- package/dist/display/Marquee.svelte.d.ts +21 -0
- package/dist/display/Section.svelte +63 -0
- package/dist/display/Section.svelte.d.ts +16 -0
- package/dist/display/Table.svelte +407 -0
- package/dist/display/Table.svelte.d.ts +32 -0
- package/dist/display/TypeWriter.svelte +23 -0
- package/dist/display/TypeWriter.svelte.d.ts +11 -0
- package/dist/display/User.svelte +0 -0
- package/dist/display/User.svelte.d.ts +26 -0
- package/dist/display/css/accordion.css +98 -0
- package/dist/display/css/alert.css +51 -0
- package/dist/display/css/avatar.css +158 -0
- package/dist/display/css/badge.css +47 -0
- package/dist/display/css/card.css +231 -0
- package/dist/display/css/carousel.css +156 -0
- package/dist/display/css/chat-box.css +188 -0
- package/dist/display/css/chip.css +91 -0
- package/dist/display/css/code.css +19 -0
- package/dist/display/css/collapsible.css +86 -0
- package/dist/display/css/divider.css +54 -0
- package/dist/display/css/empty.css +8 -0
- package/dist/display/css/item.css +149 -0
- package/dist/display/css/listbox.css +24 -0
- package/dist/display/css/marquee.css +138 -0
- package/dist/display/css/section.css +85 -0
- package/dist/display/css/table.css +361 -0
- package/dist/form/Checkbox.svelte +45 -0
- package/dist/form/Checkbox.svelte.d.ts +13 -0
- package/dist/form/ComboBox.svelte +448 -0
- package/dist/form/ComboBox.svelte.d.ts +29 -0
- package/dist/form/CsvField.svelte +389 -0
- package/dist/form/CsvField.svelte.d.ts +21 -0
- package/dist/form/DateField.svelte +292 -0
- package/dist/form/DateField.svelte.d.ts +18 -0
- package/dist/form/Dropzone.svelte +196 -0
- package/dist/form/Dropzone.svelte.d.ts +30 -0
- package/dist/form/ImageCropper.svelte +254 -0
- package/dist/form/ImageCropper.svelte.d.ts +14 -0
- package/dist/form/PasswordField.svelte +170 -0
- package/dist/form/PasswordField.svelte.d.ts +28 -0
- package/dist/form/PhoneField.svelte +485 -0
- package/dist/form/PhoneField.svelte.d.ts +25 -0
- package/dist/form/PinField.svelte +139 -0
- package/dist/form/PinField.svelte.d.ts +17 -0
- package/dist/form/RadioGroup.svelte +70 -0
- package/dist/form/RadioGroup.svelte.d.ts +19 -0
- package/dist/form/Select.svelte +350 -0
- package/dist/form/Select.svelte.d.ts +26 -0
- package/dist/form/Slider.svelte +60 -0
- package/dist/form/Slider.svelte.d.ts +15 -0
- package/dist/form/TextField.svelte +154 -0
- package/dist/form/TextField.svelte.d.ts +31 -0
- package/dist/form/Textarea.svelte +137 -0
- package/dist/form/Textarea.svelte.d.ts +27 -0
- package/dist/form/Toggle.svelte +45 -0
- package/dist/form/Toggle.svelte.d.ts +13 -0
- package/dist/form/css/checkbox.css +46 -0
- package/dist/form/css/combo-box.css +69 -0
- package/dist/form/css/control.css +177 -0
- package/dist/form/css/csv-field.css +0 -0
- package/dist/form/css/date.css +56 -0
- package/dist/form/css/dropzone.css +133 -0
- package/dist/form/css/field.css +17 -0
- package/dist/form/css/image-cropper.css +155 -0
- package/dist/form/css/password.css +35 -0
- package/dist/form/css/radio-group.css +57 -0
- package/dist/form/css/select.css +18 -0
- package/dist/form/css/slider.css +80 -0
- package/dist/form/css/textarea.css +130 -0
- package/dist/form/css/toggle.css +27 -0
- package/dist/form/js/countries.d.ts +13 -0
- package/dist/form/js/countries.js +307 -0
- package/dist/form/js/phone-examples.d.ts +248 -0
- package/dist/form/js/phone-examples.js +247 -0
- package/dist/hooks/use-auth.svelte.d.ts +11 -0
- package/dist/hooks/use-auth.svelte.js +59 -0
- package/dist/hooks/use-chat.svelte.d.ts +40 -0
- package/dist/hooks/use-chat.svelte.js +265 -0
- package/dist/hooks/use-clipboard.svelte.d.ts +9 -0
- package/dist/hooks/use-clipboard.svelte.js +52 -0
- package/dist/hooks/use-fetch.svelte.d.ts +11 -0
- package/dist/hooks/use-fetch.svelte.js +38 -0
- package/dist/hooks/use-form.svelte.d.ts +31 -0
- package/dist/hooks/use-form.svelte.js +110 -0
- package/dist/hooks/use-localstorage.svelte.d.ts +3 -0
- package/dist/hooks/use-localstorage.svelte.js +26 -0
- package/dist/hooks/use-scroll.svelte.d.ts +6 -0
- package/dist/hooks/use-scroll.svelte.js +34 -0
- package/dist/hooks/use-search.svelte.d.ts +49 -0
- package/dist/hooks/use-search.svelte.js +229 -0
- package/dist/hooks/use-table.svelte.d.ts +85 -0
- package/dist/hooks/use-table.svelte.js +362 -0
- package/dist/hooks/use-websocket.svelte.d.ts +18 -0
- package/dist/hooks/use-websocket.svelte.js +79 -0
- package/dist/icons/index.d.ts +132 -0
- package/dist/icons/index.js +132 -0
- package/dist/index.css +115 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +76 -0
- package/dist/layout/AppBar.svelte +94 -0
- package/dist/layout/AppBar.svelte.d.ts +17 -0
- package/dist/layout/Footer.svelte +94 -0
- package/dist/layout/Footer.svelte.d.ts +17 -0
- package/dist/layout/FooterLinks.svelte +28 -0
- package/dist/layout/FooterLinks.svelte.d.ts +11 -0
- package/dist/layout/Provider.svelte +52 -0
- package/dist/layout/Provider.svelte.d.ts +10 -0
- package/dist/layout/Scaffold.svelte +46 -0
- package/dist/layout/Scaffold.svelte.d.ts +15 -0
- package/dist/layout/Sidebar.svelte +40 -0
- package/dist/layout/Sidebar.svelte.d.ts +13 -0
- package/dist/layout/css/app-bar.css +35 -0
- package/dist/layout/css/bottom-bar.css +12 -0
- package/dist/layout/css/footer-links.css +17 -0
- package/dist/layout/css/footer.css +35 -0
- package/dist/layout/css/scaffold.css +15 -0
- package/dist/layout/css/sidebar.css +17 -0
- package/dist/navigation/BottomNav.svelte +0 -0
- package/dist/navigation/BottomNav.svelte.d.ts +26 -0
- package/dist/navigation/NavMenu.svelte +254 -0
- package/dist/navigation/SideNav.svelte +249 -0
- package/dist/navigation/Tabs.svelte +79 -0
- package/dist/navigation/Tabs.svelte.d.ts +19 -0
- package/dist/navigation/css/bottom-nav.css +0 -0
- package/dist/navigation/css/nav-menu.css +168 -0
- package/dist/navigation/css/side-nav.css +244 -0
- package/dist/navigation/css/tabs.css +118 -0
- package/dist/overlay/AlertDialog.svelte +0 -0
- package/dist/overlay/AlertDialog.svelte.d.ts +26 -0
- package/dist/overlay/Command.svelte +0 -0
- package/dist/overlay/Command.svelte.d.ts +26 -0
- package/dist/overlay/Drawer.svelte +129 -0
- package/dist/overlay/Drawer.svelte.d.ts +20 -0
- package/dist/overlay/Dropdown.svelte +140 -0
- package/dist/overlay/Modal.svelte +102 -0
- package/dist/overlay/Modal.svelte.d.ts +19 -0
- package/dist/overlay/PopoverStack.svelte +0 -0
- package/dist/overlay/PopoverStack.svelte.d.ts +26 -0
- package/dist/overlay/Toast.svelte +83 -0
- package/dist/overlay/Toast.svelte.d.ts +9 -0
- package/dist/overlay/Tooltip.svelte +140 -0
- package/dist/overlay/Tooltip.svelte.d.ts +12 -0
- package/dist/overlay/css/drawer.css +75 -0
- package/dist/overlay/css/dropdown.css +24 -0
- package/dist/overlay/css/hovercard.css +11 -0
- package/dist/overlay/css/modal.css +51 -0
- package/dist/overlay/css/toast.css +80 -0
- package/dist/overlay/css/tooltip.css +89 -0
- package/dist/stores/i18n.svelte.d.ts +16 -0
- package/dist/stores/i18n.svelte.js +137 -0
- package/dist/stores/theme.svelte.d.ts +5 -0
- package/dist/stores/theme.svelte.js +55 -0
- package/dist/stores/toast.svelte.d.ts +19 -0
- package/dist/stores/toast.svelte.js +38 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.js +1 -0
- package/dist/utils/charts.d.ts +27 -0
- package/dist/utils/charts.js +140 -0
- package/dist/utils/class-names.d.ts +1 -0
- package/dist/utils/class-names.js +3 -0
- package/dist/utils/click-outside.d.ts +3 -0
- package/dist/utils/click-outside.js +9 -0
- package/dist/utils/popover.d.ts +3 -0
- package/dist/utils/popover.js +17 -0
- package/dist/utils/ulid.d.ts +1 -0
- package/dist/utils/ulid.js +22 -0
- package/dist/utils/validate-schema.d.ts +2 -0
- package/dist/utils/validate-schema.js +97 -0
- package/package.json +69 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default Loading;
|
|
2
|
+
type Loading = SvelteComponent<{
|
|
3
|
+
[x: string]: never;
|
|
4
|
+
}, {
|
|
5
|
+
[evt: string]: CustomEvent<any>;
|
|
6
|
+
}, {}> & {
|
|
7
|
+
$$bindings?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
declare const Loading: $$__sveltets_2_IsomorphicComponent<{
|
|
10
|
+
[x: string]: never;
|
|
11
|
+
}, {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
}, {}, {}, string>;
|
|
14
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
15
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
16
|
+
$$bindings?: Bindings;
|
|
17
|
+
} & Exports;
|
|
18
|
+
(internal: unknown, props: {
|
|
19
|
+
$$events?: Events;
|
|
20
|
+
$$slots?: Slots;
|
|
21
|
+
}): Exports & {
|
|
22
|
+
$set?: any;
|
|
23
|
+
$on?: any;
|
|
24
|
+
};
|
|
25
|
+
z_$$bindings?: Bindings;
|
|
26
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../utils/class-names.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
import { onMount, tick } from 'svelte';
|
|
5
|
+
|
|
6
|
+
type MarqueeItem = {
|
|
7
|
+
id: string | number;
|
|
8
|
+
content: Snippet;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
items?: MarqueeItem[];
|
|
13
|
+
speed?: 'slow' | 'normal' | 'fast';
|
|
14
|
+
pauseOnHover?: boolean;
|
|
15
|
+
reverse?: boolean;
|
|
16
|
+
orientation?: 'horizontal' | 'vertical';
|
|
17
|
+
fade?: boolean;
|
|
18
|
+
fadeColor?: string;
|
|
19
|
+
gap?: string;
|
|
20
|
+
class?: string;
|
|
21
|
+
itemClass?: string;
|
|
22
|
+
children?: Snippet;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
class: className,
|
|
27
|
+
itemClass,
|
|
28
|
+
items = [],
|
|
29
|
+
speed = 'normal',
|
|
30
|
+
pauseOnHover = false,
|
|
31
|
+
reverse = false,
|
|
32
|
+
orientation = 'horizontal',
|
|
33
|
+
fade = false,
|
|
34
|
+
fadeColor = 'white',
|
|
35
|
+
gap = '0',
|
|
36
|
+
children
|
|
37
|
+
}: Props = $props();
|
|
38
|
+
|
|
39
|
+
let containerEl = $state<HTMLElement>();
|
|
40
|
+
let viewportEl = $state<HTMLElement>();
|
|
41
|
+
let contentEl = $state<HTMLElement>();
|
|
42
|
+
let isPaused = $state(false);
|
|
43
|
+
let shouldAnimate = $state(false);
|
|
44
|
+
|
|
45
|
+
const isVertical = $derived(orientation === 'vertical');
|
|
46
|
+
|
|
47
|
+
const speedClasses = {
|
|
48
|
+
slow: 'is-slow',
|
|
49
|
+
normal: 'is-normal',
|
|
50
|
+
fast: 'is-fast'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const checkOverflow = () => {
|
|
54
|
+
if (!contentEl || !viewportEl) return;
|
|
55
|
+
|
|
56
|
+
if (isVertical) {
|
|
57
|
+
shouldAnimate = contentEl.scrollHeight > viewportEl.clientHeight;
|
|
58
|
+
} else {
|
|
59
|
+
shouldAnimate = contentEl.scrollWidth > viewportEl.clientWidth;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleMouseEnter = () => {
|
|
64
|
+
if (pauseOnHover) {
|
|
65
|
+
isPaused = true;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleMouseLeave = () => {
|
|
70
|
+
if (pauseOnHover) {
|
|
71
|
+
isPaused = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
onMount(() => {
|
|
76
|
+
tick().then(() => {
|
|
77
|
+
checkOverflow();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Observer para detectar cambios en el tamaño
|
|
81
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
82
|
+
checkOverflow();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (viewportEl) {
|
|
86
|
+
resizeObserver.observe(viewportEl);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
resizeObserver.disconnect();
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
$effect(() => {
|
|
95
|
+
items;
|
|
96
|
+
tick().then(() => {
|
|
97
|
+
checkOverflow();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
$effect(() => {
|
|
102
|
+
if (containerEl && fadeColor) {
|
|
103
|
+
containerEl.style.setProperty('--fade-color', fadeColor);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<div
|
|
109
|
+
class={cn('marquee', isVertical && 'is-vertical', fade && 'has-fade', className)}
|
|
110
|
+
bind:this={containerEl}
|
|
111
|
+
onmouseenter={handleMouseEnter}
|
|
112
|
+
onmouseleave={handleMouseLeave}
|
|
113
|
+
role="region"
|
|
114
|
+
aria-label="Scrolling content"
|
|
115
|
+
>
|
|
116
|
+
<div class={cn('marquee-viewport', isVertical && 'is-vertical')} bind:this={viewportEl}>
|
|
117
|
+
<div
|
|
118
|
+
class={cn(
|
|
119
|
+
'marquee-container',
|
|
120
|
+
speedClasses[speed],
|
|
121
|
+
shouldAnimate && 'is-animating',
|
|
122
|
+
isPaused && 'is-paused',
|
|
123
|
+
reverse && 'is-reverse',
|
|
124
|
+
isVertical && 'is-vertical'
|
|
125
|
+
)}
|
|
126
|
+
style="gap: {gap};"
|
|
127
|
+
>
|
|
128
|
+
<div
|
|
129
|
+
class={cn('marquee-content', isVertical && 'is-vertical')}
|
|
130
|
+
bind:this={contentEl}
|
|
131
|
+
style="gap: {gap};"
|
|
132
|
+
>
|
|
133
|
+
{#if children}
|
|
134
|
+
{@render children()}
|
|
135
|
+
{:else}
|
|
136
|
+
{#each items as item (item.id)}
|
|
137
|
+
<div class={cn('marquee-item', itemClass)}>
|
|
138
|
+
{@render item.content()}
|
|
139
|
+
</div>
|
|
140
|
+
{/each}
|
|
141
|
+
{/if}
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{#if shouldAnimate}
|
|
145
|
+
<!-- Duplicado para el efecto infinito -->
|
|
146
|
+
<div
|
|
147
|
+
class={cn('marquee-content', isVertical && 'is-vertical')}
|
|
148
|
+
aria-hidden="true"
|
|
149
|
+
style="gap: {gap};"
|
|
150
|
+
>
|
|
151
|
+
{#if children}
|
|
152
|
+
{@render children()}
|
|
153
|
+
{:else}
|
|
154
|
+
{#each items as item (`${item.id}-duplicate`)}
|
|
155
|
+
<div class={cn('marquee-item', itemClass)}>
|
|
156
|
+
{@render item.content()}
|
|
157
|
+
</div>
|
|
158
|
+
{/each}
|
|
159
|
+
{/if}
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type MarqueeItem = {
|
|
3
|
+
id: string | number;
|
|
4
|
+
content: Snippet;
|
|
5
|
+
};
|
|
6
|
+
type Props = {
|
|
7
|
+
items?: MarqueeItem[];
|
|
8
|
+
speed?: 'slow' | 'normal' | 'fast';
|
|
9
|
+
pauseOnHover?: boolean;
|
|
10
|
+
reverse?: boolean;
|
|
11
|
+
orientation?: 'horizontal' | 'vertical';
|
|
12
|
+
fade?: boolean;
|
|
13
|
+
fadeColor?: string;
|
|
14
|
+
gap?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
itemClass?: string;
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
};
|
|
19
|
+
declare const Marquee: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type Marquee = ReturnType<typeof Marquee>;
|
|
21
|
+
export default Marquee;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../utils/class-names.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
class?: string;
|
|
7
|
+
bodyClass?: string;
|
|
8
|
+
coverClass?: string;
|
|
9
|
+
overlayClass?: string;
|
|
10
|
+
children: Snippet;
|
|
11
|
+
cover?: string;
|
|
12
|
+
variant?:
|
|
13
|
+
| 'primary'
|
|
14
|
+
| 'secondary'
|
|
15
|
+
| 'muted'
|
|
16
|
+
| 'success'
|
|
17
|
+
| 'info'
|
|
18
|
+
| 'warning'
|
|
19
|
+
| 'danger'
|
|
20
|
+
| 'surface'
|
|
21
|
+
| 'ghost';
|
|
22
|
+
isSolid?: boolean;
|
|
23
|
+
isBoxed?: boolean;
|
|
24
|
+
hasOverlay?: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
class: className,
|
|
29
|
+
bodyClass,
|
|
30
|
+
coverClass,
|
|
31
|
+
overlayClass,
|
|
32
|
+
cover,
|
|
33
|
+
hasOverlay,
|
|
34
|
+
variant = 'ghost',
|
|
35
|
+
isSolid,
|
|
36
|
+
children,
|
|
37
|
+
isBoxed
|
|
38
|
+
}: Props = $props();
|
|
39
|
+
|
|
40
|
+
const variantClasses = {
|
|
41
|
+
primary: 'is-primary',
|
|
42
|
+
secondary: 'is-secondary',
|
|
43
|
+
muted: 'is-muted',
|
|
44
|
+
success: 'is-success',
|
|
45
|
+
info: 'is-info',
|
|
46
|
+
warning: 'is-warning',
|
|
47
|
+
danger: 'is-danger',
|
|
48
|
+
surface: 'is-surface',
|
|
49
|
+
ghost: 'is-ghost'
|
|
50
|
+
};
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<div class={cn('section', variantClasses[variant], isSolid && 'is-solid', className)}>
|
|
54
|
+
{#if cover}
|
|
55
|
+
<img src={cover} alt="cover" class={cn('section-cover', coverClass)} />
|
|
56
|
+
{/if}
|
|
57
|
+
{#if hasOverlay}
|
|
58
|
+
<div class={cn('section-overlay', overlayClass)}></div>
|
|
59
|
+
{/if}
|
|
60
|
+
<div class={cn('section-body', isBoxed && 'boxed', bodyClass)}>
|
|
61
|
+
{@render children()}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type Props = {
|
|
3
|
+
class?: string;
|
|
4
|
+
bodyClass?: string;
|
|
5
|
+
coverClass?: string;
|
|
6
|
+
overlayClass?: string;
|
|
7
|
+
children: Snippet;
|
|
8
|
+
cover?: string;
|
|
9
|
+
variant?: 'primary' | 'secondary' | 'muted' | 'success' | 'info' | 'warning' | 'danger' | 'surface' | 'ghost';
|
|
10
|
+
isSolid?: boolean;
|
|
11
|
+
isBoxed?: boolean;
|
|
12
|
+
hasOverlay?: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare const Section: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type Section = ReturnType<typeof Section>;
|
|
16
|
+
export default Section;
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { TableState } from '../hooks/use-table.svelte.js';
|
|
3
|
+
import { cn } from '../utils/class-names.js';
|
|
4
|
+
import { Empty, TextField, Select, Checkbox, Icon, Button } from '../index.js';
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
import {
|
|
7
|
+
ArrowLeft24RegularIcon,
|
|
8
|
+
ArrowRight24RegularIcon,
|
|
9
|
+
ArrowSortDownLines24RegularIcon,
|
|
10
|
+
ArrowSortUpLines24RegularIcon
|
|
11
|
+
} from '../icons/index.js';
|
|
12
|
+
|
|
13
|
+
const handleSort = (column: { field: string; sortable?: boolean }) => {
|
|
14
|
+
if (!column.sortable) return;
|
|
15
|
+
|
|
16
|
+
const newOrder = table.sortBy === column.field && table.sortOrder === 'ASC' ? 'DESC' : 'ASC';
|
|
17
|
+
table.setSort(column.field, newOrder);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const getSortIcon = (field: string): string => {
|
|
21
|
+
if (table.sortBy !== field) {
|
|
22
|
+
return 'fluent:arrow-sort-down-lines-24-regular';
|
|
23
|
+
}
|
|
24
|
+
return table.sortOrder === 'ASC'
|
|
25
|
+
? 'fluent:arrow-sort-up-lines-24-regular'
|
|
26
|
+
: 'fluent:arrow-sort-down-lines-24-regular';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const isColumnSorted = (field: string): boolean => {
|
|
30
|
+
return table.sortBy === field;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type Props = {
|
|
34
|
+
table: TableState;
|
|
35
|
+
isStriped?: boolean;
|
|
36
|
+
isBordered?: boolean;
|
|
37
|
+
hasDividers?: boolean;
|
|
38
|
+
noDataTitle?: string;
|
|
39
|
+
noDataDescription?: string;
|
|
40
|
+
noDataType?: 'playlist' | 'result' | 'data' | 'template';
|
|
41
|
+
size?: 'sm' | 'md' | 'lg';
|
|
42
|
+
showPagination?: boolean;
|
|
43
|
+
paginationAlign?: 'start' | 'center' | 'end';
|
|
44
|
+
loadingRows?: number;
|
|
45
|
+
showSearch?: boolean;
|
|
46
|
+
searchPlaceholder?: string;
|
|
47
|
+
showRowsPerPage?: boolean;
|
|
48
|
+
rowsPerPageOptions?: number[];
|
|
49
|
+
rowsPerPageLabel?: string;
|
|
50
|
+
showTotal?: boolean;
|
|
51
|
+
totalLabel?: string;
|
|
52
|
+
showToolbar?: boolean;
|
|
53
|
+
toolbarClass?: string;
|
|
54
|
+
showSelectionCount?: boolean;
|
|
55
|
+
selectionCountLabel?: string;
|
|
56
|
+
toolbarStart?: Snippet;
|
|
57
|
+
toolbarEnd?: Snippet;
|
|
58
|
+
selectionActions?: Snippet<[any[]]>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const {
|
|
62
|
+
table,
|
|
63
|
+
isStriped,
|
|
64
|
+
isBordered,
|
|
65
|
+
hasDividers,
|
|
66
|
+
noDataTitle = 'No data',
|
|
67
|
+
noDataDescription = 'There are no records to display',
|
|
68
|
+
noDataType = 'data',
|
|
69
|
+
size = 'md',
|
|
70
|
+
showPagination,
|
|
71
|
+
paginationAlign = 'center',
|
|
72
|
+
loadingRows = 5,
|
|
73
|
+
showSearch,
|
|
74
|
+
searchPlaceholder = 'Search by name...',
|
|
75
|
+
showRowsPerPage = false,
|
|
76
|
+
rowsPerPageOptions = [5, 10, 25, 50, 100],
|
|
77
|
+
rowsPerPageLabel = 'Rows per page:',
|
|
78
|
+
showTotal = false,
|
|
79
|
+
totalLabel = 'Total',
|
|
80
|
+
showToolbar = false,
|
|
81
|
+
toolbarClass,
|
|
82
|
+
showSelectionCount = true,
|
|
83
|
+
selectionCountLabel = 'selected',
|
|
84
|
+
toolbarStart,
|
|
85
|
+
toolbarEnd,
|
|
86
|
+
selectionActions
|
|
87
|
+
}: Props = $props();
|
|
88
|
+
|
|
89
|
+
const tableSizes = {
|
|
90
|
+
sm: 'is-sm',
|
|
91
|
+
md: 'is-md',
|
|
92
|
+
lg: 'is-lg'
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const shouldShowToolbar = $derived(showToolbar || showSearch || toolbarStart || toolbarEnd);
|
|
96
|
+
|
|
97
|
+
const hasSelection = $derived(table.selectable && table.selectedRows.length > 0);
|
|
98
|
+
|
|
99
|
+
let searchValue = $state(table.search ?? '');
|
|
100
|
+
|
|
101
|
+
const rowsPerPageSelectOptions = $derived(
|
|
102
|
+
rowsPerPageOptions.map((size) => ({
|
|
103
|
+
id: size,
|
|
104
|
+
label: String(size)
|
|
105
|
+
}))
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
let selectedPageSize = $derived(
|
|
109
|
+
rowsPerPageSelectOptions.find((opt) => opt.id === table.pageSize)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const handleSearchInput = (value: unknown) => {
|
|
113
|
+
searchValue = String(value);
|
|
114
|
+
table.setSearch(searchValue);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleRowsPerPageChange = (value: unknown) => {
|
|
118
|
+
table.setPageSize(Number(value));
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleSelectAllChange = () => {
|
|
122
|
+
table.toggleAll();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleRowSelectChange = (row: any) => {
|
|
126
|
+
table.toggleRow(row);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const getPageNumbers = (current: number, total: number): (number | string)[] => {
|
|
130
|
+
const pages: (number | string)[] = [];
|
|
131
|
+
const delta = 1;
|
|
132
|
+
|
|
133
|
+
if (total <= 7) {
|
|
134
|
+
for (let i = 1; i <= total; i++) {
|
|
135
|
+
pages.push(i);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
pages.push(1);
|
|
139
|
+
const start = Math.max(2, current - delta);
|
|
140
|
+
const end = Math.min(total - 1, current + delta);
|
|
141
|
+
|
|
142
|
+
if (start > 2) {
|
|
143
|
+
pages.push('...');
|
|
144
|
+
}
|
|
145
|
+
for (let i = start; i <= end; i++) {
|
|
146
|
+
pages.push(i);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (end < total - 1) {
|
|
150
|
+
pages.push('...');
|
|
151
|
+
}
|
|
152
|
+
pages.push(total);
|
|
153
|
+
}
|
|
154
|
+
return pages;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const pageNumbers = $derived(
|
|
158
|
+
showPagination && table.page && table.totalPages
|
|
159
|
+
? getPageNumbers(table.page, table.totalPages)
|
|
160
|
+
: []
|
|
161
|
+
);
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
{#snippet InputIcon()}
|
|
165
|
+
<SearchIcon class="control-icon" />
|
|
166
|
+
{/snippet}
|
|
167
|
+
|
|
168
|
+
<div class="table-wrapper">
|
|
169
|
+
{#if shouldShowToolbar}
|
|
170
|
+
<div class={cn('table-toolbar', toolbarClass)}>
|
|
171
|
+
<div class="table-toolbar-start">
|
|
172
|
+
{#if showSearch}
|
|
173
|
+
<TextField
|
|
174
|
+
value={searchValue}
|
|
175
|
+
placeholder={searchPlaceholder}
|
|
176
|
+
prefix={InputIcon}
|
|
177
|
+
variant="outlined"
|
|
178
|
+
size="sm"
|
|
179
|
+
oninput={handleSearchInput}
|
|
180
|
+
class="table-search"
|
|
181
|
+
/>
|
|
182
|
+
{/if}
|
|
183
|
+
{#if toolbarStart}
|
|
184
|
+
{@render toolbarStart()}
|
|
185
|
+
{/if}
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div class="table-toolbar-end">
|
|
189
|
+
{#if toolbarEnd}
|
|
190
|
+
{@render toolbarEnd()}
|
|
191
|
+
{/if}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
{/if}
|
|
195
|
+
|
|
196
|
+
{#if hasSelection}
|
|
197
|
+
<div class="table-selection-bar">
|
|
198
|
+
<div class="table-selection-info">
|
|
199
|
+
{#if showSelectionCount}
|
|
200
|
+
<span class="table-selection-count">
|
|
201
|
+
{table.selectedRows.length}
|
|
202
|
+
{selectionCountLabel}
|
|
203
|
+
</span>
|
|
204
|
+
{/if}
|
|
205
|
+
<button type="button" class="table-selection-clear" onclick={() => table.clearSelection()}>
|
|
206
|
+
Clear selection
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
{#if selectionActions}
|
|
210
|
+
<div class="table-selection-actions">
|
|
211
|
+
{@render selectionActions(table.selectedRows)}
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
</div>
|
|
215
|
+
{/if}
|
|
216
|
+
|
|
217
|
+
<div class="table-container">
|
|
218
|
+
<table
|
|
219
|
+
class={cn(
|
|
220
|
+
'table',
|
|
221
|
+
tableSizes[size],
|
|
222
|
+
isStriped && 'is-striped',
|
|
223
|
+
isBordered && 'is-bordered',
|
|
224
|
+
hasDividers && 'has-dividers'
|
|
225
|
+
)}
|
|
226
|
+
>
|
|
227
|
+
<thead class="table-header">
|
|
228
|
+
<tr>
|
|
229
|
+
{#if table.selectable}
|
|
230
|
+
<th class="table-head table-checkbox-cell" style="width: 48px;">
|
|
231
|
+
<Checkbox
|
|
232
|
+
checked={table.isAllSelected}
|
|
233
|
+
onchange={handleSelectAllChange}
|
|
234
|
+
class={cn('table-checkbox', table.isIndeterminate && 'is-indeterminate')}
|
|
235
|
+
/>
|
|
236
|
+
</th>
|
|
237
|
+
{/if}
|
|
238
|
+
{#each table.columns as column}
|
|
239
|
+
<th
|
|
240
|
+
style="width: {column.width}; text-align: {column.align || 'left'}"
|
|
241
|
+
class:sortable={column.sortable}
|
|
242
|
+
class:sorted={isColumnSorted(column.field)}
|
|
243
|
+
class="table-head"
|
|
244
|
+
>
|
|
245
|
+
<div
|
|
246
|
+
class="table-head-content"
|
|
247
|
+
style="justify-content: {column.align === 'right'
|
|
248
|
+
? 'flex-end'
|
|
249
|
+
: column.align === 'center'
|
|
250
|
+
? 'center'
|
|
251
|
+
: 'flex-start'}"
|
|
252
|
+
>
|
|
253
|
+
<span class="table-head-label">{column.label}</span>
|
|
254
|
+
{#if column.sortable}
|
|
255
|
+
<Button
|
|
256
|
+
variant="ghost"
|
|
257
|
+
size="xs"
|
|
258
|
+
onclick={() => handleSort(column)}
|
|
259
|
+
class="table-sort-btn {isColumnSorted(column.field) ? 'is-active' : ''}"
|
|
260
|
+
>
|
|
261
|
+
{#if table.sortBy !== column.field}
|
|
262
|
+
<Icon icon={ArrowSortDownLines24RegularIcon} />
|
|
263
|
+
{:else if table.sortOrder === 'ASC'}
|
|
264
|
+
<Icon icon={ArrowSortUpLines24RegularIcon} />
|
|
265
|
+
{:else}
|
|
266
|
+
<Icon icon={ArrowSortDownLines24RegularIcon} />
|
|
267
|
+
{/if}
|
|
268
|
+
</Button>
|
|
269
|
+
{/if}
|
|
270
|
+
</div>
|
|
271
|
+
</th>
|
|
272
|
+
{/each}
|
|
273
|
+
</tr>
|
|
274
|
+
</thead>
|
|
275
|
+
<tbody class="table-body">
|
|
276
|
+
{#if table.isLoading}
|
|
277
|
+
{#each Array(loadingRows) as _, index}
|
|
278
|
+
<tr class="table-loading-row">
|
|
279
|
+
{#if table.selectable}
|
|
280
|
+
<td class="table-checkbox-cell">
|
|
281
|
+
<div class="skeleton-loader skeleton-checkbox"></div>
|
|
282
|
+
</td>
|
|
283
|
+
{/if}
|
|
284
|
+
{#each table.columns as column}
|
|
285
|
+
<td style="text-align: {column.align || 'left'}">
|
|
286
|
+
<div class="skeleton-loader"></div>
|
|
287
|
+
</td>
|
|
288
|
+
{/each}
|
|
289
|
+
</tr>
|
|
290
|
+
{/each}
|
|
291
|
+
{:else if table.data.length === 0}
|
|
292
|
+
<tr class="table-empty-row">
|
|
293
|
+
<td
|
|
294
|
+
colspan={table.selectable ? table.columns.length + 1 : table.columns.length}
|
|
295
|
+
class="table-empty-cell"
|
|
296
|
+
>
|
|
297
|
+
<div class="empty-state">
|
|
298
|
+
<div class="empty-icon">
|
|
299
|
+
<Empty type={noDataType} />
|
|
300
|
+
</div>
|
|
301
|
+
<div class="empty-content">
|
|
302
|
+
<h3 class="empty-title">{noDataTitle}</h3>
|
|
303
|
+
<p class="empty-description">{noDataDescription}</p>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
</td>
|
|
307
|
+
</tr>
|
|
308
|
+
{:else}
|
|
309
|
+
{#each table.data as item, index}
|
|
310
|
+
<tr class:table-row-selected={table.selectable && table.isRowSelected(item)}>
|
|
311
|
+
{#if table.selectable}
|
|
312
|
+
<td class="table-checkbox-cell">
|
|
313
|
+
<Checkbox
|
|
314
|
+
checked={table.isRowSelected(item)}
|
|
315
|
+
onchange={() => handleRowSelectChange(item)}
|
|
316
|
+
class="table-checkbox"
|
|
317
|
+
/>
|
|
318
|
+
</td>
|
|
319
|
+
{/if}
|
|
320
|
+
{#each table.columns as column}
|
|
321
|
+
<td style="text-align: {column.align || 'left'}">
|
|
322
|
+
{#if column.render}
|
|
323
|
+
{@render column.render(item)}
|
|
324
|
+
{:else}
|
|
325
|
+
{item[column.field]}
|
|
326
|
+
{/if}
|
|
327
|
+
</td>
|
|
328
|
+
{/each}
|
|
329
|
+
</tr>
|
|
330
|
+
{/each}
|
|
331
|
+
{/if}
|
|
332
|
+
</tbody>
|
|
333
|
+
</table>
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
{#if (showTotal || showRowsPerPage) && !table.isLoading}
|
|
337
|
+
<div class="table-footer-info">
|
|
338
|
+
{#if showTotal}
|
|
339
|
+
<div class="table-total">
|
|
340
|
+
{totalLabel}: {table.total ?? 0}
|
|
341
|
+
{table.total === 1 ? 'item' : 'items'}
|
|
342
|
+
</div>
|
|
343
|
+
{/if}
|
|
344
|
+
{#if showRowsPerPage}
|
|
345
|
+
<div class="table-rows-per-page">
|
|
346
|
+
<span class="table-rows-per-page-label">{rowsPerPageLabel}</span>
|
|
347
|
+
<Select
|
|
348
|
+
options={rowsPerPageSelectOptions}
|
|
349
|
+
value={table.pageSize}
|
|
350
|
+
selected={selectedPageSize}
|
|
351
|
+
onchange={handleRowsPerPageChange}
|
|
352
|
+
variant="outlined"
|
|
353
|
+
size="sm"
|
|
354
|
+
class="table-rows-per-page-select"
|
|
355
|
+
/>
|
|
356
|
+
</div>
|
|
357
|
+
{/if}
|
|
358
|
+
</div>
|
|
359
|
+
{/if}
|
|
360
|
+
|
|
361
|
+
{#if showPagination && table.totalPages && table.totalPages > 1 && !table.isLoading}
|
|
362
|
+
<div
|
|
363
|
+
class="pagination-container"
|
|
364
|
+
style="justify-content: {paginationAlign === 'start'
|
|
365
|
+
? 'flex-start'
|
|
366
|
+
: paginationAlign === 'end'
|
|
367
|
+
? 'flex-end'
|
|
368
|
+
: 'center'}"
|
|
369
|
+
>
|
|
370
|
+
<div class="pagination">
|
|
371
|
+
<button
|
|
372
|
+
class="pagination-btn"
|
|
373
|
+
onclick={() => table.prevPage?.()}
|
|
374
|
+
disabled={!table.hasPrevPage}
|
|
375
|
+
aria-label="Previous page"
|
|
376
|
+
>
|
|
377
|
+
<Icon icon={ArrowLeft24RegularIcon} />
|
|
378
|
+
</button>
|
|
379
|
+
|
|
380
|
+
{#each pageNumbers as pageNum}
|
|
381
|
+
{#if pageNum === '...'}
|
|
382
|
+
<span class="pagination-ellipsis">...</span>
|
|
383
|
+
{:else}
|
|
384
|
+
<button
|
|
385
|
+
class="pagination-btn"
|
|
386
|
+
class:active={pageNum === table.page}
|
|
387
|
+
onclick={() => table.goToPage?.(pageNum as number)}
|
|
388
|
+
aria-label="Page {pageNum}"
|
|
389
|
+
aria-current={pageNum === table.page ? 'page' : undefined}
|
|
390
|
+
>
|
|
391
|
+
{pageNum}
|
|
392
|
+
</button>
|
|
393
|
+
{/if}
|
|
394
|
+
{/each}
|
|
395
|
+
|
|
396
|
+
<button
|
|
397
|
+
class="pagination-btn"
|
|
398
|
+
onclick={() => table.nextPage?.()}
|
|
399
|
+
disabled={!table.hasNextPage}
|
|
400
|
+
aria-label="Next page"
|
|
401
|
+
>
|
|
402
|
+
<Icon icon={ArrowRight24RegularIcon} />
|
|
403
|
+
</button>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
{/if}
|
|
407
|
+
</div>
|