fluentui-svelte 0.1.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/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/components/auto-suggest-box/auto-suggest-box.d.ts +2 -0
- package/dist/components/auto-suggest-box/auto-suggest-box.js +2 -0
- package/dist/components/auto-suggest-box/auto-suggest-box.svelte +457 -0
- package/dist/components/auto-suggest-box/auto-suggest-box.svelte.d.ts +4 -0
- package/dist/components/auto-suggest-box/docs/llms.md +73 -0
- package/dist/components/auto-suggest-box/option.svelte +82 -0
- package/dist/components/auto-suggest-box/option.svelte.d.ts +4 -0
- package/dist/components/auto-suggest-box/types.d.ts +140 -0
- package/dist/components/auto-suggest-box/types.js +1 -0
- package/dist/components/avatar/avatar.svelte +269 -0
- package/dist/components/avatar/avatar.svelte.d.ts +20 -0
- package/dist/components/avatar/docs/llms.md +92 -0
- package/dist/components/avatar/type.d.ts +63 -0
- package/dist/components/avatar/type.js +1 -0
- package/dist/components/badge/badge-icon.svelte +103 -0
- package/dist/components/badge/badge-icon.svelte.d.ts +25 -0
- package/dist/components/badge/badge.svelte +163 -0
- package/dist/components/badge/badge.svelte.d.ts +20 -0
- package/dist/components/badge/docs/llms.md +133 -0
- package/dist/components/badge/types.d.ts +70 -0
- package/dist/components/badge/types.js +1 -0
- package/dist/components/badge/utils.svelte.d.ts +16 -0
- package/dist/components/badge/utils.svelte.js +35 -0
- package/dist/components/button/button.svelte +203 -0
- package/dist/components/button/button.svelte.d.ts +38 -0
- package/dist/components/button/docs/llms.md +102 -0
- package/dist/components/button/split-button.svelte +66 -0
- package/dist/components/button/split-button.svelte.d.ts +4 -0
- package/dist/components/button/types.d.ts +84 -0
- package/dist/components/button/types.js +1 -0
- package/dist/components/calendar-date-picker/calendar-date-picker.svelte +119 -0
- package/dist/components/calendar-date-picker/calendar-date-picker.svelte.d.ts +4 -0
- package/dist/components/calendar-date-picker/docs/llms.md +33 -0
- package/dist/components/calendar-date-picker/types.d.ts +36 -0
- package/dist/components/calendar-date-picker/types.js +1 -0
- package/dist/components/calendar-view/calendar-view-controls.svelte +115 -0
- package/dist/components/calendar-view/calendar-view-controls.svelte.d.ts +3 -0
- package/dist/components/calendar-view/calendar-view-days.svelte +210 -0
- package/dist/components/calendar-view/calendar-view-days.svelte.d.ts +3 -0
- package/dist/components/calendar-view/calendar-view-grid.d.ts +38 -0
- package/dist/components/calendar-view/calendar-view-grid.js +140 -0
- package/dist/components/calendar-view/calendar-view-header.svelte +66 -0
- package/dist/components/calendar-view/calendar-view-header.svelte.d.ts +18 -0
- package/dist/components/calendar-view/calendar-view-item.svelte +181 -0
- package/dist/components/calendar-view/calendar-view-item.svelte.d.ts +4 -0
- package/dist/components/calendar-view/calendar-view-months.svelte +165 -0
- package/dist/components/calendar-view/calendar-view-months.svelte.d.ts +3 -0
- package/dist/components/calendar-view/calendar-view-years.svelte +161 -0
- package/dist/components/calendar-view/calendar-view-years.svelte.d.ts +3 -0
- package/dist/components/calendar-view/calendar-view.svelte +451 -0
- package/dist/components/calendar-view/calendar-view.svelte.d.ts +9 -0
- package/dist/components/calendar-view/calendar-view.svelte.js +196 -0
- package/dist/components/calendar-view/docs/llms.md +121 -0
- package/dist/components/calendar-view/index.d.ts +1 -0
- package/dist/components/calendar-view/index.js +1 -0
- package/dist/components/calendar-view/types.d.ts +174 -0
- package/dist/components/calendar-view/types.js +1 -0
- package/dist/components/checkbox/checkbox.svelte +171 -0
- package/dist/components/checkbox/checkbox.svelte.d.ts +40 -0
- package/dist/components/checkbox/docs/llms.md +78 -0
- package/dist/components/checkbox/types.d.ts +50 -0
- package/dist/components/checkbox/types.js +1 -0
- package/dist/components/date-picker/date-picker.svelte +381 -0
- package/dist/components/date-picker/date-picker.svelte.d.ts +4 -0
- package/dist/components/date-picker/docs/llms.md +52 -0
- package/dist/components/date-picker/index.d.ts +1 -0
- package/dist/components/date-picker/index.js +1 -0
- package/dist/components/date-picker/types.d.ts +74 -0
- package/dist/components/date-picker/types.js +1 -0
- package/dist/components/dialog/dialog-actions.svelte +37 -0
- package/dist/components/dialog/dialog-actions.svelte.d.ts +4 -0
- package/dist/components/dialog/dialog-content.svelte +17 -0
- package/dist/components/dialog/dialog-content.svelte.d.ts +4 -0
- package/dist/components/dialog/dialog-surface.svelte +95 -0
- package/dist/components/dialog/dialog-surface.svelte.d.ts +4 -0
- package/dist/components/dialog/dialog-title.svelte +33 -0
- package/dist/components/dialog/dialog-title.svelte.d.ts +25 -0
- package/dist/components/dialog/dialog-trigger.svelte +23 -0
- package/dist/components/dialog/dialog-trigger.svelte.d.ts +4 -0
- package/dist/components/dialog/dialog.svelte +64 -0
- package/dist/components/dialog/dialog.svelte.d.ts +7 -0
- package/dist/components/dialog/dialog.svelte.js +2 -0
- package/dist/components/dialog/docs/llms.md +131 -0
- package/dist/components/dialog/types.d.ts +92 -0
- package/dist/components/dialog/types.js +1 -0
- package/dist/components/divider/divider.svelte +120 -0
- package/dist/components/divider/divider.svelte.d.ts +39 -0
- package/dist/components/divider/docs/llms.md +83 -0
- package/dist/components/divider/types.d.ts +32 -0
- package/dist/components/divider/types.js +1 -0
- package/dist/components/dropdown/docs/llms.md +70 -0
- package/dist/components/dropdown/dropdown-context.d.ts +2 -0
- package/dist/components/dropdown/dropdown-context.js +2 -0
- package/dist/components/dropdown/dropdown-option.svelte +31 -0
- package/dist/components/dropdown/dropdown-option.svelte.d.ts +4 -0
- package/dist/components/dropdown/dropdown.svelte +309 -0
- package/dist/components/dropdown/dropdown.svelte.d.ts +4 -0
- package/dist/components/dropdown/types.d.ts +82 -0
- package/dist/components/dropdown/types.js +1 -0
- package/dist/components/dropdown-select/docs/llms.md +72 -0
- package/dist/components/dropdown-select/dropdown-select-option.svelte +87 -0
- package/dist/components/dropdown-select/dropdown-select-option.svelte.d.ts +4 -0
- package/dist/components/dropdown-select/dropdown-select.svelte +165 -0
- package/dist/components/dropdown-select/dropdown-select.svelte.d.ts +4 -0
- package/dist/components/dropdown-select/dropdown-select.svelte.js +48 -0
- package/dist/components/dropdown-select/index.d.ts +2 -0
- package/dist/components/dropdown-select/index.js +2 -0
- package/dist/components/dropdown-select/types.d.ts +46 -0
- package/dist/components/dropdown-select/types.js +1 -0
- package/dist/components/expander/docs/llms.md +88 -0
- package/dist/components/expander/expander.svelte +249 -0
- package/dist/components/expander/expander.svelte.d.ts +21 -0
- package/dist/components/expander/types.d.ts +47 -0
- package/dist/components/expander/types.js +1 -0
- package/dist/components/flyout/docs/llms.md +31 -0
- package/dist/components/flyout/flyout.svelte +148 -0
- package/dist/components/flyout/flyout.svelte.d.ts +19 -0
- package/dist/components/flyout/types.d.ts +36 -0
- package/dist/components/flyout/types.js +1 -0
- package/dist/components/hyperlink/docs/llms.md +33 -0
- package/dist/components/hyperlink/hyperlink.svelte +66 -0
- package/dist/components/hyperlink/hyperlink.svelte.d.ts +16 -0
- package/dist/components/hyperlink/types.d.ts +14 -0
- package/dist/components/hyperlink/types.js +1 -0
- package/dist/components/info-bar/docs/llms.md +81 -0
- package/dist/components/info-bar/info-bar.svelte +140 -0
- package/dist/components/info-bar/info-bar.svelte.d.ts +19 -0
- package/dist/components/info-bar/types.d.ts +27 -0
- package/dist/components/info-bar/types.js +1 -0
- package/dist/components/label/docs/llms.md +67 -0
- package/dist/components/label/label.svelte +95 -0
- package/dist/components/label/label.svelte.d.ts +4 -0
- package/dist/components/label/types.d.ts +32 -0
- package/dist/components/label/types.js +1 -0
- package/dist/components/list-view/docs/llms.md +110 -0
- package/dist/components/list-view/list-view-item.svelte +168 -0
- package/dist/components/list-view/list-view-item.svelte.d.ts +25 -0
- package/dist/components/list-view/list-view.svelte +137 -0
- package/dist/components/list-view/list-view.svelte.d.ts +25 -0
- package/dist/components/list-view/types.d.ts +95 -0
- package/dist/components/list-view/types.js +1 -0
- package/dist/components/list-view/utils.d.ts +6 -0
- package/dist/components/list-view/utils.js +12 -0
- package/dist/components/menu/context-menu-trigger.svelte +84 -0
- package/dist/components/menu/context-menu-trigger.svelte.d.ts +9 -0
- package/dist/components/menu/docs/llms.md +169 -0
- package/dist/components/menu/menu-divider.svelte +9 -0
- package/dist/components/menu/menu-divider.svelte.d.ts +26 -0
- package/dist/components/menu/menu-group.svelte +31 -0
- package/dist/components/menu/menu-group.svelte.d.ts +4 -0
- package/dist/components/menu/menu-item-checkbox.svelte +143 -0
- package/dist/components/menu/menu-item-checkbox.svelte.d.ts +15 -0
- package/dist/components/menu/menu-item-radio.svelte +147 -0
- package/dist/components/menu/menu-item-radio.svelte.d.ts +15 -0
- package/dist/components/menu/menu-item-switch.svelte +194 -0
- package/dist/components/menu/menu-item-switch.svelte.d.ts +15 -0
- package/dist/components/menu/menu-item.svelte +124 -0
- package/dist/components/menu/menu-item.svelte.d.ts +18 -0
- package/dist/components/menu/menu-list.svelte +38 -0
- package/dist/components/menu/menu-list.svelte.d.ts +5 -0
- package/dist/components/menu/menu-popover.svelte +125 -0
- package/dist/components/menu/menu-popover.svelte.d.ts +12 -0
- package/dist/components/menu/menu-trigger.svelte +121 -0
- package/dist/components/menu/menu-trigger.svelte.d.ts +4 -0
- package/dist/components/menu/menu.svelte +124 -0
- package/dist/components/menu/menu.svelte.d.ts +4 -0
- package/dist/components/menu/menu.svelte.js +77 -0
- package/dist/components/menu/types.d.ts +127 -0
- package/dist/components/menu/types.js +1 -0
- package/dist/components/number-box/docs/llms.md +37 -0
- package/dist/components/number-box/number-box.svelte +115 -0
- package/dist/components/number-box/number-box.svelte.d.ts +15 -0
- package/dist/components/number-box/types.d.ts +16 -0
- package/dist/components/number-box/types.js +1 -0
- package/dist/components/persona/docs/llms.md +107 -0
- package/dist/components/persona/persona.svelte +79 -0
- package/dist/components/persona/persona.svelte.d.ts +4 -0
- package/dist/components/persona/types.d.ts +42 -0
- package/dist/components/persona/types.js +1 -0
- package/dist/components/progress-bar/docs/llms.md +67 -0
- package/dist/components/progress-bar/progress-bar.svelte +167 -0
- package/dist/components/progress-bar/progress-bar.svelte.d.ts +15 -0
- package/dist/components/progress-bar/types.d.ts +39 -0
- package/dist/components/progress-bar/types.js +1 -0
- package/dist/components/progress-ring/docs/llms.md +76 -0
- package/dist/components/progress-ring/progress-ring.svelte +145 -0
- package/dist/components/progress-ring/progress-ring.svelte.d.ts +15 -0
- package/dist/components/progress-ring/types.d.ts +42 -0
- package/dist/components/progress-ring/types.js +1 -0
- package/dist/components/radio-button/docs/llms.md +64 -0
- package/dist/components/radio-button/radio-button.svelte +129 -0
- package/dist/components/radio-button/radio-button.svelte.d.ts +19 -0
- package/dist/components/radio-button/types.d.ts +24 -0
- package/dist/components/radio-button/types.js +1 -0
- package/dist/components/skeleton/docs/llms.md +67 -0
- package/dist/components/skeleton/skeleton.svelte +106 -0
- package/dist/components/skeleton/skeleton.svelte.d.ts +39 -0
- package/dist/components/skeleton/types.d.ts +24 -0
- package/dist/components/skeleton/types.js +1 -0
- package/dist/components/slider/docs/llms.md +75 -0
- package/dist/components/slider/slider.svelte +486 -0
- package/dist/components/slider/slider.svelte.d.ts +19 -0
- package/dist/components/slider/types.d.ts +102 -0
- package/dist/components/slider/types.js +1 -0
- package/dist/components/text-box/docs/llms.md +86 -0
- package/dist/components/text-box/text-box-button.svelte +65 -0
- package/dist/components/text-box/text-box-button.svelte.d.ts +9 -0
- package/dist/components/text-box/text-box.svelte +299 -0
- package/dist/components/text-box/text-box.svelte.d.ts +16 -0
- package/dist/components/text-box/types.d.ts +60 -0
- package/dist/components/text-box/types.js +1 -0
- package/dist/components/textarea/docs/llms.md +55 -0
- package/dist/components/textarea/textarea.svelte +151 -0
- package/dist/components/textarea/textarea.svelte.d.ts +17 -0
- package/dist/components/textarea/types.d.ts +33 -0
- package/dist/components/textarea/types.js +1 -0
- package/dist/components/time-picker/docs/llms.md +47 -0
- package/dist/components/time-picker/index.d.ts +1 -0
- package/dist/components/time-picker/index.js +1 -0
- package/dist/components/time-picker/time-picker.svelte +336 -0
- package/dist/components/time-picker/time-picker.svelte.d.ts +4 -0
- package/dist/components/time-picker/types.d.ts +54 -0
- package/dist/components/time-picker/types.js +1 -0
- package/dist/components/toggle-switch/docs/llms.md +39 -0
- package/dist/components/toggle-switch/toggle-switch.svelte +151 -0
- package/dist/components/toggle-switch/toggle-switch.svelte.d.ts +17 -0
- package/dist/components/toggle-switch/types.d.ts +20 -0
- package/dist/components/toggle-switch/types.js +1 -0
- package/dist/components/tooltip/docs/llms.md +74 -0
- package/dist/components/tooltip/tooltip.svelte +246 -0
- package/dist/components/tooltip/tooltip.svelte.d.ts +18 -0
- package/dist/components/tooltip/types.d.ts +67 -0
- package/dist/components/tooltip/types.js +1 -0
- package/dist/components/tree-view/docs/llms.md +117 -0
- package/dist/components/tree-view/tree-view-item-content.svelte +158 -0
- package/dist/components/tree-view/tree-view-item-content.svelte.d.ts +4 -0
- package/dist/components/tree-view/tree-view-item.svelte +197 -0
- package/dist/components/tree-view/tree-view-item.svelte.d.ts +4 -0
- package/dist/components/tree-view/tree-view.svelte +220 -0
- package/dist/components/tree-view/tree-view.svelte.d.ts +4 -0
- package/dist/components/tree-view/tree-view.svelte.js +111 -0
- package/dist/components/tree-view/types.d.ts +185 -0
- package/dist/components/tree-view/types.js +1 -0
- package/dist/css/class-darkmode.css +186 -0
- package/dist/css/media-darkmode.css +189 -0
- package/dist/css/reset.css +53 -0
- package/dist/css/theme.css +276 -0
- package/dist/css/typography.css +67 -0
- package/dist/css/utils.d.ts +1 -0
- package/dist/css/utils.js +153 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +129 -0
- package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte +449 -0
- package/dist/internal/components/dynamic-carousel/dynamic-carousel.svelte.d.ts +22 -0
- package/dist/internal/components/render-soc/render-soc.svelte +29 -0
- package/dist/internal/components/render-soc/render-soc.svelte.d.ts +30 -0
- package/dist/internal/constants.d.ts +26 -0
- package/dist/internal/constants.js +4036 -0
- package/dist/internal/context.d.ts +14 -0
- package/dist/internal/context.js +13 -0
- package/dist/internal/dom-utils.svelte.d.ts +6 -0
- package/dist/internal/dom-utils.svelte.js +46 -0
- package/dist/internal/floating-ui/floating.svelte.d.ts +15 -0
- package/dist/internal/floating-ui/floating.svelte.js +26 -0
- package/dist/internal/floating-ui/utils.d.ts +3 -0
- package/dist/internal/floating-ui/utils.js +30 -0
- package/dist/internal/index.d.ts +9 -0
- package/dist/internal/index.js +9 -0
- package/dist/internal/polyfills/expander.svelte.d.ts +21 -0
- package/dist/internal/polyfills/expander.svelte.js +96 -0
- package/dist/internal/transitions.d.ts +32 -0
- package/dist/internal/transitions.js +37 -0
- package/dist/internal/utils.d.ts +22 -0
- package/dist/internal/utils.js +63 -0
- package/dist/providers/fluentui-svelte/fluentui-svelte.d.ts +19 -0
- package/dist/providers/fluentui-svelte/fluentui-svelte.js +11 -0
- package/dist/providers/fluentui-svelte/fluentui-svelte.svelte +68 -0
- package/dist/providers/fluentui-svelte/fluentui-svelte.svelte.d.ts +9 -0
- package/dist/types/index.d.ts +117 -0
- package/dist/types/index.js +1 -0
- package/package.json +142 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
import type { ComputePositionConfig } from '@floating-ui/dom';
|
|
3
|
+
import type { CalendarViewProps } from '../calendar-view/types.js';
|
|
4
|
+
/** @propsmith CalendarDatePickerProps */
|
|
5
|
+
export type CalendarDatePickerProps = {
|
|
6
|
+
/** The DOM reference of the picker element.
|
|
7
|
+
* @bindable
|
|
8
|
+
*/
|
|
9
|
+
ref?: HTMLDivElement;
|
|
10
|
+
/** The selected date.
|
|
11
|
+
* @default null
|
|
12
|
+
* @bindable
|
|
13
|
+
*/
|
|
14
|
+
value?: Date | null;
|
|
15
|
+
/** The options used to format the date rendered on the trigger.
|
|
16
|
+
* @default { year: 'numeric', month: 'numeric', day: 'numeric' }
|
|
17
|
+
*/
|
|
18
|
+
format?: Intl.DateTimeFormatOptions;
|
|
19
|
+
/** The floating UI configuration object of the calendar popup. Keys left out keep their default.
|
|
20
|
+
*/
|
|
21
|
+
calendarPosition?: Partial<ComputePositionConfig>;
|
|
22
|
+
/** Called whenever the selected date changes. */
|
|
23
|
+
onChange?: (event: Event, value: Date | null) => void;
|
|
24
|
+
/** The accessible name of the calendar popup, which is announced as a dialog.
|
|
25
|
+
* @default 'Choose a date'
|
|
26
|
+
*/
|
|
27
|
+
popupLabel?: string;
|
|
28
|
+
/** The DOM reference of the trigger button.
|
|
29
|
+
* @bindable
|
|
30
|
+
*/
|
|
31
|
+
triggerRef?: HTMLButtonElement;
|
|
32
|
+
/** The DOM reference of the calendar popup.
|
|
33
|
+
* @bindable
|
|
34
|
+
*/
|
|
35
|
+
popupRef?: HTMLDivElement;
|
|
36
|
+
} & Pick<CalendarViewProps, 'locale' | 'blackoutDates' | 'headers' | 'minDate' | 'maxDate' | 'weekStart'> & HTMLAttributes<HTMLDivElement>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '../../index.js';
|
|
3
|
+
import { CaretDownFilled, CaretUpFilled, DismissRegular } from 'fluentui-icons-svelte';
|
|
4
|
+
import { getCalendarViewContext } from './calendar-view.svelte.js';
|
|
5
|
+
|
|
6
|
+
const CalendarContext = getCalendarViewContext();
|
|
7
|
+
|
|
8
|
+
if (!CalendarContext) {
|
|
9
|
+
throw new Error('CalendarViewControls must be used within a CalendarView');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let locale = $derived(CalendarContext.config.locale);
|
|
13
|
+
|
|
14
|
+
let selectionMode = $derived(CalendarContext.config.selectionMode);
|
|
15
|
+
|
|
16
|
+
let view = $derived(CalendarContext.state.view);
|
|
17
|
+
|
|
18
|
+
let page = $derived(CalendarContext.state.page);
|
|
19
|
+
|
|
20
|
+
let range = $derived(CalendarContext.state.range);
|
|
21
|
+
|
|
22
|
+
let viewLabel = $derived.by(() => {
|
|
23
|
+
let label = '';
|
|
24
|
+
if (view === 'days') {
|
|
25
|
+
label = new Intl.DateTimeFormat(locale, {
|
|
26
|
+
year: 'numeric',
|
|
27
|
+
month: 'long'
|
|
28
|
+
}).format(page);
|
|
29
|
+
} else if (view === 'months') {
|
|
30
|
+
label = new Intl.DateTimeFormat(locale, {
|
|
31
|
+
year: 'numeric'
|
|
32
|
+
}).format(page);
|
|
33
|
+
} else if (view === 'years') {
|
|
34
|
+
const decadeStart = Math.floor(page.getFullYear() / 10) * 10;
|
|
35
|
+
const decadeEnd = decadeStart + 9;
|
|
36
|
+
|
|
37
|
+
label = new Intl.DateTimeFormat(locale, {
|
|
38
|
+
year: 'numeric'
|
|
39
|
+
}).formatRange(new Date(decadeStart, 0, 1), new Date(decadeEnd, 0, 1));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// In some locales, the first letter may not be capitalized
|
|
43
|
+
return label.charAt(0).toUpperCase() + label.slice(1);
|
|
44
|
+
});
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<div class="fs-calendar-controls">
|
|
48
|
+
<Button
|
|
49
|
+
class="period-selector"
|
|
50
|
+
disabled={view === 'years'}
|
|
51
|
+
appearance="subtle"
|
|
52
|
+
aria-label="Select month"
|
|
53
|
+
aria-live="polite"
|
|
54
|
+
onclick={(e) => CalendarContext.methods.updateView?.(e, view === 'days' ? 'months' : 'years')}
|
|
55
|
+
>
|
|
56
|
+
{viewLabel}
|
|
57
|
+
</Button>
|
|
58
|
+
<div class="navigation">
|
|
59
|
+
<Button appearance="subtle" aria-label="Previous month" onclick={() => CalendarContext.methods.updatePage(-1)}>
|
|
60
|
+
<CaretUpFilled width="16" />
|
|
61
|
+
</Button>
|
|
62
|
+
<Button appearance="subtle" aria-label="Next month" onclick={() => CalendarContext.methods.updatePage(1)}>
|
|
63
|
+
<CaretDownFilled width="16" />
|
|
64
|
+
</Button>
|
|
65
|
+
<!--
|
|
66
|
+
Only a range needs this. Clicking a selected day already takes it back in the
|
|
67
|
+
other two modes, but a range has no such click: every click builds it further
|
|
68
|
+
or starts it over.
|
|
69
|
+
-->
|
|
70
|
+
{#if selectionMode === 'range'}
|
|
71
|
+
<Button
|
|
72
|
+
appearance="subtle"
|
|
73
|
+
aria-label="Clear the selected dates"
|
|
74
|
+
disabled={!range.start && !range.end}
|
|
75
|
+
onclick={(e) => CalendarContext.methods.clearSelection(e)}
|
|
76
|
+
>
|
|
77
|
+
<DismissRegular width="16" />
|
|
78
|
+
</Button>
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<style>
|
|
84
|
+
.fs-calendar-controls {
|
|
85
|
+
display: flex;
|
|
86
|
+
justify-content: space-between;
|
|
87
|
+
padding: 0.5rem;
|
|
88
|
+
border-bottom: 1px solid var(--fs-control-surface-stroke);
|
|
89
|
+
width: 100%;
|
|
90
|
+
gap: 0.5rem;
|
|
91
|
+
border-bottom: 1px solid var(--fs-control-stroke-default);
|
|
92
|
+
& > :global(.period-selector) {
|
|
93
|
+
flex-grow: 1;
|
|
94
|
+
justify-content: flex-start;
|
|
95
|
+
font-weight: 500;
|
|
96
|
+
font-size: var(--fs-body2-font-size);
|
|
97
|
+
/* A third button on the right needs room, and this is where it comes from. */
|
|
98
|
+
min-width: 0;
|
|
99
|
+
}
|
|
100
|
+
& .navigation {
|
|
101
|
+
display: flex;
|
|
102
|
+
gap: 0.5rem;
|
|
103
|
+
position: relative;
|
|
104
|
+
& > :global(.fs-button) {
|
|
105
|
+
min-width: max-content;
|
|
106
|
+
padding-inline: 0.5rem;
|
|
107
|
+
& > :global(svg) {
|
|
108
|
+
width: 16px;
|
|
109
|
+
height: 16px;
|
|
110
|
+
fill: var(--fs-control-strong-default);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fly } from 'svelte/transition';
|
|
3
|
+
import { circOut } from 'svelte/easing';
|
|
4
|
+
import {
|
|
5
|
+
compareDates,
|
|
6
|
+
getCalendarDays,
|
|
7
|
+
getCalendarViewContext,
|
|
8
|
+
getMonthLocale,
|
|
9
|
+
hasBlackoutBetween,
|
|
10
|
+
indexOfDate,
|
|
11
|
+
rangePositionOf,
|
|
12
|
+
startOfDay
|
|
13
|
+
} from './calendar-view.svelte.js';
|
|
14
|
+
import CalendarViewItem from './calendar-view-item.svelte';
|
|
15
|
+
import { getGlobalFSContext, getReducedMotion } from '../../providers/fluentui-svelte/fluentui-svelte.js';
|
|
16
|
+
|
|
17
|
+
import { cellDate, createCalendarNavigation, DAY_GRID } from './calendar-view-grid.js';
|
|
18
|
+
import { getCSSDuration } from '../../internal/index.js';
|
|
19
|
+
import { on } from 'svelte/events';
|
|
20
|
+
import type { CalendarDateRange } from './types.js';
|
|
21
|
+
|
|
22
|
+
const CalendarContext = getCalendarViewContext();
|
|
23
|
+
|
|
24
|
+
if (!CalendarContext) {
|
|
25
|
+
throw new Error('CalendarViewDays must be used within a CalendarView');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { selectDay, updatePage } = CalendarContext.methods;
|
|
29
|
+
|
|
30
|
+
let reducedMotion = getReducedMotion();
|
|
31
|
+
|
|
32
|
+
let { value, range, page, pageAnimationDirection } = $derived(CalendarContext.state);
|
|
33
|
+
|
|
34
|
+
let { locale, minDate, weekStart, blackoutDates, headers, maxDate, selectionMode, blackoutBreaksRange } = $derived(
|
|
35
|
+
CalendarContext.config
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
let calendarDays = $derived(getCalendarDays(page, weekStart));
|
|
39
|
+
|
|
40
|
+
let bodyElement: HTMLElement | null = $state(null);
|
|
41
|
+
|
|
42
|
+
let pageAnimationDuration: number = $derived(reducedMotion() ? 0 : getCSSDuration('--fs-normal-duration') || 333);
|
|
43
|
+
|
|
44
|
+
// The day the pointer or the keyboard is offering as the end of a half-built range.
|
|
45
|
+
// Two signals rather than one: leaving with the pointer must not wipe out the
|
|
46
|
+
// preview a focused cell is still holding.
|
|
47
|
+
let hoveredDay: Date | null = $state(null);
|
|
48
|
+
|
|
49
|
+
let focusedDay: Date | null = $state(null);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The range to paint: the one that is committed, or the one the user is pointing at.
|
|
53
|
+
*
|
|
54
|
+
* Nothing is painted for a backwards preview or for a span this configuration will
|
|
55
|
+
* not accept — the absence of a band is how the grid says "that click will start over".
|
|
56
|
+
*/
|
|
57
|
+
let bandRange = $derived.by<CalendarDateRange | null>(() => {
|
|
58
|
+
if (selectionMode !== 'range' || !range.start) return null;
|
|
59
|
+
|
|
60
|
+
const end = range.end ?? hoveredDay ?? focusedDay;
|
|
61
|
+
|
|
62
|
+
if (!end || startOfDay(end) < startOfDay(range.start)) return null;
|
|
63
|
+
if (blackoutBreaksRange && hasBlackoutBetween(range.start, end, blackoutDates)) return null;
|
|
64
|
+
|
|
65
|
+
return { start: range.start, end };
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const isSelected = (day: Date) => {
|
|
69
|
+
if (selectionMode === 'range') {
|
|
70
|
+
return (
|
|
71
|
+
(!!range.start && compareDates(range.start, day, 'day')) || (!!range.end && compareDates(range.end, day, 'day'))
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return !!value && (Array.isArray(value) ? indexOfDate(value, day, 'day') > -1 : compareDates(value, day, 'day'));
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const isDisabled = (day: Date) => (minDate && minDate > day) || (maxDate && maxDate < day);
|
|
79
|
+
|
|
80
|
+
const isBlackout = (day: Date) => !!blackoutDates && indexOfDate(blackoutDates, day, 'day') > -1;
|
|
81
|
+
|
|
82
|
+
/** What the grid as a whole is showing. */
|
|
83
|
+
let pageLabel = $derived(new Intl.DateTimeFormat(locale, { year: 'numeric', month: 'long' }).format(page));
|
|
84
|
+
|
|
85
|
+
/** "Thursday, 15 January 2026" — the weekday is why the header row can stay decorative. */
|
|
86
|
+
const dayLabel = (day: Date) => new Intl.DateTimeFormat(locale, { dateStyle: 'full' }).format(day);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Follows the day the user is offering as the end of the range.
|
|
90
|
+
*
|
|
91
|
+
* Delegated on the grid rather than bound per cell: `pointerover` and `focusin`
|
|
92
|
+
* both bubble, so six weeks of days cost four listeners instead of a hundred and
|
|
93
|
+
* sixty-eight. The day is resolved from the whole cell rather than from the button,
|
|
94
|
+
* because the button is a circle and the corners of its cell are not inside it —
|
|
95
|
+
* reading only the button would blink the band off as the pointer crossed them.
|
|
96
|
+
*/
|
|
97
|
+
function rangePreview(node: HTMLElement) {
|
|
98
|
+
if (selectionMode !== 'range') return;
|
|
99
|
+
|
|
100
|
+
const dayUnder = (target: EventTarget | null) => {
|
|
101
|
+
const button = (target as Element)?.closest?.('[role="gridcell"]')?.querySelector('button');
|
|
102
|
+
|
|
103
|
+
return button && !button.disabled ? cellDate(button) : null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const off = [
|
|
107
|
+
on(node, 'pointerover', (event) => (hoveredDay = dayUnder(event.target))),
|
|
108
|
+
on(node, 'pointerleave', () => (hoveredDay = null)),
|
|
109
|
+
on(node, 'focusin', (event) => (focusedDay = dayUnder(event.target))),
|
|
110
|
+
on(node, 'focusout', (event) => {
|
|
111
|
+
if (!node.contains(event.relatedTarget as Node)) focusedDay = null;
|
|
112
|
+
})
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
return () => off.forEach((unsubscribe) => unsubscribe());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const globalContext = getGlobalFSContext();
|
|
119
|
+
|
|
120
|
+
const navigate = createCalendarNavigation({
|
|
121
|
+
grid: DAY_GRID,
|
|
122
|
+
body: () => bodyElement,
|
|
123
|
+
page: () => page,
|
|
124
|
+
disabled: (day) => !!isDisabled(day),
|
|
125
|
+
blackout: isBlackout,
|
|
126
|
+
updatePage
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Tabspot reports what it cannot decide — running out of cells, or landing on a
|
|
130
|
+
// date this page does not own — and the calendar answers by turning the page.
|
|
131
|
+
$effect(() => {
|
|
132
|
+
const root = bodyElement?.closest<HTMLElement>('[data-calendar-grid]');
|
|
133
|
+
const instance = globalContext?.state.tabspotInstance;
|
|
134
|
+
|
|
135
|
+
if (!root || !instance) return;
|
|
136
|
+
|
|
137
|
+
return instance.subscribe(root, navigate);
|
|
138
|
+
});
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
{#key page}
|
|
142
|
+
<div
|
|
143
|
+
bind:this={bodyElement}
|
|
144
|
+
class="fs-calendar-view-days"
|
|
145
|
+
data-calendar-page
|
|
146
|
+
role="grid"
|
|
147
|
+
aria-label={pageLabel}
|
|
148
|
+
aria-multiselectable={selectionMode !== 'single' || undefined}
|
|
149
|
+
{@attach rangePreview}
|
|
150
|
+
in:fly={{
|
|
151
|
+
opacity: 1,
|
|
152
|
+
duration: pageAnimationDirection !== 'neutral' ? pageAnimationDuration : pageAnimationDuration,
|
|
153
|
+
easing: circOut,
|
|
154
|
+
y: pageAnimationDirection === 'neutral' ? 0 : pageAnimationDirection === 'up' ? -215 : 215
|
|
155
|
+
}}
|
|
156
|
+
out:fly={{
|
|
157
|
+
opacity: 0,
|
|
158
|
+
duration: pageAnimationDirection !== 'neutral' ? pageAnimationDuration : pageAnimationDuration,
|
|
159
|
+
easing: circOut,
|
|
160
|
+
y: pageAnimationDirection === 'neutral' ? 0 : pageAnimationDirection === 'up' ? 215 : -215
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
{#each Array(6) as _, week (week)}
|
|
164
|
+
<div class="fs-calendar-view-week" role="row">
|
|
165
|
+
{#each calendarDays.slice(week * 7, week * 7 + 7) as day, i (week * 7 + i)}
|
|
166
|
+
{@const selected = isSelected(day)}
|
|
167
|
+
{@const inMonth = compareDates(day, page, 'month')}
|
|
168
|
+
{@const header =
|
|
169
|
+
(headers && day.getDate() === 1 && getMonthLocale(day.getMonth(), { locale, format: 'short' })) || ''}
|
|
170
|
+
<CalendarViewItem
|
|
171
|
+
{header}
|
|
172
|
+
{selected}
|
|
173
|
+
rangePosition={bandRange ? rangePositionOf(day, bandRange) : undefined}
|
|
174
|
+
outOfRange={!inMonth}
|
|
175
|
+
current={compareDates(day, new Date(), 'day')}
|
|
176
|
+
disabled={isDisabled(day)}
|
|
177
|
+
blackout={isBlackout(day)}
|
|
178
|
+
buttonAttributes={{
|
|
179
|
+
tabindex: inMonth ? 0 : -1,
|
|
180
|
+
'aria-label': dayLabel(day),
|
|
181
|
+
'data-date': day.getTime(),
|
|
182
|
+
onclick: (e: MouseEvent) => {
|
|
183
|
+
selectDay(e, day);
|
|
184
|
+
}
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
{day.getDate()}
|
|
188
|
+
</CalendarViewItem>
|
|
189
|
+
{/each}
|
|
190
|
+
</div>
|
|
191
|
+
{/each}
|
|
192
|
+
</div>
|
|
193
|
+
{/key}
|
|
194
|
+
|
|
195
|
+
<style>
|
|
196
|
+
.fs-calendar-view-days {
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
inline-size: 100%;
|
|
200
|
+
position: absolute;
|
|
201
|
+
left: 0;
|
|
202
|
+
bottom: 0;
|
|
203
|
+
padding-bottom: 0.5rem;
|
|
204
|
+
& .fs-calendar-view-week {
|
|
205
|
+
display: grid;
|
|
206
|
+
grid-template-columns: repeat(7, 1fr);
|
|
207
|
+
grid-auto-rows: 1fr;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TabspotEventListener, TabspotNodeOptions } from 'tabspot';
|
|
2
|
+
import type { CalendarGrid, CalendarNavigationOptions, DateComparisonPrecision, View } from './types.js';
|
|
3
|
+
export declare const CALENDAR_GRID_CONFIG: TabspotNodeOptions;
|
|
4
|
+
/** Six weeks of seven days; a day belongs to the page when it shares its month. */
|
|
5
|
+
export declare const DAY_GRID: CalendarGrid;
|
|
6
|
+
/** The twelve months of the page year plus the first four of the next one. */
|
|
7
|
+
export declare const MONTH_GRID: CalendarGrid;
|
|
8
|
+
/** The page decade padded with its neighbours. */
|
|
9
|
+
export declare const YEAR_GRID: CalendarGrid;
|
|
10
|
+
/** The date a rendered cell stands for. */
|
|
11
|
+
export declare function cellDate(element: Element | null): Date | null;
|
|
12
|
+
/** Precision at which a date names exactly one cell of a view. */
|
|
13
|
+
export declare const VIEW_PRECISION: Record<View, DateComparisonPrecision>;
|
|
14
|
+
/**
|
|
15
|
+
* Hands focus to the view rendered inside `root`.
|
|
16
|
+
*
|
|
17
|
+
* `date` — the cell that had focus before a view change — wins when the new view
|
|
18
|
+
* renders it. Otherwise focus falls back to the selection, then to today, then to
|
|
19
|
+
* the first date of the page: the order WAI-ARIA prescribes for a date grid.
|
|
20
|
+
*/
|
|
21
|
+
export declare function focusCalendarView(root: HTMLElement, date: Date | null, precision: DateComparisonPrecision): void;
|
|
22
|
+
/**
|
|
23
|
+
* Turns the page when navigation runs off the rendered grid.
|
|
24
|
+
*
|
|
25
|
+
* Tabspot owns everything that happens *inside* the six-by-seven (or four-by-four)
|
|
26
|
+
* matrix: which cell an arrow resolves to, wrapping from one row into the next,
|
|
27
|
+
* `Home`/`End`, and the fact that a disabled cell cannot take focus. It reports
|
|
28
|
+
* the two things it cannot decide on its own, and this listener answers them:
|
|
29
|
+
*
|
|
30
|
+
* - `atEdge` — the move ran out of cells. Only the calendar knows there is a date
|
|
31
|
+
* beyond that edge, so it turns the page and lands on it.
|
|
32
|
+
* - the move resolved to a cell that does not belong to the page (an adjacent
|
|
33
|
+
* month, year or decade) or to a blacked-out date — page to it, or step past it.
|
|
34
|
+
*
|
|
35
|
+
* Both cases claim the key: the grid ran out of cells but the calendar did not,
|
|
36
|
+
* so the browser must not scroll the page underneath it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createCalendarNavigation({ grid, body, page, disabled, blackout, updatePage }: CalendarNavigationOptions): TabspotEventListener;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { tick } from 'svelte';
|
|
2
|
+
import { SvelteDate } from 'svelte/reactivity';
|
|
3
|
+
import { compareDates } from './calendar-view.svelte.js';
|
|
4
|
+
export const CALENDAR_GRID_CONFIG = {
|
|
5
|
+
root: { manageSpecialKeys: { Home: true, End: true } },
|
|
6
|
+
mover: {
|
|
7
|
+
layout: 'grid',
|
|
8
|
+
flow: 'linear',
|
|
9
|
+
rows: { by: 'selector', row: '[role="row"]' },
|
|
10
|
+
items: '[data-calendar-page]:not([inert]) button',
|
|
11
|
+
skip: '.blackout',
|
|
12
|
+
activation: { mode: 'focus', roving: false }
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
/** Six weeks of seven days; a day belongs to the page when it shares its month. */
|
|
16
|
+
export const DAY_GRID = {
|
|
17
|
+
columns: 7,
|
|
18
|
+
size: 42,
|
|
19
|
+
precision: 'month',
|
|
20
|
+
shift: (date, amount) => new SvelteDate(new SvelteDate(date).setDate(date.getDate() + amount))
|
|
21
|
+
};
|
|
22
|
+
/** The twelve months of the page year plus the first four of the next one. */
|
|
23
|
+
export const MONTH_GRID = {
|
|
24
|
+
columns: 4,
|
|
25
|
+
size: 16,
|
|
26
|
+
precision: 'year',
|
|
27
|
+
shift: (date, amount) => new SvelteDate(new SvelteDate(date).setMonth(date.getMonth() + amount, 1))
|
|
28
|
+
};
|
|
29
|
+
/** The page decade padded with its neighbours. */
|
|
30
|
+
export const YEAR_GRID = {
|
|
31
|
+
columns: 4,
|
|
32
|
+
size: 16,
|
|
33
|
+
precision: 'decade',
|
|
34
|
+
shift: (date, amount) => new SvelteDate(new SvelteDate(date).setFullYear(date.getFullYear() + amount))
|
|
35
|
+
};
|
|
36
|
+
/** How far one move travels, in cells. `undefined` for directions the calendar ignores. */
|
|
37
|
+
function amountFor(direction, columns) {
|
|
38
|
+
switch (direction) {
|
|
39
|
+
case 'left':
|
|
40
|
+
case 'home':
|
|
41
|
+
return -1;
|
|
42
|
+
case 'right':
|
|
43
|
+
case 'end':
|
|
44
|
+
return 1;
|
|
45
|
+
case 'up':
|
|
46
|
+
return -columns;
|
|
47
|
+
case 'down':
|
|
48
|
+
return columns;
|
|
49
|
+
default:
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The date a rendered cell stands for. */
|
|
54
|
+
export function cellDate(element) {
|
|
55
|
+
const value = element?.getAttribute?.('data-date');
|
|
56
|
+
return value ? new SvelteDate(Number(value)) : null;
|
|
57
|
+
}
|
|
58
|
+
/** Precision at which a date names exactly one cell of a view. */
|
|
59
|
+
export const VIEW_PRECISION = {
|
|
60
|
+
days: 'day',
|
|
61
|
+
months: 'month',
|
|
62
|
+
years: 'year'
|
|
63
|
+
};
|
|
64
|
+
/** Cells of the live page that can take focus. */
|
|
65
|
+
const FOCUSABLE_CELL = '[data-calendar-page]:not([inert]) button:not([disabled])';
|
|
66
|
+
function cellFor(root, date, precision) {
|
|
67
|
+
for (const cell of root.querySelectorAll(FOCUSABLE_CELL)) {
|
|
68
|
+
const value = cellDate(cell);
|
|
69
|
+
if (value && compareDates(value, date, precision))
|
|
70
|
+
return cell;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Hands focus to the view rendered inside `root`.
|
|
76
|
+
*
|
|
77
|
+
* `date` — the cell that had focus before a view change — wins when the new view
|
|
78
|
+
* renders it. Otherwise focus falls back to the selection, then to today, then to
|
|
79
|
+
* the first date of the page: the order WAI-ARIA prescribes for a date grid.
|
|
80
|
+
*/
|
|
81
|
+
export function focusCalendarView(root, date, precision) {
|
|
82
|
+
const target = (date && cellFor(root, date, precision)) ??
|
|
83
|
+
root.querySelector(`${FOCUSABLE_CELL}.selected:not(.out-of-range)`) ??
|
|
84
|
+
root.querySelector(`${FOCUSABLE_CELL}.current:not(.out-of-range)`) ??
|
|
85
|
+
root.querySelector(`${FOCUSABLE_CELL}:not(.out-of-range)`);
|
|
86
|
+
target?.focus({ preventScroll: true });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Turns the page when navigation runs off the rendered grid.
|
|
90
|
+
*
|
|
91
|
+
* Tabspot owns everything that happens *inside* the six-by-seven (or four-by-four)
|
|
92
|
+
* matrix: which cell an arrow resolves to, wrapping from one row into the next,
|
|
93
|
+
* `Home`/`End`, and the fact that a disabled cell cannot take focus. It reports
|
|
94
|
+
* the two things it cannot decide on its own, and this listener answers them:
|
|
95
|
+
*
|
|
96
|
+
* - `atEdge` — the move ran out of cells. Only the calendar knows there is a date
|
|
97
|
+
* beyond that edge, so it turns the page and lands on it.
|
|
98
|
+
* - the move resolved to a cell that does not belong to the page (an adjacent
|
|
99
|
+
* month, year or decade) or to a blacked-out date — page to it, or step past it.
|
|
100
|
+
*
|
|
101
|
+
* Both cases claim the key: the grid ran out of cells but the calendar did not,
|
|
102
|
+
* so the browser must not scroll the page underneath it.
|
|
103
|
+
*/
|
|
104
|
+
export function createCalendarNavigation({ grid, body, page, disabled, blackout, updatePage }) {
|
|
105
|
+
const onPage = (date) => compareDates(date, page(), grid.precision);
|
|
106
|
+
/** Resolve `target` to a focusable cell, turning the page when it lies outside it. */
|
|
107
|
+
async function focusDate(target, amount) {
|
|
108
|
+
// Blacked-out dates are rendered but cannot be selected: keep going.
|
|
109
|
+
for (let step = 0; step < grid.size && blackout(target); step++) {
|
|
110
|
+
target = grid.shift(target, amount);
|
|
111
|
+
}
|
|
112
|
+
if (blackout(target) || disabled(target))
|
|
113
|
+
return;
|
|
114
|
+
if (!onPage(target)) {
|
|
115
|
+
updatePage(Math.sign(amount), 'neutral');
|
|
116
|
+
await tick();
|
|
117
|
+
}
|
|
118
|
+
body()?.querySelector(`button[data-date="${target.getTime()}"]`)?.focus();
|
|
119
|
+
}
|
|
120
|
+
return (event) => {
|
|
121
|
+
const amount = amountFor(event.direction, grid.columns);
|
|
122
|
+
if (amount === undefined)
|
|
123
|
+
return;
|
|
124
|
+
if (event.atEdge) {
|
|
125
|
+
const origin = cellDate(event.from);
|
|
126
|
+
if (!origin)
|
|
127
|
+
return;
|
|
128
|
+
event.preventDefault();
|
|
129
|
+
focusDate(grid.shift(origin, amount), amount);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Tabspot steps over blacked-out cells itself, so the only landing left to
|
|
133
|
+
// answer for is one on a date this page does not own.
|
|
134
|
+
const landed = cellDate(event.to);
|
|
135
|
+
if (!landed || onPage(landed))
|
|
136
|
+
return;
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
focusDate(landed, amount);
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getCalendarViewContext, getWeekdayLocale } from './calendar-view.svelte.js';
|
|
3
|
+
|
|
4
|
+
const context = getCalendarViewContext();
|
|
5
|
+
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error('CalendarViewHeader must be used within a CalendarView');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let locale = context.config.locale;
|
|
11
|
+
|
|
12
|
+
let weekStart = context.config.weekStart;
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!--
|
|
16
|
+
Decorative: every cell announces its own full date — "Thursday, 15 January 2026" —
|
|
17
|
+
so repeating the weekday here would only add noise. Marking it as a row of
|
|
18
|
+
`columnheader`s would also drag the grid structure into the clipped viewport.
|
|
19
|
+
-->
|
|
20
|
+
<div class="fs-calendar-view-header" aria-hidden="true">
|
|
21
|
+
<div class="calendar-header-row">
|
|
22
|
+
{#each Array(7) as _, i (i)}
|
|
23
|
+
<div class="calendar-header-item">
|
|
24
|
+
{getWeekdayLocale(i, { format: 'short', locale, offset: weekStart })}
|
|
25
|
+
</div>
|
|
26
|
+
{/each}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.fs-calendar-view-header {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
padding-top: 0.4rem;
|
|
35
|
+
width: 100%;
|
|
36
|
+
position: relative;
|
|
37
|
+
z-index: 1;
|
|
38
|
+
&::before {
|
|
39
|
+
content: '';
|
|
40
|
+
border-radius: calc(var(--fs-control-overlay-border-radius) - 0.063rem);
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
position: absolute;
|
|
44
|
+
inset: 0;
|
|
45
|
+
background-size: 2.5rem;
|
|
46
|
+
opacity: 0.065;
|
|
47
|
+
filter: grayscale(1);
|
|
48
|
+
z-index: 0;
|
|
49
|
+
pointer-events: none;
|
|
50
|
+
}
|
|
51
|
+
& .calendar-header-row {
|
|
52
|
+
display: grid;
|
|
53
|
+
height: 40px;
|
|
54
|
+
grid-template-columns: repeat(7, 1fr);
|
|
55
|
+
& .calendar-header-item {
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
text-align: center;
|
|
60
|
+
font-weight: 600;
|
|
61
|
+
font-size: var(--fs-body2-font-size);
|
|
62
|
+
color: var(--fs-text-color-secondary);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const CalendarViewHeader: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type CalendarViewHeader = InstanceType<typeof CalendarViewHeader>;
|
|
18
|
+
export default CalendarViewHeader;
|