svelte-comp 1.3.5 → 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 -101
- package/dist/App.svelte +1046 -1046
- package/dist/Container.svelte +59 -59
- package/dist/app.css +234 -234
- package/dist/app.d.ts +10 -10
- package/dist/lib/Accordion.svelte +155 -155
- package/dist/lib/Badge.svelte +44 -44
- package/dist/lib/Button.svelte +185 -185
- package/dist/lib/Calendar.svelte +384 -384
- package/dist/lib/Card.svelte +103 -103
- package/dist/lib/Carousel.svelte +293 -293
- package/dist/lib/CheckBox.svelte +210 -210
- package/dist/lib/CodeView.svelte +308 -308
- package/dist/lib/ColorPicker.svelte +159 -159
- package/dist/lib/ContextMenu.svelte +328 -328
- 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 -295
- 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 -159
- package/dist/lib/Switch.svelte +168 -168
- package/dist/lib/Table.svelte +299 -299
- package/dist/lib/Tabs.svelte +213 -213
- package/dist/lib/ThemeToggle.svelte +128 -128
- package/dist/lib/TimePicker.svelte +312 -312
- package/dist/lib/TimePickerNew.svelte +634 -634
- package/dist/lib/Toast.svelte +123 -123
- package/dist/lib/Tooltip.svelte +110 -110
- package/dist/lib/Topbar.svelte +112 -112
- package/dist/styles.css +234 -234
- package/package.json +52 -52
package/dist/lib/Button.svelte
CHANGED
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
<!-- src/lib/Button.svelte -->
|
|
2
|
-
<script lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* @component Button
|
|
5
|
-
* @description Versatile button supporting multiple variants, sizes, loading state, and link behavior.
|
|
6
|
-
*
|
|
7
|
-
* @prop disabled {boolean} - Disables interaction
|
|
8
|
-
* @default false
|
|
9
|
-
*
|
|
10
|
-
* @prop children {Snippet} - Content rendered inside the button
|
|
11
|
-
*
|
|
12
|
-
* @prop onClick {(e: MouseEvent) => void} - Click handler
|
|
13
|
-
*
|
|
14
|
-
* @prop sz {SizeKey} - Button size variant
|
|
15
|
-
* @options xs|sm|md|lg|xl
|
|
16
|
-
* @default md
|
|
17
|
-
*
|
|
18
|
-
* @prop variant {ButtonVariant} - Visual style preset
|
|
19
|
-
* @options primary|secondary|pill|danger|success|warning|ghost|link|info
|
|
20
|
-
* @default primary
|
|
21
|
-
*
|
|
22
|
-
* @prop type {"button" | "submit" | "reset"} - Button type attribute
|
|
23
|
-
* @default "button"
|
|
24
|
-
*
|
|
25
|
-
* @prop loaded {boolean} - Shows loading spinner and blocks clicks
|
|
26
|
-
* @default false
|
|
27
|
-
*
|
|
28
|
-
* @prop link {string} - Navigates to a URL when clicked
|
|
29
|
-
*
|
|
30
|
-
* @prop class {string} - Additional classes for the button
|
|
31
|
-
* @default ""
|
|
32
|
-
*
|
|
33
|
-
* @note `disabled` and `loaded` both prevent click events.
|
|
34
|
-
* @note Automatically shows a centered spinner when `loaded` is `true`.
|
|
35
|
-
* @note Link navigation supports `target` and `rel` attributes.
|
|
36
|
-
* @note Accessible with `aria-disabled` and `aria-busy` states.
|
|
37
|
-
* @note The component uses CSS variables for colors, spacing, and transitions.
|
|
38
|
-
*/
|
|
39
|
-
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
40
|
-
import type { Snippet } from "svelte";
|
|
41
|
-
import { type SizeKey, type ButtonVariant, TEXT } from "./types";
|
|
42
|
-
import { cx } from "../utils";
|
|
43
|
-
|
|
44
|
-
type Props = HTMLButtonAttributes & {
|
|
45
|
-
disabled?: boolean;
|
|
46
|
-
children?: Snippet;
|
|
47
|
-
onClick?: (e: MouseEvent) => void;
|
|
48
|
-
sz?: SizeKey;
|
|
49
|
-
variant?: ButtonVariant;
|
|
50
|
-
type?: "button" | "submit" | "reset";
|
|
51
|
-
loaded?: boolean;
|
|
52
|
-
link?: string;
|
|
53
|
-
class?: string;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
let {
|
|
57
|
-
disabled,
|
|
58
|
-
children,
|
|
59
|
-
onClick,
|
|
60
|
-
sz = "md",
|
|
61
|
-
variant = "primary",
|
|
62
|
-
type = "button",
|
|
63
|
-
loaded = false,
|
|
64
|
-
link,
|
|
65
|
-
class: externalClass = "",
|
|
66
|
-
...rest
|
|
67
|
-
}: Props = $props();
|
|
68
|
-
|
|
69
|
-
const base = `
|
|
70
|
-
relative inline-flex items-center justify-center gap-2 rounded-[var(--radius-md)] border font-medium
|
|
71
|
-
transition-all duration-[var(--transition-fast)] ease-[var(--timing-default)] whitespace-nowrap select-none
|
|
72
|
-
focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)]
|
|
73
|
-
[@media(pointer:coarse)]:min-h-11 [@media(pointer:coarse)]:min-w-11
|
|
74
|
-
disabled:opacity-[var(--opacity-disabled)]
|
|
75
|
-
disabled:cursor-not-allowed
|
|
76
|
-
disabled:brightness-100
|
|
77
|
-
disabled:active:scale-100
|
|
78
|
-
disabled:hover:brightness-100
|
|
79
|
-
`;
|
|
80
|
-
|
|
81
|
-
const sizes: Record<SizeKey, string> = {
|
|
82
|
-
xs: "px-2 py-0.5 h-6",
|
|
83
|
-
sm: "px-3 py-1 h-7",
|
|
84
|
-
md: "px-4 py-2 h-8",
|
|
85
|
-
lg: "px-5 py-2.5 h-9",
|
|
86
|
-
xl: "px-6 py-3 h-10",
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const variants: Record<ButtonVariant, string> = {
|
|
90
|
-
primary:
|
|
91
|
-
"bg-[var(--color-bg-primary)] text-[var(--color-text-inverse,#fff)] border-[var(--border-color-primary)] hover:brightness-110 active:scale-95",
|
|
92
|
-
secondary:
|
|
93
|
-
"bg-[var(--color-bg-secondary)] [color:var(--color-text-default)] border-[var(--border-color-default)] hover:bg-[var(--color-bg-hover)] active:scale-95",
|
|
94
|
-
pill: "bg-[var(--color-bg-primary)] text-[var(--color-text-inverse,#fff)] border-[var(--border-color-primary)] rounded-full hover:brightness-110 active:scale-95",
|
|
95
|
-
danger:
|
|
96
|
-
"bg-[var(--color-bg-danger)] text-[var(--color-text-inverse,#fff)] border-[var(--color-bg-danger)] hover:brightness-110 active:scale-95",
|
|
97
|
-
success:
|
|
98
|
-
"bg-[var(--color-bg-success)] text-[var(--color-text-inverse,#fff)] border-[var(--color-bg-success)] hover:brightness-110 active:scale-95",
|
|
99
|
-
warning:
|
|
100
|
-
"bg-[var(--color-bg-warning)] text-[var(--color-text-inverse,#fff)] border-[var(--color-bg-warning)] hover:brightness-110 active:scale-95",
|
|
101
|
-
ghost:
|
|
102
|
-
"bg-transparent [color:var(--color-text-default)] border-transparent hover:bg-[var(--color-bg-hover)] active:bg-[var(--color-bg-active)] active:scale-95",
|
|
103
|
-
link: "bg-transparent underline border-transparent [color:var(--color-text-link)] hover:brightness-110 active:scale-95 transition-transform ",
|
|
104
|
-
info: "bg-[var(--color-bg-secondary)] text-[var(--color-text-inverse,#fff)] border-[var(--border-color-default)] hover:bg-[var(--color-bg-hover)] active:scale-95",
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const buttonClass = $derived(
|
|
108
|
-
cx(base, sizes[sz], TEXT[sz], variants[variant], externalClass)
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const state = $derived(loaded ? "loading" : disabled ? "disabled" : "idle");
|
|
112
|
-
|
|
113
|
-
function handleClick(e: MouseEvent) {
|
|
114
|
-
if (disabled || loaded) {
|
|
115
|
-
e.preventDefault();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (type === "submit" || type === "reset") {
|
|
120
|
-
onClick?.(e);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
onClick?.(e);
|
|
125
|
-
if (!link || e.defaultPrevented) return;
|
|
126
|
-
|
|
127
|
-
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
navigateToLink();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function navigateToLink() {
|
|
135
|
-
if (!link || typeof window === "undefined") return;
|
|
136
|
-
const safeLink = getSafeLink(link);
|
|
137
|
-
if (!safeLink) return;
|
|
138
|
-
|
|
139
|
-
const restAttrs = rest as Record<string, unknown>;
|
|
140
|
-
const target =
|
|
141
|
-
typeof restAttrs.target === "string" ? restAttrs.target : undefined;
|
|
142
|
-
|
|
143
|
-
if (target === "_blank") {
|
|
144
|
-
window.open(safeLink, "_blank", "noopener,noreferrer");
|
|
145
|
-
} else {
|
|
146
|
-
window.location.assign(safeLink);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function getSafeLink(value: string) {
|
|
151
|
-
try {
|
|
152
|
-
const url = new URL(value, window.location.href);
|
|
153
|
-
if (!["http:", "https:", "mailto:", "tel:"].includes(url.protocol)) {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
return value;
|
|
157
|
-
} catch {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
</script>
|
|
162
|
-
|
|
163
|
-
<button
|
|
164
|
-
{type}
|
|
165
|
-
{disabled}
|
|
166
|
-
data-state={state}
|
|
167
|
-
aria-disabled={disabled || loaded || undefined}
|
|
168
|
-
aria-busy={loaded || undefined}
|
|
169
|
-
onclick={handleClick}
|
|
170
|
-
class={buttonClass}
|
|
171
|
-
{...rest}
|
|
172
|
-
>
|
|
173
|
-
<span class={cx({ "opacity-0 pointer-events-none": loaded })}>
|
|
174
|
-
{@render children?.()}
|
|
175
|
-
</span>
|
|
176
|
-
|
|
177
|
-
{#if loaded}
|
|
178
|
-
<span
|
|
179
|
-
class={cx(
|
|
180
|
-
"absolute inset-0 m-auto w-[1em] h-[1em] border-2 [border-color:var(--color-spinner,currentColor)] border-r-transparent rounded-full motion-safe:[animation:spin_0.6s_linear_infinite]"
|
|
181
|
-
)}
|
|
182
|
-
aria-hidden="true"
|
|
183
|
-
></span>
|
|
184
|
-
{/if}
|
|
185
|
-
</button>
|
|
1
|
+
<!-- src/lib/Button.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* @component Button
|
|
5
|
+
* @description Versatile button supporting multiple variants, sizes, loading state, and link behavior.
|
|
6
|
+
*
|
|
7
|
+
* @prop disabled {boolean} - Disables interaction
|
|
8
|
+
* @default false
|
|
9
|
+
*
|
|
10
|
+
* @prop children {Snippet} - Content rendered inside the button
|
|
11
|
+
*
|
|
12
|
+
* @prop onClick {(e: MouseEvent) => void} - Click handler
|
|
13
|
+
*
|
|
14
|
+
* @prop sz {SizeKey} - Button size variant
|
|
15
|
+
* @options xs|sm|md|lg|xl
|
|
16
|
+
* @default md
|
|
17
|
+
*
|
|
18
|
+
* @prop variant {ButtonVariant} - Visual style preset
|
|
19
|
+
* @options primary|secondary|pill|danger|success|warning|ghost|link|info
|
|
20
|
+
* @default primary
|
|
21
|
+
*
|
|
22
|
+
* @prop type {"button" | "submit" | "reset"} - Button type attribute
|
|
23
|
+
* @default "button"
|
|
24
|
+
*
|
|
25
|
+
* @prop loaded {boolean} - Shows loading spinner and blocks clicks
|
|
26
|
+
* @default false
|
|
27
|
+
*
|
|
28
|
+
* @prop link {string} - Navigates to a URL when clicked
|
|
29
|
+
*
|
|
30
|
+
* @prop class {string} - Additional classes for the button
|
|
31
|
+
* @default ""
|
|
32
|
+
*
|
|
33
|
+
* @note `disabled` and `loaded` both prevent click events.
|
|
34
|
+
* @note Automatically shows a centered spinner when `loaded` is `true`.
|
|
35
|
+
* @note Link navigation supports `target` and `rel` attributes.
|
|
36
|
+
* @note Accessible with `aria-disabled` and `aria-busy` states.
|
|
37
|
+
* @note The component uses CSS variables for colors, spacing, and transitions.
|
|
38
|
+
*/
|
|
39
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
40
|
+
import type { Snippet } from "svelte";
|
|
41
|
+
import { type SizeKey, type ButtonVariant, TEXT } from "./types";
|
|
42
|
+
import { cx } from "../utils";
|
|
43
|
+
|
|
44
|
+
type Props = HTMLButtonAttributes & {
|
|
45
|
+
disabled?: boolean;
|
|
46
|
+
children?: Snippet;
|
|
47
|
+
onClick?: (e: MouseEvent) => void;
|
|
48
|
+
sz?: SizeKey;
|
|
49
|
+
variant?: ButtonVariant;
|
|
50
|
+
type?: "button" | "submit" | "reset";
|
|
51
|
+
loaded?: boolean;
|
|
52
|
+
link?: string;
|
|
53
|
+
class?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
let {
|
|
57
|
+
disabled,
|
|
58
|
+
children,
|
|
59
|
+
onClick,
|
|
60
|
+
sz = "md",
|
|
61
|
+
variant = "primary",
|
|
62
|
+
type = "button",
|
|
63
|
+
loaded = false,
|
|
64
|
+
link,
|
|
65
|
+
class: externalClass = "",
|
|
66
|
+
...rest
|
|
67
|
+
}: Props = $props();
|
|
68
|
+
|
|
69
|
+
const base = `
|
|
70
|
+
relative inline-flex items-center justify-center gap-2 rounded-[var(--radius-md)] border font-medium
|
|
71
|
+
transition-all duration-[var(--transition-fast)] ease-[var(--timing-default)] whitespace-nowrap select-none
|
|
72
|
+
focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)]
|
|
73
|
+
[@media(pointer:coarse)]:min-h-11 [@media(pointer:coarse)]:min-w-11
|
|
74
|
+
disabled:opacity-[var(--opacity-disabled)]
|
|
75
|
+
disabled:cursor-not-allowed
|
|
76
|
+
disabled:brightness-100
|
|
77
|
+
disabled:active:scale-100
|
|
78
|
+
disabled:hover:brightness-100
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
const sizes: Record<SizeKey, string> = {
|
|
82
|
+
xs: "px-2 py-0.5 h-6",
|
|
83
|
+
sm: "px-3 py-1 h-7",
|
|
84
|
+
md: "px-4 py-2 h-8",
|
|
85
|
+
lg: "px-5 py-2.5 h-9",
|
|
86
|
+
xl: "px-6 py-3 h-10",
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const variants: Record<ButtonVariant, string> = {
|
|
90
|
+
primary:
|
|
91
|
+
"bg-[var(--color-bg-primary)] text-[var(--color-text-inverse,#fff)] border-[var(--border-color-primary)] hover:brightness-110 active:scale-95",
|
|
92
|
+
secondary:
|
|
93
|
+
"bg-[var(--color-bg-secondary)] [color:var(--color-text-default)] border-[var(--border-color-default)] hover:bg-[var(--color-bg-hover)] active:scale-95",
|
|
94
|
+
pill: "bg-[var(--color-bg-primary)] text-[var(--color-text-inverse,#fff)] border-[var(--border-color-primary)] rounded-full hover:brightness-110 active:scale-95",
|
|
95
|
+
danger:
|
|
96
|
+
"bg-[var(--color-bg-danger)] text-[var(--color-text-inverse,#fff)] border-[var(--color-bg-danger)] hover:brightness-110 active:scale-95",
|
|
97
|
+
success:
|
|
98
|
+
"bg-[var(--color-bg-success)] text-[var(--color-text-inverse,#fff)] border-[var(--color-bg-success)] hover:brightness-110 active:scale-95",
|
|
99
|
+
warning:
|
|
100
|
+
"bg-[var(--color-bg-warning)] text-[var(--color-text-inverse,#fff)] border-[var(--color-bg-warning)] hover:brightness-110 active:scale-95",
|
|
101
|
+
ghost:
|
|
102
|
+
"bg-transparent [color:var(--color-text-default)] border-transparent hover:bg-[var(--color-bg-hover)] active:bg-[var(--color-bg-active)] active:scale-95",
|
|
103
|
+
link: "bg-transparent underline border-transparent [color:var(--color-text-link)] hover:brightness-110 active:scale-95 transition-transform ",
|
|
104
|
+
info: "bg-[var(--color-bg-secondary)] text-[var(--color-text-inverse,#fff)] border-[var(--border-color-default)] hover:bg-[var(--color-bg-hover)] active:scale-95",
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const buttonClass = $derived(
|
|
108
|
+
cx(base, sizes[sz], TEXT[sz], variants[variant], externalClass)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const state = $derived(loaded ? "loading" : disabled ? "disabled" : "idle");
|
|
112
|
+
|
|
113
|
+
function handleClick(e: MouseEvent) {
|
|
114
|
+
if (disabled || loaded) {
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (type === "submit" || type === "reset") {
|
|
120
|
+
onClick?.(e);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
onClick?.(e);
|
|
125
|
+
if (!link || e.defaultPrevented) return;
|
|
126
|
+
|
|
127
|
+
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
navigateToLink();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function navigateToLink() {
|
|
135
|
+
if (!link || typeof window === "undefined") return;
|
|
136
|
+
const safeLink = getSafeLink(link);
|
|
137
|
+
if (!safeLink) return;
|
|
138
|
+
|
|
139
|
+
const restAttrs = rest as Record<string, unknown>;
|
|
140
|
+
const target =
|
|
141
|
+
typeof restAttrs.target === "string" ? restAttrs.target : undefined;
|
|
142
|
+
|
|
143
|
+
if (target === "_blank") {
|
|
144
|
+
window.open(safeLink, "_blank", "noopener,noreferrer");
|
|
145
|
+
} else {
|
|
146
|
+
window.location.assign(safeLink);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getSafeLink(value: string) {
|
|
151
|
+
try {
|
|
152
|
+
const url = new URL(value, window.location.href);
|
|
153
|
+
if (!["http:", "https:", "mailto:", "tel:"].includes(url.protocol)) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
</script>
|
|
162
|
+
|
|
163
|
+
<button
|
|
164
|
+
{type}
|
|
165
|
+
{disabled}
|
|
166
|
+
data-state={state}
|
|
167
|
+
aria-disabled={disabled || loaded || undefined}
|
|
168
|
+
aria-busy={loaded || undefined}
|
|
169
|
+
onclick={handleClick}
|
|
170
|
+
class={buttonClass}
|
|
171
|
+
{...rest}
|
|
172
|
+
>
|
|
173
|
+
<span class={cx({ "opacity-0 pointer-events-none": loaded })}>
|
|
174
|
+
{@render children?.()}
|
|
175
|
+
</span>
|
|
176
|
+
|
|
177
|
+
{#if loaded}
|
|
178
|
+
<span
|
|
179
|
+
class={cx(
|
|
180
|
+
"absolute inset-0 m-auto w-[1em] h-[1em] border-2 [border-color:var(--color-spinner,currentColor)] border-r-transparent rounded-full motion-safe:[animation:spin_0.6s_linear_infinite]"
|
|
181
|
+
)}
|
|
182
|
+
aria-hidden="true"
|
|
183
|
+
></span>
|
|
184
|
+
{/if}
|
|
185
|
+
</button>
|