@lekoala/date-picker 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /*** @lekoala/date-picker v0.4.0 - https://github.com/lekoala/date-picker ***/
1
+ /*** @lekoala/date-picker v0.5.0 - https://github.com/lekoala/date-picker ***/
2
2
  (() => {
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -181,8 +181,10 @@
181
181
  for (let i = 0;i < 7; i++)
182
182
  week.push(addDays(cursor, i));
183
183
  weeks.push(week);
184
- if (compareDates(week[6], end) >= 0)
185
- break;
184
+ if (compareDates(week[6], end) >= 0) {
185
+ if (!options.fixedWeeks || weeks.length >= 6 || compareDates(week[6], addDays("9999-12-31", -7)) > 0)
186
+ break;
187
+ }
186
188
  cursor = addDays(cursor, 7);
187
189
  }
188
190
  return weeks;
@@ -296,6 +298,30 @@
296
298
  }
297
299
  }
298
300
 
301
+ // src/control-classes.js
302
+ function normalizeControlClass(value) {
303
+ if (typeof value !== "string")
304
+ return "";
305
+ return [
306
+ ...new Set(value.split(/\s+/).filter((token) => token && !token.startsWith("dp-") && ![...token].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127)))
307
+ ].join(" ");
308
+ }
309
+ function sameControlClasses(a, b) {
310
+ return a.month === b.month && a.year === b.year && a.nav === b.nav;
311
+ }
312
+ var defaults = { month: "", year: "", nav: "" };
313
+ function getDefaultControlClasses() {
314
+ return { ...defaults };
315
+ }
316
+ function resolveControlClasses(classes) {
317
+ const result = getDefaultControlClasses();
318
+ for (const role of ["month", "year", "nav"]) {
319
+ if (classes && Object.hasOwn(classes, role))
320
+ result[role] = normalizeControlClass(classes[role]);
321
+ }
322
+ return result;
323
+ }
324
+
299
325
  // src/date-range.js
300
326
  function normalizeRange(range) {
301
327
  if (range == null)
@@ -480,6 +506,23 @@
480
506
 
481
507
  // src/intl.js
482
508
  var BIDI = /[\u200e\u200f\u061c\u202a-\u202e\u2066-\u2069]/g;
509
+ var dateFormatters = new Map;
510
+ var digitMaps = new Map;
511
+ function cached(cache, key, create) {
512
+ const existing = cache.get(key);
513
+ if (existing !== undefined)
514
+ return existing;
515
+ const value = create();
516
+ const oldest = cache.keys().next().value;
517
+ if (cache.size >= 64 && oldest !== undefined)
518
+ cache.delete(oldest);
519
+ cache.set(key, value);
520
+ return value;
521
+ }
522
+ function dateFormatter(locale, options) {
523
+ const key = JSON.stringify([locale, options]);
524
+ return cached(dateFormatters, key, () => new Intl.DateTimeFormat(locale, options));
525
+ }
483
526
  function resolveLocale(locale = "") {
484
527
  if (locale)
485
528
  return locale;
@@ -490,11 +533,13 @@
490
533
  return "en-US";
491
534
  }
492
535
  function digitMap(locale) {
493
- const formatter = new Intl.NumberFormat(locale, { useGrouping: false });
494
- const map = new Map;
495
- for (let i = 0;i <= 9; i++)
496
- map.set(formatter.format(i), String(i));
497
- return map;
536
+ return cached(digitMaps, locale, () => {
537
+ const formatter = new Intl.NumberFormat(locale, { useGrouping: false });
538
+ const map = new Map;
539
+ for (let i = 0;i <= 9; i++)
540
+ map.set(formatter.format(i), String(i));
541
+ return map;
542
+ });
498
543
  }
499
544
  function normalizeDigits(text, locale, options = {}) {
500
545
  let result = String(text || "").replace(BIDI, "");
@@ -506,7 +551,7 @@
506
551
  }
507
552
  function formatLongDate(value, locale = "") {
508
553
  const resolved = resolveLocale(locale);
509
- return new Intl.DateTimeFormat(resolved, {
554
+ return dateFormatter(resolved, {
510
555
  calendar: "gregory",
511
556
  weekday: "long",
512
557
  year: "numeric",
@@ -517,7 +562,7 @@
517
562
  }
518
563
  function formatMonthYear(value, locale = "") {
519
564
  const resolved = resolveLocale(locale);
520
- return new Intl.DateTimeFormat(resolved, {
565
+ return dateFormatter(resolved, {
521
566
  calendar: "gregory",
522
567
  year: "numeric",
523
568
  month: "long",
@@ -527,7 +572,7 @@
527
572
  function monthYearOrder(locale = "") {
528
573
  const resolved = resolveLocale(locale);
529
574
  try {
530
- const parts = new Intl.DateTimeFormat(resolved, {
575
+ const parts = dateFormatter(resolved, {
531
576
  calendar: "gregory",
532
577
  year: "numeric",
533
578
  month: "long",
@@ -552,12 +597,12 @@
552
597
  }
553
598
  function monthNames(locale = "", style = "long") {
554
599
  const resolved = resolveLocale(locale);
555
- const formatter = new Intl.DateTimeFormat(resolved, { calendar: "gregory", month: style, timeZone: "UTC" });
600
+ const formatter = dateFormatter(resolved, { calendar: "gregory", month: style, timeZone: "UTC" });
556
601
  return Array.from({ length: 12 }, (_, index) => formatter.format(toIntlDate(`2026-${String(index + 1).padStart(2, "0")}-15`)));
557
602
  }
558
603
  function weekdayNames(locale = "", firstDay = 1, style = "short") {
559
604
  const resolved = resolveLocale(locale);
560
- const formatter = new Intl.DateTimeFormat(resolved, {
605
+ const formatter = dateFormatter(resolved, {
561
606
  calendar: "gregory",
562
607
  weekday: style,
563
608
  timeZone: "UTC"
@@ -575,7 +620,7 @@
575
620
  }
576
621
  function createDateAdapter(locale = "") {
577
622
  const resolved = resolveLocale(locale);
578
- const formatter = new Intl.DateTimeFormat(resolved, {
623
+ const formatter = dateFormatter(resolved, {
579
624
  calendar: "gregory",
580
625
  year: "numeric",
581
626
  month: "2-digit",
@@ -654,9 +699,9 @@
654
699
  rangeOrderStart: "Start must be on or before end",
655
700
  rangeOrderEnd: "End must be on or after start"
656
701
  };
657
- var defaults = { ...DEFAULT_MESSAGES };
702
+ var defaults2 = { ...DEFAULT_MESSAGES };
658
703
  function getDefaultMessages() {
659
- return { ...defaults };
704
+ return { ...defaults2 };
660
705
  }
661
706
 
662
707
  // src/source.js
@@ -756,7 +801,9 @@
756
801
  this._highlightedRange = { start: "", end: "" };
757
802
  this._paintedRange = { start: "", end: "" };
758
803
  this._paintedPreview = false;
804
+ this._renderedStates = new Map;
759
805
  this._messages = getDefaultMessages();
806
+ this._controlClasses = getDefaultControlClasses();
760
807
  this._onClick = this._onClick.bind(this);
761
808
  this._onChange = this._onChange.bind(this);
762
809
  this._onKeyDown = this._onKeyDown.bind(this);
@@ -893,6 +940,17 @@
893
940
  set showWeekNumbers(value) {
894
941
  this.toggleAttribute("show-week-numbers", Boolean(value));
895
942
  }
943
+ get controlClasses() {
944
+ return { ...this._controlClasses };
945
+ }
946
+ set controlClasses(value) {
947
+ const classes = resolveControlClasses(value);
948
+ if (sameControlClasses(classes, this._controlClasses))
949
+ return;
950
+ this._controlClasses = classes;
951
+ if (this._connected)
952
+ this.render();
953
+ }
896
954
  get messages() {
897
955
  return this._messages;
898
956
  }
@@ -973,18 +1031,10 @@
973
1031
  return compareDates(candidate, last) > 0 ? last : candidate;
974
1032
  }
975
1033
  _weeks(display = this.display) {
976
- const weeks = getMonthWeeks(clampDisplayMonth(display, this.firstDay), { firstDay: this.firstDay });
977
- if (!this.fixedWeeks || weeks.length >= 6)
978
- return weeks;
979
- let cursor = weeks[weeks.length - 1]?.[6] || "";
980
- while (weeks.length < 6 && cursor) {
981
- const week = [];
982
- for (let i = 1;i <= 7; i++)
983
- week.push(addDays(cursor, i));
984
- weeks.push(week);
985
- cursor = week[6];
986
- }
987
- return weeks;
1034
+ return getMonthWeeks(clampDisplayMonth(display, this.firstDay), {
1035
+ firstDay: this.firstDay,
1036
+ fixedWeeks: this.fixedWeeks
1037
+ });
988
1038
  }
989
1039
  _focusTargetKey(element) {
990
1040
  if (element.matches(".dp-month-select, .dp-year-input") && element.id) {
@@ -1171,6 +1221,8 @@
1171
1221
  ].filter(Boolean).join(". ");
1172
1222
  }
1173
1223
  _updateRangeDom() {
1224
+ const previousRange = this._paintedRange;
1225
+ const previousPreview = this._paintedPreview;
1174
1226
  this._paintedRange = { ...this._highlightedRange };
1175
1227
  this._paintedPreview = this.hasAttribute("data-range-preview");
1176
1228
  for (const cell of this.querySelectorAll(".dp-day[data-date]")) {
@@ -1178,13 +1230,20 @@
1178
1230
  continue;
1179
1231
  const date = cell.dataset.date || "";
1180
1232
  const position = rangePosition(date, this._highlightedRange);
1233
+ if (position === rangePosition(date, previousRange) && previousPreview === this._paintedPreview)
1234
+ continue;
1181
1235
  for (const [name, on] of Object.entries(rangeAttributeMap(position))) {
1182
1236
  if (on)
1183
1237
  cell.setAttribute(name, "true");
1184
1238
  else
1185
1239
  cell.removeAttribute(name);
1186
1240
  }
1187
- cell.setAttribute("aria-label", this._dayLabel(date, this.getDateState(date), position));
1241
+ if (!previousPreview || !this._paintedPreview) {
1242
+ const state = this._renderedStates.get(date) || {};
1243
+ const label = this._dayLabel(date, state, position);
1244
+ if (cell.getAttribute("aria-label") !== label)
1245
+ cell.setAttribute("aria-label", label);
1246
+ }
1188
1247
  }
1189
1248
  }
1190
1249
  focusGrid() {
@@ -1347,10 +1406,12 @@
1347
1406
  return `<option value="${String(month).padStart(2, "0")}"${month === displayMonth ? " selected" : ""}${disabled ? " disabled" : ""}>${escapeHtml(name)}</option>`;
1348
1407
  }).join("");
1349
1408
  const dayHeaders = weekdayShort.map((name, index) => `<th scope="col" abbr="${escapeHtml(weekdayLong[index])}">${escapeHtml(name.replace(".", ""))}</th>`).join("");
1409
+ this._renderedStates.clear();
1350
1410
  const rows = weeks.map((week) => {
1351
1411
  const weekNumber = showWeeks ? `<th scope="row" class="dp-week-number">${isoWeekNumber(week[0])}</th>` : "";
1352
1412
  const cells = week.map((date) => {
1353
1413
  const state = this.getDateState(date);
1414
+ this._renderedStates.set(date, state);
1354
1415
  const outside = monthKey(date) !== display;
1355
1416
  const selected = this.value === date;
1356
1417
  const focused = this.focusedDate === date;
@@ -1365,18 +1426,22 @@
1365
1426
  const focusKey = focusedElement instanceof HTMLElement && this.contains(focusedElement) ? this._focusTargetKey(focusedElement) : "";
1366
1427
  const yearFirst = monthYearOrder(locale)[0] === "year";
1367
1428
  this.toggleAttribute("data-year-first", yearFirst);
1429
+ const classes = this._controlClasses;
1430
+ const monthClass = escapeHtml(classes.month || "dp-skin");
1431
+ const yearClass = escapeHtml(classes.year || "dp-skin");
1432
+ const navClass = escapeHtml(classes.nav || "dp-skin");
1368
1433
  const monthControl = `<label class="dp-visually-hidden" for="${this._id}-month">${escapeHtml(this._messages.month)}</label>
1369
- <select id="${this._id}-month" class="dp-month-select" aria-label="${escapeHtml(this._messages.month)}">${monthOptions}</select>`;
1434
+ <select id="${this._id}-month" class="dp-month-select ${monthClass}" aria-label="${escapeHtml(this._messages.month)}">${monthOptions}</select>`;
1370
1435
  const yearControl = `<label class="dp-visually-hidden" for="${this._id}-year">${escapeHtml(this._messages.year)}</label>
1371
- <input id="${this._id}-year" class="dp-year-input" type="number" inputmode="numeric" value="${displayYear}"${minYearAttr}${maxYearAttr} aria-label="${escapeHtml(this._messages.year)}">`;
1436
+ <input id="${this._id}-year" class="dp-year-input ${yearClass}" type="number" inputmode="numeric" value="${displayYear}"${minYearAttr}${maxYearAttr} aria-label="${escapeHtml(this._messages.year)}">`;
1372
1437
  this._rendering = true;
1373
1438
  this.innerHTML = `
1374
1439
  <div class="dp-calendar-shell">
1375
1440
  <div class="dp-calendar-header">
1376
1441
  ${yearFirst ? yearControl : monthControl}
1377
1442
  ${yearFirst ? monthControl : yearControl}
1378
- <button type="button" class="dp-nav dp-prev" data-calendar-action="previous" aria-label="${escapeHtml(this._messages.previousMonth)}"${prevDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 10 4-4 4 4"/></svg></button>
1379
- <button type="button" class="dp-nav dp-next" data-calendar-action="next" aria-label="${escapeHtml(this._messages.nextMonth)}"${nextDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 6 4 4 4-4"/></svg></button>
1443
+ <button type="button" class="dp-nav dp-prev ${navClass}" data-calendar-action="previous" aria-label="${escapeHtml(this._messages.previousMonth)}"${prevDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 10 4-4 4 4"/></svg></button>
1444
+ <button type="button" class="dp-nav dp-next ${navClass}" data-calendar-action="next" aria-label="${escapeHtml(this._messages.nextMonth)}"${nextDisabled ? " disabled" : ""}><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m4 6 4 4 4-4"/></svg></button>
1380
1445
  </div>
1381
1446
  <h2 id="${headingId}" class="dp-calendar-heading dp-visually-hidden" aria-live="polite">${escapeHtml(formatMonthYear(`${display}-15`, locale))}</h2>
1382
1447
  <table id="${gridId}" class="dp-grid" role="grid" aria-labelledby="${headingId}">
@@ -1406,7 +1471,7 @@
1406
1471
  const extra = cell.querySelector(".dp-day-extra");
1407
1472
  if (!(extra instanceof HTMLElement))
1408
1473
  continue;
1409
- const content = this._renderDay(date, this.getDateState(date));
1474
+ const content = this._renderDay(date, this._renderedStates.get(date) || {});
1410
1475
  if (content instanceof Node)
1411
1476
  extra.append(content);
1412
1477
  else if (content !== undefined && content !== null)
@@ -1422,13 +1487,15 @@
1422
1487
  function crossAxisFor(side) {
1423
1488
  return side === "top" || side === "bottom" ? "x" : "y";
1424
1489
  }
1490
+ var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
1491
+ var PLACEMENTS = new Set(Object.keys(OPPOSITE).flatMap((side) => [side, `${side}-start`, `${side}-end`]));
1492
+ var COORDINATE_SPACES = new Set(["viewport", "document"]);
1425
1493
  function parsePlacement(placement) {
1494
+ if (!PLACEMENTS.has(placement))
1495
+ throw new TypeError(`Unknown placement: ${placement}`);
1426
1496
  const [side, align = null] = placement.split("-");
1427
1497
  return { side, align, crossAxis: crossAxisFor(side) };
1428
1498
  }
1429
- function flipSide(side) {
1430
- return { top: "bottom", bottom: "top", left: "right", right: "left" }[side] || side;
1431
- }
1432
1499
  function computeCoords(reference, floating, side, align, rtl, distance) {
1433
1500
  const crossAxis = crossAxisFor(side);
1434
1501
  const commonX = reference.x + reference.width / 2 - floating.width / 2;
@@ -1460,17 +1527,12 @@
1460
1527
  function overflowOn(position, size, start, end) {
1461
1528
  return Math.max(start - position, 0) + Math.max(position + size - end, 0);
1462
1529
  }
1463
- function isRTL(element) {
1464
- const direction = "dir" in element ? element.dir : "";
1465
- if (direction === "rtl")
1466
- return true;
1467
- if (direction === "ltr")
1468
- return false;
1469
- const win = element.ownerDocument?.defaultView;
1470
- if (win?.CSS?.supports?.("selector(:dir(rtl))") && typeof element.matches === "function") {
1471
- return element.matches(":dir(rtl)");
1530
+ function isRTL(reference) {
1531
+ const win = reference.ownerDocument?.defaultView;
1532
+ if (win && reference instanceof win.Element) {
1533
+ return win.getComputedStyle(reference).direction === "rtl";
1472
1534
  }
1473
- return Boolean(win?.Element && element instanceof win.Element && win.getComputedStyle(element).direction === "rtl");
1535
+ return "dir" in reference && reference.dir === "rtl";
1474
1536
  }
1475
1537
  var STABLE_SCROLLBAR_MAX_WIDTH = 25;
1476
1538
  function getViewportBoundary(doc) {
@@ -1508,8 +1570,14 @@
1508
1570
  const percent = (referenceCenter - boxStart) / size * 100;
1509
1571
  return Math.round(Math.min(100, Math.max(0, percent)) * 1000) / 1000;
1510
1572
  }
1511
- function isOutsideBoundary(rect, boundary) {
1512
- return rect.right < boundary.x || rect.left > boundary.right || rect.bottom < boundary.y || rect.top > boundary.bottom;
1573
+ function isOutsideBoundary(rects, boundary) {
1574
+ for (let i = 0;i < rects.length; i++) {
1575
+ const rect = rects[i];
1576
+ if (rect.right >= boundary.x && rect.left <= boundary.right && rect.bottom >= boundary.y && rect.top <= boundary.bottom) {
1577
+ return false;
1578
+ }
1579
+ }
1580
+ return true;
1513
1581
  }
1514
1582
  function getAvailableHeight(referenceRect, side, boundary, distance, padding) {
1515
1583
  if (side === "top") {
@@ -1564,10 +1632,22 @@
1564
1632
  for (const [subscription, type] of notifications) {
1565
1633
  if (!subscriptions.has(subscription) || !subscription.floating.isConnected)
1566
1634
  continue;
1567
- subscription.callback({ type });
1635
+ try {
1636
+ subscription.callback({ type });
1637
+ } catch (error) {
1638
+ reportError(error);
1639
+ }
1568
1640
  }
1569
1641
  });
1570
1642
  }
1643
+ function reportError(error) {
1644
+ if (typeof win.reportError === "function")
1645
+ win.reportError(error);
1646
+ else
1647
+ win.setTimeout(() => {
1648
+ throw error;
1649
+ });
1650
+ }
1571
1651
  function notifyAll(type) {
1572
1652
  for (const subscription of subscriptions)
1573
1653
  queue(subscription, type);
@@ -1658,28 +1738,32 @@
1658
1738
  return trackerFor(floating).add(reference, floating, callback);
1659
1739
  }
1660
1740
  function positionOnce(reference, floating, options) {
1741
+ const placement = options.placement === undefined ? "bottom-start" : options.placement;
1742
+ let { side, align, crossAxis } = parsePlacement(placement);
1743
+ const coordinateSpace = options.coordinateSpace === undefined ? "viewport" : options.coordinateSpace;
1744
+ if (!COORDINATE_SPACES.has(coordinateSpace)) {
1745
+ throw new TypeError(`Unknown coordinateSpace: ${coordinateSpace}`);
1746
+ }
1661
1747
  if (!isVisible(floating))
1662
1748
  return null;
1663
- const placement = options.placement || "bottom-start";
1664
1749
  const distance = options.distance || 0;
1665
1750
  const flip = options.flip !== false;
1666
1751
  const shift = options.shift !== false;
1667
1752
  const shiftPadding = options.shiftPadding ?? 4;
1668
- let { side, align, crossAxis } = parsePlacement(placement);
1669
1753
  const rtl = align ? isRTL(reference) : false;
1670
1754
  const rects = reference.getClientRects();
1671
- const referenceRect = side === "bottom" ? rects[rects.length - 1] : rects[0];
1672
- if (!referenceRect)
1755
+ if (!rects.length)
1673
1756
  return null;
1757
+ const rectFor = (s) => s === "bottom" ? rects[rects.length - 1] : rects[0];
1674
1758
  const boundary = getBoundary(reference, options);
1675
- if (!boundary || isOutsideBoundary(referenceRect, boundary))
1759
+ if (!boundary || isOutsideBoundary(rects, boundary))
1676
1760
  return null;
1677
1761
  const floatingRect = getFloatingSize(floating);
1678
1762
  const limits = {
1679
1763
  x: { size: floatingRect.width, start: boundary.x, end: boundary.right },
1680
1764
  y: { size: floatingRect.height, start: boundary.y, end: boundary.bottom }
1681
1765
  };
1682
- const place = (nextSide, nextAlign) => computeCoords(referenceRect, floatingRect, nextSide, nextAlign, rtl, distance);
1766
+ const place = (nextSide, nextAlign) => computeCoords(rectFor(nextSide), floatingRect, nextSide, nextAlign, rtl, distance);
1683
1767
  const overflowAt = (position, axis) => {
1684
1768
  const { size, start, end } = limits[axis];
1685
1769
  return overflowOn(position, size, start + shiftPadding, end - shiftPadding);
@@ -1689,31 +1773,30 @@
1689
1773
  return overflowAt(shift ? clampToBoundary(position, size, start, end, shiftPadding) : position, axis);
1690
1774
  };
1691
1775
  let coords = place(side, align);
1776
+ const overflowFor = (nextSide, at) => {
1777
+ const cross = crossAxisFor(nextSide);
1778
+ const main = cross === "x" ? "y" : "x";
1779
+ const mainOverflow = overflowAt(at[main], main);
1780
+ return { main: mainOverflow, total: mainOverflow + shiftedOverflowAt(at[cross], cross) };
1781
+ };
1692
1782
  if (flip) {
1693
- const mainAxis = crossAxis === "x" ? "y" : "x";
1694
- let overflow = overflowAt(coords[mainAxis], mainAxis);
1695
- if (overflow > 0) {
1696
- const opposite = flipSide(side);
1697
- const flipped = place(opposite, align);
1698
- const flippedOverflow = overflowAt(flipped[mainAxis], mainAxis);
1699
- if (flippedOverflow < overflow) {
1700
- side = opposite;
1701
- coords = flipped;
1702
- overflow = flippedOverflow;
1703
- }
1704
- }
1705
- if (mainAxis === "x" && overflow > 0) {
1706
- const above = place("top", align);
1707
- const below = place("bottom", align);
1708
- const useBottom = overflowAt(below.y, "y") < overflowAt(above.y, "y");
1709
- const candidate = useBottom ? below : above;
1710
- const swapped = overflowAt(candidate.y, "y") + shiftedOverflowAt(candidate.x, "x");
1711
- if (swapped < overflow + shiftedOverflowAt(coords.y, "y")) {
1712
- side = useBottom ? "bottom" : "top";
1713
- crossAxis = "x";
1714
- coords = candidate;
1783
+ let best = overflowFor(side, coords);
1784
+ const consider = (nextSide) => {
1785
+ const next = place(nextSide, align);
1786
+ const overflow = overflowFor(nextSide, next);
1787
+ if (overflow.total < best.total) {
1788
+ side = nextSide;
1789
+ coords = next;
1790
+ best = overflow;
1715
1791
  }
1792
+ };
1793
+ if (best.main > 0)
1794
+ consider(OPPOSITE[side]);
1795
+ if (crossAxis === "y" && best.main > 0) {
1796
+ consider("top");
1797
+ consider("bottom");
1716
1798
  }
1799
+ crossAxis = crossAxisFor(side);
1717
1800
  }
1718
1801
  if (crossAxis === "x" && shift && align) {
1719
1802
  const overflow = overflowAt(coords.x, "x");
@@ -1730,12 +1813,13 @@
1730
1813
  const { size, start, end } = limits[crossAxis];
1731
1814
  coords[crossAxis] = clampToBoundary(coords[crossAxis], size, start, end, shiftPadding);
1732
1815
  }
1733
- const arrowX = arrowPercent(referenceRect.x + referenceRect.width / 2, coords.x, floatingRect.width);
1734
- const arrowY = arrowPercent(referenceRect.y + referenceRect.height / 2, coords.y, floatingRect.height);
1735
- const availableHeight = `${getAvailableHeight(referenceRect, side, boundary, distance, shiftPadding)}px`;
1816
+ const resolvedRect = rectFor(side);
1817
+ const arrowX = arrowPercent(resolvedRect.x + resolvedRect.width / 2, coords.x, floatingRect.width);
1818
+ const arrowY = arrowPercent(resolvedRect.y + resolvedRect.height / 2, coords.y, floatingRect.height);
1819
+ const availableHeight = `${getAvailableHeight(resolvedRect, side, boundary, distance, shiftPadding)}px`;
1736
1820
  const { style } = floating;
1737
1821
  const roomChanged = style.getPropertyValue("--available-height") !== availableHeight;
1738
- const win = options.coordinateSpace === "document" ? reference.ownerDocument.defaultView : null;
1822
+ const win = coordinateSpace === "document" ? reference.ownerDocument.defaultView : null;
1739
1823
  const originX = win ? win.scrollX : 0;
1740
1824
  const originY = win ? win.scrollY : 0;
1741
1825
  style.left = `${coords.x + originX}px`;
@@ -1863,7 +1947,7 @@
1863
1947
  input.setCustomValidity("");
1864
1948
  return;
1865
1949
  }
1866
- input.setCustomValidity(text === canonical ? "" : this.messages.invalidDate);
1950
+ input.setCustomValidity(this.messages.invalidDate);
1867
1951
  }
1868
1952
  async commit(options = {}) {
1869
1953
  const input = this.input;
@@ -1993,6 +2077,7 @@
1993
2077
  this._open = false;
1994
2078
  this._suppressFocusOpen = false;
1995
2079
  this._messages = getDefaultMessages();
2080
+ this._controlClasses = getDefaultControlClasses();
1996
2081
  this._source = null;
1997
2082
  this._dateState = null;
1998
2083
  this._renderDay = null;
@@ -2247,6 +2332,17 @@
2247
2332
  get open() {
2248
2333
  return this._open;
2249
2334
  }
2335
+ get controlClasses() {
2336
+ return { ...this._controlClasses };
2337
+ }
2338
+ set controlClasses(value) {
2339
+ const classes = resolveControlClasses(value);
2340
+ if (sameControlClasses(classes, this._controlClasses))
2341
+ return;
2342
+ this._controlClasses = classes;
2343
+ if (this._calendar)
2344
+ this._calendar.controlClasses = this._controlClasses;
2345
+ }
2250
2346
  get messages() {
2251
2347
  return this._messages;
2252
2348
  }
@@ -2410,6 +2506,23 @@
2410
2506
  this._refreshLocale();
2411
2507
  this._refreshButtonLabel();
2412
2508
  }
2509
+ _bindPopup(signal, input, restore) {
2510
+ this.ownerDocument.addEventListener("pointerdown", (event) => {
2511
+ if (!this._open || event.composedPath().includes(this))
2512
+ return;
2513
+ this.hide(false);
2514
+ }, { capture: true, signal });
2515
+ this.ownerDocument.addEventListener("reset", (event) => {
2516
+ if (!(event.target instanceof HTMLFormElement) || !input || event.target !== input.form)
2517
+ return;
2518
+ this.hide(false);
2519
+ queueMicrotask(() => {
2520
+ if (!event.defaultPrevented)
2521
+ restore();
2522
+ });
2523
+ }, { capture: true, signal });
2524
+ this.addEventListener("keydown", (event) => this._onEscape(event), { signal });
2525
+ }
2413
2526
  _bind() {
2414
2527
  const input = this._input;
2415
2528
  const button = this._buttons[0]?.button;
@@ -2472,22 +2585,7 @@
2472
2585
  });
2473
2586
  }, { signal });
2474
2587
  calendar.addEventListener("dateloadend", () => void this.validate(), { signal });
2475
- this.ownerDocument.addEventListener("pointerdown", (event) => {
2476
- if (!this._open || event.composedPath().includes(this))
2477
- return;
2478
- this.hide(false);
2479
- }, { capture: true, signal });
2480
- this.ownerDocument.addEventListener("reset", (event) => {
2481
- const form = event.target;
2482
- if (form instanceof HTMLFormElement && this._input && form === this._input.form) {
2483
- this.hide(false);
2484
- queueMicrotask(() => {
2485
- if (!event.defaultPrevented)
2486
- this._restoreDefault();
2487
- });
2488
- }
2489
- }, { capture: true, signal });
2490
- this.addEventListener("keydown", (event) => this._onEscape(event), { signal });
2588
+ this._bindPopup(signal, this._input, () => this._restoreDefault());
2491
2589
  }
2492
2590
  _bindRange() {
2493
2591
  const calendar = this._calendar;
@@ -2567,22 +2665,7 @@
2567
2665
  calendar.addEventListener("pointercancel", (event) => this._onGridPointerCancel(event), { signal });
2568
2666
  calendar.addEventListener("lostpointercapture", (event) => this._onGridPointerCancel(event), { signal });
2569
2667
  calendar.addEventListener("dateloadend", () => void this.validate(), { signal });
2570
- this.ownerDocument.addEventListener("pointerdown", (event) => {
2571
- if (!this._open || event.composedPath().includes(this))
2572
- return;
2573
- this.hide(false);
2574
- }, { capture: true, signal });
2575
- this.ownerDocument.addEventListener("reset", (event) => {
2576
- const form = event.target;
2577
- if (form instanceof HTMLFormElement && this._fields && form === this._fields[0].input.form) {
2578
- this.hide(false);
2579
- queueMicrotask(() => {
2580
- if (!event.defaultPrevented)
2581
- this._restoreRangeDefault();
2582
- });
2583
- }
2584
- }, { capture: true, signal });
2585
- this.addEventListener("keydown", (event) => this._onEscape(event), { signal });
2668
+ this._bindPopup(signal, this._fields?.[0]?.input, () => this._restoreRangeDefault());
2586
2669
  }
2587
2670
  _activateRangeEndpoint(endpoint) {
2588
2671
  const calendar = this._calendar;
@@ -2879,6 +2962,7 @@
2879
2962
  calendar.min = this.min;
2880
2963
  calendar.max = this.max;
2881
2964
  calendar.monthFormat = this.monthFormat;
2965
+ calendar.controlClasses = this._controlClasses;
2882
2966
  calendar.messages = this._messages;
2883
2967
  calendar.source = this._source;
2884
2968
  calendar.dateState = this._dateState;