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.
Files changed (145) hide show
  1. package/.prettierignore +1 -0
  2. package/.prettierrc +7 -0
  3. package/CHANGELOG.md +7 -0
  4. package/LICENSE +21 -0
  5. package/README.md +33 -0
  6. package/package.json +83 -0
  7. package/src/app.html +12 -0
  8. package/src/global.d.ts +1 -0
  9. package/src/lib/AutoSuggestBox/AutoSuggestBox.scss +44 -0
  10. package/src/lib/AutoSuggestBox/AutoSuggestBox.svelte +173 -0
  11. package/src/lib/Button/Button.scss +94 -0
  12. package/src/lib/Button/Button.svelte +48 -0
  13. package/src/lib/CalendarDatePicker/CalendarDatePicker.scss +15 -0
  14. package/src/lib/CalendarDatePicker/CalendarDatePicker.svelte +86 -0
  15. package/src/lib/CalendarView/CalendarView.scss +156 -0
  16. package/src/lib/CalendarView/CalendarView.svelte +753 -0
  17. package/src/lib/CalendarView/CalendarViewItem.scss +130 -0
  18. package/src/lib/CalendarView/CalendarViewItem.svelte +33 -0
  19. package/src/lib/Checkbox/Checkbox.scss +117 -0
  20. package/src/lib/Checkbox/Checkbox.svelte +81 -0
  21. package/src/lib/ComboBox/ComboBox.scss +152 -0
  22. package/src/lib/ComboBox/ComboBox.svelte +360 -0
  23. package/src/lib/ComboBox/ComboBoxItem.scss +80 -0
  24. package/src/lib/ComboBox/ComboBoxItem.svelte +30 -0
  25. package/src/lib/ContentDialog/ContentDialog.scss +68 -0
  26. package/src/lib/ContentDialog/ContentDialog.svelte +123 -0
  27. package/src/lib/ContextMenu/ContextMenu.scss +11 -0
  28. package/src/lib/ContextMenu/ContextMenu.svelte +104 -0
  29. package/src/lib/Expander/Expander.scss +134 -0
  30. package/src/lib/Expander/Expander.svelte +123 -0
  31. package/src/lib/Flipper/Flipper.svelte +49 -0
  32. package/src/lib/Flyout/FlyoutSurface.scss +14 -0
  33. package/src/lib/Flyout/FlyoutSurface.svelte +21 -0
  34. package/src/lib/Flyout/FlyoutWrapper.scss +81 -0
  35. package/src/lib/Flyout/FlyoutWrapper.svelte +126 -0
  36. package/src/lib/IconButton/IconButton.scss +31 -0
  37. package/src/lib/IconButton/IconButton.svelte +49 -0
  38. package/src/lib/InfoBadge/InfoBadge.scss +39 -0
  39. package/src/lib/InfoBadge/InfoBadge.svelte +81 -0
  40. package/src/lib/InfoBar/InfoBar.scss +122 -0
  41. package/src/lib/InfoBar/InfoBar.svelte +133 -0
  42. package/src/lib/ListItem/ListItem.scss +74 -0
  43. package/src/lib/ListItem/ListItem.svelte +88 -0
  44. package/src/lib/MenuBar/MenuBar.scss +10 -0
  45. package/src/lib/MenuBar/MenuBar.svelte +49 -0
  46. package/src/lib/MenuBar/MenuBarItem.scss +38 -0
  47. package/src/lib/MenuBar/MenuBarItem.svelte +135 -0
  48. package/src/lib/MenuBar/flyoutState.ts +5 -0
  49. package/src/lib/MenuFlyout/MenuFlyoutDivider.scss +7 -0
  50. package/src/lib/MenuFlyout/MenuFlyoutDivider.svelte +14 -0
  51. package/src/lib/MenuFlyout/MenuFlyoutItem.scss +147 -0
  52. package/src/lib/MenuFlyout/MenuFlyoutItem.svelte +239 -0
  53. package/src/lib/MenuFlyout/MenuFlyoutSurface.scss +42 -0
  54. package/src/lib/MenuFlyout/MenuFlyoutSurface.svelte +28 -0
  55. package/src/lib/MenuFlyout/MenuFlyoutWrapper.scss +64 -0
  56. package/src/lib/MenuFlyout/MenuFlyoutWrapper.svelte +114 -0
  57. package/src/lib/NavigationView/NavigationView.scss +0 -0
  58. package/src/lib/NavigationView/NavigationView.svelte +82 -0
  59. package/src/lib/NumberBox/NumberBox.scss +31 -0
  60. package/src/lib/NumberBox/NumberBox.svelte +267 -0
  61. package/src/lib/PersonPicture/PersonPicture.scss +35 -0
  62. package/src/lib/PersonPicture/PersonPicture.svelte +62 -0
  63. package/src/lib/ProgressBar/ProgressBar.scss +83 -0
  64. package/src/lib/ProgressBar/ProgressBar.svelte +60 -0
  65. package/src/lib/ProgressRing/ProgressRing.scss +37 -0
  66. package/src/lib/ProgressRing/ProgressRing.svelte +73 -0
  67. package/src/lib/RadioButton/RadioButton.scss +114 -0
  68. package/src/lib/RadioButton/RadioButton.svelte +67 -0
  69. package/src/lib/RangeSlider/RangeSlider.svelte +91 -0
  70. package/src/lib/ScrollView/ScrollView.svelte +9 -0
  71. package/src/lib/Slider/Slider.scss +263 -0
  72. package/src/lib/Slider/Slider.svelte +261 -0
  73. package/src/lib/TextBlock/TextBlock.scss +62 -0
  74. package/src/lib/TextBlock/TextBlock.svelte +70 -0
  75. package/src/lib/TextBox/TextBox.scss +108 -0
  76. package/src/lib/TextBox/TextBox.svelte +225 -0
  77. package/src/lib/TextBox/TextBoxButton.scss +34 -0
  78. package/src/lib/TextBox/TextBoxButton.svelte +27 -0
  79. package/src/lib/ToggleSwitch/ToggleSwitch.scss +118 -0
  80. package/src/lib/ToggleSwitch/ToggleSwitch.svelte +55 -0
  81. package/src/lib/Tooltip/TooltipSurface.scss +16 -0
  82. package/src/lib/Tooltip/TooltipSurface.svelte +27 -0
  83. package/src/lib/Tooltip/TooltipWrapper.scss +66 -0
  84. package/src/lib/Tooltip/TooltipWrapper.svelte +117 -0
  85. package/src/lib/_mixins.scss +130 -0
  86. package/src/lib/index.ts +33 -0
  87. package/src/lib/internal.ts +213 -0
  88. package/src/lib/svelte-jsx.d.ts +14 -0
  89. package/src/lib/theme.css +414 -0
  90. package/src/routes/__layout.svelte +48 -0
  91. package/src/routes/docs/__layout.svelte +122 -0
  92. package/src/routes/docs/components/button.md +43 -0
  93. package/src/routes/docs/components/calendarview.md +188 -0
  94. package/src/routes/docs/components/checkbox.md +87 -0
  95. package/src/routes/docs/components/contentdialog.md +155 -0
  96. package/src/routes/docs/components/expander.md +115 -0
  97. package/src/routes/docs/components/flyout.md +107 -0
  98. package/src/routes/docs/components/iconbutton.md +39 -0
  99. package/src/routes/docs/components/infobadge.md +54 -0
  100. package/src/routes/docs/components/infobar.md +102 -0
  101. package/src/routes/docs/components/listitem.md +87 -0
  102. package/src/routes/docs/components/personpicture.md +125 -0
  103. package/src/routes/docs/components/progressring.md +83 -0
  104. package/src/routes/docs/components/radiobutton.md +88 -0
  105. package/src/routes/docs/components/slider.md +165 -0
  106. package/src/routes/docs/components/textblock.md +46 -0
  107. package/src/routes/docs/components/textbox.md +124 -0
  108. package/src/routes/docs/components/toggleswitch.md +73 -0
  109. package/src/routes/docs/getting-started.md +116 -0
  110. package/src/routes/docs/index.md +37 -0
  111. package/src/routes/docs/internals/index.md +0 -0
  112. package/src/routes/index.svelte +121 -0
  113. package/src/routes/test/__layout-test.svelte +1 -0
  114. package/src/routes/test/index.svelte +757 -0
  115. package/src/routes/test/nav.svelte +7 -0
  116. package/src/site/data/docs.ts +176 -0
  117. package/src/site/data/home.ts +12 -0
  118. package/src/site/lib/APIDocs/APIDocs.svelte +178 -0
  119. package/src/site/lib/APIDocs/ParsedComponent.d.ts +85 -0
  120. package/src/site/lib/CopyBox/CopyBox.svelte +23 -0
  121. package/src/site/lib/Example/Example.scss +33 -0
  122. package/src/site/lib/Example/Example.svelte +18 -0
  123. package/src/site/lib/HeroCard/HeroCard.scss +24 -0
  124. package/src/site/lib/HeroCard/HeroCard.svelte +36 -0
  125. package/src/site/lib/Metadata/Metadata.svelte +14 -0
  126. package/src/site/lib/Navbar/Navbar.scss +92 -0
  127. package/src/site/lib/Navbar/Navbar.svelte +47 -0
  128. package/src/site/lib/PageSection/PageSection.scss +57 -0
  129. package/src/site/lib/PageSection/PageSection.svelte +10 -0
  130. package/src/site/lib/Showcase/Showcase.scss +53 -0
  131. package/src/site/lib/Showcase/Showcase.svelte +67 -0
  132. package/src/site/lib/Toc/Toc.scss +18 -0
  133. package/src/site/lib/Toc/Toc.svelte +59 -0
  134. package/src/site/lib/TreeView/TreeView.svelte +89 -0
  135. package/src/site/lib/index.ts +9 -0
  136. package/src/site/styles/_markdown.scss +260 -0
  137. package/src/site/styles/_mixins.scss +319 -0
  138. package/src/site/styles/global.scss +40 -0
  139. package/src/site/styles/pages/docs.scss +74 -0
  140. package/src/site/styles/pages/home.scss +134 -0
  141. package/static/bloom-mica-dark.png +0 -0
  142. package/static/bloom-mica-light.png +0 -0
  143. package/static/logo.svg +11 -0
  144. package/svelte.config.js +57 -0
  145. 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>