bits-ui 2.4.1 → 2.5.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/dist/bits/calendar/calendar.svelte.d.ts +77 -1
- package/dist/bits/calendar/calendar.svelte.js +169 -9
- package/dist/bits/calendar/components/calendar-month-select.svelte +54 -0
- package/dist/bits/calendar/components/calendar-month-select.svelte.d.ts +4 -0
- package/dist/bits/calendar/components/calendar-year-select.svelte +51 -0
- package/dist/bits/calendar/components/calendar-year-select.svelte.d.ts +4 -0
- package/dist/bits/calendar/components/calendar.svelte +6 -0
- package/dist/bits/calendar/exports.d.ts +3 -1
- package/dist/bits/calendar/exports.js +2 -0
- package/dist/bits/calendar/types.d.ts +94 -12
- package/dist/bits/combobox/types.d.ts +1 -1
- package/dist/bits/date-field/date-field.svelte.js +5 -1
- package/dist/bits/date-picker/components/date-picker-calendar.svelte +3 -0
- package/dist/bits/date-picker/components/date-picker.svelte +4 -0
- package/dist/bits/date-picker/date-picker.svelte.d.ts +2 -0
- package/dist/bits/date-picker/exports.d.ts +3 -1
- package/dist/bits/date-picker/exports.js +2 -0
- package/dist/bits/date-picker/types.d.ts +13 -1
- package/dist/bits/date-range-field/date-range-field.svelte.js +5 -1
- package/dist/bits/date-range-picker/components/date-range-picker-calendar.svelte +5 -0
- package/dist/bits/date-range-picker/components/date-range-picker.svelte +10 -0
- package/dist/bits/date-range-picker/date-range-picker.svelte.d.ts +5 -0
- package/dist/bits/date-range-picker/exports.d.ts +3 -1
- package/dist/bits/date-range-picker/exports.js +2 -0
- package/dist/bits/date-range-picker/types.d.ts +33 -1
- package/dist/bits/range-calendar/components/range-calendar.svelte +10 -0
- package/dist/bits/range-calendar/exports.d.ts +3 -1
- package/dist/bits/range-calendar/exports.js +2 -0
- package/dist/bits/range-calendar/range-calendar.svelte.d.ts +20 -0
- package/dist/bits/range-calendar/range-calendar.svelte.js +120 -6
- package/dist/bits/range-calendar/types.d.ts +44 -12
- package/dist/bits/select/components/select-hidden-input.svelte +6 -2
- package/dist/bits/select/components/select-hidden-input.svelte.d.ts +2 -1
- package/dist/bits/select/components/select.svelte +3 -2
- package/dist/bits/select/types.d.ts +5 -0
- package/dist/internal/date-time/calendar-helpers.svelte.d.ts +8 -2
- package/dist/internal/date-time/calendar-helpers.svelte.js +47 -15
- package/dist/internal/date-time/formatter.d.ts +8 -1
- package/dist/internal/date-time/formatter.js +16 -3
- package/dist/shared/attributes.d.ts +3 -1
- package/package.json +1 -1
|
@@ -30,11 +30,14 @@ type CalendarRootStateProps = WithRefProps<WritableBoxedValues<{
|
|
|
30
30
|
readonly: boolean;
|
|
31
31
|
disableDaysOutsideMonth: boolean;
|
|
32
32
|
initialFocus: boolean;
|
|
33
|
+
maxDays: number | undefined;
|
|
33
34
|
/**
|
|
34
35
|
* This is strictly used by the `DatePicker` component to close the popover when a date
|
|
35
36
|
* is selected. It is not intended to be used by the user.
|
|
36
37
|
*/
|
|
37
38
|
onDateSelect?: () => void;
|
|
39
|
+
monthFormat: Intl.DateTimeFormatOptions["month"] | ((month: number) => string);
|
|
40
|
+
yearFormat: Intl.DateTimeFormatOptions["year"] | ((year: number) => string);
|
|
38
41
|
}> & {
|
|
39
42
|
defaultPlaceholder: DateValue;
|
|
40
43
|
}>;
|
|
@@ -56,7 +59,9 @@ export declare class CalendarRootState {
|
|
|
56
59
|
* calendar's days of the week is strongly recommended, as it guarantees that
|
|
57
60
|
* the days are correctly formatted for the current locale and calendar view.
|
|
58
61
|
*/
|
|
59
|
-
weekdays: string[];
|
|
62
|
+
readonly weekdays: string[];
|
|
63
|
+
readonly initialPlaceholderYear: number;
|
|
64
|
+
readonly defaultYears: number[];
|
|
60
65
|
/**
|
|
61
66
|
* Navigates to the next page of the calendar.
|
|
62
67
|
*/
|
|
@@ -298,6 +303,75 @@ export declare class CalendarHeaderState {
|
|
|
298
303
|
readonly "data-readonly": "" | undefined;
|
|
299
304
|
};
|
|
300
305
|
}
|
|
306
|
+
export type CalendarMonthSelectStateProps = WithRefProps<ReadableBoxedValues<{
|
|
307
|
+
months: number[];
|
|
308
|
+
monthFormat: Intl.DateTimeFormatOptions["month"] | ((month: number) => string);
|
|
309
|
+
disabled: boolean;
|
|
310
|
+
}>>;
|
|
311
|
+
export declare class CalendarMonthSelectState {
|
|
312
|
+
readonly opts: CalendarMonthSelectStateProps;
|
|
313
|
+
readonly root: CalendarRootState | RangeCalendarRootState;
|
|
314
|
+
constructor(opts: CalendarMonthSelectStateProps, root: CalendarRootState | RangeCalendarRootState);
|
|
315
|
+
readonly monthItems: {
|
|
316
|
+
value: number;
|
|
317
|
+
label: string;
|
|
318
|
+
}[];
|
|
319
|
+
readonly currentMonth: number;
|
|
320
|
+
readonly isDisabled: boolean;
|
|
321
|
+
readonly snippetProps: {
|
|
322
|
+
monthItems: {
|
|
323
|
+
value: number;
|
|
324
|
+
label: string;
|
|
325
|
+
}[];
|
|
326
|
+
selectedMonthItem: {
|
|
327
|
+
value: number;
|
|
328
|
+
label: string;
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
onchange(event: Event): void;
|
|
332
|
+
props: {
|
|
333
|
+
readonly id: string;
|
|
334
|
+
readonly value: number;
|
|
335
|
+
readonly disabled: boolean;
|
|
336
|
+
readonly "data-disabled": "" | undefined;
|
|
337
|
+
readonly onchange: (event: Event) => void;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
export type CalendarYearSelectStateProps = WithRefProps<ReadableBoxedValues<{
|
|
341
|
+
years: number[] | undefined;
|
|
342
|
+
yearFormat: Intl.DateTimeFormatOptions["year"] | ((year: number) => string);
|
|
343
|
+
disabled: boolean;
|
|
344
|
+
}>>;
|
|
345
|
+
export declare class CalendarYearSelectState {
|
|
346
|
+
readonly opts: CalendarYearSelectStateProps;
|
|
347
|
+
readonly root: CalendarRootState | RangeCalendarRootState;
|
|
348
|
+
constructor(opts: CalendarYearSelectStateProps, root: CalendarRootState | RangeCalendarRootState);
|
|
349
|
+
readonly years: number[];
|
|
350
|
+
readonly yearItems: {
|
|
351
|
+
value: number;
|
|
352
|
+
label: string;
|
|
353
|
+
}[];
|
|
354
|
+
readonly currentYear: number;
|
|
355
|
+
readonly isDisabled: boolean;
|
|
356
|
+
readonly snippetProps: {
|
|
357
|
+
yearItems: {
|
|
358
|
+
value: number;
|
|
359
|
+
label: string;
|
|
360
|
+
}[];
|
|
361
|
+
selectedYearItem: {
|
|
362
|
+
value: number;
|
|
363
|
+
label: string;
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
onchange(event: Event): void;
|
|
367
|
+
props: {
|
|
368
|
+
readonly id: string;
|
|
369
|
+
readonly value: number;
|
|
370
|
+
readonly disabled: boolean;
|
|
371
|
+
readonly "data-disabled": "" | undefined;
|
|
372
|
+
readonly onchange: (event: Event) => void;
|
|
373
|
+
};
|
|
374
|
+
}
|
|
301
375
|
export declare const CalendarRootContext: Context<RangeCalendarRootState | CalendarRootState>;
|
|
302
376
|
export declare function useCalendarRoot(props: CalendarRootStateProps): RangeCalendarRootState | CalendarRootState;
|
|
303
377
|
export declare function useCalendarGrid(props: CalendarGridStateProps): CalendarGridState;
|
|
@@ -311,4 +385,6 @@ export declare function useCalendarGridRow(props: CalendarGridRowStateProps): Ca
|
|
|
311
385
|
export declare function useCalendarHeadCell(props: CalendarHeadCellStateProps): CalendarHeadCellState;
|
|
312
386
|
export declare function useCalendarHeader(props: CalendarHeaderStateProps): CalendarHeaderState;
|
|
313
387
|
export declare function useCalendarHeading(props: CalendarHeadingStateProps): CalendarHeadingState;
|
|
388
|
+
export declare function useCalendarMonthSelect(props: CalendarMonthSelectStateProps): CalendarMonthSelectState;
|
|
389
|
+
export declare function useCalendarYearSelect(props: CalendarYearSelectStateProps): CalendarYearSelectState;
|
|
314
390
|
export {};
|
|
@@ -7,7 +7,7 @@ import { getAriaDisabled, getAriaHidden, getAriaReadonly, getAriaSelected, getDa
|
|
|
7
7
|
import { useId } from "../../internal/use-id.js";
|
|
8
8
|
import { getAnnouncer } from "../../internal/date-time/announcer.js";
|
|
9
9
|
import { createFormatter } from "../../internal/date-time/formatter.js";
|
|
10
|
-
import { calendarAttrs, createAccessibleHeading, createMonths, getCalendarElementProps, getCalendarHeadingValue, getDateWithPreviousTime, getIsNextButtonDisabled, getIsPrevButtonDisabled, getWeekdays, handleCalendarKeydown, handleCalendarNextPage, handleCalendarPrevPage, shiftCalendarFocus, useEnsureNonDisabledPlaceholder, useMonthViewOptionsSync, useMonthViewPlaceholderSync, } from "../../internal/date-time/calendar-helpers.svelte.js";
|
|
10
|
+
import { calendarAttrs, createAccessibleHeading, createMonths, getCalendarElementProps, getCalendarHeadingValue, getDateWithPreviousTime, getDefaultYears, getIsNextButtonDisabled, getIsPrevButtonDisabled, getWeekdays, handleCalendarKeydown, handleCalendarNextPage, handleCalendarPrevPage, shiftCalendarFocus, useEnsureNonDisabledPlaceholder, useMonthViewOptionsSync, useMonthViewPlaceholderSync, } from "../../internal/date-time/calendar-helpers.svelte.js";
|
|
11
11
|
import { getDateValueType, isBefore, toDate } from "../../internal/date-time/utils.js";
|
|
12
12
|
export class CalendarRootState {
|
|
13
13
|
opts;
|
|
@@ -21,7 +21,11 @@ export class CalendarRootState {
|
|
|
21
21
|
this.opts = opts;
|
|
22
22
|
this.domContext = new DOMContext(opts.ref);
|
|
23
23
|
this.announcer = getAnnouncer(null);
|
|
24
|
-
this.formatter = createFormatter(
|
|
24
|
+
this.formatter = createFormatter({
|
|
25
|
+
initialLocale: this.opts.locale.current,
|
|
26
|
+
monthFormat: this.opts.monthFormat,
|
|
27
|
+
yearFormat: this.opts.yearFormat,
|
|
28
|
+
});
|
|
25
29
|
this.setMonths = this.setMonths.bind(this);
|
|
26
30
|
this.nextPage = this.nextPage.bind(this);
|
|
27
31
|
this.prevPage = this.prevPage.bind(this);
|
|
@@ -128,6 +132,14 @@ export class CalendarRootState {
|
|
|
128
132
|
weekdayFormat: this.opts.weekdayFormat.current,
|
|
129
133
|
});
|
|
130
134
|
});
|
|
135
|
+
initialPlaceholderYear = $derived.by(() => untrack(() => this.opts.placeholder.current.year));
|
|
136
|
+
defaultYears = $derived.by(() => {
|
|
137
|
+
return getDefaultYears({
|
|
138
|
+
minValue: this.opts.minValue.current,
|
|
139
|
+
maxValue: this.opts.maxValue.current,
|
|
140
|
+
placeholderYear: this.initialPlaceholderYear,
|
|
141
|
+
});
|
|
142
|
+
});
|
|
131
143
|
#setupInitialFocusEffect() {
|
|
132
144
|
$effect(() => {
|
|
133
145
|
const initialFocus = untrack(() => this.opts.initialFocus.current);
|
|
@@ -153,7 +165,7 @@ export class CalendarRootState {
|
|
|
153
165
|
});
|
|
154
166
|
}
|
|
155
167
|
#setupFormatterEffect() {
|
|
156
|
-
$effect(() => {
|
|
168
|
+
$effect.pre(() => {
|
|
157
169
|
if (this.formatter.getLocale() === this.opts.locale.current)
|
|
158
170
|
return;
|
|
159
171
|
this.formatter.setLocale(this.opts.locale.current);
|
|
@@ -240,6 +252,8 @@ export class CalendarRootState {
|
|
|
240
252
|
return false;
|
|
241
253
|
});
|
|
242
254
|
headingValue = $derived.by(() => {
|
|
255
|
+
this.opts.monthFormat.current;
|
|
256
|
+
this.opts.yearFormat.current;
|
|
243
257
|
return getCalendarHeadingValue({
|
|
244
258
|
months: this.months,
|
|
245
259
|
formatter: this.formatter,
|
|
@@ -285,10 +299,20 @@ export class CalendarRootState {
|
|
|
285
299
|
numberOfMonths: this.opts.numberOfMonths.current,
|
|
286
300
|
});
|
|
287
301
|
}
|
|
302
|
+
#isMultipleSelectionValid(selectedDates) {
|
|
303
|
+
// only validate for multiple type and when maxDays is set
|
|
304
|
+
if (this.opts.type.current !== "multiple")
|
|
305
|
+
return true;
|
|
306
|
+
if (!this.opts.maxDays.current)
|
|
307
|
+
return true;
|
|
308
|
+
const selectedCount = selectedDates.length;
|
|
309
|
+
if (this.opts.maxDays.current && selectedCount > this.opts.maxDays.current)
|
|
310
|
+
return false;
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
288
313
|
handleCellClick(_, date) {
|
|
289
|
-
if (this.opts.readonly.current
|
|
290
|
-
|
|
291
|
-
if (this.opts.isDateDisabled.current?.(date) ||
|
|
314
|
+
if (this.opts.readonly.current ||
|
|
315
|
+
this.opts.isDateDisabled.current?.(date) ||
|
|
292
316
|
this.opts.isDateUnavailable.current?.(date)) {
|
|
293
317
|
return;
|
|
294
318
|
}
|
|
@@ -314,8 +338,10 @@ export class CalendarRootState {
|
|
|
314
338
|
}
|
|
315
339
|
}
|
|
316
340
|
handleMultipleUpdate(prev, date) {
|
|
317
|
-
if (!prev)
|
|
318
|
-
|
|
341
|
+
if (!prev) {
|
|
342
|
+
const newSelection = [date];
|
|
343
|
+
return this.#isMultipleSelectionValid(newSelection) ? newSelection : [date];
|
|
344
|
+
}
|
|
319
345
|
if (!Array.isArray(prev)) {
|
|
320
346
|
if (DEV)
|
|
321
347
|
throw new Error("Invalid value for multiple prop.");
|
|
@@ -324,7 +350,15 @@ export class CalendarRootState {
|
|
|
324
350
|
const index = prev.findIndex((d) => isSameDay(d, date));
|
|
325
351
|
const preventDeselect = this.opts.preventDeselect.current;
|
|
326
352
|
if (index === -1) {
|
|
327
|
-
|
|
353
|
+
// adding a new date - check if it would be valid
|
|
354
|
+
const newSelection = [...prev, date];
|
|
355
|
+
if (this.#isMultipleSelectionValid(newSelection)) {
|
|
356
|
+
return newSelection;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// reset to just the newly selected date when constraints are violated
|
|
360
|
+
return [date];
|
|
361
|
+
}
|
|
328
362
|
}
|
|
329
363
|
else if (preventDeselect) {
|
|
330
364
|
return prev;
|
|
@@ -641,6 +675,126 @@ export class CalendarHeaderState {
|
|
|
641
675
|
...attachRef(this.opts.ref),
|
|
642
676
|
}));
|
|
643
677
|
}
|
|
678
|
+
export class CalendarMonthSelectState {
|
|
679
|
+
opts;
|
|
680
|
+
root;
|
|
681
|
+
constructor(opts, root) {
|
|
682
|
+
this.opts = opts;
|
|
683
|
+
this.root = root;
|
|
684
|
+
this.onchange = this.onchange.bind(this);
|
|
685
|
+
}
|
|
686
|
+
monthItems = $derived.by(() => {
|
|
687
|
+
this.root.opts.locale.current;
|
|
688
|
+
const monthNumbers = this.opts.months.current;
|
|
689
|
+
const monthFormat = this.opts.monthFormat.current;
|
|
690
|
+
const months = [];
|
|
691
|
+
for (const month of monthNumbers) {
|
|
692
|
+
// create a date with the current year and the month to get localized name
|
|
693
|
+
const date = this.root.opts.placeholder.current.set({ month });
|
|
694
|
+
let label;
|
|
695
|
+
if (typeof monthFormat === "function") {
|
|
696
|
+
label = monthFormat(month);
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
label = this.root.formatter.custom(toDate(date), { month: monthFormat });
|
|
700
|
+
}
|
|
701
|
+
months.push({
|
|
702
|
+
value: month,
|
|
703
|
+
label,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
return months;
|
|
707
|
+
});
|
|
708
|
+
currentMonth = $derived.by(() => this.root.opts.placeholder.current.month);
|
|
709
|
+
isDisabled = $derived.by(() => this.root.opts.disabled.current || this.opts.disabled.current);
|
|
710
|
+
snippetProps = $derived.by(() => {
|
|
711
|
+
return {
|
|
712
|
+
monthItems: this.monthItems,
|
|
713
|
+
selectedMonthItem: this.monthItems.find((month) => month.value === this.currentMonth),
|
|
714
|
+
};
|
|
715
|
+
});
|
|
716
|
+
onchange(event) {
|
|
717
|
+
if (this.isDisabled)
|
|
718
|
+
return;
|
|
719
|
+
const target = event.target;
|
|
720
|
+
const month = parseInt(target.value, 10);
|
|
721
|
+
if (!isNaN(month)) {
|
|
722
|
+
this.root.opts.placeholder.current = this.root.opts.placeholder.current.set({ month });
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
props = $derived.by(() => ({
|
|
726
|
+
id: this.opts.id.current,
|
|
727
|
+
value: this.currentMonth,
|
|
728
|
+
disabled: this.isDisabled,
|
|
729
|
+
"data-disabled": getDataDisabled(this.isDisabled),
|
|
730
|
+
[this.root.getBitsAttr("month-select")]: "",
|
|
731
|
+
//
|
|
732
|
+
onchange: this.onchange,
|
|
733
|
+
...attachRef(this.opts.ref),
|
|
734
|
+
}));
|
|
735
|
+
}
|
|
736
|
+
export class CalendarYearSelectState {
|
|
737
|
+
opts;
|
|
738
|
+
root;
|
|
739
|
+
constructor(opts, root) {
|
|
740
|
+
this.opts = opts;
|
|
741
|
+
this.root = root;
|
|
742
|
+
this.onchange = this.onchange.bind(this);
|
|
743
|
+
}
|
|
744
|
+
years = $derived.by(() => {
|
|
745
|
+
if (this.opts.years.current && this.opts.years.current.length)
|
|
746
|
+
return this.opts.years.current;
|
|
747
|
+
return this.root.defaultYears;
|
|
748
|
+
});
|
|
749
|
+
yearItems = $derived.by(() => {
|
|
750
|
+
this.root.opts.locale.current;
|
|
751
|
+
const yearFormat = this.opts.yearFormat.current;
|
|
752
|
+
const localYears = [];
|
|
753
|
+
for (const year of this.years) {
|
|
754
|
+
// create a date with the year to get localized formatting
|
|
755
|
+
const date = this.root.opts.placeholder.current.set({ year });
|
|
756
|
+
let label;
|
|
757
|
+
if (typeof yearFormat === "function") {
|
|
758
|
+
label = yearFormat(year);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
label = this.root.formatter.custom(toDate(date), { year: yearFormat });
|
|
762
|
+
}
|
|
763
|
+
localYears.push({
|
|
764
|
+
value: year,
|
|
765
|
+
label,
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
return localYears;
|
|
769
|
+
});
|
|
770
|
+
currentYear = $derived.by(() => this.root.opts.placeholder.current.year);
|
|
771
|
+
isDisabled = $derived.by(() => this.root.opts.disabled.current || this.opts.disabled.current);
|
|
772
|
+
snippetProps = $derived.by(() => {
|
|
773
|
+
return {
|
|
774
|
+
yearItems: this.yearItems,
|
|
775
|
+
selectedYearItem: this.yearItems.find((year) => year.value === this.currentYear),
|
|
776
|
+
};
|
|
777
|
+
});
|
|
778
|
+
onchange(event) {
|
|
779
|
+
if (this.isDisabled)
|
|
780
|
+
return;
|
|
781
|
+
const target = event.target;
|
|
782
|
+
const year = parseInt(target.value, 10);
|
|
783
|
+
if (!isNaN(year)) {
|
|
784
|
+
this.root.opts.placeholder.current = this.root.opts.placeholder.current.set({ year });
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
props = $derived.by(() => ({
|
|
788
|
+
id: this.opts.id.current,
|
|
789
|
+
value: this.currentYear,
|
|
790
|
+
disabled: this.isDisabled,
|
|
791
|
+
"data-disabled": getDataDisabled(this.isDisabled),
|
|
792
|
+
[this.root.getBitsAttr("year-select")]: "",
|
|
793
|
+
//
|
|
794
|
+
onchange: this.onchange,
|
|
795
|
+
...attachRef(this.opts.ref),
|
|
796
|
+
}));
|
|
797
|
+
}
|
|
644
798
|
export const CalendarRootContext = new Context("Calendar.Root | RangeCalender.Root");
|
|
645
799
|
const CalendarCellContext = new Context("Calendar.Cell | RangeCalendar.Cell");
|
|
646
800
|
export function useCalendarRoot(props) {
|
|
@@ -679,3 +833,9 @@ export function useCalendarHeader(props) {
|
|
|
679
833
|
export function useCalendarHeading(props) {
|
|
680
834
|
return new CalendarHeadingState(props, CalendarRootContext.get());
|
|
681
835
|
}
|
|
836
|
+
export function useCalendarMonthSelect(props) {
|
|
837
|
+
return new CalendarMonthSelectState(props, CalendarRootContext.get());
|
|
838
|
+
}
|
|
839
|
+
export function useCalendarYearSelect(props) {
|
|
840
|
+
return new CalendarYearSelectState(props, CalendarRootContext.get());
|
|
841
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { box, mergeProps } from "svelte-toolbelt";
|
|
3
|
+
import type { CalendarMonthSelectProps } from "../types.js";
|
|
4
|
+
import { useCalendarMonthSelect } from "../calendar.svelte.js";
|
|
5
|
+
import { createId } from "../../../internal/create-id.js";
|
|
6
|
+
|
|
7
|
+
const uid = $props.id();
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
children,
|
|
11
|
+
child,
|
|
12
|
+
ref = $bindable(null),
|
|
13
|
+
id = createId(uid),
|
|
14
|
+
months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
|
15
|
+
monthFormat = "long",
|
|
16
|
+
disabled = false,
|
|
17
|
+
"aria-label": ariaLabel = "Select a month",
|
|
18
|
+
...restProps
|
|
19
|
+
}: CalendarMonthSelectProps = $props();
|
|
20
|
+
|
|
21
|
+
const monthSelectState = useCalendarMonthSelect({
|
|
22
|
+
id: box.with(() => id),
|
|
23
|
+
ref: box.with(
|
|
24
|
+
() => ref,
|
|
25
|
+
(v) => (ref = v)
|
|
26
|
+
),
|
|
27
|
+
months: box.with(() => months),
|
|
28
|
+
monthFormat: box.with(() => monthFormat),
|
|
29
|
+
disabled: box.with(() => Boolean(disabled)),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const mergedProps = $derived(
|
|
33
|
+
mergeProps(restProps, monthSelectState.props, { "aria-label": ariaLabel })
|
|
34
|
+
);
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
{#if child}
|
|
38
|
+
{@render child({ props: mergedProps, ...monthSelectState.snippetProps })}
|
|
39
|
+
{:else}
|
|
40
|
+
<select {...mergedProps}>
|
|
41
|
+
{#if children}
|
|
42
|
+
{@render children?.(monthSelectState.snippetProps)}
|
|
43
|
+
{:else}
|
|
44
|
+
{#each monthSelectState.monthItems as month (month.value)}
|
|
45
|
+
<option
|
|
46
|
+
value={month.value}
|
|
47
|
+
selected={month.value === monthSelectState.currentMonth}
|
|
48
|
+
>
|
|
49
|
+
{month.label}
|
|
50
|
+
</option>
|
|
51
|
+
{/each}
|
|
52
|
+
{/if}
|
|
53
|
+
</select>
|
|
54
|
+
{/if}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { box, mergeProps } from "svelte-toolbelt";
|
|
3
|
+
import type { CalendarYearSelectProps } from "../types.js";
|
|
4
|
+
import { useCalendarYearSelect } from "../calendar.svelte.js";
|
|
5
|
+
import { createId } from "../../../internal/create-id.js";
|
|
6
|
+
|
|
7
|
+
const uid = $props.id();
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
children,
|
|
11
|
+
child,
|
|
12
|
+
ref = $bindable(null),
|
|
13
|
+
id = createId(uid),
|
|
14
|
+
years,
|
|
15
|
+
yearFormat = "numeric",
|
|
16
|
+
disabled = false,
|
|
17
|
+
"aria-label": ariaLabel = "Select a year",
|
|
18
|
+
...restProps
|
|
19
|
+
}: CalendarYearSelectProps = $props();
|
|
20
|
+
|
|
21
|
+
const yearSelectState = useCalendarYearSelect({
|
|
22
|
+
id: box.with(() => id),
|
|
23
|
+
ref: box.with(
|
|
24
|
+
() => ref,
|
|
25
|
+
(v) => (ref = v)
|
|
26
|
+
),
|
|
27
|
+
years: box.with(() => years),
|
|
28
|
+
yearFormat: box.with(() => yearFormat),
|
|
29
|
+
disabled: box.with(() => Boolean(disabled)),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const mergedProps = $derived(
|
|
33
|
+
mergeProps(restProps, yearSelectState.props, { "aria-label": ariaLabel })
|
|
34
|
+
);
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
{#if child}
|
|
38
|
+
{@render child({ props: mergedProps, ...yearSelectState.snippetProps })}
|
|
39
|
+
{:else}
|
|
40
|
+
<select {...mergedProps}>
|
|
41
|
+
{#if children}
|
|
42
|
+
{@render children?.(yearSelectState.snippetProps)}
|
|
43
|
+
{:else}
|
|
44
|
+
{#each yearSelectState.yearItems as year (year.value)}
|
|
45
|
+
<option value={year.value} selected={year.value === yearSelectState.currentYear}>
|
|
46
|
+
{year.label}
|
|
47
|
+
</option>
|
|
48
|
+
{/each}
|
|
49
|
+
{/if}
|
|
50
|
+
</select>
|
|
51
|
+
{/if}
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
type,
|
|
36
36
|
disableDaysOutsideMonth = true,
|
|
37
37
|
initialFocus = false,
|
|
38
|
+
maxDays,
|
|
39
|
+
monthFormat = "long",
|
|
40
|
+
yearFormat = "numeric",
|
|
38
41
|
...restProps
|
|
39
42
|
}: CalendarRootProps = $props();
|
|
40
43
|
|
|
@@ -93,6 +96,7 @@
|
|
|
93
96
|
maxValue: box.with(() => maxValue),
|
|
94
97
|
disableDaysOutsideMonth: box.with(() => disableDaysOutsideMonth),
|
|
95
98
|
initialFocus: box.with(() => initialFocus),
|
|
99
|
+
maxDays: box.with(() => maxDays),
|
|
96
100
|
placeholder: box.with(
|
|
97
101
|
() => placeholder as DateValue,
|
|
98
102
|
(v) => {
|
|
@@ -110,6 +114,8 @@
|
|
|
110
114
|
}
|
|
111
115
|
),
|
|
112
116
|
type: box.with(() => type),
|
|
117
|
+
monthFormat: box.with(() => monthFormat),
|
|
118
|
+
yearFormat: box.with(() => yearFormat),
|
|
113
119
|
defaultPlaceholder,
|
|
114
120
|
});
|
|
115
121
|
|
|
@@ -8,6 +8,8 @@ export { default as HeadCell } from "./components/calendar-head-cell.svelte";
|
|
|
8
8
|
export { default as GridRow } from "./components/calendar-grid-row.svelte";
|
|
9
9
|
export { default as Header } from "./components/calendar-header.svelte";
|
|
10
10
|
export { default as Heading } from "./components/calendar-heading.svelte";
|
|
11
|
+
export { default as MonthSelect } from "./components/calendar-month-select.svelte";
|
|
11
12
|
export { default as NextButton } from "./components/calendar-next-button.svelte";
|
|
12
13
|
export { default as PrevButton } from "./components/calendar-prev-button.svelte";
|
|
13
|
-
export
|
|
14
|
+
export { default as YearSelect } from "./components/calendar-year-select.svelte";
|
|
15
|
+
export type { CalendarRootProps as RootProps, CalendarPrevButtonProps as PrevButtonProps, CalendarNextButtonProps as NextButtonProps, CalendarHeadingProps as HeadingProps, CalendarHeaderProps as HeaderProps, CalendarGridProps as GridProps, CalendarGridHeadProps as GridHeadProps, CalendarHeadCellProps as HeadCellProps, CalendarGridBodyProps as GridBodyProps, CalendarGridRowProps as GridRowProps, CalendarCellProps as CellProps, CalendarDayProps as DayProps, CalendarMonthSelectProps as MonthSelectProps, CalendarYearSelectProps as YearSelectProps, } from "./types.js";
|
|
@@ -8,5 +8,7 @@ export { default as HeadCell } from "./components/calendar-head-cell.svelte";
|
|
|
8
8
|
export { default as GridRow } from "./components/calendar-grid-row.svelte";
|
|
9
9
|
export { default as Header } from "./components/calendar-header.svelte";
|
|
10
10
|
export { default as Heading } from "./components/calendar-heading.svelte";
|
|
11
|
+
export { default as MonthSelect } from "./components/calendar-month-select.svelte";
|
|
11
12
|
export { default as NextButton } from "./components/calendar-next-button.svelte";
|
|
12
13
|
export { default as PrevButton } from "./components/calendar-prev-button.svelte";
|
|
14
|
+
export { default as YearSelect } from "./components/calendar-year-select.svelte";
|