@ibobbyts/svelte-ui-utils 0.2.5 → 0.2.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/README.md +11 -8
- package/dist/data-table/DateRangeFilter.svelte +95 -0
- package/dist/data-table/DateRangeFilter.svelte.d.ts.map +1 -1
- package/dist/i18n.d.ts +5 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +18 -3
- package/dist/style.css +41 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -401,14 +401,17 @@ can clip the menu even when the menu has a high `z-index`. Keep the nearest
|
|
|
401
401
|
filter container at `overflow: visible`, or move the clipping/scrolling behavior
|
|
402
402
|
to a parent that does not wrap the dropdown menu directly.
|
|
403
403
|
|
|
404
|
-
`dateRange` renders two browser date inputs
|
|
405
|
-
`last 24 hours`, `last 7 days`, `last 30 days`,
|
|
406
|
-
`this month`, and `this year`. Manual changes
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
`
|
|
411
|
-
|
|
404
|
+
`dateRange` renders two browser date inputs, preset buttons, and quick month/year
|
|
405
|
+
selects. The presets are `last 24 hours`, `last 7 days`, `last 30 days`,
|
|
406
|
+
`today`, `this week`, `this month`, and `this year`. Manual changes and quick
|
|
407
|
+
month/year changes emit `{ startDate, endDate, preset: null }`. When the month is
|
|
408
|
+
selected first, the year select fills with the most recent matching year. For
|
|
409
|
+
example, in July 2026, July resolves to 2026 and August resolves to 2025. Pass
|
|
410
|
+
`defaultPreset` to apply a preset on mount when the current value is empty.
|
|
411
|
+
Clicking the currently active preset clears both date inputs and emits an empty
|
|
412
|
+
range. The `last24Hours` preset also emits `startDateTime` and `endDateTime` so
|
|
413
|
+
a consuming app can run an exact timestamp query while still showing the covered
|
|
414
|
+
dates in the inputs.
|
|
412
415
|
|
|
413
416
|
`numberRange` renders min/max number inputs and supports `prefixLabel`, for
|
|
414
417
|
example `$` for currency filters.
|
|
@@ -28,10 +28,16 @@
|
|
|
28
28
|
'thisMonth',
|
|
29
29
|
'thisYear'
|
|
30
30
|
];
|
|
31
|
+
const monthNumbers = Array.from({ length: 12 }, (_, index) => index + 1);
|
|
32
|
+
|
|
33
|
+
let quickYear = '';
|
|
34
|
+
let quickMonth = '';
|
|
31
35
|
|
|
32
36
|
$: messages = getUiMessages(language);
|
|
33
37
|
$: resolvedStartLabel = startLabel ?? messages.dateRange.startLabel;
|
|
34
38
|
$: resolvedEndLabel = endLabel ?? messages.dateRange.endLabel;
|
|
39
|
+
$: currentYear = startOfDay(now()).getFullYear();
|
|
40
|
+
$: quickYearOptions = Array.from({ length: 21 }, (_, index) => currentYear - index);
|
|
35
41
|
|
|
36
42
|
function pad(value: number) {
|
|
37
43
|
return String(value).padStart(2, '0');
|
|
@@ -62,6 +68,31 @@
|
|
|
62
68
|
return addDays(start, -offset);
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
function endOfMonth(year: number, month: number) {
|
|
72
|
+
return new Date(year, month, 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveYearForMonth(month: number) {
|
|
76
|
+
const today = startOfDay(now());
|
|
77
|
+
return month <= today.getMonth() + 1 ? today.getFullYear() : today.getFullYear() - 1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveQuickRange(year: number, month: number | null): DateRangeFilterValue {
|
|
81
|
+
const start = month === null ? new Date(year, 0, 1) : new Date(year, month - 1, 1);
|
|
82
|
+
const end = month === null ? new Date(year, 11, 31) : endOfMonth(year, month);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
startDate: formatDate(start),
|
|
86
|
+
endDate: formatDate(end),
|
|
87
|
+
preset: null
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function clearQuickSelection() {
|
|
92
|
+
quickYear = '';
|
|
93
|
+
quickMonth = '';
|
|
94
|
+
}
|
|
95
|
+
|
|
65
96
|
function resolvePreset(preset: DateRangePreset): DateRangeFilterValue {
|
|
66
97
|
const current = now();
|
|
67
98
|
const today = startOfDay(current);
|
|
@@ -142,6 +173,7 @@
|
|
|
142
173
|
}
|
|
143
174
|
|
|
144
175
|
function updateDate(part: 'startDate' | 'endDate', nextValue: string) {
|
|
176
|
+
clearQuickSelection();
|
|
145
177
|
emit({
|
|
146
178
|
startDate: part === 'startDate' ? nextValue : value.startDate,
|
|
147
179
|
endDate: part === 'endDate' ? nextValue : value.endDate,
|
|
@@ -149,7 +181,39 @@
|
|
|
149
181
|
});
|
|
150
182
|
}
|
|
151
183
|
|
|
184
|
+
function updateQuickYear(nextValue: string) {
|
|
185
|
+
quickYear = nextValue;
|
|
186
|
+
|
|
187
|
+
if (!nextValue) {
|
|
188
|
+
clearQuickSelection();
|
|
189
|
+
emit(emptyRange());
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
emit(resolveQuickRange(Number(nextValue), quickMonth ? Number(quickMonth) : null));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function updateQuickMonth(nextValue: string) {
|
|
197
|
+
quickMonth = nextValue;
|
|
198
|
+
|
|
199
|
+
if (!nextValue) {
|
|
200
|
+
if (quickYear) {
|
|
201
|
+
emit(resolveQuickRange(Number(quickYear), null));
|
|
202
|
+
} else {
|
|
203
|
+
emit(emptyRange());
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!quickYear) {
|
|
209
|
+
quickYear = String(resolveYearForMonth(Number(nextValue)));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
emit(resolveQuickRange(Number(quickYear), Number(nextValue)));
|
|
213
|
+
}
|
|
214
|
+
|
|
152
215
|
function applyPreset(preset: DateRangePreset) {
|
|
216
|
+
clearQuickSelection();
|
|
153
217
|
if (value.preset === preset) {
|
|
154
218
|
emit(emptyRange());
|
|
155
219
|
return;
|
|
@@ -162,6 +226,10 @@
|
|
|
162
226
|
return presetLabels[preset] ?? messages.dateRange.presetLabels[preset];
|
|
163
227
|
}
|
|
164
228
|
|
|
229
|
+
function monthLabel(month: number) {
|
|
230
|
+
return messages.dateRange.monthLabels[month - 1] ?? String(month);
|
|
231
|
+
}
|
|
232
|
+
|
|
165
233
|
onMount(() => {
|
|
166
234
|
if (defaultPreset && isEmptyRange(value)) {
|
|
167
235
|
emit(resolvePreset(defaultPreset));
|
|
@@ -196,6 +264,33 @@
|
|
|
196
264
|
>
|
|
197
265
|
{labelFor(preset)}
|
|
198
266
|
</button>
|
|
267
|
+
{#if preset === 'last30Days' || preset === 'thisYear'}
|
|
268
|
+
<span class="suu-filter-preset-divider" aria-hidden="true"></span>
|
|
269
|
+
{/if}
|
|
199
270
|
{/each}
|
|
271
|
+
<label class="suu-filter-preset-select">
|
|
272
|
+
<span class="suu-visually-hidden">{messages.dateRange.quickMonthLabel}</span>
|
|
273
|
+
<select
|
|
274
|
+
aria-label={messages.dateRange.quickMonthLabel}
|
|
275
|
+
on:change={(event) => updateQuickMonth((event.currentTarget as HTMLSelectElement).value)}
|
|
276
|
+
>
|
|
277
|
+
<option value="" selected={quickMonth === ''}>{messages.dateRange.quickMonthPlaceholder}</option>
|
|
278
|
+
{#each monthNumbers as month}
|
|
279
|
+
<option value={month} selected={quickMonth === String(month)}>{monthLabel(month)}</option>
|
|
280
|
+
{/each}
|
|
281
|
+
</select>
|
|
282
|
+
</label>
|
|
283
|
+
<label class="suu-filter-preset-select">
|
|
284
|
+
<span class="suu-visually-hidden">{messages.dateRange.quickYearLabel}</span>
|
|
285
|
+
<select
|
|
286
|
+
aria-label={messages.dateRange.quickYearLabel}
|
|
287
|
+
on:change={(event) => updateQuickYear((event.currentTarget as HTMLSelectElement).value)}
|
|
288
|
+
>
|
|
289
|
+
<option value="" selected={quickYear === ''}>{messages.dateRange.quickYearPlaceholder}</option>
|
|
290
|
+
{#each quickYearOptions as year}
|
|
291
|
+
<option value={year} selected={quickYear === String(year)}>{year}</option>
|
|
292
|
+
{/each}
|
|
293
|
+
</select>
|
|
294
|
+
</label>
|
|
200
295
|
</div>
|
|
201
296
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateRangeFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/DateRangeFilter.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"DateRangeFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/data-table/DateRangeFilter.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA0RxE,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,eAAe;YAVkN,oBAAoB;eAAa,UAAU;iBAAe,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;mBAAiB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;oBAAkB,eAAe,GAAG,SAAS;UAAQ,MAAM,IAAI;mBAAiB,CAAC,GAAG,CAAC;eAAa,CAAC,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC,GAAG,SAAS;;;kBAU5b,CAAC;AACnF,KAAK,eAAe,GAAG,YAAY,CAAC,OAAO,eAAe,CAAC,CAAC;AAC9D,eAAe,eAAe,CAAC"}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export interface UiMessages {
|
|
|
19
19
|
startLabel: string;
|
|
20
20
|
endLabel: string;
|
|
21
21
|
presetLabels: Record<DateRangePresetKey, string>;
|
|
22
|
+
quickMonthLabel: string;
|
|
23
|
+
quickMonthPlaceholder: string;
|
|
24
|
+
quickYearLabel: string;
|
|
25
|
+
quickYearPlaceholder: string;
|
|
26
|
+
monthLabels: string[];
|
|
22
27
|
};
|
|
23
28
|
numberRange: {
|
|
24
29
|
minLabel: string;
|
package/dist/i18n.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/lib/i18n.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,sCAAuC,CAAC;AAEhE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,KAAK,kBAAkB,GACnB,aAAa,GACb,WAAW,GACX,YAAY,GACZ,OAAO,GACP,UAAU,GACV,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,cAAc,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/lib/i18n.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,sCAAuC,CAAC;AAEhE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,KAAK,kBAAkB,GACnB,aAAa,GACb,WAAW,GACX,YAAY,GACZ,OAAO,GACP,UAAU,GACV,WAAW,GACX,UAAU,CAAC;AAEf,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,cAAc,EAAE;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;QACjD,eAAe,EAAE,MAAM,CAAC;QACxB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;IACF,WAAW,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAoHD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,CAiB9F;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,CAE1F"}
|
package/dist/i18n.js
CHANGED
|
@@ -25,7 +25,12 @@ const messages = {
|
|
|
25
25
|
thisWeek: 'This Week',
|
|
26
26
|
thisMonth: 'This Month',
|
|
27
27
|
thisYear: 'This Year'
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
|
+
quickMonthLabel: 'Month',
|
|
30
|
+
quickMonthPlaceholder: 'Month',
|
|
31
|
+
quickYearLabel: 'Year',
|
|
32
|
+
quickYearPlaceholder: 'Year',
|
|
33
|
+
monthLabels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
29
34
|
},
|
|
30
35
|
numberRange: {
|
|
31
36
|
minLabel: 'Min',
|
|
@@ -57,7 +62,12 @@ const messages = {
|
|
|
57
62
|
thisWeek: '本周',
|
|
58
63
|
thisMonth: '本月',
|
|
59
64
|
thisYear: '今年'
|
|
60
|
-
}
|
|
65
|
+
},
|
|
66
|
+
quickMonthLabel: '月份',
|
|
67
|
+
quickMonthPlaceholder: '月份',
|
|
68
|
+
quickYearLabel: '年份',
|
|
69
|
+
quickYearPlaceholder: '年份',
|
|
70
|
+
monthLabels: ['1 月', '2 月', '3 月', '4 月', '5 月', '6 月', '7 月', '8 月', '9 月', '10 月', '11 月', '12 月']
|
|
61
71
|
},
|
|
62
72
|
numberRange: {
|
|
63
73
|
minLabel: '最小值',
|
|
@@ -89,7 +99,12 @@ const messages = {
|
|
|
89
99
|
thisWeek: '本週',
|
|
90
100
|
thisMonth: '本月',
|
|
91
101
|
thisYear: '今年'
|
|
92
|
-
}
|
|
102
|
+
},
|
|
103
|
+
quickMonthLabel: '月份',
|
|
104
|
+
quickMonthPlaceholder: '月份',
|
|
105
|
+
quickYearLabel: '年份',
|
|
106
|
+
quickYearPlaceholder: '年份',
|
|
107
|
+
monthLabels: ['1 月', '2 月', '3 月', '4 月', '5 月', '6 月', '7 月', '8 月', '9 月', '10 月', '11 月', '12 月']
|
|
93
108
|
},
|
|
94
109
|
numberRange: {
|
|
95
110
|
minLabel: '最小值',
|
package/dist/style.css
CHANGED
|
@@ -33,6 +33,18 @@
|
|
|
33
33
|
--suu-shadow: 0 16px 34px rgb(0 0 0 / 0.32);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
.suu-visually-hidden {
|
|
37
|
+
position: absolute;
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
width: 1px;
|
|
40
|
+
height: 1px;
|
|
41
|
+
padding: 0;
|
|
42
|
+
border: 0;
|
|
43
|
+
margin: -1px;
|
|
44
|
+
clip: rect(0 0 0 0);
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
.suu-toast-region {
|
|
37
49
|
position: fixed;
|
|
38
50
|
z-index: 1000;
|
|
@@ -551,6 +563,7 @@
|
|
|
551
563
|
.suu-filter-preset-row {
|
|
552
564
|
display: flex;
|
|
553
565
|
flex-wrap: wrap;
|
|
566
|
+
align-items: center;
|
|
554
567
|
gap: 6px;
|
|
555
568
|
}
|
|
556
569
|
|
|
@@ -582,6 +595,34 @@
|
|
|
582
595
|
background: color-mix(in srgb, var(--suu-color-accent) 12%, var(--suu-color-bg));
|
|
583
596
|
}
|
|
584
597
|
|
|
598
|
+
.suu-filter-preset-divider {
|
|
599
|
+
width: 1px;
|
|
600
|
+
height: 22px;
|
|
601
|
+
margin: 0 3px;
|
|
602
|
+
background: var(--suu-color-border);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.suu-filter-preset-select select {
|
|
606
|
+
min-height: 30px;
|
|
607
|
+
border: 1px solid var(--suu-color-border);
|
|
608
|
+
border-radius: var(--suu-radius);
|
|
609
|
+
background: var(--suu-color-bg);
|
|
610
|
+
color: var(--suu-color-text);
|
|
611
|
+
cursor: pointer;
|
|
612
|
+
font: inherit;
|
|
613
|
+
font-size: 0.8125rem;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.suu-filter-preset-select select {
|
|
617
|
+
padding: 4px 28px 4px 8px;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.suu-filter-preset-select select:focus {
|
|
621
|
+
border-color: var(--suu-color-accent);
|
|
622
|
+
outline: 2px solid color-mix(in srgb, var(--suu-color-accent) 22%, transparent);
|
|
623
|
+
outline-offset: 1px;
|
|
624
|
+
}
|
|
625
|
+
|
|
585
626
|
.suu-input-affix {
|
|
586
627
|
display: flex;
|
|
587
628
|
align-items: stretch;
|
package/package.json
CHANGED