@lekoala/slot-picker 0.1.0 → 0.1.2

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 (78) hide show
  1. package/README.md +85 -39
  2. package/custom-elements.json +0 -5
  3. package/dist/slot-picker.css +22 -0
  4. package/dist/slot-picker.js +160 -134
  5. package/dist/slot-picker.min.css +1 -1
  6. package/dist/slot-picker.min.js +24 -24
  7. package/dist/types/date.d.ts +8 -1
  8. package/dist/types/date.d.ts.map +1 -1
  9. package/dist/types/index.d.ts +2 -2
  10. package/dist/types/index.d.ts.map +1 -1
  11. package/dist/types/locales/ar.d.ts +0 -1
  12. package/dist/types/locales/ar.d.ts.map +1 -1
  13. package/dist/types/locales/de.d.ts +0 -1
  14. package/dist/types/locales/de.d.ts.map +1 -1
  15. package/dist/types/locales/en.d.ts +0 -1
  16. package/dist/types/locales/en.d.ts.map +1 -1
  17. package/dist/types/locales/es.d.ts +0 -1
  18. package/dist/types/locales/es.d.ts.map +1 -1
  19. package/dist/types/locales/fr.d.ts +0 -1
  20. package/dist/types/locales/fr.d.ts.map +1 -1
  21. package/dist/types/locales/hi.d.ts +0 -1
  22. package/dist/types/locales/hi.d.ts.map +1 -1
  23. package/dist/types/locales/id.d.ts +0 -1
  24. package/dist/types/locales/id.d.ts.map +1 -1
  25. package/dist/types/locales/it.d.ts +0 -1
  26. package/dist/types/locales/it.d.ts.map +1 -1
  27. package/dist/types/locales/ja.d.ts +0 -1
  28. package/dist/types/locales/ja.d.ts.map +1 -1
  29. package/dist/types/locales/ko.d.ts +0 -1
  30. package/dist/types/locales/ko.d.ts.map +1 -1
  31. package/dist/types/locales/nl.d.ts +0 -1
  32. package/dist/types/locales/nl.d.ts.map +1 -1
  33. package/dist/types/locales/pl.d.ts +0 -1
  34. package/dist/types/locales/pl.d.ts.map +1 -1
  35. package/dist/types/locales/pt-BR.d.ts +0 -1
  36. package/dist/types/locales/pt-BR.d.ts.map +1 -1
  37. package/dist/types/locales/pt-PT.d.ts +0 -1
  38. package/dist/types/locales/pt-PT.d.ts.map +1 -1
  39. package/dist/types/locales/ru.d.ts +0 -1
  40. package/dist/types/locales/ru.d.ts.map +1 -1
  41. package/dist/types/locales/tr.d.ts +0 -1
  42. package/dist/types/locales/tr.d.ts.map +1 -1
  43. package/dist/types/locales/zh-CN.d.ts +0 -1
  44. package/dist/types/locales/zh-CN.d.ts.map +1 -1
  45. package/dist/types/messages.d.ts +0 -3
  46. package/dist/types/messages.d.ts.map +1 -1
  47. package/dist/types/model.d.ts +58 -23
  48. package/dist/types/model.d.ts.map +1 -1
  49. package/dist/types/slot-picker.d.ts +25 -15
  50. package/dist/types/slot-picker.d.ts.map +1 -1
  51. package/dist/types/views/shared.d.ts +5 -4
  52. package/dist/types/views/shared.d.ts.map +1 -1
  53. package/docs/USE_CASES.md +24 -18
  54. package/package.json +1 -1
  55. package/src/date.js +11 -1
  56. package/src/index.js +13 -2
  57. package/src/locales/ar.js +0 -1
  58. package/src/locales/de.js +0 -1
  59. package/src/locales/en.js +0 -1
  60. package/src/locales/es.js +0 -1
  61. package/src/locales/fr.js +0 -1
  62. package/src/locales/hi.js +0 -1
  63. package/src/locales/id.js +0 -1
  64. package/src/locales/it.js +0 -1
  65. package/src/locales/ja.js +0 -1
  66. package/src/locales/ko.js +0 -1
  67. package/src/locales/nl.js +0 -1
  68. package/src/locales/pl.js +0 -1
  69. package/src/locales/pt-BR.js +0 -1
  70. package/src/locales/pt-PT.js +0 -1
  71. package/src/locales/ru.js +0 -1
  72. package/src/locales/tr.js +0 -1
  73. package/src/locales/zh-CN.js +0 -1
  74. package/src/messages.js +0 -1
  75. package/src/model.js +149 -55
  76. package/src/slot-picker.css +22 -0
  77. package/src/slot-picker.js +123 -100
  78. package/src/views/shared.js +10 -7
@@ -1,4 +1,4 @@
1
- /*** @lekoala/slot-picker v0.1.0 - https://github.com/lekoala/slot-picker ***/
1
+ /*** @lekoala/slot-picker v0.1.2 - https://github.com/lekoala/slot-picker ***/
2
2
  (() => {
3
3
  // src/date.js
4
4
  var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
@@ -27,11 +27,8 @@
27
27
  function compareDates(a, b) {
28
28
  return a.localeCompare(b);
29
29
  }
30
- function daysBetween(a, b) {
31
- return Math.round((toUtcDate(b).getTime() - toUtcDate(a).getTime()) / 86400000);
32
- }
33
- function rangeEnd(start, dayCount) {
34
- return addDays(start, Math.max(1, dayCount) - 1);
30
+ function weekdayIndex(value) {
31
+ return toUtcDate(value).getUTCDay();
35
32
  }
36
33
  function toUtcDate(value) {
37
34
  const { year, month, day } = parts(value);
@@ -57,7 +54,6 @@
57
54
  loading: "Loading availability",
58
55
  empty: "No availability",
59
56
  closed: "Closed",
60
- closedRange: "Closed during this period",
61
57
  rangeEmptyTitle: "No availability in this period",
62
58
  rangeEmptyDescription: "From {start} to {end}",
63
59
  rangeEmptyNext: "See the next period",
@@ -135,13 +131,10 @@
135
131
  };
136
132
  }).sort((a, b) => a.date.localeCompare(b.date));
137
133
  }
138
- function visibleDays(days, start, dayCount) {
134
+ function visibleDays(days, dates) {
139
135
  const normalized = normalizeDays(days);
140
136
  const byDate = new Map(normalized.map((day) => [day.date, day]));
141
- return Array.from({ length: dayCount }, (_, index) => {
142
- const date = addDays(start, index);
143
- return byDate.get(date) ?? { date, slots: [] };
144
- });
137
+ return dates.map((date) => byDate.get(date) ?? { date, slots: [] });
145
138
  }
146
139
  function collapsedDays(days, maxVisibleRows, expanded) {
147
140
  const maxRows = Math.max(0, ...days.map((day) => day.slots.length));
@@ -155,13 +148,64 @@
155
148
  function isValidRange(min, max) {
156
149
  return !min || !max || compareDates(min, max) <= 0;
157
150
  }
158
- function boundedDayCount(dayCount, min, max) {
159
- const requested = Math.max(1, Number(dayCount) || 1);
160
- if (!min || !max)
161
- return requested;
162
- if (compareDates(min, max) > 0)
163
- return 0;
164
- return Math.min(requested, daysBetween(min, max) + 1);
151
+ function clampDate(date, min, max) {
152
+ if (min && compareDates(date, min) < 0)
153
+ return min;
154
+ if (max && compareDates(date, max) > 0)
155
+ return max;
156
+ return date;
157
+ }
158
+ function normalizeHiddenDays(input) {
159
+ if (input === null || input === undefined)
160
+ return [];
161
+ if (!Array.isArray(input))
162
+ throw new TypeError("hiddenDays must be an array of weekday indexes");
163
+ const indexes = input.map((value) => {
164
+ const index = Number(value);
165
+ if (!Number.isInteger(index) || index < 0 || index > 6) {
166
+ throw new TypeError("hiddenDays entries must be integers from 0 (Sunday) to 6 (Saturday)");
167
+ }
168
+ return index;
169
+ });
170
+ const hidden = [...new Set(indexes)].sort((a, b) => a - b);
171
+ if (hidden.length === 7)
172
+ throw new TypeError("hiddenDays cannot hide every weekday");
173
+ return hidden;
174
+ }
175
+ function walk(from, count, hidden, step, bound) {
176
+ const dates = [];
177
+ let date = from;
178
+ while (dates.length < count) {
179
+ if (bound && (step > 0 ? compareDates(date, bound) > 0 : compareDates(date, bound) < 0))
180
+ break;
181
+ if (!hidden.includes(weekdayIndex(date)))
182
+ dates.push(date);
183
+ date = addDays(date, step);
184
+ }
185
+ return dates;
186
+ }
187
+ function projectDates(start, dayCount, hiddenDays = [], min = "", max = "") {
188
+ const count = Math.max(0, Math.trunc(Number(dayCount) || 0));
189
+ if (!count || !isDateValue(start) || !isValidRange(min, max))
190
+ return [];
191
+ const hidden = normalizeHiddenDays(hiddenDays);
192
+ const from = clampDate(start, min, max);
193
+ const forward = walk(from, count, hidden, 1, max);
194
+ if (forward.length === count)
195
+ return forward;
196
+ const backward = walk(addDays(forward[0] ?? from, -1), count - forward.length, hidden, -1, min);
197
+ return [...backward.reverse(), ...forward];
198
+ }
199
+ function stepStart(dates, direction, hiddenDays = [], min = "", max = "") {
200
+ if (!dates.length)
201
+ return "";
202
+ const hidden = normalizeHiddenDays(hiddenDays);
203
+ const count = dates.length;
204
+ if (direction > 0) {
205
+ return projectDates(addDays(dates[count - 1], 1), count, hidden, min, max)[0] ?? dates[0];
206
+ }
207
+ const backward = walk(addDays(dates[0], -1), count, hidden, -1, min);
208
+ return projectDates(backward[backward.length - 1] ?? dates[0], count, hidden, min, max)[0] ?? dates[0];
165
209
  }
166
210
  function normalizeBreakpoints(breakpoints) {
167
211
  return breakpoints.map((breakpoint) => ({
@@ -178,46 +222,47 @@
178
222
  const match = ladder.find((breakpoint) => size >= breakpoint.minWidth) ?? ladder[ladder.length - 1];
179
223
  return Math.min(requested, match.dayCount);
180
224
  }
181
- function clampStart(start, min, max, dayCount) {
182
- let next = start;
183
- if (min && compareDates(next, min) < 0)
184
- next = min;
185
- const count = Math.max(1, dayCount);
186
- if (max) {
187
- const latest = addDays(max, -(count - 1));
188
- if (compareDates(latest, min || next) < 0)
189
- next = min || latest;
190
- else if (compareDates(next, latest) > 0)
191
- next = latest;
192
- }
193
- return next;
225
+ function clampStart(start, min, max, dayCount, hiddenDays = []) {
226
+ return projectDates(start, dayCount, hiddenDays, min, max)[0] ?? clampDate(start, min, max);
194
227
  }
195
- function ensureVisible(start, dayCount, date, min, max) {
228
+ function ensureVisible(start, dayCount, date, min, max, hiddenDays = []) {
229
+ const hidden = normalizeHiddenDays(hiddenDays);
196
230
  const count = Math.max(1, dayCount);
197
- const bounded = clampStart(start, min, max, count);
231
+ const dates = projectDates(start, count, hidden, min, max);
232
+ if (!dates.length)
233
+ return clampDate(start, min, max);
234
+ const first = dates[0];
198
235
  if (!isDateValue(date))
199
- return bounded;
200
- const end = rangeEnd(bounded, count);
201
- if (compareDates(date, bounded) >= 0 && compareDates(date, end) <= 0)
202
- return bounded;
203
- const candidate = compareDates(date, bounded) < 0 ? date : addDays(date, -(count - 1));
204
- return clampStart(candidate, min, max, count);
236
+ return first;
237
+ if (compareDates(date, first) < 0)
238
+ return projectDates(date, count, hidden, min, max)[0] ?? first;
239
+ if (compareDates(date, dates[dates.length - 1]) <= 0)
240
+ return first;
241
+ const target = walk(date, 1, hidden, 1, max)[0];
242
+ if (!target)
243
+ return first;
244
+ const backward = walk(target, count, hidden, -1, min);
245
+ return projectDates(backward[backward.length - 1] ?? target, count, hidden, min, max)[0] ?? first;
205
246
  }
206
247
  function hasDayContent(days) {
207
248
  return days.some((day) => Array.isArray(day.slots) && day.slots.length > 0 || Boolean(day.notice) || Boolean(day.closed));
208
249
  }
209
- function rangeDetail(start, dayCount) {
210
- return { start, end: rangeEnd(start, dayCount), dayCount };
250
+ function rangeDetail(dates) {
251
+ if (!dates.length)
252
+ return { start: "", end: "", dayCount: 0 };
253
+ return { start: dates[0], end: dates[dates.length - 1], dayCount: dates.length };
211
254
  }
212
- function resolveActiveDate(start, dayCount, activeDate) {
255
+ function resolveActiveDate(dates, activeDate) {
256
+ if (!dates.length)
257
+ return "";
258
+ const last = dates[dates.length - 1];
213
259
  if (!isDateValue(activeDate))
214
- return start;
215
- if (compareDates(activeDate, start) < 0)
216
- return start;
217
- const end = rangeEnd(start, dayCount);
218
- if (compareDates(activeDate, end) > 0)
219
- return end;
220
- return activeDate;
260
+ return dates[0];
261
+ if (compareDates(activeDate, dates[0]) <= 0)
262
+ return dates[0];
263
+ if (compareDates(activeDate, last) >= 0)
264
+ return last;
265
+ return dates.find((date) => compareDates(date, activeDate) >= 0) ?? last;
221
266
  }
222
267
  function firstEnabledIndex(day) {
223
268
  return day.slots.findIndex((slot) => !slot.disabled);
@@ -369,7 +414,7 @@
369
414
  const description = slot.description ? ` aria-description="${escapeAttr(slot.description)}"` : "";
370
415
  const disabled = slot.disabled ? ' disabled aria-disabled="true"' : "";
371
416
  const tone = slot.tone ? ` data-tone="${escapeAttr(slot.tone)}"` : "";
372
- return `<button type="button" role="option" class="sp-slot" data-day-index="${dayIndex}" data-slot-index="${slotIndex}" data-value="${escapeAttr(value)}"${tone} aria-selected="${selected ? "true" : "false"}" tabindex="${tabbed ? 0 : -1}"${disabled}${description}>${escapeHtml(slot.start)}</button>`;
417
+ return `<button type="button" role="option" class="sp-slot" data-day-index="${dayIndex}" data-slot-index="${slotIndex}" data-value="${escapeAttr(value)}"${tone} aria-selected="${selected ? "true" : "false"}" tabindex="${tabbed ? 0 : -1}"${disabled}${description}><span class="sp-slot-time">${escapeHtml(slot.start)}</span></button>`;
373
418
  }
374
419
  function noticeIndicator(options = {}) {
375
420
  if (options.interactive) {
@@ -393,7 +438,7 @@
393
438
  return messages.oneSlot;
394
439
  return messages.manySlots.replace("{n}", String(count));
395
440
  }
396
- function rangeEmpty({ start, end, invalid, canNext, nextAvailability, closed, locale, messages }) {
441
+ function rangeEmpty({ start, end, invalid, canNext, nextAvailability, locale, messages }) {
397
442
  const showDescription = !invalid && Boolean(start);
398
443
  let description = "";
399
444
  if (showDescription) {
@@ -413,9 +458,8 @@
413
458
  actions.push(`<button type="button" class="sp-range-empty-availability">${escapeHtml(messages.nextAvailability)}</button>`);
414
459
  }
415
460
  const action = actions.length ? `<div class="sp-range-empty-actions">${actions.join("")}</div>` : "";
416
- const title = closed ? messages.closedRange : messages.rangeEmptyTitle;
417
461
  return `<div class="sp-range-empty">
418
- <strong class="sp-range-empty-title">${escapeHtml(title)}</strong>
462
+ <strong class="sp-range-empty-title">${escapeHtml(messages.rangeEmptyTitle)}</strong>
419
463
  ${description}
420
464
  ${action}
421
465
  </div>`;
@@ -577,11 +621,11 @@
577
621
  "home-date",
578
622
  "next-availability",
579
623
  "notice-display",
580
- "closed-days",
581
624
  "locale",
582
625
  "lang"
583
626
  ];
584
627
  #days = [];
628
+ #hiddenDays = [];
585
629
  #sourceController = new SlotSourceController;
586
630
  #messages = null;
587
631
  #loading = false;
@@ -642,7 +686,7 @@
642
686
  if (!isDateValue(value))
643
687
  throw new TypeError("start must be YYYY-MM-DD");
644
688
  const count = this.visibleDayCount;
645
- this.setAttribute("start", count > 0 ? clampStart(value, this.min, this.max, count) : value);
689
+ this.setAttribute("start", count > 0 ? clampStart(value, this.min, this.max, count, this.#hiddenDays) : value);
646
690
  }
647
691
  get dayCount() {
648
692
  const value = Number.parseInt(this.getAttribute("day-count") || "5", 10);
@@ -652,13 +696,25 @@
652
696
  const next = Math.max(1, Math.min(14, Number(value) || 5));
653
697
  this.setAttribute("day-count", String(next));
654
698
  }
655
- get visibleDayCount() {
656
- if (!isValidRange(this.min, this.max))
657
- return 0;
658
- const bounded = boundedDayCount(this.dayCount, this.min, this.max);
699
+ get hiddenDays() {
700
+ return [...this.#hiddenDays];
701
+ }
702
+ set hiddenDays(value) {
703
+ this.#hiddenDays = normalizeHiddenDays(value);
704
+ if (!this.isConnected || this.#batching)
705
+ return;
706
+ this.#reconcile({ pinActive: true });
707
+ }
708
+ #requestedDayCount() {
659
709
  if (!this.responsive || this.#measuredWidth === null)
660
- return bounded;
661
- return resolveVisibleDayCount(bounded, this.#measuredWidth, this.#breakpoints ?? RESPONSIVE_BREAKPOINTS);
710
+ return this.dayCount;
711
+ return resolveVisibleDayCount(this.dayCount, this.#measuredWidth, this.#breakpoints ?? RESPONSIVE_BREAKPOINTS);
712
+ }
713
+ #projection() {
714
+ return projectDates(this.start, this.#requestedDayCount(), this.#hiddenDays, this.min, this.max);
715
+ }
716
+ get visibleDayCount() {
717
+ return this.#projection().length;
662
718
  }
663
719
  get responsive() {
664
720
  return this.hasAttribute("responsive");
@@ -714,12 +770,10 @@
714
770
  throw new TypeError("value must be a valid YYYY-MM-DDTHH:mm or empty");
715
771
  }
716
772
  get activeDate() {
717
- const count = this.visibleDayCount;
718
- if (!count)
773
+ const dates = this.#projection();
774
+ if (!dates.length)
719
775
  return "";
720
- const raw = this.getAttribute("active-date") || "";
721
- const base = isDateValue(raw) ? raw : this.start;
722
- return resolveActiveDate(this.start, count, base);
776
+ return resolveActiveDate(dates, this.getAttribute("active-date") || "");
723
777
  }
724
778
  set activeDate(value) {
725
779
  if (!value)
@@ -762,15 +816,6 @@
762
816
  }
763
817
  this.setAttribute("notice-display", value);
764
818
  }
765
- get closedDays() {
766
- return this.getAttribute("closed-days") === "hide" ? "hide" : "show";
767
- }
768
- set closedDays(value) {
769
- if (value !== "show" && value !== "hide") {
770
- throw new TypeError('closed-days must be "show" or "hide"');
771
- }
772
- this.setAttribute("closed-days", value);
773
- }
774
819
  get expanded() {
775
820
  return this.hasAttribute("expanded");
776
821
  }
@@ -815,23 +860,19 @@
815
860
  this.setAttribute("locale", value);
816
861
  }
817
862
  get range() {
818
- const count = this.visibleDayCount;
819
- if (!count)
820
- return { start: "", end: "", dayCount: 0 };
821
- return rangeDetail(this.start, count);
863
+ return rangeDetail(this.#projection());
822
864
  }
823
865
  previous() {
824
- this.#navigate(-this.visibleDayCount);
866
+ this.#navigate(-1);
825
867
  }
826
868
  next() {
827
- this.#navigate(this.visibleDayCount);
869
+ this.#navigate(1);
828
870
  }
829
871
  goTo(date) {
830
- const count = this.visibleDayCount;
831
- if (!count)
872
+ if (!this.visibleDayCount)
832
873
  return "";
833
- const target = isDateValue(date) ? this.#clampToBounds(date) : this.start;
834
- this.#mutate(() => this.#moveTo(target, count));
874
+ const target = isDateValue(date) ? clampDate(date, this.min, this.max) : this.start;
875
+ this.#mutate(() => this.#moveTo(target));
835
876
  this.#commit();
836
877
  return this.activeDate;
837
878
  }
@@ -875,8 +916,8 @@
875
916
  this.homeDate = options.homeDate ?? "";
876
917
  if (options.responsiveBreakpoints !== undefined)
877
918
  this.responsiveBreakpoints = options.responsiveBreakpoints;
878
- if (options.closedDays !== undefined)
879
- this.closedDays = options.closedDays;
919
+ if (options.hiddenDays !== undefined)
920
+ this.hiddenDays = options.hiddenDays;
880
921
  });
881
922
  this.#reconcile({ pinActive: options.start === undefined });
882
923
  }
@@ -900,39 +941,32 @@
900
941
  if (options.responsiveBreakpoints != null && !Array.isArray(options.responsiveBreakpoints)) {
901
942
  throw new TypeError("responsiveBreakpoints must be an array or null");
902
943
  }
903
- if (options.closedDays !== undefined && options.closedDays !== "show" && options.closedDays !== "hide") {
904
- throw new TypeError('closedDays must be "show" or "hide"');
905
- }
944
+ if (options.hiddenDays !== undefined)
945
+ normalizeHiddenDays(options.hiddenDays);
906
946
  }
907
947
  async reload() {
908
948
  await this.#load();
909
949
  }
910
- #navigate(delta) {
911
- const count = this.visibleDayCount;
912
- if (!count)
950
+ #navigate(direction) {
951
+ const dates = this.#projection();
952
+ if (!dates.length)
913
953
  return;
914
- const next = clampStart(addDays(this.start, delta), this.min, this.max, count);
915
- if (next === this.start)
954
+ const next = stepStart(dates, direction, this.#hiddenDays, this.min, this.max);
955
+ if (!next || next === this.start)
916
956
  return;
917
957
  this.#mutate(() => this.setAttribute("start", next));
918
958
  this.#commit();
919
959
  }
920
- #moveTo(target, count) {
921
- const nextStart = ensureVisible(this.start, count, target, this.min, this.max);
960
+ #moveTo(target) {
961
+ const count = this.#requestedDayCount();
962
+ const nextStart = ensureVisible(this.start, count, target, this.min, this.max, this.#hiddenDays);
922
963
  if (nextStart !== this.getAttribute("start"))
923
964
  this.setAttribute("start", nextStart);
924
- const nextActive = resolveActiveDate(nextStart, count, target);
965
+ const dates = projectDates(nextStart, count, this.#hiddenDays, this.min, this.max);
966
+ const nextActive = resolveActiveDate(dates, target);
925
967
  if (this.getAttribute("active-date") !== nextActive)
926
968
  this.setAttribute("active-date", nextActive);
927
969
  }
928
- #clampToBounds(date) {
929
- let next = date;
930
- if (this.min && compareDates(next, this.min) < 0)
931
- next = this.min;
932
- if (this.max && compareDates(next, this.max) > 0)
933
- next = this.max;
934
- return next;
935
- }
936
970
  #mutate(fn) {
937
971
  const wasBatching = this.#batching;
938
972
  this.#batching = true;
@@ -948,8 +982,8 @@
948
982
  const wasBatching = this.#batching;
949
983
  this.#batching = true;
950
984
  try {
951
- const count = this.visibleDayCount;
952
- const invalid = count === 0;
985
+ const count = this.#requestedDayCount();
986
+ const invalid = this.visibleDayCount === 0;
953
987
  this.toggleAttribute("data-invalid-range", invalid);
954
988
  if (!invalid) {
955
989
  const rawActive = this.getAttribute("active-date") || "";
@@ -957,18 +991,19 @@
957
991
  let anchor = this.start;
958
992
  if (pinActive) {
959
993
  if (previous?.dayCount && isDateValue(rawActive)) {
960
- anchor = resolveActiveDate(previous.start, previous.dayCount, rawActive);
994
+ anchor = clampDate(rawActive, previous.start, previous.end);
961
995
  } else if (isDateValue(rawActive)) {
962
996
  anchor = rawActive;
963
997
  } else if (previous?.start) {
964
998
  anchor = previous.start;
965
999
  }
966
1000
  }
967
- const nextStart = ensureVisible(this.start, count, anchor, this.min, this.max);
1001
+ const nextStart = ensureVisible(this.start, count, anchor, this.min, this.max, this.#hiddenDays);
968
1002
  if (nextStart !== this.getAttribute("start"))
969
1003
  this.setAttribute("start", nextStart);
970
1004
  if (pinActive && isDateValue(anchor)) {
971
- const nextActive = resolveActiveDate(nextStart, count, anchor);
1005
+ const dates = projectDates(nextStart, count, this.#hiddenDays, this.min, this.max);
1006
+ const nextActive = resolveActiveDate(dates, anchor);
972
1007
  if (this.getAttribute("active-date") !== nextActive)
973
1008
  this.setAttribute("active-date", nextActive);
974
1009
  }
@@ -1025,10 +1060,10 @@
1025
1060
  return content ? content.getBoundingClientRect().width : null;
1026
1061
  }
1027
1062
  #setActiveDate(date, emit = true) {
1028
- const count = this.visibleDayCount;
1029
- if (!count)
1063
+ const dates = this.#projection();
1064
+ if (!dates.length)
1030
1065
  return;
1031
- const resolved = resolveActiveDate(this.start, count, date);
1066
+ const resolved = resolveActiveDate(dates, date);
1032
1067
  const before = this.activeDate;
1033
1068
  if (resolved === before && this.getAttribute("active-date") === resolved)
1034
1069
  return;
@@ -1116,40 +1151,31 @@
1116
1151
  return this.querySelector('.sp-slot[tabindex="0"]') ?? this.querySelector('.sp-strip-day[tabindex="0"]');
1117
1152
  }
1118
1153
  #projectedDays() {
1119
- const count = this.visibleDayCount;
1120
- if (!count)
1121
- return [];
1122
- const days = visibleDays(this.#days, this.start, count);
1123
- if (this.closedDays !== "hide")
1124
- return days;
1125
- const active = this.activeDate;
1126
- return days.filter((day) => !day.closed || this.layout === "day" && day.date === active);
1154
+ return visibleDays(this.#days, this.#projection());
1127
1155
  }
1128
1156
  #render() {
1129
1157
  const focused = document.activeElement;
1130
1158
  const fallbackSelector = focused instanceof Element && this.contains(focused) ? this.#focusSelector(focused) : "";
1131
1159
  const pendingSelector = this.#pendingFocus;
1132
1160
  this.#pendingFocus = "";
1133
- const count = this.visibleDayCount;
1134
- const invalid = count === 0;
1135
- const civilDays = count ? visibleDays(this.#days, this.start, count) : [];
1136
- const days = this.#projectedDays();
1137
- const allClosed = civilDays.length > 0 && civilDays.every((day) => day.closed);
1161
+ const dates = this.#projection();
1162
+ const range = rangeDetail(dates);
1163
+ const invalid = range.dayCount === 0;
1164
+ const days = visibleDays(this.#days, dates);
1138
1165
  const messages = resolveMessages(this.#messages);
1139
1166
  const locale = this.locale;
1140
- const canPrevious = !invalid && (!this.min || compareDates(this.start, this.min) > 0);
1141
- const canNext = !invalid && (!this.max || compareDates(rangeEnd(this.start, count), this.max) < 0);
1167
+ const canPrevious = !invalid && stepStart(dates, -1, this.#hiddenDays, this.min, this.max) !== range.start;
1168
+ const canNext = !invalid && stepStart(dates, 1, this.#hiddenDays, this.min, this.max) !== range.start;
1142
1169
  const empty = !this.#loading && !this.#error && !hasDayContent(days);
1143
1170
  let content;
1144
1171
  let hasOverflow = false;
1145
1172
  if (empty) {
1146
1173
  content = rangeEmpty({
1147
- start: this.range.start,
1148
- end: this.range.end,
1174
+ start: range.start,
1175
+ end: range.end,
1149
1176
  invalid,
1150
1177
  canNext,
1151
1178
  nextAvailability: this.hasAttribute("next-availability"),
1152
- closed: allClosed,
1153
1179
  locale,
1154
1180
  messages
1155
1181
  });
@@ -1180,7 +1206,7 @@
1180
1206
  }
1181
1207
  const status = this.#loading ? `<div class="sp-status sp-visually-hidden" role="status">${escapeHtml(messages.loading)}</div>` : this.#error ? `<div class="sp-status sp-status-error" role="status">${escapeHtml(String(this.#error))}</div>` : "";
1182
1208
  const homeAvailable = this.hasAttribute("home-date") && !invalid;
1183
- const homeInRange = homeAvailable && compareDates(this.homeDate, this.range.start) >= 0 && compareDates(this.homeDate, this.range.end) <= 0;
1209
+ const homeInRange = homeAvailable && compareDates(this.homeDate, range.start) >= 0 && compareDates(this.homeDate, range.end) <= 0;
1184
1210
  this.style.setProperty("--_sp-day-count", String(Math.max(1, days.length)));
1185
1211
  this.style.setProperty("--_sp-max-visible-rows", String(this.maxVisibleRows));
1186
1212
  if (this.#loading)
@@ -1 +1 @@
1
- :where(slot-picker){--sp-bg:#fff;--sp-fg:#1f2328;--sp-muted:color-mix(in srgb,var(--sp-fg)58%,var(--sp-bg));--sp-border:color-mix(in srgb,var(--sp-fg)16%,var(--sp-bg));--sp-hover:color-mix(in srgb,var(--sp-fg)5%,var(--sp-bg));--sp-notice-hover-bg:color-mix(in srgb,var(--sp-fg)12%,var(--sp-bg));--sp-accent:#2563eb;--sp-accent-fg:#fff;--sp-accent-soft:color-mix(in srgb,var(--sp-accent)16%,var(--sp-bg));--sp-focus:var(--sp-accent);--sp-radius:.5rem;--sp-gap:.5rem;--sp-day-gap:.75rem;--sp-slot-columns:3;--sp-card-bg:transparent;--sp-slot-block-size:2.75rem;--sp-slot-bg:var(--sp-accent-soft);--sp-slot-fg:var(--sp-fg);--sp-slot-border:transparent;--sp-slot-hover-bg:color-mix(in srgb,var(--sp-accent)22%,var(--sp-bg));--sp-slot-selected-bg:var(--sp-accent);--sp-slot-selected-fg:var(--sp-accent-fg);--sp-nav-size:2.5rem;--sp-day-header-block-size:3.4rem;--sp-collapsed-rows:var(--_sp-max-visible-rows,5);--sp-collapsed-body-block-size:calc(var(--sp-collapsed-rows)*(var(--sp-slot-block-size) + var(--sp-gap)) - var(--sp-gap));--sp-footer-block-size:2.5rem;--sp-shortcuts-block-size:2.5rem;--sp-collapsed-block-size:calc(var(--sp-day-header-block-size) + var(--sp-collapsed-body-block-size) + var(--sp-footer-block-size));--sp-more-fade-size:4rem;--sp-more-bg:var(--sp-bg);--sp-panel-min-block-size:calc(2*var(--sp-slot-block-size) + var(--sp-gap));--sp-closed-opacity:.55;--sp-loading-fade-opacity:.72;--sp-loading-fade-delay:.4s;display:block;color:var(--sp-fg);font:inherit}.sp-visually-hidden{position:absolute!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;white-space:nowrap!important;border:0!important;block-size:1px!important;inline-size:1px!important;margin:-1px!important;padding:0!important}.sp-shell{display:block}.sp-projection{display:grid;grid-template-columns:auto minmax(0,1fr)auto;align-items: start;gap:var(--sp-gap)}.sp-content{position:relative;opacity:1;min-inline-size:0;transition:opacity .16s}slot-picker[aria-busy=true] .sp-content{opacity:var(--sp-loading-fade-opacity,.72);transition-delay:var(--sp-loading-fade-delay,.4s)}@media (prefers-reduced-motion:reduce){.sp-content{transition-duration:0s}}slot-picker:not([layout=day]) .sp-content{min-block-size:var(--sp-collapsed-block-size)}.sp-shortcuts{display:flex;justify-content:flex-start;margin-block-start:var(--sp-gap)}slot-picker[home-date] .sp-shortcuts{min-block-size:var(--sp-shortcuts-block-size,2.5rem)}.sp-home{display:inline-flex;color:var(--sp-accent);font:inherit;cursor:pointer;background:0 0;border:0;align-items: center;gap:.35rem;padding:.25rem 0}.sp-home:hover{text-decoration:underline}.sp-grid{display:grid;grid-template-columns:repeat(var(--_sp-day-count,5),minmax(0,1fr));gap:var(--sp-day-gap)}.sp-day{min-inline-size:0}.sp-day-header{position:relative;display:grid;box-sizing:border-box;min-block-size:var(--sp-day-header-block-size);text-align:center;justify-items:center;gap:.1rem;padding-block-start:.15rem;padding-block-end:.55rem;line-height:1.25}.sp-weekday{color:var(--sp-muted);font-size:.85rem}.sp-date{font-size:.9rem;font-weight:650}.sp-notice{position:absolute;display:grid;color:var(--sp-muted);pointer-events:none;place-items: center;block-size:1.5rem;inline-size:1.5rem;top:0}.sp-notice:not(:-webkit-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){right:0}.sp-notice:not(:-moz-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){right:0}.sp-notice:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){right:0}.sp-notice:-webkit-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){left:0}.sp-notice:-moz-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){left:0}.sp-notice:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){left:0}.sp-notice[data-day-index]{pointer-events:auto;cursor:pointer;background:0 0;border:0;border-radius:50%;padding:0;transition:background-color .12s}.sp-notice[data-day-index]:hover{background:var(--sp-notice-hover-bg);color:var(--sp-fg)}.sp-notice-dot{background:currentColor;border-radius:50%;block-size:.45rem;inline-size:.45rem}.sp-slots{display:grid;gap:var(--sp-gap)}.sp-slot,.sp-empty{display:grid;min-block-size:var(--sp-slot-block-size);box-sizing:border-box;border-radius:var(--sp-radius);font:inherit;place-items: center}.sp-slot{border:1px solid var(--sp-slot-border);background:var(--sp-slot-bg);color:var(--sp-slot-fg);cursor:pointer}.sp-slot:hover:not(:disabled):not([aria-selected=true]){background:var(--sp-slot-hover-bg)}.sp-slot[aria-selected=true]{background:var(--sp-slot-selected-bg);color:var(--sp-slot-selected-fg)}.sp-slot:disabled{cursor:not-allowed;opacity:.45}.sp-slot:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-slot[aria-selected=true]:focus-visible{outline-color:var(--sp-fg)}.sp-empty{color:var(--sp-muted);user-select:none}.sp-nav{display:grid;inline-size:var(--sp-nav-size);block-size:var(--sp-nav-size);border:1px solid var(--sp-border);background:var(--sp-bg);color:var(--sp-fg);cursor:pointer;border-radius:50%;place-items: center;margin-block-start:.45rem;padding:0}.sp-nav:hover:not(:disabled){background:var(--sp-hover)}.sp-nav:disabled{cursor:not-allowed;opacity:.4}.sp-nav svg{display:block;pointer-events:none;block-size:1rem;inline-size:1rem}[dir=rtl] .sp-nav svg,html[dir=rtl] .sp-nav svg{transform:scaleX(-1)}.sp-more{display:flex;justify-content:center;margin-block-start:1rem}slot-picker:not([expanded]) .sp-more{position:absolute;display:grid;min-block-size:var(--sp-footer-block-size);place-items: end center;margin-block-start:0;bottom:0;left:0;right:0}slot-picker:not([expanded]) .sp-more:before{content:"";position:absolute;block-size:var(--sp-more-fade-size,4rem);background:linear-gradient(to bottom,transparent,var(--sp-more-bg,var(--sp-bg)));pointer-events:none;bottom:0;left:0;right:0}slot-picker[expanded] .sp-more{position:static}slot-picker[expanded] .sp-more:before{content:none}.sp-more-button{position:relative;z-index:1;color:var(--sp-accent);font:inherit;cursor:pointer;background:0 0;border:0}.sp-status{color:var(--sp-muted);text-align:center;margin-block-end:.75rem}.sp-status-error{color:var(--sp-fg)}.sp-day-empty{display:grid;min-block-size:var(--sp-slot-block-size);color:var(--sp-muted);text-align:center;place-items: center;padding-inline-start:.5rem;padding-inline-end:.5rem;font-size:.9rem}.sp-day[data-closed],.sp-panel[data-closed],.sp-strip-day[data-closed]{opacity:var(--sp-closed-opacity,.55)}.sp-range-empty{display:grid;box-sizing:border-box;min-block-size:var(--sp-collapsed-block-size);border-radius:var(--sp-radius);background:var(--sp-card-bg);text-align:center;align-content: center;justify-items:center;gap:.5rem;padding:1.5rem 1rem}slot-picker[layout=day] .sp-range-empty{min-block-size:var(--sp-panel-min-block-size)}slot-picker[layout=day] .sp-day-empty{min-block-size:var(--sp-panel-min-block-size);align-content: center}.sp-range-empty-title{font-size:1rem}.sp-range-empty-description{color:var(--sp-muted);margin:0}.sp-range-empty-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:.5rem}.sp-range-empty-next{border-radius:var(--sp-radius);background:var(--sp-accent);color:var(--sp-accent-fg);font:inherit;cursor:pointer;border:0;padding:.6rem 1rem}.sp-range-empty-next:hover{background:color-mix(in srgb,var(--sp-accent)85%,var(--sp-bg))}.sp-range-empty-availability{border:1px solid var(--sp-border);border-radius:var(--sp-radius);background:var(--sp-bg);color:var(--sp-accent);font:inherit;cursor:pointer;padding:.6rem 1rem}.sp-range-empty-availability:hover{background:var(--sp-hover)}.sp-range-empty-next:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-range-empty-availability:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-daystrip{display:grid;grid-template-columns:repeat(var(--_sp-day-count,5),minmax(0,1fr));gap:var(--sp-gap);margin-block-end:1rem}.sp-strip-day{display:grid;border:1px solid var(--sp-border);border-radius:var(--sp-radius);background:var(--sp-card-bg);color:var(--sp-fg);font:inherit;cursor:pointer;justify-items:center;gap:.1rem;min-inline-size:0;padding-block-start:.6rem;padding-block-end:.55rem;padding-inline-start:.25rem;padding-inline-end:.25rem}.sp-strip-day:hover{background:var(--sp-hover)}.sp-strip-day[aria-pressed=true]{border-color:var(--sp-accent);background:var(--sp-accent-soft)}.sp-strip-weekday{color:var(--sp-muted);font-size:.85rem}.sp-strip-day[data-today] .sp-strip-weekday{font-weight:700}.sp-strip-date{font-size:.9rem}.sp-strip-count{color:var(--sp-muted);text-align:center;overflow-wrap:anywhere;max-inline-size:100%;font-size:.8rem}.sp-panel-header{position:relative;display:flex;text-align:center;justify-content:center;align-items: center;gap:.5rem;margin-block-end:.75rem}.sp-panel-title{font-size:1rem}.sp-panel-slots{display:grid;grid-template-columns:repeat(var(--sp-slot-columns,3),minmax(0,1fr));gap:var(--sp-gap)}.sp-day-notice{display:block;box-sizing:border-box;overflow:hidden;border-radius:var(--sp-radius);background:var(--sp-hover);text-align:center;min-inline-size:0;max-inline-size:100%;margin-block-end:.75rem;padding:.75rem 1rem}.sp-day-notice-label{display:-webkit-box;overflow:hidden;overflow-wrap:anywhere;-webkit-line-clamp:2;-webkit-box-orient:vertical;margin-block-end:.25rem}.sp-day-notice-description{display:none;overflow-wrap:anywhere;color:var(--sp-muted);min-inline-size:0;max-inline-size:100%;margin:0;font-size:.9rem}slot-picker[layout=day] .sp-day-notice-label{display:block;-webkit-line-clamp:unset}slot-picker[layout=day] .sp-day-notice-description{display:block}.sp-nav:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-home:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-more-button:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-notice[data-day-index]:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-strip-day:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}@media (forced-colors:active){.sp-slot{border-color:buttontext}.sp-slot[aria-selected=true]{border-width:2px}.sp-strip-day[aria-pressed=true]{border-width:2px;border-color:buttontext}.sp-day-notice{border:1px solid buttontext}.sp-notice-dot{background:canvastext}}
1
+ :where(slot-picker){--sp-bg:#fff;--sp-fg:#1f2328;--sp-muted:color-mix(in srgb,var(--sp-fg)58%,var(--sp-bg));--sp-border:color-mix(in srgb,var(--sp-fg)16%,var(--sp-bg));--sp-hover:color-mix(in srgb,var(--sp-fg)5%,var(--sp-bg));--sp-notice-hover-bg:color-mix(in srgb,var(--sp-fg)12%,var(--sp-bg));--sp-accent:#2563eb;--sp-accent-fg:#fff;--sp-accent-soft:color-mix(in srgb,var(--sp-accent)16%,var(--sp-bg));--sp-focus:var(--sp-accent);--sp-radius:.5rem;--sp-gap:.5rem;--sp-day-gap:.75rem;--sp-slot-columns:3;--sp-card-bg:transparent;--sp-slot-block-size:2.75rem;--sp-slot-bg:var(--sp-accent-soft);--sp-slot-fg:var(--sp-fg);--sp-slot-border:transparent;--sp-slot-hover-bg:color-mix(in srgb,var(--sp-accent)22%,var(--sp-bg));--sp-slot-selected-bg:var(--sp-accent);--sp-slot-selected-fg:var(--sp-accent-fg);--sp-nav-size:2.5rem;--sp-day-header-block-size:3.4rem;--sp-collapsed-rows:var(--_sp-max-visible-rows,5);--sp-collapsed-body-block-size:calc(var(--sp-collapsed-rows)*(var(--sp-slot-block-size) + var(--sp-gap)) - var(--sp-gap));--sp-footer-block-size:2.5rem;--sp-shortcuts-block-size:2.5rem;--sp-collapsed-block-size:calc(var(--sp-day-header-block-size) + var(--sp-collapsed-body-block-size) + var(--sp-footer-block-size));--sp-more-fade-size:4rem;--sp-more-bg:var(--sp-bg);--sp-panel-min-block-size:calc(2*var(--sp-slot-block-size) + var(--sp-gap));--sp-closed-opacity:.55;--sp-loading-fade-opacity:.72;--sp-loading-fade-delay:.4s;display:block;color:var(--sp-fg);font:inherit}.sp-visually-hidden{position:absolute!important;overflow:hidden!important;clip:rect(0 0 0 0)!important;white-space:nowrap!important;border:0!important;block-size:1px!important;inline-size:1px!important;margin:-1px!important;padding:0!important}.sp-shell{display:block}.sp-projection{display:grid;grid-template-columns:auto minmax(0,1fr)auto;align-items: start;gap:var(--sp-gap)}.sp-content{position:relative;opacity:1;min-inline-size:0;transition:opacity .16s}slot-picker[aria-busy=true] .sp-content{opacity:var(--sp-loading-fade-opacity,.72);transition-delay:var(--sp-loading-fade-delay,.4s)}@media (prefers-reduced-motion:reduce){.sp-content{transition-duration:0s}}slot-picker:not([layout=day]) .sp-content{min-block-size:var(--sp-collapsed-block-size)}.sp-shortcuts{display:flex;justify-content:flex-start;margin-block-start:var(--sp-gap)}slot-picker[home-date] .sp-shortcuts{min-block-size:var(--sp-shortcuts-block-size,2.5rem)}.sp-home{display:inline-flex;color:var(--sp-accent);font:inherit;cursor:pointer;background:0 0;border:0;align-items: center;gap:.35rem;padding:.25rem 0}.sp-home:hover{text-decoration:underline}.sp-grid{display:grid;grid-template-columns:repeat(var(--_sp-day-count,5),minmax(0,1fr));gap:var(--sp-day-gap)}.sp-day{min-inline-size:0}.sp-day-header{position:relative;display:grid;box-sizing:border-box;min-block-size:var(--sp-day-header-block-size);text-align:center;justify-items:center;gap:.1rem;padding-block-start:.15rem;padding-block-end:.55rem;line-height:1.25}.sp-weekday{color:var(--sp-muted);font-size:.85rem}.sp-date{font-size:.9rem;font-weight:650}.sp-notice{position:absolute;display:grid;color:var(--sp-muted);pointer-events:none;place-items: center;block-size:1.5rem;inline-size:1.5rem;top:0}.sp-notice:not(:-webkit-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){right:0}.sp-notice:not(:-moz-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){right:0}.sp-notice:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){right:0}.sp-notice:-webkit-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){left:0}.sp-notice:-moz-any(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){left:0}.sp-notice:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){left:0}.sp-notice[data-day-index]{pointer-events:auto;cursor:pointer;background:0 0;border:0;border-radius:50%;padding:0;transition:background-color .12s}.sp-notice[data-day-index]:hover{background:var(--sp-notice-hover-bg);color:var(--sp-fg)}.sp-notice-dot{background:currentColor;border-radius:50%;block-size:.45rem;inline-size:.45rem}.sp-slots{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--sp-gap)}.sp-slot,.sp-empty{display:grid;min-block-size:var(--sp-slot-block-size);box-sizing:border-box;border-radius:var(--sp-radius);font:inherit;place-items: center;min-inline-size:0;max-inline-size:100%}.sp-slot-time{display:inline-flex;justify-content:center;align-items: center}.sp-slot{border:1px solid var(--sp-slot-border);background:var(--sp-slot-bg);color:var(--sp-slot-fg);cursor:pointer}.sp-slot:hover:not(:disabled):not([aria-selected=true]){background:var(--sp-slot-hover-bg)}.sp-slot[aria-selected=true]{background:var(--sp-slot-selected-bg);color:var(--sp-slot-selected-fg)}.sp-slot:disabled{cursor:not-allowed;opacity:.45}.sp-slot:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-slot[aria-selected=true]:focus-visible{outline-color:var(--sp-fg)}.sp-empty{color:var(--sp-muted);user-select:none}.sp-nav{display:grid;inline-size:var(--sp-nav-size);block-size:var(--sp-nav-size);border:1px solid var(--sp-border);background:var(--sp-bg);color:var(--sp-fg);cursor:pointer;border-radius:50%;place-items: center;margin-block-start:.45rem;padding:0}.sp-nav:hover:not(:disabled){background:var(--sp-hover)}.sp-nav:disabled{cursor:not-allowed;opacity:.4}.sp-nav svg{display:block;pointer-events:none;block-size:1rem;inline-size:1rem}[dir=rtl] .sp-nav svg,html[dir=rtl] .sp-nav svg{transform:scaleX(-1)}.sp-more{display:flex;justify-content:center;margin-block-start:1rem}slot-picker:not([expanded]) .sp-more{position:absolute;display:grid;min-block-size:var(--sp-footer-block-size);place-items: end center;margin-block-start:0;bottom:0;left:0;right:0}slot-picker:not([expanded]) .sp-more:before{content:"";position:absolute;block-size:var(--sp-more-fade-size,4rem);background:linear-gradient(to bottom,transparent,var(--sp-more-bg,var(--sp-bg)));pointer-events:none;bottom:0;left:0;right:0}slot-picker[expanded] .sp-more{position:static}slot-picker[expanded] .sp-more:before{content:none}.sp-more-button{position:relative;z-index:1;color:var(--sp-accent);font:inherit;cursor:pointer;background:0 0;border:0}.sp-status{color:var(--sp-muted);text-align:center;margin-block-end:.75rem}.sp-status-error{color:var(--sp-fg)}.sp-day-empty{display:grid;min-block-size:var(--sp-slot-block-size);color:var(--sp-muted);text-align:center;place-items: center;padding-inline-start:.5rem;padding-inline-end:.5rem;font-size:.9rem}.sp-day[data-closed],.sp-panel[data-closed],.sp-strip-day[data-closed]{opacity:var(--sp-closed-opacity,.55)}.sp-range-empty{display:grid;box-sizing:border-box;min-block-size:var(--sp-collapsed-block-size);border-radius:var(--sp-radius);background:var(--sp-card-bg);text-align:center;align-content: center;justify-items:center;gap:.5rem;padding:1.5rem 1rem}slot-picker[layout=day] .sp-range-empty{min-block-size:var(--sp-panel-min-block-size)}slot-picker[layout=day] .sp-day-empty{min-block-size:var(--sp-panel-min-block-size);align-content: center}.sp-range-empty-title{font-size:1rem}.sp-range-empty-description{color:var(--sp-muted);margin:0}.sp-range-empty-actions{display:flex;flex-wrap:wrap;justify-content:center;gap:.5rem}.sp-range-empty-next{border-radius:var(--sp-radius);background:var(--sp-accent);color:var(--sp-accent-fg);font:inherit;cursor:pointer;border:0;padding:.6rem 1rem}.sp-range-empty-next:hover{background:color-mix(in srgb,var(--sp-accent)85%,var(--sp-bg))}.sp-range-empty-availability{border:1px solid var(--sp-border);border-radius:var(--sp-radius);background:var(--sp-bg);color:var(--sp-accent);font:inherit;cursor:pointer;padding:.6rem 1rem}.sp-range-empty-availability:hover{background:var(--sp-hover)}.sp-range-empty-next:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-range-empty-availability:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-daystrip{display:grid;grid-template-columns:repeat(var(--_sp-day-count,5),minmax(0,1fr));gap:var(--sp-gap);margin-block-end:1rem}.sp-strip-day{display:grid;border:1px solid var(--sp-border);border-radius:var(--sp-radius);background:var(--sp-card-bg);color:var(--sp-fg);font:inherit;cursor:pointer;justify-items:center;gap:.1rem;min-inline-size:0;padding-block-start:.6rem;padding-block-end:.55rem;padding-inline-start:.25rem;padding-inline-end:.25rem}.sp-strip-day:hover{background:var(--sp-hover)}.sp-strip-day[aria-pressed=true]{border-color:var(--sp-accent);background:var(--sp-accent-soft)}.sp-strip-weekday{color:var(--sp-muted);font-size:.85rem}.sp-strip-day[data-today] .sp-strip-weekday{font-weight:700}.sp-strip-date{font-size:.9rem}.sp-strip-count{color:var(--sp-muted);text-align:center;overflow-wrap:anywhere;max-inline-size:100%;font-size:.8rem}.sp-panel-header{position:relative;display:flex;text-align:center;justify-content:center;align-items: center;gap:.5rem;margin-block-end:.75rem}.sp-panel-title{font-size:1rem}.sp-panel-slots{display:grid;grid-template-columns:repeat(var(--sp-slot-columns,3),minmax(0,1fr));gap:var(--sp-gap)}.sp-day-notice{display:block;box-sizing:border-box;overflow:hidden;border-radius:var(--sp-radius);background:var(--sp-hover);text-align:center;min-inline-size:0;max-inline-size:100%;margin-block-end:.75rem;padding:.75rem 1rem}.sp-day-notice-label{display:-webkit-box;overflow:hidden;overflow-wrap:anywhere;-webkit-line-clamp:2;-webkit-box-orient:vertical;margin-block-end:.25rem}.sp-day-notice-description{display:none;overflow-wrap:anywhere;color:var(--sp-muted);min-inline-size:0;max-inline-size:100%;margin:0;font-size:.9rem}slot-picker[layout=day] .sp-day-notice-label{display:block;-webkit-line-clamp:unset}slot-picker[layout=day] .sp-day-notice-description{display:block}.sp-nav:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-home:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-more-button:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-notice[data-day-index]:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}.sp-strip-day:focus-visible{outline:2px solid var(--sp-focus);outline-offset:2px}@media (forced-colors:active){.sp-slot{border-color:buttontext}.sp-slot[aria-selected=true]{border-width:2px}.sp-strip-day[aria-pressed=true]{border-width:2px;border-color:buttontext}.sp-day-notice{border:1px solid buttontext}.sp-notice-dot{background:canvastext}}