fluent-svelte-extra 1.0.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/.prettierignore +1 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/package.json +83 -0
- package/src/app.html +12 -0
- package/src/global.d.ts +1 -0
- package/src/lib/AutoSuggestBox/AutoSuggestBox.scss +44 -0
- package/src/lib/AutoSuggestBox/AutoSuggestBox.svelte +173 -0
- package/src/lib/Button/Button.scss +94 -0
- package/src/lib/Button/Button.svelte +48 -0
- package/src/lib/CalendarDatePicker/CalendarDatePicker.scss +15 -0
- package/src/lib/CalendarDatePicker/CalendarDatePicker.svelte +86 -0
- package/src/lib/CalendarView/CalendarView.scss +156 -0
- package/src/lib/CalendarView/CalendarView.svelte +753 -0
- package/src/lib/CalendarView/CalendarViewItem.scss +130 -0
- package/src/lib/CalendarView/CalendarViewItem.svelte +33 -0
- package/src/lib/Checkbox/Checkbox.scss +117 -0
- package/src/lib/Checkbox/Checkbox.svelte +81 -0
- package/src/lib/ComboBox/ComboBox.scss +152 -0
- package/src/lib/ComboBox/ComboBox.svelte +360 -0
- package/src/lib/ComboBox/ComboBoxItem.scss +80 -0
- package/src/lib/ComboBox/ComboBoxItem.svelte +30 -0
- package/src/lib/ContentDialog/ContentDialog.scss +68 -0
- package/src/lib/ContentDialog/ContentDialog.svelte +123 -0
- package/src/lib/ContextMenu/ContextMenu.scss +11 -0
- package/src/lib/ContextMenu/ContextMenu.svelte +104 -0
- package/src/lib/Expander/Expander.scss +134 -0
- package/src/lib/Expander/Expander.svelte +123 -0
- package/src/lib/Flipper/Flipper.svelte +49 -0
- package/src/lib/Flyout/FlyoutSurface.scss +14 -0
- package/src/lib/Flyout/FlyoutSurface.svelte +21 -0
- package/src/lib/Flyout/FlyoutWrapper.scss +81 -0
- package/src/lib/Flyout/FlyoutWrapper.svelte +126 -0
- package/src/lib/IconButton/IconButton.scss +31 -0
- package/src/lib/IconButton/IconButton.svelte +49 -0
- package/src/lib/InfoBadge/InfoBadge.scss +39 -0
- package/src/lib/InfoBadge/InfoBadge.svelte +81 -0
- package/src/lib/InfoBar/InfoBar.scss +122 -0
- package/src/lib/InfoBar/InfoBar.svelte +133 -0
- package/src/lib/ListItem/ListItem.scss +74 -0
- package/src/lib/ListItem/ListItem.svelte +88 -0
- package/src/lib/MenuBar/MenuBar.scss +10 -0
- package/src/lib/MenuBar/MenuBar.svelte +49 -0
- package/src/lib/MenuBar/MenuBarItem.scss +38 -0
- package/src/lib/MenuBar/MenuBarItem.svelte +135 -0
- package/src/lib/MenuBar/flyoutState.ts +5 -0
- package/src/lib/MenuFlyout/MenuFlyoutDivider.scss +7 -0
- package/src/lib/MenuFlyout/MenuFlyoutDivider.svelte +14 -0
- package/src/lib/MenuFlyout/MenuFlyoutItem.scss +147 -0
- package/src/lib/MenuFlyout/MenuFlyoutItem.svelte +239 -0
- package/src/lib/MenuFlyout/MenuFlyoutSurface.scss +42 -0
- package/src/lib/MenuFlyout/MenuFlyoutSurface.svelte +28 -0
- package/src/lib/MenuFlyout/MenuFlyoutWrapper.scss +64 -0
- package/src/lib/MenuFlyout/MenuFlyoutWrapper.svelte +114 -0
- package/src/lib/NavigationView/NavigationView.scss +0 -0
- package/src/lib/NavigationView/NavigationView.svelte +82 -0
- package/src/lib/NumberBox/NumberBox.scss +31 -0
- package/src/lib/NumberBox/NumberBox.svelte +267 -0
- package/src/lib/PersonPicture/PersonPicture.scss +35 -0
- package/src/lib/PersonPicture/PersonPicture.svelte +62 -0
- package/src/lib/ProgressBar/ProgressBar.scss +83 -0
- package/src/lib/ProgressBar/ProgressBar.svelte +60 -0
- package/src/lib/ProgressRing/ProgressRing.scss +37 -0
- package/src/lib/ProgressRing/ProgressRing.svelte +73 -0
- package/src/lib/RadioButton/RadioButton.scss +114 -0
- package/src/lib/RadioButton/RadioButton.svelte +67 -0
- package/src/lib/RangeSlider/RangeSlider.svelte +91 -0
- package/src/lib/ScrollView/ScrollView.svelte +9 -0
- package/src/lib/Slider/Slider.scss +263 -0
- package/src/lib/Slider/Slider.svelte +261 -0
- package/src/lib/TextBlock/TextBlock.scss +62 -0
- package/src/lib/TextBlock/TextBlock.svelte +70 -0
- package/src/lib/TextBox/TextBox.scss +108 -0
- package/src/lib/TextBox/TextBox.svelte +225 -0
- package/src/lib/TextBox/TextBoxButton.scss +34 -0
- package/src/lib/TextBox/TextBoxButton.svelte +27 -0
- package/src/lib/ToggleSwitch/ToggleSwitch.scss +118 -0
- package/src/lib/ToggleSwitch/ToggleSwitch.svelte +55 -0
- package/src/lib/Tooltip/TooltipSurface.scss +16 -0
- package/src/lib/Tooltip/TooltipSurface.svelte +27 -0
- package/src/lib/Tooltip/TooltipWrapper.scss +66 -0
- package/src/lib/Tooltip/TooltipWrapper.svelte +117 -0
- package/src/lib/_mixins.scss +130 -0
- package/src/lib/index.ts +33 -0
- package/src/lib/internal.ts +213 -0
- package/src/lib/svelte-jsx.d.ts +14 -0
- package/src/lib/theme.css +414 -0
- package/src/routes/__layout.svelte +48 -0
- package/src/routes/docs/__layout.svelte +122 -0
- package/src/routes/docs/components/button.md +43 -0
- package/src/routes/docs/components/calendarview.md +188 -0
- package/src/routes/docs/components/checkbox.md +87 -0
- package/src/routes/docs/components/contentdialog.md +155 -0
- package/src/routes/docs/components/expander.md +115 -0
- package/src/routes/docs/components/flyout.md +107 -0
- package/src/routes/docs/components/iconbutton.md +39 -0
- package/src/routes/docs/components/infobadge.md +54 -0
- package/src/routes/docs/components/infobar.md +102 -0
- package/src/routes/docs/components/listitem.md +87 -0
- package/src/routes/docs/components/personpicture.md +125 -0
- package/src/routes/docs/components/progressring.md +83 -0
- package/src/routes/docs/components/radiobutton.md +88 -0
- package/src/routes/docs/components/slider.md +165 -0
- package/src/routes/docs/components/textblock.md +46 -0
- package/src/routes/docs/components/textbox.md +124 -0
- package/src/routes/docs/components/toggleswitch.md +73 -0
- package/src/routes/docs/getting-started.md +116 -0
- package/src/routes/docs/index.md +37 -0
- package/src/routes/docs/internals/index.md +0 -0
- package/src/routes/index.svelte +121 -0
- package/src/routes/test/__layout-test.svelte +1 -0
- package/src/routes/test/index.svelte +757 -0
- package/src/routes/test/nav.svelte +7 -0
- package/src/site/data/docs.ts +176 -0
- package/src/site/data/home.ts +12 -0
- package/src/site/lib/APIDocs/APIDocs.svelte +178 -0
- package/src/site/lib/APIDocs/ParsedComponent.d.ts +85 -0
- package/src/site/lib/CopyBox/CopyBox.svelte +23 -0
- package/src/site/lib/Example/Example.scss +33 -0
- package/src/site/lib/Example/Example.svelte +18 -0
- package/src/site/lib/HeroCard/HeroCard.scss +24 -0
- package/src/site/lib/HeroCard/HeroCard.svelte +36 -0
- package/src/site/lib/Metadata/Metadata.svelte +14 -0
- package/src/site/lib/Navbar/Navbar.scss +92 -0
- package/src/site/lib/Navbar/Navbar.svelte +47 -0
- package/src/site/lib/PageSection/PageSection.scss +57 -0
- package/src/site/lib/PageSection/PageSection.svelte +10 -0
- package/src/site/lib/Showcase/Showcase.scss +53 -0
- package/src/site/lib/Showcase/Showcase.svelte +67 -0
- package/src/site/lib/Toc/Toc.scss +18 -0
- package/src/site/lib/Toc/Toc.svelte +59 -0
- package/src/site/lib/TreeView/TreeView.svelte +89 -0
- package/src/site/lib/index.ts +9 -0
- package/src/site/styles/_markdown.scss +260 -0
- package/src/site/styles/_mixins.scss +319 -0
- package/src/site/styles/global.scss +40 -0
- package/src/site/styles/pages/docs.scss +74 -0
- package/src/site/styles/pages/home.scss +134 -0
- package/static/bloom-mica-dark.png +0 -0
- package/static/bloom-mica-light.png +0 -0
- package/static/logo.svg +11 -0
- package/svelte.config.js +57 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createEventDispatcher, onMount, tick } from "svelte";
|
|
3
|
+
import { fly } from "svelte/transition";
|
|
4
|
+
import { circOut } from "svelte/easing";
|
|
5
|
+
import { get_current_component } from "svelte/internal";
|
|
6
|
+
import { createEventForwarder, getCSSDuration } from "../internal";
|
|
7
|
+
|
|
8
|
+
import CalendarViewItem from "./CalendarViewItem.svelte";
|
|
9
|
+
|
|
10
|
+
type View = "days" | "months" | "years";
|
|
11
|
+
type AnimationDirection = "up" | "down" | "neutral";
|
|
12
|
+
type DateTimeWeekdayFormat = "long" | "short" | "narrow";
|
|
13
|
+
type DateTimeMonthFormat = "long" | "short" | "narrow" | "numeric" | "2-digit";
|
|
14
|
+
type DateComparisonPrecision = "time" | "day" | "month" | "year" | "decade";
|
|
15
|
+
|
|
16
|
+
interface WeekdayLocaleOptions {
|
|
17
|
+
locale?: string;
|
|
18
|
+
format?: DateTimeWeekdayFormat;
|
|
19
|
+
offset?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface MonthLocaleOptions {
|
|
23
|
+
locale?: string;
|
|
24
|
+
format?: DateTimeMonthFormat;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface FocusIncrementAmount {
|
|
28
|
+
ArrowUp: number;
|
|
29
|
+
ArrowDown: number;
|
|
30
|
+
ArrowLeft: number;
|
|
31
|
+
ArrowRight: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Locale code used for specifying the language of the calendar. If unset, the locale will be automatically inferred from `navigator.language`. */
|
|
35
|
+
export let locale: string = undefined;
|
|
36
|
+
|
|
37
|
+
/** Determines if multiple dates can be manually selected by the user. */
|
|
38
|
+
export let multiple = false;
|
|
39
|
+
|
|
40
|
+
/** Controls whether header text will be shown above items representing the first day of a month or the first month of a year. */
|
|
41
|
+
export let headers = false;
|
|
42
|
+
|
|
43
|
+
/** The currently selected calendar date(s). */
|
|
44
|
+
export let value: Date | Date[] | null = null;
|
|
45
|
+
|
|
46
|
+
/** Array of specifically excluded dates that cannot be selected by the user. */
|
|
47
|
+
export let blackout: Date[] = undefined;
|
|
48
|
+
|
|
49
|
+
/** The minimum date that can be manually selected by the user. */
|
|
50
|
+
export let min: Date = undefined;
|
|
51
|
+
|
|
52
|
+
/** The maximum date that can be manually selected by the user. */
|
|
53
|
+
export let max: Date = undefined;
|
|
54
|
+
|
|
55
|
+
/** The selection view that the calendar will start in. Views can be manually changed by the user when clicking the calendar's header. */
|
|
56
|
+
export let view: View = "days";
|
|
57
|
+
|
|
58
|
+
/** Number representing the day that the calendar week starts on. 0 is sunday, 6 is saturday. */
|
|
59
|
+
export let weekStart = 0;
|
|
60
|
+
|
|
61
|
+
/** INTERNAL USE ONLY: Applies flyout styles to the outer CalendarView container. */
|
|
62
|
+
export let __floating = false;
|
|
63
|
+
|
|
64
|
+
/** Specifies a custom class name for the calendar's outer container element. */
|
|
65
|
+
let className = "";
|
|
66
|
+
export { className as class };
|
|
67
|
+
|
|
68
|
+
/** Obtains a bound DOM reference to the calendar's outer container element. */
|
|
69
|
+
export let element: HTMLDivElement = null;
|
|
70
|
+
|
|
71
|
+
const dispatch = createEventDispatcher();
|
|
72
|
+
const forwardEvents = createEventForwarder(get_current_component(), ["change"]);
|
|
73
|
+
const bodyElementBinding = node => (bodyElement = node); // bind:this breaks with our page transition for some reason
|
|
74
|
+
|
|
75
|
+
let header = "";
|
|
76
|
+
let viewAnimationDirection: AnimationDirection = "neutral";
|
|
77
|
+
let pageAnimationDirection: AnimationDirection = "neutral";
|
|
78
|
+
let pageAnimationDuration = 0;
|
|
79
|
+
let bodyElement: HTMLTableSectionElement = null;
|
|
80
|
+
let firstValue = Array.isArray(value) ? value[0] : value;
|
|
81
|
+
let page =
|
|
82
|
+
(!min || firstValue >= min) && (!max || firstValue < max)
|
|
83
|
+
? new Date(
|
|
84
|
+
(firstValue ?? new Date()).getFullYear(),
|
|
85
|
+
(firstValue ?? new Date()).getMonth(),
|
|
86
|
+
1
|
|
87
|
+
)
|
|
88
|
+
: firstValue < min
|
|
89
|
+
? new Date(min.getFullYear(), min.getMonth(), 1)
|
|
90
|
+
: new Date(max.getFullYear(), max.getMonth(), 1);
|
|
91
|
+
|
|
92
|
+
$: firstValue = Array.isArray(value) ? value[0] : value;
|
|
93
|
+
$: view, updatePage(0);
|
|
94
|
+
$: nextPage = getPageByOffset(1, page, view);
|
|
95
|
+
$: if (view === "days") {
|
|
96
|
+
header = new Intl.DateTimeFormat(locale, {
|
|
97
|
+
year: "numeric",
|
|
98
|
+
month: "long"
|
|
99
|
+
}).format(page);
|
|
100
|
+
} else if (view === "months") {
|
|
101
|
+
header = new Intl.DateTimeFormat(locale, {
|
|
102
|
+
year: "numeric"
|
|
103
|
+
}).format(page);
|
|
104
|
+
} else if (view === "years") {
|
|
105
|
+
const decadeStart = Math.floor(page.getFullYear() / 10) * 10;
|
|
106
|
+
const decadeEnd = decadeStart + 9;
|
|
107
|
+
|
|
108
|
+
// https://github.com/microsoft/TypeScript/issues/46905
|
|
109
|
+
header = (<any>new Intl.DateTimeFormat(locale, {
|
|
110
|
+
year: "numeric"
|
|
111
|
+
})).formatRange(new Date(decadeStart, 0, 1), new Date(decadeEnd, 0, 1));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
onMount(() => {
|
|
115
|
+
pageAnimationDuration = getCSSDuration("--fds-control-slow-duration");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
function getWeekdayLocale(
|
|
119
|
+
day: number,
|
|
120
|
+
{ locale = undefined, format = "long", offset = 0 }: WeekdayLocaleOptions = {}
|
|
121
|
+
) {
|
|
122
|
+
return new Intl.DateTimeFormat(locale, {
|
|
123
|
+
weekday: format,
|
|
124
|
+
timeZone: "UTC"
|
|
125
|
+
}).format(new Date(Date.UTC(2000, 1, day + offset - 1)));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getMonthLocale(
|
|
129
|
+
month: number,
|
|
130
|
+
{ locale = undefined, format = "long" }: MonthLocaleOptions = {}
|
|
131
|
+
) {
|
|
132
|
+
return new Intl.DateTimeFormat(locale, {
|
|
133
|
+
month: format
|
|
134
|
+
}).format(new Date(2000, month));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getMonthLength(year: number, month: number) {
|
|
138
|
+
return new Date(year, month + 1, 0).getDate() - 1;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getMonthDays(year: number, month: number): Date[] {
|
|
142
|
+
const days: Date[] = [];
|
|
143
|
+
for (let i = 0; i < getMonthLength(year, month) + 1; i++) {
|
|
144
|
+
days.push(new Date(year, month, i + 1));
|
|
145
|
+
}
|
|
146
|
+
return days;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getYearMonths(year: number): Date[] {
|
|
150
|
+
const days: Date[] = [];
|
|
151
|
+
for (let i = 0; i < 12; i++) {
|
|
152
|
+
days.push(new Date(year, i, 1));
|
|
153
|
+
}
|
|
154
|
+
return days;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function compareDates(a: Date, b: Date, precision: DateComparisonPrecision = "time") {
|
|
158
|
+
switch (precision) {
|
|
159
|
+
case "time":
|
|
160
|
+
return a.getTime() === b.getTime();
|
|
161
|
+
case "day":
|
|
162
|
+
return (
|
|
163
|
+
a.getFullYear() === b.getFullYear() &&
|
|
164
|
+
a.getMonth() === b.getMonth() &&
|
|
165
|
+
a.getDate() === b.getDate()
|
|
166
|
+
);
|
|
167
|
+
case "month":
|
|
168
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
169
|
+
case "year":
|
|
170
|
+
return a.getFullYear() === b.getFullYear();
|
|
171
|
+
case "decade":
|
|
172
|
+
return (
|
|
173
|
+
Math.floor(a.getFullYear() / 10) * 10 === Math.floor(b.getFullYear() / 10) * 10
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function indexOfDate(array: Date[], date: Date, precision: DateComparisonPrecision = "time") {
|
|
179
|
+
return array.findIndex(d => compareDates(d, date, precision));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function getCalendarDays(date: Date): Date[] {
|
|
183
|
+
const year = date.getFullYear();
|
|
184
|
+
const month = date.getMonth();
|
|
185
|
+
const firstWeekday = new Date(year, month, 1).getDay();
|
|
186
|
+
const calendarRows = 6;
|
|
187
|
+
|
|
188
|
+
let days: Date[] = [];
|
|
189
|
+
let nextMonth = month + 1;
|
|
190
|
+
let lastMonth = month - 1;
|
|
191
|
+
let nextMonthYear = year;
|
|
192
|
+
let lastMonthYear = year;
|
|
193
|
+
|
|
194
|
+
const daysBefore = (firstWeekday - weekStart + 7) % 7;
|
|
195
|
+
if (daysBefore > 0) {
|
|
196
|
+
if (lastMonth === -1) {
|
|
197
|
+
lastMonth = 11;
|
|
198
|
+
lastMonthYear = year - 1;
|
|
199
|
+
}
|
|
200
|
+
days = getMonthDays(lastMonthYear, lastMonth).slice(-daysBefore);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
days = days.concat(getMonthDays(year, month));
|
|
204
|
+
|
|
205
|
+
if (nextMonth === 12) {
|
|
206
|
+
nextMonth = 0;
|
|
207
|
+
nextMonthYear = year + 1;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const daysAfter = 7 * calendarRows - days.length;
|
|
211
|
+
days = days.concat(getMonthDays(nextMonthYear, nextMonth).slice(0, daysAfter));
|
|
212
|
+
|
|
213
|
+
return days;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function getCalendarMonths(date: Date): Date[] {
|
|
217
|
+
const year = date.getFullYear();
|
|
218
|
+
|
|
219
|
+
let months: Date[] = [];
|
|
220
|
+
|
|
221
|
+
return months.concat(getYearMonths(year), getYearMonths(year + 1).slice(0, 4));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function getCalendarYears(date: Date): Date[] {
|
|
225
|
+
const decadeStart = Math.floor(date.getFullYear() / 10) * 10;
|
|
226
|
+
|
|
227
|
+
let years: Date[] = [];
|
|
228
|
+
|
|
229
|
+
for (let i = 0; i < 12; i++) {
|
|
230
|
+
years.push(new Date(decadeStart + i, 0, 1));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (decadeStart % 20 === 0) {
|
|
234
|
+
for (let i = 0; i < 2; i++) {
|
|
235
|
+
years.unshift(new Date(decadeStart - (i + 1), 0, 1));
|
|
236
|
+
}
|
|
237
|
+
for (let i = 0; i < 4; i++) {
|
|
238
|
+
years.push(new Date(decadeStart + i + 12, 0, 1));
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
for (let i = 0; i < 6; i++) {
|
|
242
|
+
years.push(new Date(decadeStart + i + 12, 0, 1));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return years;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function getPageByOffset(offset: number, page: Date, view: View) {
|
|
250
|
+
if (view === "days") {
|
|
251
|
+
return new Date(page.getFullYear(), page.getMonth() + offset, 1);
|
|
252
|
+
} else if (view === "months") {
|
|
253
|
+
return new Date(page.getFullYear() + offset, 0, 1);
|
|
254
|
+
} else if (view === "years") {
|
|
255
|
+
return new Date(Math.floor(page.getFullYear() / 10) * 10 + offset * 10, 0, 1);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function updatePage(amount: number = 0, directionOverride: AnimationDirection = undefined) {
|
|
260
|
+
page = getPageByOffset(amount, page, view);
|
|
261
|
+
if (directionOverride) {
|
|
262
|
+
pageAnimationDirection = directionOverride;
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (amount <= -1) {
|
|
266
|
+
pageAnimationDirection = "up";
|
|
267
|
+
} else if (amount >= 1) {
|
|
268
|
+
pageAnimationDirection = "down";
|
|
269
|
+
} else {
|
|
270
|
+
pageAnimationDirection = "neutral";
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function updateView(newView: View) {
|
|
275
|
+
if (
|
|
276
|
+
(view === "days" && newView === "months") ||
|
|
277
|
+
(view === "months" && newView === "years")
|
|
278
|
+
) {
|
|
279
|
+
viewAnimationDirection = "up";
|
|
280
|
+
} else if (
|
|
281
|
+
(view === "years" && newView === "months") ||
|
|
282
|
+
(view === "months" && newView === "days")
|
|
283
|
+
) {
|
|
284
|
+
viewAnimationDirection = "down";
|
|
285
|
+
} else {
|
|
286
|
+
viewAnimationDirection = "neutral";
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
pageAnimationDirection = "neutral";
|
|
290
|
+
view = newView;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function handleKeyDown(event: KeyboardEvent, date: Date) {
|
|
294
|
+
const { key } = event;
|
|
295
|
+
|
|
296
|
+
if (
|
|
297
|
+
key === "ArrowUp" ||
|
|
298
|
+
key === "ArrowDown" ||
|
|
299
|
+
key === "ArrowLeft" ||
|
|
300
|
+
key === "ArrowRight" ||
|
|
301
|
+
key === "Home" ||
|
|
302
|
+
key === "End"
|
|
303
|
+
) {
|
|
304
|
+
event.preventDefault();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (event.ctrlKey && (key === "ArrowUp" || key === "ArrowDown")) {
|
|
308
|
+
if (key === "ArrowUp") {
|
|
309
|
+
updateView(view === "days" ? "months" : "years");
|
|
310
|
+
} else if (key === "ArrowDown") {
|
|
311
|
+
updateView(view === "years" ? "months" : "days");
|
|
312
|
+
}
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let focusOrder = bodyElement.querySelectorAll("button");
|
|
317
|
+
let focusedDate = date;
|
|
318
|
+
|
|
319
|
+
const focusIndex = Array.from(focusOrder).indexOf(
|
|
320
|
+
<HTMLButtonElement>document.activeElement
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
if (focusOrder.length === 0) return;
|
|
324
|
+
|
|
325
|
+
if (view === "days") {
|
|
326
|
+
let focusIncrementAmount: FocusIncrementAmount = {
|
|
327
|
+
ArrowUp: -7,
|
|
328
|
+
ArrowDown: 7,
|
|
329
|
+
ArrowLeft: -1,
|
|
330
|
+
ArrowRight: 1
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
if (!focusIncrementAmount[key] || event.shiftKey) return;
|
|
334
|
+
|
|
335
|
+
focusedDate = new Date(
|
|
336
|
+
new Date(focusedDate).setDate(focusedDate.getDate() + focusIncrementAmount[key])
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
const nextDateIsBlackout = blackout && indexOfDate(blackout, focusedDate, "day") > -1;
|
|
340
|
+
|
|
341
|
+
if (nextDateIsBlackout) {
|
|
342
|
+
focusedDate.setDate(focusedDate.getDate() + focusIncrementAmount[key]);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const calendarDays = getCalendarDays(focusedDate);
|
|
346
|
+
const newFocusedDate = calendarDays.find(day => compareDates(day, focusedDate, "time"));
|
|
347
|
+
|
|
348
|
+
if (min > newFocusedDate || max < newFocusedDate) return;
|
|
349
|
+
|
|
350
|
+
if (focusedDate.getMonth() !== page.getMonth()) {
|
|
351
|
+
if (key === "ArrowLeft" || key === "ArrowUp") {
|
|
352
|
+
updatePage(-1, "neutral");
|
|
353
|
+
} else if (key === "ArrowRight" || key === "ArrowDown") {
|
|
354
|
+
updatePage(1, "neutral");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
await tick();
|
|
358
|
+
focusOrder = bodyElement.querySelectorAll("button");
|
|
359
|
+
focusedDate = newFocusedDate;
|
|
360
|
+
focusOrder?.[calendarDays.indexOf(newFocusedDate)].focus();
|
|
361
|
+
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
focusOrder?.[
|
|
366
|
+
focusIndex + focusIncrementAmount[key] * (nextDateIsBlackout ? 2 : 1)
|
|
367
|
+
].focus();
|
|
368
|
+
} else if (view === "months" || view === "years") {
|
|
369
|
+
let calendar: Date[] = [];
|
|
370
|
+
const focusIncrementAmount: FocusIncrementAmount = {
|
|
371
|
+
ArrowUp: -4,
|
|
372
|
+
ArrowDown: 4,
|
|
373
|
+
ArrowLeft: -1,
|
|
374
|
+
ArrowRight: 1
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
if (!focusIncrementAmount[key] || event.shiftKey) return;
|
|
378
|
+
|
|
379
|
+
if (view === "months") {
|
|
380
|
+
focusedDate = new Date(
|
|
381
|
+
new Date(focusedDate).setMonth(
|
|
382
|
+
focusedDate.getMonth() + focusIncrementAmount[key],
|
|
383
|
+
1
|
|
384
|
+
)
|
|
385
|
+
);
|
|
386
|
+
} else {
|
|
387
|
+
focusedDate = new Date(
|
|
388
|
+
new Date(focusedDate).setFullYear(
|
|
389
|
+
focusedDate.getFullYear() + focusIncrementAmount[key]
|
|
390
|
+
)
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
calendar =
|
|
395
|
+
view === "months" ? getCalendarMonths(focusedDate) : getCalendarYears(focusedDate);
|
|
396
|
+
const newFocusedDate = calendar.find(day =>
|
|
397
|
+
compareDates(day, focusedDate, view === "months" ? "month" : "year")
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
const aboveMinimumMonths =
|
|
401
|
+
min?.getMonth() > newFocusedDate.getMonth() &&
|
|
402
|
+
min?.getFullYear() === newFocusedDate.getFullYear();
|
|
403
|
+
const aboveMinimumYears = min?.getFullYear() > newFocusedDate.getFullYear();
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
(view === "months" ? aboveMinimumMonths : aboveMinimumYears) ||
|
|
407
|
+
max < newFocusedDate
|
|
408
|
+
)
|
|
409
|
+
return;
|
|
410
|
+
|
|
411
|
+
if (!compareDates(focusedDate, page, view === "months" ? "year" : "decade")) {
|
|
412
|
+
if (key === "ArrowLeft" || key === "ArrowUp") {
|
|
413
|
+
updatePage(-1, "neutral");
|
|
414
|
+
} else if (key === "ArrowRight" || key === "ArrowDown") {
|
|
415
|
+
updatePage(1, "neutral");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
await tick();
|
|
419
|
+
|
|
420
|
+
focusedDate = newFocusedDate;
|
|
421
|
+
focusOrder = bodyElement.querySelectorAll("button");
|
|
422
|
+
focusOrder?.[calendar.indexOf(newFocusedDate)].focus();
|
|
423
|
+
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
focusOrder?.[focusIndex + focusIncrementAmount[key]].focus();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function selectDay(day) {
|
|
432
|
+
if (multiple) {
|
|
433
|
+
if (!Array.isArray(value)) {
|
|
434
|
+
if (value !== null) {
|
|
435
|
+
value = [value];
|
|
436
|
+
} else {
|
|
437
|
+
value = [day];
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (indexOfDate(value, day) === -1) {
|
|
442
|
+
value.push(day);
|
|
443
|
+
value = value;
|
|
444
|
+
} else {
|
|
445
|
+
value = value
|
|
446
|
+
.slice(0, indexOfDate(value, day))
|
|
447
|
+
.concat(value.slice(indexOfDate(value, day) + 1));
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
if (Array.isArray(value)) value = null;
|
|
451
|
+
if (day.getTime() === (<Date>value)?.getTime()) {
|
|
452
|
+
value = null;
|
|
453
|
+
} else {
|
|
454
|
+
value = day;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
dispatch("change", value);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function selectMonth(month: Date) {
|
|
461
|
+
page = new Date(new Date(month.setDate(1)));
|
|
462
|
+
updateView("days");
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function selectYear(month: Date) {
|
|
466
|
+
page.setFullYear(month.getFullYear());
|
|
467
|
+
updateView("months");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function fadeScale(node, { delay = 0, duration = 0, easing = x => x, baseScale = 0 }) {
|
|
471
|
+
const o = +getComputedStyle(node).opacity;
|
|
472
|
+
const is = 1 - baseScale;
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
delay,
|
|
476
|
+
duration,
|
|
477
|
+
css: t => {
|
|
478
|
+
const eased = easing(t);
|
|
479
|
+
return `opacity: ${eased * o}; transform: scale(${eased * is + baseScale})`;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
</script>
|
|
484
|
+
|
|
485
|
+
<!--
|
|
486
|
+
@component
|
|
487
|
+
A calendar view lets a user view and interact with a calendar that they can navigate by month, year, or decade. A user can select a single date or multiple dates. [Docs](https://fluent-svelte.vercel.app/docs/components/calendarview)
|
|
488
|
+
- Usage:
|
|
489
|
+
```tsx
|
|
490
|
+
<CalendarView value={new Date(2022, 2, 14)} />
|
|
491
|
+
```
|
|
492
|
+
-->
|
|
493
|
+
<div
|
|
494
|
+
class="calendar-view {className}"
|
|
495
|
+
class:floating={__floating}
|
|
496
|
+
use:forwardEvents
|
|
497
|
+
bind:this={element}
|
|
498
|
+
{...$$restProps}
|
|
499
|
+
>
|
|
500
|
+
<header class="calendar-view-header">
|
|
501
|
+
<div class="calendar-view-header-text" role="heading" aria-live="polite">
|
|
502
|
+
<button
|
|
503
|
+
on:click={() => updateView(view === "days" ? "months" : "years")}
|
|
504
|
+
type="button"
|
|
505
|
+
disabled={view === "years"}>{header}</button
|
|
506
|
+
>
|
|
507
|
+
</div>
|
|
508
|
+
<div class="calendar-view-pagination-controls">
|
|
509
|
+
<button type="button" disabled={view && min >= page} on:click={() => updatePage(-1)}>
|
|
510
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
511
|
+
<path
|
|
512
|
+
d="M4.95681 10.998C4.14912 10.998 3.67466 10.09 4.13591 9.42698L6.76854 5.64257C7.36532 4.78469 8.63448 4.7847 9.23126 5.64257L11.8639 9.42698C12.3251 10.09 11.8507 10.998 11.043 10.998H4.95681Z"
|
|
513
|
+
/>
|
|
514
|
+
</svg>
|
|
515
|
+
</button>
|
|
516
|
+
<button type="button" disabled={max < nextPage} on:click={() => updatePage(1)}>
|
|
517
|
+
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
518
|
+
<path
|
|
519
|
+
d="M4.95681 5C4.14912 5 3.67466 5.90803 4.13591 6.57107L6.76854 10.3555C7.36532 11.2134 8.63448 11.2133 9.23126 10.3555L11.8639 6.57106C12.3251 5.90803 11.8507 5 11.043 5H4.95681Z"
|
|
520
|
+
/>
|
|
521
|
+
</svg>
|
|
522
|
+
</button>
|
|
523
|
+
</div>
|
|
524
|
+
</header>
|
|
525
|
+
<div class="calendar-view-table-wrapper">
|
|
526
|
+
{#key view}
|
|
527
|
+
<table
|
|
528
|
+
class="calendar-view-table view-{view}"
|
|
529
|
+
role="grid"
|
|
530
|
+
in:fadeScale={{
|
|
531
|
+
duration: viewAnimationDirection !== "neutral" ? 500 : 0,
|
|
532
|
+
easing: circOut,
|
|
533
|
+
baseScale: viewAnimationDirection === "up" ? 1.29 : 0.84,
|
|
534
|
+
delay: viewAnimationDirection !== "neutral" ? 150 : 0
|
|
535
|
+
}}
|
|
536
|
+
out:fadeScale|local={{
|
|
537
|
+
duration: viewAnimationDirection !== "neutral" ? 150 : 0,
|
|
538
|
+
easing: circOut,
|
|
539
|
+
baseScale: viewAnimationDirection === "up" ? 0.84 : 1.29,
|
|
540
|
+
delay: 0
|
|
541
|
+
}}
|
|
542
|
+
>
|
|
543
|
+
{#if view === "days"}
|
|
544
|
+
<thead>
|
|
545
|
+
<tr>
|
|
546
|
+
{#each Array(7) as _, day}
|
|
547
|
+
<th
|
|
548
|
+
scope="col"
|
|
549
|
+
{...{
|
|
550
|
+
abbr: getWeekdayLocale(day, { locale, offset: weekStart })
|
|
551
|
+
}}
|
|
552
|
+
>
|
|
553
|
+
{getWeekdayLocale(day, {
|
|
554
|
+
locale,
|
|
555
|
+
format: "short",
|
|
556
|
+
offset: weekStart
|
|
557
|
+
})}
|
|
558
|
+
</th>
|
|
559
|
+
{/each}
|
|
560
|
+
</tr>
|
|
561
|
+
</thead>
|
|
562
|
+
{/if}
|
|
563
|
+
{#key page}
|
|
564
|
+
<tbody
|
|
565
|
+
use:bodyElementBinding
|
|
566
|
+
in:fly={{
|
|
567
|
+
opacity: 1,
|
|
568
|
+
duration: pageAnimationDuration,
|
|
569
|
+
easing: circOut,
|
|
570
|
+
y:
|
|
571
|
+
pageAnimationDirection === "neutral"
|
|
572
|
+
? 0
|
|
573
|
+
: pageAnimationDirection === "up"
|
|
574
|
+
? -198
|
|
575
|
+
: 198
|
|
576
|
+
}}
|
|
577
|
+
out:fly|local={{
|
|
578
|
+
opacity: 1,
|
|
579
|
+
duration: pageAnimationDuration,
|
|
580
|
+
easing: circOut,
|
|
581
|
+
y:
|
|
582
|
+
pageAnimationDirection === "neutral"
|
|
583
|
+
? 0
|
|
584
|
+
: pageAnimationDirection === "up"
|
|
585
|
+
? 198
|
|
586
|
+
: -198
|
|
587
|
+
}}
|
|
588
|
+
>
|
|
589
|
+
{#if view === "days"}
|
|
590
|
+
{#each Array(6) as _, week}
|
|
591
|
+
<tr>
|
|
592
|
+
{#each getCalendarDays(page).slice(week * 7, week * 7 + 7) as day, i}
|
|
593
|
+
{@const selected =
|
|
594
|
+
value !== null &&
|
|
595
|
+
(Array.isArray(value)
|
|
596
|
+
? indexOfDate(value, day, "day") > -1
|
|
597
|
+
: compareDates(value, day, "day"))}
|
|
598
|
+
{@const inMonth = compareDates(day, page, "month")}
|
|
599
|
+
{@const firstFocusableDay = getCalendarDays(page).find(
|
|
600
|
+
d =>
|
|
601
|
+
compareDates(d, page, "month") &&
|
|
602
|
+
(!blackout ||
|
|
603
|
+
indexOfDate(blackout, d, "day") === -1) &&
|
|
604
|
+
(!min || min <= d) &&
|
|
605
|
+
(!max || max >= d)
|
|
606
|
+
)}
|
|
607
|
+
|
|
608
|
+
<td role="gridcell">
|
|
609
|
+
<CalendarViewItem
|
|
610
|
+
on:click={() => selectDay(day)}
|
|
611
|
+
on:keydown={e => handleKeyDown(e, day)}
|
|
612
|
+
outOfRange={!inMonth}
|
|
613
|
+
current={compareDates(day, new Date(), "day")}
|
|
614
|
+
disabled={min > day || max < day}
|
|
615
|
+
blackout={blackout &&
|
|
616
|
+
indexOfDate(blackout, day, "day") > -1}
|
|
617
|
+
header={page &&
|
|
618
|
+
headers &&
|
|
619
|
+
day.getDate() === 1 &&
|
|
620
|
+
getMonthLocale(day.getMonth(), {
|
|
621
|
+
locale,
|
|
622
|
+
format: "short"
|
|
623
|
+
})}
|
|
624
|
+
tabindex={firstFocusableDay &&
|
|
625
|
+
compareDates(firstFocusableDay, day, "day")
|
|
626
|
+
? 0
|
|
627
|
+
: -1}
|
|
628
|
+
{selected}
|
|
629
|
+
>
|
|
630
|
+
{day.getDate()}
|
|
631
|
+
</CalendarViewItem>
|
|
632
|
+
</td>
|
|
633
|
+
{/each}
|
|
634
|
+
</tr>
|
|
635
|
+
{/each}
|
|
636
|
+
{:else}
|
|
637
|
+
{#each Array(4) as _, row}
|
|
638
|
+
<tr>
|
|
639
|
+
{#if view === "months"}
|
|
640
|
+
{#each getCalendarMonths(page).slice(row * 4, row * 4 + 4) as month, i}
|
|
641
|
+
{@const selected =
|
|
642
|
+
value !== null &&
|
|
643
|
+
(Array.isArray(value)
|
|
644
|
+
? indexOfDate(value, month, "month") > -1
|
|
645
|
+
: compareDates(value, month, "month"))}
|
|
646
|
+
{@const inYear =
|
|
647
|
+
month.getFullYear() === page.getFullYear()}
|
|
648
|
+
{@const firstFocusableMonth = getCalendarMonths(
|
|
649
|
+
page
|
|
650
|
+
).find(
|
|
651
|
+
d =>
|
|
652
|
+
compareDates(d, page, "year") &&
|
|
653
|
+
(!min ||
|
|
654
|
+
new Date(
|
|
655
|
+
min.getFullYear(),
|
|
656
|
+
min.getMonth(),
|
|
657
|
+
1
|
|
658
|
+
) <=
|
|
659
|
+
new Date(
|
|
660
|
+
d.getFullYear(),
|
|
661
|
+
d.getMonth(),
|
|
662
|
+
1
|
|
663
|
+
)) &&
|
|
664
|
+
(!max || max >= d)
|
|
665
|
+
)}
|
|
666
|
+
|
|
667
|
+
<td role="gridcell">
|
|
668
|
+
<CalendarViewItem
|
|
669
|
+
on:click={() => selectMonth(month)}
|
|
670
|
+
on:keydown={e => handleKeyDown(e, month)}
|
|
671
|
+
variant="monthYear"
|
|
672
|
+
outOfRange={!inYear}
|
|
673
|
+
current={compareDates(
|
|
674
|
+
month,
|
|
675
|
+
new Date(),
|
|
676
|
+
"month"
|
|
677
|
+
)}
|
|
678
|
+
disabled={(min?.getMonth() > month.getMonth() &&
|
|
679
|
+
min?.getFullYear() ===
|
|
680
|
+
month.getFullYear()) ||
|
|
681
|
+
max < month}
|
|
682
|
+
header={page &&
|
|
683
|
+
headers &&
|
|
684
|
+
month.getMonth() === 0 &&
|
|
685
|
+
month.getFullYear().toString()}
|
|
686
|
+
{selected}
|
|
687
|
+
tabindex={firstFocusableMonth &&
|
|
688
|
+
compareDates(
|
|
689
|
+
firstFocusableMonth,
|
|
690
|
+
month,
|
|
691
|
+
"month"
|
|
692
|
+
)
|
|
693
|
+
? 0
|
|
694
|
+
: -1}
|
|
695
|
+
>
|
|
696
|
+
{getMonthLocale(month.getMonth(), {
|
|
697
|
+
locale,
|
|
698
|
+
format: "short"
|
|
699
|
+
})}
|
|
700
|
+
</CalendarViewItem>
|
|
701
|
+
</td>
|
|
702
|
+
{/each}
|
|
703
|
+
{:else if view === "years"}
|
|
704
|
+
{#each getCalendarYears(page).slice(row * 4, row * 4 + 4) as year, i}
|
|
705
|
+
{@const selected =
|
|
706
|
+
value !== null &&
|
|
707
|
+
(Array.isArray(value)
|
|
708
|
+
? indexOfDate(value, year, "year") > -1
|
|
709
|
+
: compareDates(value, year, "year"))}
|
|
710
|
+
{@const inDecade = compareDates(year, page, "decade")}
|
|
711
|
+
{@const firstFocusableYear = getCalendarYears(
|
|
712
|
+
page
|
|
713
|
+
).find(
|
|
714
|
+
d =>
|
|
715
|
+
compareDates(d, page, "decade") &&
|
|
716
|
+
(!min ||
|
|
717
|
+
min.getFullYear() <= d.getFullYear()) &&
|
|
718
|
+
(!max || max >= d)
|
|
719
|
+
)}
|
|
720
|
+
|
|
721
|
+
<td role="gridcell">
|
|
722
|
+
<CalendarViewItem
|
|
723
|
+
on:click={() => selectYear(year)}
|
|
724
|
+
on:keydown={e => handleKeyDown(e, year)}
|
|
725
|
+
variant="monthYear"
|
|
726
|
+
outOfRange={!inDecade}
|
|
727
|
+
current={compareDates(year, new Date(), "year")}
|
|
728
|
+
disabled={min?.getFullYear() >
|
|
729
|
+
year.getFullYear() || max < year}
|
|
730
|
+
{selected}
|
|
731
|
+
tabindex={firstFocusableYear &&
|
|
732
|
+
compareDates(firstFocusableYear, year, "year")
|
|
733
|
+
? 0
|
|
734
|
+
: -1}
|
|
735
|
+
>
|
|
736
|
+
{year.getFullYear()}
|
|
737
|
+
</CalendarViewItem>
|
|
738
|
+
</td>
|
|
739
|
+
{/each}
|
|
740
|
+
{/if}
|
|
741
|
+
</tr>
|
|
742
|
+
{/each}
|
|
743
|
+
{/if}
|
|
744
|
+
</tbody>
|
|
745
|
+
{/key}
|
|
746
|
+
</table>
|
|
747
|
+
{/key}
|
|
748
|
+
</div>
|
|
749
|
+
</div>
|
|
750
|
+
|
|
751
|
+
<style lang="scss">
|
|
752
|
+
@use "./CalendarView";
|
|
753
|
+
</style>
|