svelte-comp 1.3.3 → 1.3.6
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.md +21 -21
- package/README.md +101 -100
- package/dist/App.svelte +507 -507
- package/dist/Container.svelte +59 -59
- package/dist/app.css +234 -235
- package/dist/app.d.ts +10 -0
- package/dist/lib/Accordion.svelte +155 -155
- package/dist/lib/Badge.svelte +44 -44
- package/dist/lib/Button.svelte +185 -170
- package/dist/lib/Calendar.svelte +384 -384
- package/dist/lib/Card.svelte +103 -103
- package/dist/lib/Carousel.svelte +293 -293
- package/dist/lib/Carousel.svelte.d.ts +1 -1
- package/dist/lib/CheckBox.svelte +210 -210
- package/dist/lib/CodeView.svelte +308 -307
- package/dist/lib/ColorPicker.svelte +159 -159
- package/dist/lib/ContextMenu.svelte +328 -322
- package/dist/lib/DatePicker.svelte +246 -246
- package/dist/lib/Dialog.svelte +233 -233
- package/dist/lib/Field.svelte +299 -299
- package/dist/lib/FilePicker.svelte +295 -240
- package/dist/lib/FilePicker.svelte.d.ts +6 -1
- package/dist/lib/Form.svelte +438 -438
- package/dist/lib/Hamburger.svelte +217 -217
- package/dist/lib/InstallPWA.svelte +94 -94
- package/dist/lib/Menu.svelte +623 -623
- package/dist/lib/NoticeBase.svelte +140 -140
- package/dist/lib/PaginatedCard.svelte +73 -73
- package/dist/lib/Pagination.svelte +119 -119
- package/dist/lib/PrimaryColorSelect.svelte +111 -111
- package/dist/lib/ProgressBar.svelte +141 -141
- package/dist/lib/ProgressCircle.svelte +190 -190
- package/dist/lib/Radio.svelte +189 -189
- package/dist/lib/SearchInput.svelte +104 -104
- package/dist/lib/Select.svelte +524 -524
- package/dist/lib/Slider.svelte +253 -253
- package/dist/lib/Splitter.svelte +159 -150
- package/dist/lib/Switch.svelte +168 -167
- package/dist/lib/Table.svelte +299 -299
- package/dist/lib/Tabs.svelte +213 -213
- package/dist/lib/ThemeToggle.svelte +128 -127
- package/dist/lib/TimePicker.svelte +312 -312
- package/dist/lib/TimePickerNew.svelte +634 -0
- package/dist/lib/TimePickerNew.svelte.d.ts +49 -0
- package/dist/lib/Toast.svelte +123 -123
- package/dist/lib/Tooltip.svelte +110 -110
- package/dist/lib/Topbar.svelte +107 -107
- package/dist/lib/__tests__/Accordion.test.d.ts +1 -0
- package/dist/lib/__tests__/Accordion.test.js +171 -0
- package/dist/lib/__tests__/Badge.test.d.ts +1 -0
- package/dist/lib/__tests__/Badge.test.js +41 -0
- package/dist/lib/__tests__/Button.test.d.ts +1 -0
- package/dist/lib/__tests__/Button.test.js +269 -0
- package/dist/lib/__tests__/Calendar.test.d.ts +1 -0
- package/dist/lib/__tests__/Calendar.test.js +171 -0
- package/dist/lib/__tests__/Card.test.d.ts +1 -0
- package/dist/lib/__tests__/Card.test.js +148 -0
- package/dist/lib/__tests__/Carousel.test.d.ts +1 -0
- package/dist/lib/__tests__/Carousel.test.js +439 -0
- package/dist/lib/__tests__/CheckBox.test.d.ts +1 -0
- package/dist/lib/__tests__/CheckBox.test.js +152 -0
- package/dist/lib/__tests__/CodeView.test.d.ts +1 -0
- package/dist/lib/__tests__/CodeView.test.js +157 -0
- package/dist/lib/__tests__/ColorPicker.test.d.ts +1 -0
- package/dist/lib/__tests__/ColorPicker.test.js +93 -0
- package/dist/lib/__tests__/ContextMenu.test.d.ts +1 -0
- package/dist/lib/__tests__/ContextMenu.test.js +67 -0
- package/dist/lib/__tests__/DatePicker.test.d.ts +1 -0
- package/dist/lib/__tests__/DatePicker.test.js +108 -0
- package/dist/lib/__tests__/Dialog.test.d.ts +1 -0
- package/dist/lib/__tests__/Dialog.test.js +183 -0
- package/dist/lib/__tests__/Field.test.d.ts +1 -0
- package/dist/lib/__tests__/Field.test.js +190 -0
- package/dist/lib/__tests__/FilePicker.test.d.ts +1 -0
- package/dist/lib/__tests__/FilePicker.test.js +179 -0
- package/dist/lib/__tests__/Form.integration.test.d.ts +1 -0
- package/dist/lib/__tests__/Form.integration.test.js +158 -0
- package/dist/lib/__tests__/Form.test.d.ts +1 -0
- package/dist/lib/__tests__/Form.test.js +463 -0
- package/dist/lib/__tests__/Hamburger.test.d.ts +1 -0
- package/dist/lib/__tests__/Hamburger.test.js +161 -0
- package/dist/lib/__tests__/InstallPWA.test.d.ts +1 -0
- package/dist/lib/__tests__/InstallPWA.test.js +15 -0
- package/dist/lib/__tests__/Menu.test.d.ts +1 -0
- package/dist/lib/__tests__/Menu.test.js +285 -0
- package/dist/lib/__tests__/NoticeBase.test.d.ts +1 -0
- package/dist/lib/__tests__/NoticeBase.test.js +60 -0
- package/dist/lib/__tests__/PaginatedCard.test.d.ts +1 -0
- package/dist/lib/__tests__/PaginatedCard.test.js +89 -0
- package/dist/lib/__tests__/Pagination.test.d.ts +1 -0
- package/dist/lib/__tests__/Pagination.test.js +168 -0
- package/dist/lib/__tests__/PrimaryColorSelect.test.d.ts +1 -0
- package/dist/lib/__tests__/PrimaryColorSelect.test.js +92 -0
- package/dist/lib/__tests__/ProgressBar.test.d.ts +1 -0
- package/dist/lib/__tests__/ProgressBar.test.js +69 -0
- package/dist/lib/__tests__/ProgressCircle.test.d.ts +1 -0
- package/dist/lib/__tests__/ProgressCircle.test.js +71 -0
- package/dist/lib/__tests__/Radio.test.d.ts +1 -0
- package/dist/lib/__tests__/Radio.test.js +127 -0
- package/dist/lib/__tests__/SearchInput.test.d.ts +1 -0
- package/dist/lib/__tests__/SearchInput.test.js +80 -0
- package/dist/lib/__tests__/Select.test.d.ts +1 -0
- package/dist/lib/__tests__/Select.test.js +408 -0
- package/dist/lib/__tests__/Slider.test.d.ts +1 -0
- package/dist/lib/__tests__/Slider.test.js +213 -0
- package/dist/lib/__tests__/Splitter.test.d.ts +1 -0
- package/dist/lib/__tests__/Splitter.test.js +87 -0
- package/dist/lib/__tests__/Switch.test.d.ts +1 -0
- package/dist/lib/__tests__/Switch.test.js +97 -0
- package/dist/lib/__tests__/Table.test.d.ts +1 -0
- package/dist/lib/__tests__/Table.test.js +349 -0
- package/dist/lib/__tests__/Tabs.test.d.ts +1 -0
- package/dist/lib/__tests__/Tabs.test.js +262 -0
- package/dist/lib/__tests__/ThemeToggle.test.d.ts +1 -0
- package/dist/lib/__tests__/ThemeToggle.test.js +84 -0
- package/dist/lib/__tests__/TimePicker.test.d.ts +1 -0
- package/dist/lib/__tests__/TimePicker.test.js +146 -0
- package/dist/lib/__tests__/TimePickerNew.test.d.ts +1 -0
- package/dist/lib/__tests__/TimePickerNew.test.js +322 -0
- package/dist/lib/__tests__/Toast.test.d.ts +1 -0
- package/dist/lib/__tests__/Toast.test.js +135 -0
- package/dist/lib/__tests__/Tooltip.test.d.ts +1 -0
- package/dist/lib/__tests__/Tooltip.test.js +171 -0
- package/dist/lib/__tests__/Topbar.test.d.ts +1 -0
- package/dist/lib/__tests__/Topbar.test.js +25 -0
- package/dist/lib/__tests__/setupLangContext.d.ts +1 -0
- package/dist/lib/__tests__/setupLangContext.js +65 -0
- package/dist/lib/__tests__/storage.test.d.ts +1 -0
- package/dist/lib/__tests__/storage.test.js +124 -0
- package/dist/lib/__tests__/utils.test.d.ts +1 -0
- package/dist/lib/__tests__/utils.test.js +11 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/lang.d.ts +4 -0
- package/dist/lib/lang.js +4 -0
- package/dist/styles.css +234 -232
- package/dist/utils/index.js +15 -4
- package/package.json +52 -52
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
<!-- src/lib/NoticeBase.svelte -->
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* @component NoticeBase
|
|
5
|
-
* @description Shared base for Toast and Badge visuals.
|
|
6
|
-
*
|
|
7
|
-
* @prop title {string} - Optional title displayed above the message
|
|
8
|
-
*
|
|
9
|
-
* @prop message {string} - Notice text content
|
|
10
|
-
*
|
|
11
|
-
* @prop variant {ToastVariant} - Visual style
|
|
12
|
-
* @options success|danger|warning|info
|
|
13
|
-
* @default info
|
|
14
|
-
*
|
|
15
|
-
* @prop showIcon {boolean} - Shows an icon matching the variant
|
|
16
|
-
* @default true
|
|
17
|
-
*
|
|
18
|
-
* @prop inline {boolean} - Inline layout without overlay styling
|
|
19
|
-
* @default false
|
|
20
|
-
*
|
|
21
|
-
* @prop size {"sm" | "md"} - Size preset for spacing and typography
|
|
22
|
-
* @default "sm"
|
|
23
|
-
*
|
|
24
|
-
* @prop end {Snippet} - Trailing content (e.g. close button)
|
|
25
|
-
*
|
|
26
|
-
* @prop class {string} - Additional wrapper classes
|
|
27
|
-
* @default ""
|
|
28
|
-
*
|
|
29
|
-
* @note Used by Toast and Badge to keep styles consistent.
|
|
30
|
-
*/
|
|
31
|
-
import type { Snippet } from "svelte";
|
|
32
|
-
import type { ToastVariant } from "./types";
|
|
33
|
-
import { cx } from "../utils";
|
|
34
|
-
|
|
35
|
-
type Props = {
|
|
36
|
-
title?: string;
|
|
37
|
-
message: string;
|
|
38
|
-
variant?: ToastVariant;
|
|
39
|
-
showIcon?: boolean;
|
|
40
|
-
class?: string;
|
|
41
|
-
inline?: boolean;
|
|
42
|
-
size?: "sm" | "md";
|
|
43
|
-
end?: Snippet;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
let {
|
|
47
|
-
title,
|
|
48
|
-
message,
|
|
49
|
-
variant = "info",
|
|
50
|
-
showIcon = true,
|
|
51
|
-
inline = false,
|
|
52
|
-
size = "sm",
|
|
53
|
-
end,
|
|
54
|
-
class: externalClass = "",
|
|
55
|
-
}: Props = $props();
|
|
56
|
-
|
|
57
|
-
function variantClasses(v: ToastVariant) {
|
|
58
|
-
switch (v) {
|
|
59
|
-
case "success":
|
|
60
|
-
return "bg-[var(--color-bg-success)] text-[var(--color-text-success)]";
|
|
61
|
-
case "danger":
|
|
62
|
-
return "bg-[var(--color-bg-danger)] text-[var(--color-text-danger)]";
|
|
63
|
-
case "warning":
|
|
64
|
-
return "bg-[var(--color-bg-warning)] text-[var(--color-text-warning)]";
|
|
65
|
-
default:
|
|
66
|
-
return "bg-[var(--color-bg-page)] text-[var(--color-text-default)]";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const sizeClasses = $derived(
|
|
71
|
-
size === "md"
|
|
72
|
-
? "gap-
|
|
73
|
-
: "gap-
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const iconClass = $derived(size === "md" ? "w-5 h-5" : "w-4 h-4");
|
|
77
|
-
|
|
78
|
-
const titleClass = $derived(
|
|
79
|
-
size === "md"
|
|
80
|
-
? "font-[var(--font-weight-medium)] truncate [font-size:var(--text-md)] max-sm:[font-size:var(--text-sm)]"
|
|
81
|
-
: "font-[var(--font-weight-medium)] truncate text-sm"
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const messageClass = $derived(
|
|
85
|
-
size === "md"
|
|
86
|
-
? "line-clamp-3 [font-size:var(--text-sm)] max-sm:[font-size:var(--text-xs)]"
|
|
87
|
-
: "truncate text-xs"
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const rootClass = $derived(
|
|
91
|
-
cx(
|
|
92
|
-
"flex items-center border border-[var(--border-color-default)]",
|
|
93
|
-
sizeClasses,
|
|
94
|
-
!inline && "shadow-
|
|
95
|
-
variantClasses(variant),
|
|
96
|
-
externalClass
|
|
97
|
-
)
|
|
98
|
-
);
|
|
99
|
-
</script>
|
|
100
|
-
|
|
101
|
-
<div class={rootClass} role="status" aria-live="polite">
|
|
102
|
-
{#if showIcon}
|
|
103
|
-
<span class={cx(iconClass, "flex-shrink-0")} aria-hidden="true">
|
|
104
|
-
{#if variant === "success"}
|
|
105
|
-
<svg fill="none" viewBox="0 0 26 26">
|
|
106
|
-
<path d="M8.5 14L11.1 16.6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
107
|
-
<path d="M18.2 10L11.6 16.6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
108
|
-
<path d="M13 25C19.6 25 25 19.6 25 13C25 6.4 19.6 1 13 1C6.4 1 1 6.4 1 13C1 19.6 6.4 25 13 25Z" stroke="currentColor" stroke-width="2"/>
|
|
109
|
-
</svg>
|
|
110
|
-
{:else if variant === "danger"}
|
|
111
|
-
<svg fill="none" viewBox="0 0 26 26">
|
|
112
|
-
<path d="M13 25C19.6 25 25 19.6 25 13C25 6.4 19.6 1 13 1C6.4 1 1 6.4 1 13C1 19.6 6.4 25 13 25Z" stroke="currentColor" stroke-width="2"/>
|
|
113
|
-
<path d="M9 9.5L16.7 17.3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
114
|
-
<path d="M16.7 9.5L9 17.3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
115
|
-
</svg>
|
|
116
|
-
{:else if variant === "warning"}
|
|
117
|
-
<svg fill="none" viewBox="0 0 27 27">
|
|
118
|
-
<path d="M4.6 25.9H22.5C25.2 25.9 26.8 23 25.6 20.6L16.6 3.8C15.3 1.3 11.8 1.3 10.5 3.8L1.5 20.6C0.3 23 1.9 25.9 4.6 25.9Z" stroke="currentColor" stroke-width="2"/>
|
|
119
|
-
<path d="M13.9 18H13.2L11.9 9.3C11.9 8.6 12.5 8 13.2 8H13.9C14.6 8 15.2 8.6 15.2 9.3L13.9 18Z" fill="currentColor"/>
|
|
120
|
-
<circle cx="13.5" cy="20.6" r="1.3" fill="currentColor"/>
|
|
121
|
-
</svg>
|
|
122
|
-
{:else}
|
|
123
|
-
<svg fill="none" viewBox="0 0 26 26">
|
|
124
|
-
<path d="M13 25C19.6 25 25 19.6 25 13C25 6.4 19.6 1 13 1C6.4 1 1 6.4 1 13C1 19.6 6.4 25 13 25Z" stroke="currentColor" stroke-width="2"/>
|
|
125
|
-
<circle cx="13" cy="7.7" r="1.3" fill="currentColor"/>
|
|
126
|
-
<rect x="11.6" y="10.3" width="2.7" height="9.4" rx="1.3" fill="currentColor"/>
|
|
127
|
-
</svg>
|
|
128
|
-
{/if}
|
|
129
|
-
</span>
|
|
130
|
-
{/if}
|
|
131
|
-
|
|
132
|
-
<div class="flex-1 min-w-0">
|
|
133
|
-
{#if title}
|
|
134
|
-
<div class={titleClass}>{title}</div>
|
|
135
|
-
{/if}
|
|
136
|
-
<div class={messageClass} title={message}>{message}</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
{@render end?.()}
|
|
140
|
-
</div>
|
|
1
|
+
<!-- src/lib/NoticeBase.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* @component NoticeBase
|
|
5
|
+
* @description Shared base for Toast and Badge visuals.
|
|
6
|
+
*
|
|
7
|
+
* @prop title {string} - Optional title displayed above the message
|
|
8
|
+
*
|
|
9
|
+
* @prop message {string} - Notice text content
|
|
10
|
+
*
|
|
11
|
+
* @prop variant {ToastVariant} - Visual style
|
|
12
|
+
* @options success|danger|warning|info
|
|
13
|
+
* @default info
|
|
14
|
+
*
|
|
15
|
+
* @prop showIcon {boolean} - Shows an icon matching the variant
|
|
16
|
+
* @default true
|
|
17
|
+
*
|
|
18
|
+
* @prop inline {boolean} - Inline layout without overlay styling
|
|
19
|
+
* @default false
|
|
20
|
+
*
|
|
21
|
+
* @prop size {"sm" | "md"} - Size preset for spacing and typography
|
|
22
|
+
* @default "sm"
|
|
23
|
+
*
|
|
24
|
+
* @prop end {Snippet} - Trailing content (e.g. close button)
|
|
25
|
+
*
|
|
26
|
+
* @prop class {string} - Additional wrapper classes
|
|
27
|
+
* @default ""
|
|
28
|
+
*
|
|
29
|
+
* @note Used by Toast and Badge to keep styles consistent.
|
|
30
|
+
*/
|
|
31
|
+
import type { Snippet } from "svelte";
|
|
32
|
+
import type { ToastVariant } from "./types";
|
|
33
|
+
import { cx } from "../utils";
|
|
34
|
+
|
|
35
|
+
type Props = {
|
|
36
|
+
title?: string;
|
|
37
|
+
message: string;
|
|
38
|
+
variant?: ToastVariant;
|
|
39
|
+
showIcon?: boolean;
|
|
40
|
+
class?: string;
|
|
41
|
+
inline?: boolean;
|
|
42
|
+
size?: "sm" | "md";
|
|
43
|
+
end?: Snippet;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let {
|
|
47
|
+
title,
|
|
48
|
+
message,
|
|
49
|
+
variant = "info",
|
|
50
|
+
showIcon = true,
|
|
51
|
+
inline = false,
|
|
52
|
+
size = "sm",
|
|
53
|
+
end,
|
|
54
|
+
class: externalClass = "",
|
|
55
|
+
}: Props = $props();
|
|
56
|
+
|
|
57
|
+
function variantClasses(v: ToastVariant) {
|
|
58
|
+
switch (v) {
|
|
59
|
+
case "success":
|
|
60
|
+
return "bg-[var(--color-bg-success)] text-[var(--color-text-success)]";
|
|
61
|
+
case "danger":
|
|
62
|
+
return "bg-[var(--color-bg-danger)] text-[var(--color-text-danger)]";
|
|
63
|
+
case "warning":
|
|
64
|
+
return "bg-[var(--color-bg-warning)] text-[var(--color-text-warning)]";
|
|
65
|
+
default:
|
|
66
|
+
return "bg-[var(--color-bg-page)] text-[var(--color-text-default)]";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sizeClasses = $derived(
|
|
71
|
+
size === "md"
|
|
72
|
+
? "gap-[calc(var(--spacing-sm)+var(--spacing-xs))] px-[var(--spacing-md)] py-[calc(var(--spacing-sm)+var(--spacing-xs))] rounded-[var(--radius-lg)]"
|
|
73
|
+
: "gap-[var(--spacing-sm)] px-[calc(var(--spacing-sm)+var(--spacing-xs))] py-[calc(var(--spacing-sm)+var(--spacing-xs)/2)] rounded-[var(--radius-md)]"
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const iconClass = $derived(size === "md" ? "w-5 h-5" : "w-4 h-4");
|
|
77
|
+
|
|
78
|
+
const titleClass = $derived(
|
|
79
|
+
size === "md"
|
|
80
|
+
? "font-[var(--font-weight-medium)] truncate [font-size:var(--text-md)] max-sm:[font-size:var(--text-sm)]"
|
|
81
|
+
: "font-[var(--font-weight-medium)] truncate [font-size:var(--text-sm)]"
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const messageClass = $derived(
|
|
85
|
+
size === "md"
|
|
86
|
+
? "line-clamp-3 [font-size:var(--text-sm)] max-sm:[font-size:var(--text-xs)]"
|
|
87
|
+
: "truncate [font-size:var(--text-xs)]"
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const rootClass = $derived(
|
|
91
|
+
cx(
|
|
92
|
+
"flex items-center border border-[var(--border-color-default)]",
|
|
93
|
+
sizeClasses,
|
|
94
|
+
!inline && "shadow-[0_8px_16px_var(--shadow-color)] backdrop-blur-sm",
|
|
95
|
+
variantClasses(variant),
|
|
96
|
+
externalClass
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<div class={rootClass} role="status" aria-live="polite">
|
|
102
|
+
{#if showIcon}
|
|
103
|
+
<span class={cx(iconClass, "flex-shrink-0")} aria-hidden="true">
|
|
104
|
+
{#if variant === "success"}
|
|
105
|
+
<svg fill="none" viewBox="0 0 26 26">
|
|
106
|
+
<path d="M8.5 14L11.1 16.6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
107
|
+
<path d="M18.2 10L11.6 16.6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
108
|
+
<path d="M13 25C19.6 25 25 19.6 25 13C25 6.4 19.6 1 13 1C6.4 1 1 6.4 1 13C1 19.6 6.4 25 13 25Z" stroke="currentColor" stroke-width="2"/>
|
|
109
|
+
</svg>
|
|
110
|
+
{:else if variant === "danger"}
|
|
111
|
+
<svg fill="none" viewBox="0 0 26 26">
|
|
112
|
+
<path d="M13 25C19.6 25 25 19.6 25 13C25 6.4 19.6 1 13 1C6.4 1 1 6.4 1 13C1 19.6 6.4 25 13 25Z" stroke="currentColor" stroke-width="2"/>
|
|
113
|
+
<path d="M9 9.5L16.7 17.3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
114
|
+
<path d="M16.7 9.5L9 17.3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
115
|
+
</svg>
|
|
116
|
+
{:else if variant === "warning"}
|
|
117
|
+
<svg fill="none" viewBox="0 0 27 27">
|
|
118
|
+
<path d="M4.6 25.9H22.5C25.2 25.9 26.8 23 25.6 20.6L16.6 3.8C15.3 1.3 11.8 1.3 10.5 3.8L1.5 20.6C0.3 23 1.9 25.9 4.6 25.9Z" stroke="currentColor" stroke-width="2"/>
|
|
119
|
+
<path d="M13.9 18H13.2L11.9 9.3C11.9 8.6 12.5 8 13.2 8H13.9C14.6 8 15.2 8.6 15.2 9.3L13.9 18Z" fill="currentColor"/>
|
|
120
|
+
<circle cx="13.5" cy="20.6" r="1.3" fill="currentColor"/>
|
|
121
|
+
</svg>
|
|
122
|
+
{:else}
|
|
123
|
+
<svg fill="none" viewBox="0 0 26 26">
|
|
124
|
+
<path d="M13 25C19.6 25 25 19.6 25 13C25 6.4 19.6 1 13 1C6.4 1 1 6.4 1 13C1 19.6 6.4 25 13 25Z" stroke="currentColor" stroke-width="2"/>
|
|
125
|
+
<circle cx="13" cy="7.7" r="1.3" fill="currentColor"/>
|
|
126
|
+
<rect x="11.6" y="10.3" width="2.7" height="9.4" rx="1.3" fill="currentColor"/>
|
|
127
|
+
</svg>
|
|
128
|
+
{/if}
|
|
129
|
+
</span>
|
|
130
|
+
{/if}
|
|
131
|
+
|
|
132
|
+
<div class="flex-1 min-w-0">
|
|
133
|
+
{#if title}
|
|
134
|
+
<div class={titleClass}>{title}</div>
|
|
135
|
+
{/if}
|
|
136
|
+
<div class={messageClass} title={message}>{message}</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{@render end?.()}
|
|
140
|
+
</div>
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
<!-- src/lib/PaginatedCard.svelte -->
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* @component PaginatedCard
|
|
5
|
-
* @description A card component with built-in pagination. Renders items page by page inside a `Card` and appends `Pagination` in the footer.
|
|
6
|
-
*
|
|
7
|
-
* @prop items {Snippet[]} - Array of renderable snippets for each item
|
|
8
|
-
* @default []
|
|
9
|
-
*
|
|
10
|
-
* @prop itemsPerPage {number} - Items per page (must be >= 1)
|
|
11
|
-
* @default 1
|
|
12
|
-
*
|
|
13
|
-
* @prop header {Snippet} - Optional `Card` header content
|
|
14
|
-
*
|
|
15
|
-
* @prop footer {Snippet} - Custom footer content shown above pagination
|
|
16
|
-
*
|
|
17
|
-
* @prop class {string} - Extra classes passed to the underlying `Card`
|
|
18
|
-
* @default ""
|
|
19
|
-
*
|
|
20
|
-
* @note Maintains internal `currentPage` state (starts at `1`).
|
|
21
|
-
* @note `totalPages` is clamped to at least `1`; empty `items` still yields one page.
|
|
22
|
-
* @note Pagination is always visible; your `footer` snippet renders before it.
|
|
23
|
-
* @note Uses `Pagination.svelte` internally with `{ currentPage, totalPages, onPageChange }`.
|
|
24
|
-
* @note `itemsPerPage` must be `>= 1`; smaller values are not supported.
|
|
25
|
-
*/
|
|
26
|
-
import Card from "./Card.svelte";
|
|
27
|
-
import Pagination from "./Pagination.svelte";
|
|
28
|
-
import type { Snippet } from "svelte";
|
|
29
|
-
|
|
30
|
-
type Props = {
|
|
31
|
-
items?: Snippet[];
|
|
32
|
-
itemsPerPage?: number;
|
|
33
|
-
header?: Snippet;
|
|
34
|
-
footer?: Snippet;
|
|
35
|
-
class?: string;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
let {
|
|
39
|
-
items = [],
|
|
40
|
-
itemsPerPage = 1,
|
|
41
|
-
header,
|
|
42
|
-
footer,
|
|
43
|
-
class: externalClass = "",
|
|
44
|
-
}: Props = $props();
|
|
45
|
-
|
|
46
|
-
let currentPage = $state(1);
|
|
47
|
-
|
|
48
|
-
const totalPages = $derived(
|
|
49
|
-
Math.max(1, Math.ceil(items.length / itemsPerPage))
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
const pageItems = $derived(
|
|
53
|
-
items.slice(
|
|
54
|
-
(currentPage - 1) * itemsPerPage,
|
|
55
|
-
(currentPage - 1) * itemsPerPage + itemsPerPage
|
|
56
|
-
)
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
function handlePageChange(p: number) {
|
|
60
|
-
currentPage = p;
|
|
61
|
-
}
|
|
62
|
-
</script>
|
|
63
|
-
|
|
64
|
-
{#snippet composedFooter()}
|
|
65
|
-
{#if footer}{@render footer?.()}{/if}
|
|
66
|
-
<Pagination {currentPage} {totalPages} onPageChange={handlePageChange} />
|
|
67
|
-
{/snippet}
|
|
68
|
-
|
|
69
|
-
<Card class={externalClass} {header} footer={composedFooter}>
|
|
70
|
-
{#each pageItems as it, idx (idx)}
|
|
71
|
-
{@render it?.()}
|
|
72
|
-
{/each}
|
|
73
|
-
</Card>
|
|
1
|
+
<!-- src/lib/PaginatedCard.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* @component PaginatedCard
|
|
5
|
+
* @description A card component with built-in pagination. Renders items page by page inside a `Card` and appends `Pagination` in the footer.
|
|
6
|
+
*
|
|
7
|
+
* @prop items {Snippet[]} - Array of renderable snippets for each item
|
|
8
|
+
* @default []
|
|
9
|
+
*
|
|
10
|
+
* @prop itemsPerPage {number} - Items per page (must be >= 1)
|
|
11
|
+
* @default 1
|
|
12
|
+
*
|
|
13
|
+
* @prop header {Snippet} - Optional `Card` header content
|
|
14
|
+
*
|
|
15
|
+
* @prop footer {Snippet} - Custom footer content shown above pagination
|
|
16
|
+
*
|
|
17
|
+
* @prop class {string} - Extra classes passed to the underlying `Card`
|
|
18
|
+
* @default ""
|
|
19
|
+
*
|
|
20
|
+
* @note Maintains internal `currentPage` state (starts at `1`).
|
|
21
|
+
* @note `totalPages` is clamped to at least `1`; empty `items` still yields one page.
|
|
22
|
+
* @note Pagination is always visible; your `footer` snippet renders before it.
|
|
23
|
+
* @note Uses `Pagination.svelte` internally with `{ currentPage, totalPages, onPageChange }`.
|
|
24
|
+
* @note `itemsPerPage` must be `>= 1`; smaller values are not supported.
|
|
25
|
+
*/
|
|
26
|
+
import Card from "./Card.svelte";
|
|
27
|
+
import Pagination from "./Pagination.svelte";
|
|
28
|
+
import type { Snippet } from "svelte";
|
|
29
|
+
|
|
30
|
+
type Props = {
|
|
31
|
+
items?: Snippet[];
|
|
32
|
+
itemsPerPage?: number;
|
|
33
|
+
header?: Snippet;
|
|
34
|
+
footer?: Snippet;
|
|
35
|
+
class?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let {
|
|
39
|
+
items = [],
|
|
40
|
+
itemsPerPage = 1,
|
|
41
|
+
header,
|
|
42
|
+
footer,
|
|
43
|
+
class: externalClass = "",
|
|
44
|
+
}: Props = $props();
|
|
45
|
+
|
|
46
|
+
let currentPage = $state(1);
|
|
47
|
+
|
|
48
|
+
const totalPages = $derived(
|
|
49
|
+
Math.max(1, Math.ceil(items.length / itemsPerPage))
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const pageItems = $derived(
|
|
53
|
+
items.slice(
|
|
54
|
+
(currentPage - 1) * itemsPerPage,
|
|
55
|
+
(currentPage - 1) * itemsPerPage + itemsPerPage
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
function handlePageChange(p: number) {
|
|
60
|
+
currentPage = p;
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
{#snippet composedFooter()}
|
|
65
|
+
{#if footer}{@render footer?.()}{/if}
|
|
66
|
+
<Pagination {currentPage} {totalPages} onPageChange={handlePageChange} />
|
|
67
|
+
{/snippet}
|
|
68
|
+
|
|
69
|
+
<Card class={externalClass} {header} footer={composedFooter}>
|
|
70
|
+
{#each pageItems as it, idx (idx)}
|
|
71
|
+
{@render it?.()}
|
|
72
|
+
{/each}
|
|
73
|
+
</Card>
|
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
<!-- src/lib/Pagination.svelte -->
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* @component Pagination
|
|
5
|
-
* @description Compact pagination component for table or list navigation.
|
|
6
|
-
*
|
|
7
|
-
* @prop currentPage {number} - The active page number (1-based)
|
|
8
|
-
*
|
|
9
|
-
* @prop totalPages {number} - Total number of pages available
|
|
10
|
-
*
|
|
11
|
-
* @prop onPageChange {(page: number) => void} - Fired when a page button is clicked
|
|
12
|
-
*
|
|
13
|
-
* @prop class {string} - Custom classes applied to the pagination wrapper
|
|
14
|
-
* @default ""
|
|
15
|
-
*
|
|
16
|
-
* @note Displays “Page X of Y” and numbered page buttons.
|
|
17
|
-
* @note Prev/next buttons are disabled at the edges.
|
|
18
|
-
* @note Shows up to 3 numbered buttons centered around the current page.
|
|
19
|
-
* @note Uses `aria-current=\"page\"` on the active page for accessibility.
|
|
20
|
-
* @note Buttons are native `<button>` elements for keyboard support.
|
|
21
|
-
*/
|
|
22
|
-
import { cx, times } from "../utils";
|
|
23
|
-
import Button from "./Button.svelte";
|
|
24
|
-
|
|
25
|
-
type Props = {
|
|
26
|
-
currentPage: number;
|
|
27
|
-
totalPages: number;
|
|
28
|
-
onPageChange: (page: number) => void;
|
|
29
|
-
class?: string;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
let {
|
|
33
|
-
currentPage,
|
|
34
|
-
totalPages,
|
|
35
|
-
onPageChange,
|
|
36
|
-
class: externalClass = "",
|
|
37
|
-
}: Props = $props();
|
|
38
|
-
|
|
39
|
-
const wrapperClass = $derived(
|
|
40
|
-
cx(
|
|
41
|
-
"flex flex-wrap items-center justify-center gap-2 text-xs text-[var(--color-text-muted)] py-0.5 overflow-visible",
|
|
42
|
-
externalClass
|
|
43
|
-
)
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
function nextPage() {
|
|
47
|
-
if (currentPage < totalPages) onPageChange(currentPage + 1);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function prevPage() {
|
|
51
|
-
if (currentPage > 1) onPageChange(currentPage - 1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function getVisiblePages(): number[] {
|
|
55
|
-
const maxVisible = 3;
|
|
56
|
-
if (totalPages <= maxVisible) return times(totalPages, (i) => i + 1);
|
|
57
|
-
|
|
58
|
-
let start = currentPage - 1;
|
|
59
|
-
if (start < 1) start = 1;
|
|
60
|
-
if (start + maxVisible - 1 > totalPages)
|
|
61
|
-
start = totalPages - maxVisible + 1;
|
|
62
|
-
|
|
63
|
-
return times(maxVisible, (i) => start + i);
|
|
64
|
-
}
|
|
65
|
-
</script>
|
|
66
|
-
|
|
67
|
-
<div class={wrapperClass}>
|
|
68
|
-
<span class="pagination-count">Page {currentPage} of {totalPages}</span>
|
|
69
|
-
|
|
70
|
-
<Button
|
|
71
|
-
onClick={prevPage}
|
|
72
|
-
disabled={currentPage === 1}
|
|
73
|
-
sz="xs"
|
|
74
|
-
variant="secondary"
|
|
75
|
-
class="pagination-btn"
|
|
76
|
-
>
|
|
77
|
-
<
|
|
78
|
-
</Button>
|
|
79
|
-
|
|
80
|
-
{#each getVisiblePages() as page (page)}
|
|
81
|
-
<Button
|
|
82
|
-
onClick={() => onPageChange(page)}
|
|
83
|
-
sz="xs"
|
|
84
|
-
variant={currentPage === page ? "primary" : "secondary"}
|
|
85
|
-
aria-current={currentPage === page ? "page" : undefined}
|
|
86
|
-
class="pagination-btn"
|
|
87
|
-
>
|
|
88
|
-
{page}
|
|
89
|
-
</Button>
|
|
90
|
-
{/each}
|
|
91
|
-
|
|
92
|
-
<Button
|
|
93
|
-
onClick={nextPage}
|
|
94
|
-
disabled={currentPage === totalPages}
|
|
95
|
-
sz="xs"
|
|
96
|
-
variant="secondary"
|
|
97
|
-
class="pagination-btn"
|
|
98
|
-
>
|
|
99
|
-
>
|
|
100
|
-
</Button>
|
|
101
|
-
</div>
|
|
102
|
-
|
|
103
|
-
<style>
|
|
104
|
-
@media (max-width: 640px) {
|
|
105
|
-
:global(.pagination-btn) {
|
|
106
|
-
font-size: 10px;
|
|
107
|
-
line-height: 1;
|
|
108
|
-
height: 20px;
|
|
109
|
-
padding: 0 6px;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
@media (max-width: 480px) {
|
|
114
|
-
:global(.pagination-count) {
|
|
115
|
-
display: none;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
</style>
|
|
119
|
-
|
|
1
|
+
<!-- src/lib/Pagination.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* @component Pagination
|
|
5
|
+
* @description Compact pagination component for table or list navigation.
|
|
6
|
+
*
|
|
7
|
+
* @prop currentPage {number} - The active page number (1-based)
|
|
8
|
+
*
|
|
9
|
+
* @prop totalPages {number} - Total number of pages available
|
|
10
|
+
*
|
|
11
|
+
* @prop onPageChange {(page: number) => void} - Fired when a page button is clicked
|
|
12
|
+
*
|
|
13
|
+
* @prop class {string} - Custom classes applied to the pagination wrapper
|
|
14
|
+
* @default ""
|
|
15
|
+
*
|
|
16
|
+
* @note Displays “Page X of Y” and numbered page buttons.
|
|
17
|
+
* @note Prev/next buttons are disabled at the edges.
|
|
18
|
+
* @note Shows up to 3 numbered buttons centered around the current page.
|
|
19
|
+
* @note Uses `aria-current=\"page\"` on the active page for accessibility.
|
|
20
|
+
* @note Buttons are native `<button>` elements for keyboard support.
|
|
21
|
+
*/
|
|
22
|
+
import { cx, times } from "../utils";
|
|
23
|
+
import Button from "./Button.svelte";
|
|
24
|
+
|
|
25
|
+
type Props = {
|
|
26
|
+
currentPage: number;
|
|
27
|
+
totalPages: number;
|
|
28
|
+
onPageChange: (page: number) => void;
|
|
29
|
+
class?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
let {
|
|
33
|
+
currentPage,
|
|
34
|
+
totalPages,
|
|
35
|
+
onPageChange,
|
|
36
|
+
class: externalClass = "",
|
|
37
|
+
}: Props = $props();
|
|
38
|
+
|
|
39
|
+
const wrapperClass = $derived(
|
|
40
|
+
cx(
|
|
41
|
+
"flex flex-wrap items-center justify-center gap-2 text-xs text-[var(--color-text-muted)] py-0.5 overflow-visible",
|
|
42
|
+
externalClass
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
function nextPage() {
|
|
47
|
+
if (currentPage < totalPages) onPageChange(currentPage + 1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function prevPage() {
|
|
51
|
+
if (currentPage > 1) onPageChange(currentPage - 1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getVisiblePages(): number[] {
|
|
55
|
+
const maxVisible = 3;
|
|
56
|
+
if (totalPages <= maxVisible) return times(totalPages, (i) => i + 1);
|
|
57
|
+
|
|
58
|
+
let start = currentPage - 1;
|
|
59
|
+
if (start < 1) start = 1;
|
|
60
|
+
if (start + maxVisible - 1 > totalPages)
|
|
61
|
+
start = totalPages - maxVisible + 1;
|
|
62
|
+
|
|
63
|
+
return times(maxVisible, (i) => start + i);
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<div class={wrapperClass}>
|
|
68
|
+
<span class="pagination-count">Page {currentPage} of {totalPages}</span>
|
|
69
|
+
|
|
70
|
+
<Button
|
|
71
|
+
onClick={prevPage}
|
|
72
|
+
disabled={currentPage === 1}
|
|
73
|
+
sz="xs"
|
|
74
|
+
variant="secondary"
|
|
75
|
+
class="pagination-btn"
|
|
76
|
+
>
|
|
77
|
+
<
|
|
78
|
+
</Button>
|
|
79
|
+
|
|
80
|
+
{#each getVisiblePages() as page (page)}
|
|
81
|
+
<Button
|
|
82
|
+
onClick={() => onPageChange(page)}
|
|
83
|
+
sz="xs"
|
|
84
|
+
variant={currentPage === page ? "primary" : "secondary"}
|
|
85
|
+
aria-current={currentPage === page ? "page" : undefined}
|
|
86
|
+
class="pagination-btn"
|
|
87
|
+
>
|
|
88
|
+
{page}
|
|
89
|
+
</Button>
|
|
90
|
+
{/each}
|
|
91
|
+
|
|
92
|
+
<Button
|
|
93
|
+
onClick={nextPage}
|
|
94
|
+
disabled={currentPage === totalPages}
|
|
95
|
+
sz="xs"
|
|
96
|
+
variant="secondary"
|
|
97
|
+
class="pagination-btn"
|
|
98
|
+
>
|
|
99
|
+
>
|
|
100
|
+
</Button>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<style>
|
|
104
|
+
@media (max-width: 640px) {
|
|
105
|
+
:global(.pagination-btn) {
|
|
106
|
+
font-size: 10px;
|
|
107
|
+
line-height: 1;
|
|
108
|
+
height: 20px;
|
|
109
|
+
padding: 0 6px;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@media (max-width: 480px) {
|
|
114
|
+
:global(.pagination-count) {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
119
|
+
|