svelte-comp 1.2.2 → 1.2.7
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/README.md +1 -1
- package/dist/lang.d.ts +3 -3
- package/dist/lang.js +3 -3
- package/dist/lib/Calendar.svelte +377 -0
- package/dist/lib/Calendar.svelte.d.ts +43 -0
- package/dist/lib/CodeView.svelte +29 -29
- package/dist/lib/ColorPicker.svelte +6 -8
- package/dist/lib/DatePicker.svelte +161 -85
- package/dist/lib/Dialog.svelte +7 -9
- package/dist/lib/FilePicker.svelte +71 -70
- package/dist/lib/FilePicker.svelte.d.ts +1 -1
- package/dist/lib/Pagination.svelte +22 -1
- package/dist/lib/PrimaryColorSelect.svelte +6 -8
- package/dist/lib/SearchInput.svelte +14 -7
- package/dist/lib/SearchInput.svelte.d.ts +3 -2
- package/dist/lib/ThemeToggle.svelte +1 -1
- package/dist/lib/TimePicker.svelte +93 -58
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/lang-context.d.ts +10 -0
- package/dist/lib/lang-context.js +5 -0
- package/dist/lib/lang.d.ts +9 -0
- package/dist/lib/lang.js +9 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ npm i prismjs @types/prismjs
|
|
|
53
53
|
|
|
54
54
|
## 📁 Components included
|
|
55
55
|
|
|
56
|
-
Accordion • Button • Card • Carousel • CheckBox • CodeView • ColorPicker •
|
|
56
|
+
Accordion • Button • Calendar • Card • Carousel • CheckBox • CodeView • ColorPicker •
|
|
57
57
|
DatePicker • Dialog • Field • FilePicker • Form • Hamburger • Menu •
|
|
58
58
|
PaginatedCard • Pagination • PrimaryColorSelect • ProgressBar • ProgressCircle •
|
|
59
59
|
Radio • SearchInput • Select • Slider • Splitter • Switch • Tabs • Table • ThemeToggle •
|
package/dist/lang.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const TEXTS: {
|
|
2
2
|
readonly en: {
|
|
3
3
|
readonly app: {
|
|
4
|
-
readonly version: "v1.2.
|
|
4
|
+
readonly version: "v1.2.6";
|
|
5
5
|
readonly title: "Svelte 5 UI Components";
|
|
6
6
|
readonly footer: "© 2025 MaestroFusion360";
|
|
7
7
|
readonly authorUrl: "https://github.com/MaestroFusion360/svelte-comp";
|
|
@@ -360,7 +360,7 @@ export declare const TEXTS: {
|
|
|
360
360
|
};
|
|
361
361
|
readonly ru: {
|
|
362
362
|
readonly app: {
|
|
363
|
-
readonly version: "v1.2.
|
|
363
|
+
readonly version: "v1.2.6";
|
|
364
364
|
readonly title: "Svelte 5 UI Components";
|
|
365
365
|
readonly footer: "© 2025 MaestroFusion360";
|
|
366
366
|
readonly authorUrl: "https://github.com/MaestroFusion360/svelte-comp";
|
|
@@ -720,7 +720,7 @@ export declare const TEXTS: {
|
|
|
720
720
|
};
|
|
721
721
|
readonly es: {
|
|
722
722
|
readonly app: {
|
|
723
|
-
readonly version: "v1.2.
|
|
723
|
+
readonly version: "v1.2.6";
|
|
724
724
|
readonly title: "Svelte 5 UI Components";
|
|
725
725
|
readonly footer: "© 2025 MaestroFusion360";
|
|
726
726
|
readonly authorUrl: "https://github.com/MaestroFusion360/svelte-comp";
|
package/dist/lang.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var enTexts = {
|
|
2
2
|
app: {
|
|
3
|
-
version: "v1.2.
|
|
3
|
+
version: "v1.2.6",
|
|
4
4
|
title: "Svelte 5 UI Components",
|
|
5
5
|
footer: "© 2025 MaestroFusion360",
|
|
6
6
|
authorUrl: "https://github.com/MaestroFusion360/svelte-comp",
|
|
@@ -361,7 +361,7 @@ var enTexts = {
|
|
|
361
361
|
};
|
|
362
362
|
var ruTexts = {
|
|
363
363
|
app: {
|
|
364
|
-
version: "v1.2.
|
|
364
|
+
version: "v1.2.6",
|
|
365
365
|
title: "Svelte 5 UI Components",
|
|
366
366
|
footer: "© 2025 MaestroFusion360",
|
|
367
367
|
authorUrl: "https://github.com/MaestroFusion360/svelte-comp",
|
|
@@ -723,7 +723,7 @@ var ruTexts = {
|
|
|
723
723
|
};
|
|
724
724
|
var esTexts = {
|
|
725
725
|
app: {
|
|
726
|
-
version: "v1.2.
|
|
726
|
+
version: "v1.2.6",
|
|
727
727
|
title: "Svelte 5 UI Components",
|
|
728
728
|
footer: "© 2025 MaestroFusion360",
|
|
729
729
|
authorUrl: "https://github.com/MaestroFusion360/svelte-comp",
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
<!-- src/lib/Calendar.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* @component Calendar
|
|
5
|
+
* @description Monthly calendar grid with navigation and date selection.
|
|
6
|
+
*
|
|
7
|
+
* @prop value {string | null} - Selected date in ISO `YYYY-MM-DD` (bindable)
|
|
8
|
+
* @default null
|
|
9
|
+
*
|
|
10
|
+
* @prop min {string} - Minimum selectable date (ISO `YYYY-MM-DD`)
|
|
11
|
+
*
|
|
12
|
+
* @prop max {string} - Maximum selectable date (ISO `YYYY-MM-DD`)
|
|
13
|
+
*
|
|
14
|
+
* @prop locale {string} - Locale used for month/day labels
|
|
15
|
+
* @default "en-US"
|
|
16
|
+
*
|
|
17
|
+
* @prop weekStartsOn {0|1|2|3|4|5|6} - First day of week (0=Sun ... 6=Sat)
|
|
18
|
+
* @default 1
|
|
19
|
+
*
|
|
20
|
+
* @prop showOutsideDays {boolean} - Render days from adjacent months
|
|
21
|
+
* @default true
|
|
22
|
+
*
|
|
23
|
+
* @prop disabled {boolean} - Disables selection and navigation
|
|
24
|
+
* @default false
|
|
25
|
+
*
|
|
26
|
+
* @prop onChange {(value: string | null) => void} - Fired on date selection
|
|
27
|
+
*
|
|
28
|
+
* @prop class {string} - Additional classes for the root wrapper
|
|
29
|
+
* @default ""
|
|
30
|
+
*/
|
|
31
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
32
|
+
import { cx } from "../utils";
|
|
33
|
+
import { TEXT } from "./types";
|
|
34
|
+
|
|
35
|
+
type Props = HTMLAttributes<HTMLDivElement> & {
|
|
36
|
+
value?: string | null;
|
|
37
|
+
min?: string;
|
|
38
|
+
max?: string;
|
|
39
|
+
locale?: string;
|
|
40
|
+
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
41
|
+
showOutsideDays?: boolean;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
onChange?: (value: string | null) => void;
|
|
44
|
+
class?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
value = $bindable<string | null>(null),
|
|
49
|
+
min = "1926-01-01",
|
|
50
|
+
max,
|
|
51
|
+
locale = "en-US",
|
|
52
|
+
weekStartsOn = 1,
|
|
53
|
+
showOutsideDays = true,
|
|
54
|
+
disabled = false,
|
|
55
|
+
onChange,
|
|
56
|
+
class: externalClass = "",
|
|
57
|
+
...rest
|
|
58
|
+
}: Props = $props();
|
|
59
|
+
|
|
60
|
+
type ViewMode = "days" | "months" | "years";
|
|
61
|
+
|
|
62
|
+
function startOfDay(date: Date) {
|
|
63
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parseIso(value: string | null | undefined): Date | null {
|
|
67
|
+
if (!value) return null;
|
|
68
|
+
const [y, m, d] = value.split("-").map((part) => Number(part));
|
|
69
|
+
if (!y || !m || !d) return null;
|
|
70
|
+
const date = new Date(y, m - 1, d);
|
|
71
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function toIso(date: Date) {
|
|
75
|
+
const y = date.getFullYear();
|
|
76
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
77
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
78
|
+
return `${y}-${m}-${d}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isSameDay(a: Date, b: Date) {
|
|
82
|
+
return (
|
|
83
|
+
a.getFullYear() === b.getFullYear() &&
|
|
84
|
+
a.getMonth() === b.getMonth() &&
|
|
85
|
+
a.getDate() === b.getDate()
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let viewDate = $state(startOfDay(parseIso(value) ?? new Date()));
|
|
90
|
+
let viewMode = $state<ViewMode>("days");
|
|
91
|
+
|
|
92
|
+
const selectedDate = $derived(parseIso(value));
|
|
93
|
+
const minDate = $derived(parseIso(min));
|
|
94
|
+
const maxDate = $derived(parseIso(max));
|
|
95
|
+
const today = $derived(startOfDay(new Date()));
|
|
96
|
+
|
|
97
|
+
let lastValue = $state<string | null>(value ?? null);
|
|
98
|
+
|
|
99
|
+
$effect(() => {
|
|
100
|
+
if (value === lastValue) return;
|
|
101
|
+
lastValue = value;
|
|
102
|
+
const selected = parseIso(value);
|
|
103
|
+
if (!selected) return;
|
|
104
|
+
viewDate = new Date(selected.getFullYear(), selected.getMonth(), 1);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const monthLabel = $derived(
|
|
108
|
+
new Intl.DateTimeFormat(locale, { month: "long" }).format(viewDate)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const yearLabel = $derived(String(viewDate.getFullYear()));
|
|
112
|
+
|
|
113
|
+
const weekdayLabels = $derived.by(() => {
|
|
114
|
+
const formatter = new Intl.DateTimeFormat(locale, { weekday: "short" });
|
|
115
|
+
const labels: string[] = [];
|
|
116
|
+
const base = new Date(2023, 0, 1);
|
|
117
|
+
for (let i = 0; i < 7; i += 1) {
|
|
118
|
+
const offset = (weekStartsOn + i) % 7;
|
|
119
|
+
const date = new Date(base);
|
|
120
|
+
date.setDate(base.getDate() + offset);
|
|
121
|
+
labels.push(formatter.format(date));
|
|
122
|
+
}
|
|
123
|
+
return labels;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const dayLabelFormatter = $derived.by(
|
|
127
|
+
() =>
|
|
128
|
+
new Intl.DateTimeFormat(locale, {
|
|
129
|
+
weekday: "long",
|
|
130
|
+
year: "numeric",
|
|
131
|
+
month: "long",
|
|
132
|
+
day: "numeric",
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const monthOptions = $derived.by(() => {
|
|
137
|
+
const formatter = new Intl.DateTimeFormat(locale, { month: "short" });
|
|
138
|
+
return Array.from({ length: 12 }, (_, idx) => ({
|
|
139
|
+
index: idx,
|
|
140
|
+
label: formatter.format(new Date(2024, idx, 1)),
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const yearRangeStart = $derived(() => {
|
|
145
|
+
const year = viewDate.getFullYear();
|
|
146
|
+
return year - (year % 12);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const yearOptions = $derived.by(() =>
|
|
150
|
+
Array.from({ length: 12 }, (_, idx) => yearRangeStart() + idx)
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
type DayCell = {
|
|
154
|
+
date: Date;
|
|
155
|
+
iso: string;
|
|
156
|
+
inMonth: boolean;
|
|
157
|
+
isToday: boolean;
|
|
158
|
+
isSelected: boolean;
|
|
159
|
+
isDisabled: boolean;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const days = $derived.by(() => {
|
|
163
|
+
const year = viewDate.getFullYear();
|
|
164
|
+
const month = viewDate.getMonth();
|
|
165
|
+
const first = new Date(year, month, 1);
|
|
166
|
+
const startOffset = (first.getDay() - weekStartsOn + 7) % 7;
|
|
167
|
+
const start = new Date(year, month, 1 - startOffset);
|
|
168
|
+
const cells: DayCell[] = [];
|
|
169
|
+
for (let i = 0; i < 42; i += 1) {
|
|
170
|
+
const date = new Date(start);
|
|
171
|
+
date.setDate(start.getDate() + i);
|
|
172
|
+
const day = startOfDay(date);
|
|
173
|
+
const inMonth = day.getMonth() === month;
|
|
174
|
+
const isToday = isSameDay(day, today);
|
|
175
|
+
const isSelected = selectedDate ? isSameDay(day, selectedDate) : false;
|
|
176
|
+
const beforeMin = minDate ? day < startOfDay(minDate) : false;
|
|
177
|
+
const afterMax = maxDate ? day > startOfDay(maxDate) : false;
|
|
178
|
+
const isDisabled =
|
|
179
|
+
disabled || beforeMin || afterMax || (!showOutsideDays && !inMonth);
|
|
180
|
+
cells.push({
|
|
181
|
+
date: day,
|
|
182
|
+
iso: toIso(day),
|
|
183
|
+
inMonth,
|
|
184
|
+
isToday,
|
|
185
|
+
isSelected,
|
|
186
|
+
isDisabled,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return cells;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
function shift(delta: number) {
|
|
193
|
+
if (disabled) return;
|
|
194
|
+
if (viewMode === "days") {
|
|
195
|
+
viewDate = new Date(
|
|
196
|
+
viewDate.getFullYear(),
|
|
197
|
+
viewDate.getMonth() + delta,
|
|
198
|
+
1
|
|
199
|
+
);
|
|
200
|
+
} else if (viewMode === "months") {
|
|
201
|
+
viewDate = new Date(viewDate.getFullYear() + delta, viewDate.getMonth(), 1);
|
|
202
|
+
} else {
|
|
203
|
+
viewDate = new Date(
|
|
204
|
+
viewDate.getFullYear() + delta * 12,
|
|
205
|
+
viewDate.getMonth(),
|
|
206
|
+
1
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function selectDay(cell: DayCell) {
|
|
212
|
+
if (cell.isDisabled) return;
|
|
213
|
+
value = cell.iso;
|
|
214
|
+
onChange?.(cell.iso);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function selectMonth(index: number) {
|
|
218
|
+
if (disabled) return;
|
|
219
|
+
viewDate = new Date(viewDate.getFullYear(), index, 1);
|
|
220
|
+
viewMode = "days";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function selectYear(year: number) {
|
|
224
|
+
if (disabled) return;
|
|
225
|
+
viewDate = new Date(year, viewDate.getMonth(), 1);
|
|
226
|
+
viewMode = "months";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const wrapperClass = $derived(cx("w-full", externalClass));
|
|
230
|
+
|
|
231
|
+
const headerButtonBase =
|
|
232
|
+
"px-1 py-0.5 rounded-[var(--radius-sm)] transition-colors text-[var(--color-text-default)] hover:bg-[var(--color-bg-hover)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] disabled:opacity-[var(--opacity-disabled)] disabled:cursor-not-allowed text-[var(--text-xs)]";
|
|
233
|
+
|
|
234
|
+
const arrowButtonBase =
|
|
235
|
+
"inline-flex items-center justify-center h-3 w-3 rounded-[var(--radius-sm)] text-[var(--color-text-default)] hover:bg-[var(--color-bg-hover)] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] disabled:opacity-[var(--opacity-disabled)] disabled:cursor-not-allowed";
|
|
236
|
+
|
|
237
|
+
const dayButtonBase =
|
|
238
|
+
"rounded-full flex items-center justify-center transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] text-[var(--text-xs)]";
|
|
239
|
+
</script>
|
|
240
|
+
|
|
241
|
+
<div class={wrapperClass} style="width: 240px; max-width: 100%;" {...rest}>
|
|
242
|
+
<div class="flex items-center justify-between mb-[var(--spacing-sm)]">
|
|
243
|
+
<div class="flex items-center gap-1">
|
|
244
|
+
<button
|
|
245
|
+
type="button"
|
|
246
|
+
class={cx(headerButtonBase, TEXT.sm)}
|
|
247
|
+
onclick={() => (viewMode = viewMode === "months" ? "days" : "months")}
|
|
248
|
+
disabled={disabled}
|
|
249
|
+
>
|
|
250
|
+
{monthLabel}
|
|
251
|
+
</button>
|
|
252
|
+
<button
|
|
253
|
+
type="button"
|
|
254
|
+
class={cx(headerButtonBase, TEXT.sm)}
|
|
255
|
+
onclick={() => (viewMode = viewMode === "years" ? "days" : "years")}
|
|
256
|
+
disabled={disabled}
|
|
257
|
+
>
|
|
258
|
+
{yearLabel}
|
|
259
|
+
</button>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<div class="flex items-center gap-1">
|
|
263
|
+
<button
|
|
264
|
+
type="button"
|
|
265
|
+
class={arrowButtonBase}
|
|
266
|
+
aria-label="Previous"
|
|
267
|
+
onclick={() => shift(-1)}
|
|
268
|
+
disabled={disabled}
|
|
269
|
+
>
|
|
270
|
+
▲
|
|
271
|
+
</button>
|
|
272
|
+
<button
|
|
273
|
+
type="button"
|
|
274
|
+
class={arrowButtonBase}
|
|
275
|
+
aria-label="Next"
|
|
276
|
+
onclick={() => shift(1)}
|
|
277
|
+
disabled={disabled}
|
|
278
|
+
>
|
|
279
|
+
▼
|
|
280
|
+
</button>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
{#if viewMode === "days"}
|
|
285
|
+
<div
|
|
286
|
+
class={cx("text-center", TEXT.xs)}
|
|
287
|
+
style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--spacing-xs);"
|
|
288
|
+
>
|
|
289
|
+
{#each weekdayLabels as label, i (i)}
|
|
290
|
+
<div class="py-[var(--spacing-xs)] [color:var(--color-text-muted)]">
|
|
291
|
+
{label}
|
|
292
|
+
</div>
|
|
293
|
+
{/each}
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
<div
|
|
297
|
+
style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--spacing-xs); margin-top: var(--spacing-xs);"
|
|
298
|
+
>
|
|
299
|
+
{#each days as cell (cell.iso)}
|
|
300
|
+
<button
|
|
301
|
+
type="button"
|
|
302
|
+
class={cx(
|
|
303
|
+
dayButtonBase,
|
|
304
|
+
cell.isSelected &&
|
|
305
|
+
"bg-[var(--color-bg-primary)] text-white hover:brightness-110",
|
|
306
|
+
!cell.isSelected && "text-[var(--color-text-default)]",
|
|
307
|
+
cell.isToday &&
|
|
308
|
+
!cell.isSelected &&
|
|
309
|
+
"border border-[var(--border-color-primary)]",
|
|
310
|
+
!cell.inMonth && "text-[var(--color-text-muted)]",
|
|
311
|
+
cell.isDisabled &&
|
|
312
|
+
"opacity-[var(--opacity-disabled)] cursor-not-allowed",
|
|
313
|
+
!cell.isDisabled && "hover:bg-[var(--color-bg-hover)]"
|
|
314
|
+
)}
|
|
315
|
+
style="width: 22px; height: 22px; justify-self: center;"
|
|
316
|
+
aria-pressed={cell.isSelected}
|
|
317
|
+
aria-current={cell.isToday ? "date" : undefined}
|
|
318
|
+
aria-label={dayLabelFormatter.format(cell.date)}
|
|
319
|
+
disabled={cell.isDisabled}
|
|
320
|
+
onclick={() => selectDay(cell)}
|
|
321
|
+
>
|
|
322
|
+
{#if cell.inMonth || showOutsideDays}
|
|
323
|
+
{cell.date.getDate()}
|
|
324
|
+
{/if}
|
|
325
|
+
</button>
|
|
326
|
+
{/each}
|
|
327
|
+
</div>
|
|
328
|
+
{:else if viewMode === "months"}
|
|
329
|
+
<div
|
|
330
|
+
style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--spacing-sm);"
|
|
331
|
+
>
|
|
332
|
+
{#each monthOptions as month (month.index)}
|
|
333
|
+
<button
|
|
334
|
+
type="button"
|
|
335
|
+
class={cx(
|
|
336
|
+
"rounded-[var(--radius-md)] text-center transition-colors",
|
|
337
|
+
TEXT.xs,
|
|
338
|
+
month.index === viewDate.getMonth() &&
|
|
339
|
+
"bg-[var(--color-bg-primary)] text-white",
|
|
340
|
+
month.index !== viewDate.getMonth() &&
|
|
341
|
+
"text-[var(--color-text-default)] hover:bg-[var(--color-bg-hover)]",
|
|
342
|
+
disabled && "opacity-[var(--opacity-disabled)] cursor-not-allowed"
|
|
343
|
+
)}
|
|
344
|
+
style="height: 22px; width: 22px;"
|
|
345
|
+
onclick={() => selectMonth(month.index)}
|
|
346
|
+
disabled={disabled}
|
|
347
|
+
>
|
|
348
|
+
{month.label}
|
|
349
|
+
</button>
|
|
350
|
+
{/each}
|
|
351
|
+
</div>
|
|
352
|
+
{:else}
|
|
353
|
+
<div
|
|
354
|
+
style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--spacing-sm);"
|
|
355
|
+
>
|
|
356
|
+
{#each yearOptions as year (year)}
|
|
357
|
+
<button
|
|
358
|
+
type="button"
|
|
359
|
+
class={cx(
|
|
360
|
+
"rounded-[var(--radius-md)] text-center transition-colors",
|
|
361
|
+
TEXT.xs,
|
|
362
|
+
year === viewDate.getFullYear() &&
|
|
363
|
+
"bg-[var(--color-bg-primary)] text-white",
|
|
364
|
+
year !== viewDate.getFullYear() &&
|
|
365
|
+
"text-[var(--color-text-default)] hover:bg-[var(--color-bg-hover)]",
|
|
366
|
+
disabled && "opacity-[var(--opacity-disabled)] cursor-not-allowed"
|
|
367
|
+
)}
|
|
368
|
+
style="height: 22px; width: 22px;"
|
|
369
|
+
onclick={() => selectYear(year)}
|
|
370
|
+
disabled={disabled}
|
|
371
|
+
>
|
|
372
|
+
{year}
|
|
373
|
+
</button>
|
|
374
|
+
{/each}
|
|
375
|
+
</div>
|
|
376
|
+
{/if}
|
|
377
|
+
</div>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @component Calendar
|
|
3
|
+
* @description Monthly calendar grid with navigation and date selection.
|
|
4
|
+
*
|
|
5
|
+
* @prop value {string | null} - Selected date in ISO `YYYY-MM-DD` (bindable)
|
|
6
|
+
* @default null
|
|
7
|
+
*
|
|
8
|
+
* @prop min {string} - Minimum selectable date (ISO `YYYY-MM-DD`)
|
|
9
|
+
*
|
|
10
|
+
* @prop max {string} - Maximum selectable date (ISO `YYYY-MM-DD`)
|
|
11
|
+
*
|
|
12
|
+
* @prop locale {string} - Locale used for month/day labels
|
|
13
|
+
* @default "en-US"
|
|
14
|
+
*
|
|
15
|
+
* @prop weekStartsOn {0|1|2|3|4|5|6} - First day of week (0=Sun ... 6=Sat)
|
|
16
|
+
* @default 1
|
|
17
|
+
*
|
|
18
|
+
* @prop showOutsideDays {boolean} - Render days from adjacent months
|
|
19
|
+
* @default true
|
|
20
|
+
*
|
|
21
|
+
* @prop disabled {boolean} - Disables selection and navigation
|
|
22
|
+
* @default false
|
|
23
|
+
*
|
|
24
|
+
* @prop onChange {(value: string | null) => void} - Fired on date selection
|
|
25
|
+
*
|
|
26
|
+
* @prop class {string} - Additional classes for the root wrapper
|
|
27
|
+
* @default ""
|
|
28
|
+
*/
|
|
29
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
30
|
+
type Props = HTMLAttributes<HTMLDivElement> & {
|
|
31
|
+
value?: string | null;
|
|
32
|
+
min?: string;
|
|
33
|
+
max?: string;
|
|
34
|
+
locale?: string;
|
|
35
|
+
weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
36
|
+
showOutsideDays?: boolean;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
onChange?: (value: string | null) => void;
|
|
39
|
+
class?: string;
|
|
40
|
+
};
|
|
41
|
+
declare const Calendar: import("svelte").Component<Props, {}, "value">;
|
|
42
|
+
type Calendar = ReturnType<typeof Calendar>;
|
|
43
|
+
export default Calendar;
|
package/dist/lib/CodeView.svelte
CHANGED
|
@@ -146,12 +146,12 @@
|
|
|
146
146
|
</script>
|
|
147
147
|
|
|
148
148
|
<div
|
|
149
|
-
class={cx(
|
|
150
|
-
"cv-root w-full h-full min-h-0 flex flex-col border border-[var(--border-color-default)] bg-[var(--color-bg-surface)]",
|
|
151
|
-
"text-[var(--color-text-default)]",
|
|
152
|
-
externalClass
|
|
153
|
-
)}
|
|
154
|
-
>
|
|
149
|
+
class={cx(
|
|
150
|
+
"cv-root w-full h-full min-h-0 flex flex-col border border-[var(--border-color-default)] bg-[var(--color-bg-surface)]",
|
|
151
|
+
"text-[var(--color-text-default)]",
|
|
152
|
+
externalClass
|
|
153
|
+
)}
|
|
154
|
+
>
|
|
155
155
|
{#if title}
|
|
156
156
|
<div
|
|
157
157
|
class={cx(
|
|
@@ -176,29 +176,29 @@
|
|
|
176
176
|
</div>
|
|
177
177
|
{/if}
|
|
178
178
|
|
|
179
|
-
<div
|
|
180
|
-
class={cx(
|
|
181
|
-
"cv-body flex flex-1 min-h-0 font-mono",
|
|
182
|
-
TEXT[sz],
|
|
183
|
-
LINE_HEIGHT[sz]
|
|
184
|
-
)}
|
|
185
|
-
>
|
|
186
|
-
{#if showLineNumbers}
|
|
187
|
-
<div
|
|
188
|
-
bind:this={gutterEl}
|
|
189
|
-
class={cx(
|
|
190
|
-
"select-none px-3 py-[12px] border-r border-[var(--border-color-default)]",
|
|
191
|
-
"text-[var(--color-text-muted)] text-right overflow-hidden",
|
|
192
|
-
"cv-gutter bg-[var(--color-bg-surface)] tabular-nums h-full min-h-0"
|
|
193
|
-
)}
|
|
194
|
-
>
|
|
195
|
-
{#each lines as _, i (i)}
|
|
196
|
-
<div class={LINE_HEIGHT[sz]}>{i + 1}</div>
|
|
197
|
-
{/each}
|
|
198
|
-
</div>
|
|
199
|
-
{/if}
|
|
200
|
-
|
|
201
|
-
<div class="cv-editor relative flex-1 min-h-0">
|
|
179
|
+
<div
|
|
180
|
+
class={cx(
|
|
181
|
+
"cv-body flex flex-1 min-h-0 font-mono",
|
|
182
|
+
TEXT[sz],
|
|
183
|
+
LINE_HEIGHT[sz]
|
|
184
|
+
)}
|
|
185
|
+
>
|
|
186
|
+
{#if showLineNumbers}
|
|
187
|
+
<div
|
|
188
|
+
bind:this={gutterEl}
|
|
189
|
+
class={cx(
|
|
190
|
+
"select-none px-3 py-[12px] border-r border-[var(--border-color-default)]",
|
|
191
|
+
"text-[var(--color-text-muted)] text-right overflow-hidden",
|
|
192
|
+
"cv-gutter bg-[var(--color-bg-surface)] tabular-nums h-full min-h-0"
|
|
193
|
+
)}
|
|
194
|
+
>
|
|
195
|
+
{#each lines as _, i (i)}
|
|
196
|
+
<div class={LINE_HEIGHT[sz]}>{i + 1}</div>
|
|
197
|
+
{/each}
|
|
198
|
+
</div>
|
|
199
|
+
{/if}
|
|
200
|
+
|
|
201
|
+
<div class="cv-editor relative flex-1 min-h-0">
|
|
202
202
|
<div
|
|
203
203
|
bind:this={highlightEl}
|
|
204
204
|
class={cx("cv-highlight cv-layer", TEXT[sz], LINE_HEIGHT[sz])}
|
|
@@ -28,10 +28,9 @@
|
|
|
28
28
|
* @note `clearable=false` hides the clear button; when `disabled`, pointer/keyboard handlers are skipped.
|
|
29
29
|
*/
|
|
30
30
|
import type { HTMLAttributes } from "svelte/elements";
|
|
31
|
-
import
|
|
32
|
-
import
|
|
33
|
-
import {
|
|
34
|
-
import { TEXTS } from "./lang";
|
|
31
|
+
import Button from "./Button.svelte";
|
|
32
|
+
import { cx } from "../utils";
|
|
33
|
+
import { getComponentText, getLangContext, getLangKey } from "./lang-context";
|
|
35
34
|
|
|
36
35
|
type Props = HTMLAttributes<HTMLDivElement> & {
|
|
37
36
|
value?: string | null;
|
|
@@ -54,10 +53,9 @@
|
|
|
54
53
|
...rest
|
|
55
54
|
}: Props = $props();
|
|
56
55
|
|
|
57
|
-
const langCtx =
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const L = $derived(TEXTS[langKey].components.colorPicker);
|
|
56
|
+
const langCtx = getLangContext();
|
|
57
|
+
const langKey = $derived(getLangKey(langCtx));
|
|
58
|
+
const L = $derived(getComponentText("colorPicker", langKey));
|
|
61
59
|
|
|
62
60
|
const labelFinal = $derived(label ?? L.text);
|
|
63
61
|
const placeholderFinal = $derived(placeholder ?? L.placeholder);
|