@allxsmith/bestax-bulma 5.26.2 → 5.26.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +453 -303
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +453 -303
- package/dist/index.esm.js.map +1 -1
- package/dist/types/elements/Progress.d.ts +6 -1
- package/dist/types/form/Control.d.ts +14 -2
- package/dist/types/form/DateInput.d.ts +13 -3
- package/dist/types/form/DateTimeInput.d.ts +13 -3
- package/dist/types/form/Input.d.ts +16 -4
- package/dist/types/form/InputBase.d.ts +5 -2
- package/dist/types/form/Select.d.ts +8 -2
- package/dist/types/form/TextArea.d.ts +4 -2
- package/dist/types/form/TextAreaBase.d.ts +12 -3
- package/dist/types/form/TimeInput.d.ts +13 -3
- package/dist/types/form/TimeInputBase.d.ts +5 -1
- package/dist/types/form/_pickerInternals/Calendar.d.ts +10 -1
- package/dist/types/form/_pickerInternals/PickerPopover.d.ts +6 -0
- package/dist/types/helpers/Theme.d.ts +13 -4
- package/dist/types/helpers/portal.d.ts +2 -1
- package/dist/types/helpers/useAnchoredPosition.d.ts +6 -0
- package/dist/types/helpers/useFocusTrap.d.ts +2 -1
- package/dist/types/helpers/useOtherClasses.d.ts +13 -1
- package/dist/types-cjs/elements/Progress.d.ts +6 -1
- package/dist/types-cjs/form/Control.d.ts +14 -2
- package/dist/types-cjs/form/DateInput.d.ts +13 -3
- package/dist/types-cjs/form/DateTimeInput.d.ts +13 -3
- package/dist/types-cjs/form/Input.d.ts +16 -4
- package/dist/types-cjs/form/InputBase.d.ts +5 -2
- package/dist/types-cjs/form/Select.d.ts +8 -2
- package/dist/types-cjs/form/TextArea.d.ts +4 -2
- package/dist/types-cjs/form/TextAreaBase.d.ts +12 -3
- package/dist/types-cjs/form/TimeInput.d.ts +13 -3
- package/dist/types-cjs/form/TimeInputBase.d.ts +5 -1
- package/dist/types-cjs/form/_pickerInternals/Calendar.d.ts +10 -1
- package/dist/types-cjs/form/_pickerInternals/PickerPopover.d.ts +6 -0
- package/dist/types-cjs/helpers/Theme.d.ts +13 -4
- package/dist/types-cjs/helpers/portal.d.ts +2 -1
- package/dist/types-cjs/helpers/useAnchoredPosition.d.ts +6 -0
- package/dist/types-cjs/helpers/useFocusTrap.d.ts +2 -1
- package/dist/types-cjs/helpers/useOtherClasses.d.ts +13 -1
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/elements/_index.scss +2 -0
- package/src/scss/elements/_progress.scss +17 -0
- package/src/scss/elements/_skeleton.scss +21 -0
- package/src/scss/form/_picker-popover.scss +16 -0
- package/src/scss/form/_timeinput.scss +7 -2
package/dist/index.cjs
CHANGED
|
@@ -2941,7 +2941,8 @@ function resolvePortalContainer(container) {
|
|
|
2941
2941
|
* Tab order and focus follow the DOM too. Content portaled from inside a
|
|
2942
2942
|
* focus trap is no longer inside the trapped element, so the trap neither
|
|
2943
2943
|
* reaches it nor holds focus there: render nested overlays inside the
|
|
2944
|
-
* trapped element, or give them a trap of their own.
|
|
2944
|
+
* trapped element, or give them a trap of their own. To trap portaled
|
|
2945
|
+
* content, put `useFocusTrap`'s ref on the element inside the `Portal`.
|
|
2945
2946
|
*
|
|
2946
2947
|
* @function
|
|
2947
2948
|
* @param {PortalProps} props - Props for the Portal component.
|
|
@@ -5197,7 +5198,8 @@ const focusedElement = (doc) => getDeepestActiveElement(doc) ?? doc.body;
|
|
|
5197
5198
|
* behaviour, and a modal also has to block the page behind it, which `Modal`
|
|
5198
5199
|
* does. Content the container renders through a portal lives elsewhere in
|
|
5199
5200
|
* the DOM, so it is outside the trap: render nested overlays inside the
|
|
5200
|
-
* container, or give them a trap of their own.
|
|
5201
|
+
* container, or give them a trap of their own. To trap content a `Portal`
|
|
5202
|
+
* renders, pass a ref to the element inside the `Portal` as `containerRef`.
|
|
5201
5203
|
*
|
|
5202
5204
|
* The container has to be in the DOM when the trap turns on. The trap waits
|
|
5203
5205
|
* for hydration, so a container that only appears once the page has
|
|
@@ -5314,6 +5316,15 @@ function useFocusTrap(containerRef, options = {}) {
|
|
|
5314
5316
|
const useClientLayoutEffect = typeof document === 'undefined' ? React.useEffect : React.useLayoutEffect;
|
|
5315
5317
|
|
|
5316
5318
|
// INTERNAL: deliberately not exported from src/index.ts.
|
|
5319
|
+
/**
|
|
5320
|
+
* A `styledGap` for a panel whose stylesheet puts the gap in a margin on the
|
|
5321
|
+
* side facing the anchor, negative on a portaled panel above it, so the
|
|
5322
|
+
* larger magnitude is the gap. In pixels.
|
|
5323
|
+
*/
|
|
5324
|
+
function marginGap(panel) {
|
|
5325
|
+
const { marginTop, marginBottom } = getComputedStyle(panel);
|
|
5326
|
+
return Math.max(Math.abs(parseFloat(marginTop)) || 0, Math.abs(parseFloat(marginBottom)) || 0);
|
|
5327
|
+
}
|
|
5317
5328
|
/**
|
|
5318
5329
|
* The corner that keeps a panel of this size in the viewport, preferring
|
|
5319
5330
|
* below the anchor and lined up with its left edge.
|
|
@@ -5538,15 +5549,6 @@ function nearestDialog(event) {
|
|
|
5538
5549
|
return role === 'dialog' || role === 'alertdialog';
|
|
5539
5550
|
}) ?? null);
|
|
5540
5551
|
}
|
|
5541
|
-
/**
|
|
5542
|
-
* The gap the stylesheet puts between the panel and the trigger, in pixels.
|
|
5543
|
-
* It is a margin on whichever side faces the trigger, and a negative one on
|
|
5544
|
-
* a portaled panel above it, so the larger magnitude is the gap.
|
|
5545
|
-
*/
|
|
5546
|
-
function styledGap(panel) {
|
|
5547
|
-
const { marginTop, marginBottom } = getComputedStyle(panel);
|
|
5548
|
-
return Math.max(Math.abs(parseFloat(marginTop)) || 0, Math.abs(parseFloat(marginBottom)) || 0);
|
|
5549
|
-
}
|
|
5550
5552
|
/**
|
|
5551
5553
|
* The `Popover` component opens a panel of interactive content, such as a filter form or a set of share options, anchored to the element that toggles it.
|
|
5552
5554
|
*
|
|
@@ -5598,7 +5600,7 @@ const PopoverComponent = ({ trigger, children, open: controlledOpen, defaultOpen
|
|
|
5598
5600
|
// The gap comes from `--bulma-popover-offset` in the stylesheet, so the
|
|
5599
5601
|
// coordinates leave it out and `auto` reads it off the panel.
|
|
5600
5602
|
offset: 0,
|
|
5601
|
-
styledGap,
|
|
5603
|
+
styledGap: marginGap,
|
|
5602
5604
|
});
|
|
5603
5605
|
// The trigger renders first inside the wrapper. Read when needed rather
|
|
5604
5606
|
// than held, so a trigger that remounted is still the one found.
|
|
@@ -11062,7 +11064,7 @@ const TextAreaBase = React.forwardRef(({ color, size, isRounded, isStatic, isHov
|
|
|
11062
11064
|
TextAreaBase.displayName = 'TextAreaBase';
|
|
11063
11065
|
|
|
11064
11066
|
/**
|
|
11065
|
-
* The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static
|
|
11067
|
+
* The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states, and all Bulma helper props.
|
|
11066
11068
|
*
|
|
11067
11069
|
* @function
|
|
11068
11070
|
* @param {InputProps} props - Props for Input.
|
|
@@ -11165,7 +11167,7 @@ fieldClassName, controlClassName,
|
|
|
11165
11167
|
Select.displayName = 'Select';
|
|
11166
11168
|
|
|
11167
11169
|
/**
|
|
11168
|
-
* The `TextArea` component provides a Bulma-styled multi-line text input, supporting color, size,
|
|
11170
|
+
* The `TextArea` component provides a Bulma-styled multi-line text input, supporting color, size, hover/focus/loading states, fixed size, and all Bulma helper props.
|
|
11169
11171
|
*
|
|
11170
11172
|
* @function
|
|
11171
11173
|
* @param {TextAreaProps} props - Props for TextArea.
|
|
@@ -11251,6 +11253,208 @@ const mergeLabels = (overrides) => ({
|
|
|
11251
11253
|
...(overrides ?? {}),
|
|
11252
11254
|
});
|
|
11253
11255
|
|
|
11256
|
+
function isSameDay(a, b) {
|
|
11257
|
+
return (a.getFullYear() === b.getFullYear() &&
|
|
11258
|
+
a.getMonth() === b.getMonth() &&
|
|
11259
|
+
a.getDate() === b.getDate());
|
|
11260
|
+
}
|
|
11261
|
+
function isSameMonth(a, b) {
|
|
11262
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
11263
|
+
}
|
|
11264
|
+
function isBefore(a, b) {
|
|
11265
|
+
return a.getTime() < b.getTime();
|
|
11266
|
+
}
|
|
11267
|
+
function isAfter(a, b) {
|
|
11268
|
+
return a.getTime() > b.getTime();
|
|
11269
|
+
}
|
|
11270
|
+
function isWithin(d, min, max) {
|
|
11271
|
+
if (min && isBefore(d, min))
|
|
11272
|
+
return false;
|
|
11273
|
+
if (max && isAfter(d, max))
|
|
11274
|
+
return false;
|
|
11275
|
+
return true;
|
|
11276
|
+
}
|
|
11277
|
+
// The boundaries build on `makeDate` (below), so a date in years 0–99 keeps
|
|
11278
|
+
// its year.
|
|
11279
|
+
function startOfDay(d) {
|
|
11280
|
+
return makeDate(d.getFullYear(), d.getMonth(), d.getDate());
|
|
11281
|
+
}
|
|
11282
|
+
function endOfDay(d) {
|
|
11283
|
+
const r = makeDate(d.getFullYear(), d.getMonth(), d.getDate());
|
|
11284
|
+
r.setHours(23, 59, 59, 999);
|
|
11285
|
+
return r;
|
|
11286
|
+
}
|
|
11287
|
+
function startOfMonth(d) {
|
|
11288
|
+
return makeDate(d.getFullYear(), d.getMonth());
|
|
11289
|
+
}
|
|
11290
|
+
/**
|
|
11291
|
+
* Local midnight on `year`-`month`-`day` (`month` from 0), like the
|
|
11292
|
+
* `Date(y, m, d)` constructor except that years 0–99 stay as given rather than
|
|
11293
|
+
* turning into 1900–1999.
|
|
11294
|
+
*/
|
|
11295
|
+
function makeDate(year, month = 0, day = 1) {
|
|
11296
|
+
const d = new Date(2000, 0, 1);
|
|
11297
|
+
d.setFullYear(year, month, day);
|
|
11298
|
+
return d;
|
|
11299
|
+
}
|
|
11300
|
+
/**
|
|
11301
|
+
* First instant of the day, month or year containing `d`. Works by setters on
|
|
11302
|
+
* a copy rather than the `Date(y, m, d)` constructor, which reads years 0–99
|
|
11303
|
+
* as 1900–1999: segmented typing passes through such years on the way to a
|
|
11304
|
+
* four-digit one.
|
|
11305
|
+
*/
|
|
11306
|
+
function startOfPeriod(d, granularity) {
|
|
11307
|
+
const r = new Date(d);
|
|
11308
|
+
if (granularity === 'year')
|
|
11309
|
+
r.setMonth(0, 1);
|
|
11310
|
+
else if (granularity === 'month')
|
|
11311
|
+
r.setDate(1);
|
|
11312
|
+
r.setHours(0, 0, 0, 0);
|
|
11313
|
+
return r;
|
|
11314
|
+
}
|
|
11315
|
+
/** Last instant of the day, month or year containing `d`. */
|
|
11316
|
+
function endOfPeriod(d, granularity) {
|
|
11317
|
+
const r = new Date(d);
|
|
11318
|
+
if (granularity === 'year')
|
|
11319
|
+
r.setMonth(11, 31);
|
|
11320
|
+
// Day 0 of the next month is this month's last day.
|
|
11321
|
+
else if (granularity === 'month')
|
|
11322
|
+
r.setMonth(r.getMonth() + 1, 0);
|
|
11323
|
+
r.setHours(23, 59, 59, 999);
|
|
11324
|
+
return r;
|
|
11325
|
+
}
|
|
11326
|
+
/**
|
|
11327
|
+
* Whether the day `d` falls outside `[min, max]` or is blocked by
|
|
11328
|
+
* `shouldDisableDate` / `unselectableDates`. This is the day grid's rule for a
|
|
11329
|
+
* disabled cell.
|
|
11330
|
+
*/
|
|
11331
|
+
function isDayUnselectable(d, c) {
|
|
11332
|
+
if (!isWithin(d, c.min, c.max))
|
|
11333
|
+
return true;
|
|
11334
|
+
if (c.shouldDisableDate?.(d))
|
|
11335
|
+
return true;
|
|
11336
|
+
if (c.unselectableDates?.some(u => isSameDay(u, d)))
|
|
11337
|
+
return true;
|
|
11338
|
+
return false;
|
|
11339
|
+
}
|
|
11340
|
+
/**
|
|
11341
|
+
* Whether every day of the month or year containing `d` is unselectable, so
|
|
11342
|
+
* the period as a whole can't be picked. Walks the period's days and stops at
|
|
11343
|
+
* the first selectable one. `min` and `max` count whole days here, so a `min`
|
|
11344
|
+
* of 3pm on a month's last day leaves that day, and so its month, selectable.
|
|
11345
|
+
*/
|
|
11346
|
+
function isPeriodUnselectable(d, granularity, c) {
|
|
11347
|
+
const days = {
|
|
11348
|
+
...c,
|
|
11349
|
+
min: c.min && startOfDay(c.min),
|
|
11350
|
+
max: c.max && endOfDay(c.max),
|
|
11351
|
+
};
|
|
11352
|
+
const start = startOfPeriod(d, granularity);
|
|
11353
|
+
const end = endOfPeriod(d, granularity).getTime();
|
|
11354
|
+
// A period wholly outside the bounds needs no walk.
|
|
11355
|
+
if (days.min && end < days.min.getTime())
|
|
11356
|
+
return true;
|
|
11357
|
+
if (days.max && start.getTime() > days.max.getTime())
|
|
11358
|
+
return true;
|
|
11359
|
+
for (let day = start; day.getTime() <= end; day = addDays(day, 1)) {
|
|
11360
|
+
if (!isDayUnselectable(day, days))
|
|
11361
|
+
return false;
|
|
11362
|
+
}
|
|
11363
|
+
return true;
|
|
11364
|
+
}
|
|
11365
|
+
function addDays(d, n) {
|
|
11366
|
+
const r = new Date(d);
|
|
11367
|
+
r.setDate(r.getDate() + n);
|
|
11368
|
+
return r;
|
|
11369
|
+
}
|
|
11370
|
+
function addMonths(d, n) {
|
|
11371
|
+
const r = new Date(d);
|
|
11372
|
+
const day = r.getDate();
|
|
11373
|
+
r.setDate(1);
|
|
11374
|
+
r.setMonth(r.getMonth() + n);
|
|
11375
|
+
// Clamp day to last day of new month if original day is out of range.
|
|
11376
|
+
const lastDay = makeDate(r.getFullYear(), r.getMonth() + 1, 0).getDate();
|
|
11377
|
+
r.setDate(Math.min(day, lastDay));
|
|
11378
|
+
return r;
|
|
11379
|
+
}
|
|
11380
|
+
function addYears(d, n) {
|
|
11381
|
+
return addMonths(d, n * 12);
|
|
11382
|
+
}
|
|
11383
|
+
function setTimeOfDay(d, parts) {
|
|
11384
|
+
const r = new Date(d);
|
|
11385
|
+
if (parts.hours !== undefined)
|
|
11386
|
+
r.setHours(parts.hours);
|
|
11387
|
+
if (parts.minutes !== undefined)
|
|
11388
|
+
r.setMinutes(parts.minutes);
|
|
11389
|
+
if (parts.seconds !== undefined)
|
|
11390
|
+
r.setSeconds(parts.seconds);
|
|
11391
|
+
return r;
|
|
11392
|
+
}
|
|
11393
|
+
function clampDate(d, min, max) {
|
|
11394
|
+
if (min && isBefore(d, min))
|
|
11395
|
+
return new Date(min);
|
|
11396
|
+
if (max && isAfter(d, max))
|
|
11397
|
+
return new Date(max);
|
|
11398
|
+
return new Date(d);
|
|
11399
|
+
}
|
|
11400
|
+
/**
|
|
11401
|
+
* Snap a Date's time-of-day to the nearest grid defined by the given
|
|
11402
|
+
* increment steps. Used by the "Now" button in pickers configured with
|
|
11403
|
+
* non-1 hour / minute / second steps so the committed time always lands on
|
|
11404
|
+
* a slot that exists on the wheel. Overflows roll up: e.g.
|
|
11405
|
+
* 13:58 with step=15 → 14:00. Seconds are zeroed when `enableSeconds` is
|
|
11406
|
+
* false, regardless of step.
|
|
11407
|
+
*/
|
|
11408
|
+
function snapTimeToIncrement(d, opts = {}) {
|
|
11409
|
+
const { incrementHours = 1, incrementMinutes = 1, incrementSeconds = 1, enableSeconds = false, } = opts;
|
|
11410
|
+
const r = new Date(d);
|
|
11411
|
+
let h = r.getHours();
|
|
11412
|
+
let m = r.getMinutes();
|
|
11413
|
+
let s = enableSeconds ? r.getSeconds() : 0;
|
|
11414
|
+
if (enableSeconds && incrementSeconds > 1) {
|
|
11415
|
+
s = Math.round(s / incrementSeconds) * incrementSeconds;
|
|
11416
|
+
if (s >= 60) {
|
|
11417
|
+
s -= 60;
|
|
11418
|
+
m += 1;
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11421
|
+
if (incrementMinutes > 1) {
|
|
11422
|
+
m = Math.round(m / incrementMinutes) * incrementMinutes;
|
|
11423
|
+
if (m >= 60) {
|
|
11424
|
+
m -= 60;
|
|
11425
|
+
h += 1;
|
|
11426
|
+
}
|
|
11427
|
+
}
|
|
11428
|
+
if (incrementHours > 1) {
|
|
11429
|
+
h = Math.round(h / incrementHours) * incrementHours;
|
|
11430
|
+
// setHours handles 24+ overflow naturally (rolls forward into next day).
|
|
11431
|
+
}
|
|
11432
|
+
r.setHours(h, m, s, 0);
|
|
11433
|
+
return r;
|
|
11434
|
+
}
|
|
11435
|
+
/**
|
|
11436
|
+
* Build a 6-week × 7-day grid (42 cells) anchored on the month containing
|
|
11437
|
+
* `monthAnchor`. The first cell is the start of the week containing the first
|
|
11438
|
+
* of the month, where the week starts on `firstDayOfWeek`.
|
|
11439
|
+
*/
|
|
11440
|
+
function buildMonthGrid(monthAnchor, firstDayOfWeek = 0) {
|
|
11441
|
+
const monthStart = startOfMonth(monthAnchor);
|
|
11442
|
+
const firstWeekday = monthStart.getDay();
|
|
11443
|
+
const offset = (firstWeekday - firstDayOfWeek + 7) % 7;
|
|
11444
|
+
const gridStart = addDays(monthStart, -offset);
|
|
11445
|
+
const today = startOfDay(new Date());
|
|
11446
|
+
const cells = [];
|
|
11447
|
+
for (let i = 0; i < 42; i++) {
|
|
11448
|
+
const date = addDays(gridStart, i);
|
|
11449
|
+
cells.push({
|
|
11450
|
+
date,
|
|
11451
|
+
inCurrentMonth: date.getMonth() === monthAnchor.getMonth(),
|
|
11452
|
+
isToday: isSameDay(date, today),
|
|
11453
|
+
});
|
|
11454
|
+
}
|
|
11455
|
+
return cells;
|
|
11456
|
+
}
|
|
11457
|
+
|
|
11254
11458
|
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD';
|
|
11255
11459
|
const DEFAULT_MONTH_FORMAT = 'YYYY-MM';
|
|
11256
11460
|
const DEFAULT_YEAR_FORMAT = 'YYYY';
|
|
@@ -11369,7 +11573,8 @@ _locale) {
|
|
|
11369
11573
|
const hours = parts.hours ?? 0;
|
|
11370
11574
|
const minutes = parts.minutes ?? 0;
|
|
11371
11575
|
const seconds = parts.seconds ?? 0;
|
|
11372
|
-
const d =
|
|
11576
|
+
const d = makeDate(year, month, day);
|
|
11577
|
+
d.setHours(hours, minutes, seconds, 0);
|
|
11373
11578
|
if (d.getFullYear() !== year ||
|
|
11374
11579
|
d.getMonth() !== month ||
|
|
11375
11580
|
d.getDate() !== day) {
|
|
@@ -11535,204 +11740,11 @@ function getMonthNames(locale, length = 'long') {
|
|
|
11535
11740
|
return out;
|
|
11536
11741
|
}
|
|
11537
11742
|
|
|
11538
|
-
function isSameDay(a, b) {
|
|
11539
|
-
return (a.getFullYear() === b.getFullYear() &&
|
|
11540
|
-
a.getMonth() === b.getMonth() &&
|
|
11541
|
-
a.getDate() === b.getDate());
|
|
11542
|
-
}
|
|
11543
|
-
function isSameMonth(a, b) {
|
|
11544
|
-
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
11545
|
-
}
|
|
11546
|
-
function isBefore(a, b) {
|
|
11547
|
-
return a.getTime() < b.getTime();
|
|
11548
|
-
}
|
|
11549
|
-
function isAfter(a, b) {
|
|
11550
|
-
return a.getTime() > b.getTime();
|
|
11551
|
-
}
|
|
11552
|
-
function isWithin(d, min, max) {
|
|
11553
|
-
if (min && isBefore(d, min))
|
|
11554
|
-
return false;
|
|
11555
|
-
if (max && isAfter(d, max))
|
|
11556
|
-
return false;
|
|
11557
|
-
return true;
|
|
11558
|
-
}
|
|
11559
|
-
function startOfDay(d) {
|
|
11560
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
|
|
11561
|
-
}
|
|
11562
|
-
function endOfDay(d) {
|
|
11563
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
|
|
11564
|
-
}
|
|
11565
|
-
function startOfMonth(d) {
|
|
11566
|
-
return new Date(d.getFullYear(), d.getMonth(), 1, 0, 0, 0, 0);
|
|
11567
|
-
}
|
|
11568
|
-
/**
|
|
11569
|
-
* Local midnight on `year`-`month`-`day` (`month` from 0), like the
|
|
11570
|
-
* `Date(y, m, d)` constructor except that years 0–99 stay as given rather than
|
|
11571
|
-
* turning into 1900–1999.
|
|
11572
|
-
*/
|
|
11573
|
-
function makeDate(year, month = 0, day = 1) {
|
|
11574
|
-
const d = new Date(2000, 0, 1);
|
|
11575
|
-
d.setFullYear(year, month, day);
|
|
11576
|
-
return d;
|
|
11577
|
-
}
|
|
11578
|
-
/**
|
|
11579
|
-
* First instant of the day, month or year containing `d`. Works by setters on
|
|
11580
|
-
* a copy rather than the `Date(y, m, d)` constructor, which reads years 0–99
|
|
11581
|
-
* as 1900–1999: segmented typing passes through such years on the way to a
|
|
11582
|
-
* four-digit one.
|
|
11583
|
-
*/
|
|
11584
|
-
function startOfPeriod(d, granularity) {
|
|
11585
|
-
const r = new Date(d);
|
|
11586
|
-
if (granularity === 'year')
|
|
11587
|
-
r.setMonth(0, 1);
|
|
11588
|
-
else if (granularity === 'month')
|
|
11589
|
-
r.setDate(1);
|
|
11590
|
-
r.setHours(0, 0, 0, 0);
|
|
11591
|
-
return r;
|
|
11592
|
-
}
|
|
11593
|
-
/** Last instant of the day, month or year containing `d`. */
|
|
11594
|
-
function endOfPeriod(d, granularity) {
|
|
11595
|
-
const r = new Date(d);
|
|
11596
|
-
if (granularity === 'year')
|
|
11597
|
-
r.setMonth(11, 31);
|
|
11598
|
-
// Day 0 of the next month is this month's last day.
|
|
11599
|
-
else if (granularity === 'month')
|
|
11600
|
-
r.setMonth(r.getMonth() + 1, 0);
|
|
11601
|
-
r.setHours(23, 59, 59, 999);
|
|
11602
|
-
return r;
|
|
11603
|
-
}
|
|
11604
|
-
/**
|
|
11605
|
-
* Whether the day `d` falls outside `[min, max]` or is blocked by
|
|
11606
|
-
* `shouldDisableDate` / `unselectableDates`. This is the day grid's rule for a
|
|
11607
|
-
* disabled cell.
|
|
11608
|
-
*/
|
|
11609
|
-
function isDayUnselectable(d, c) {
|
|
11610
|
-
if (!isWithin(d, c.min, c.max))
|
|
11611
|
-
return true;
|
|
11612
|
-
if (c.shouldDisableDate?.(d))
|
|
11613
|
-
return true;
|
|
11614
|
-
if (c.unselectableDates?.some(u => isSameDay(u, d)))
|
|
11615
|
-
return true;
|
|
11616
|
-
return false;
|
|
11617
|
-
}
|
|
11618
|
-
/**
|
|
11619
|
-
* Whether every day of the month or year containing `d` is unselectable, so
|
|
11620
|
-
* the period as a whole can't be picked. Walks the period's days and stops at
|
|
11621
|
-
* the first selectable one. `min` and `max` count whole days here, so a `min`
|
|
11622
|
-
* of 3pm on a month's last day leaves that day, and so its month, selectable.
|
|
11623
|
-
*/
|
|
11624
|
-
function isPeriodUnselectable(d, granularity, c) {
|
|
11625
|
-
const days = {
|
|
11626
|
-
...c,
|
|
11627
|
-
min: c.min && startOfDay(c.min),
|
|
11628
|
-
max: c.max && endOfDay(c.max),
|
|
11629
|
-
};
|
|
11630
|
-
const start = startOfPeriod(d, granularity);
|
|
11631
|
-
const end = endOfPeriod(d, granularity).getTime();
|
|
11632
|
-
// A period wholly outside the bounds needs no walk.
|
|
11633
|
-
if (days.min && end < days.min.getTime())
|
|
11634
|
-
return true;
|
|
11635
|
-
if (days.max && start.getTime() > days.max.getTime())
|
|
11636
|
-
return true;
|
|
11637
|
-
for (let day = start; day.getTime() <= end; day = addDays(day, 1)) {
|
|
11638
|
-
if (!isDayUnselectable(day, days))
|
|
11639
|
-
return false;
|
|
11640
|
-
}
|
|
11641
|
-
return true;
|
|
11642
|
-
}
|
|
11643
|
-
function addDays(d, n) {
|
|
11644
|
-
const r = new Date(d);
|
|
11645
|
-
r.setDate(r.getDate() + n);
|
|
11646
|
-
return r;
|
|
11647
|
-
}
|
|
11648
|
-
function addMonths(d, n) {
|
|
11649
|
-
const r = new Date(d);
|
|
11650
|
-
const day = r.getDate();
|
|
11651
|
-
r.setDate(1);
|
|
11652
|
-
r.setMonth(r.getMonth() + n);
|
|
11653
|
-
// Clamp day to last day of new month if original day is out of range.
|
|
11654
|
-
const lastDay = new Date(r.getFullYear(), r.getMonth() + 1, 0).getDate();
|
|
11655
|
-
r.setDate(Math.min(day, lastDay));
|
|
11656
|
-
return r;
|
|
11657
|
-
}
|
|
11658
|
-
function addYears(d, n) {
|
|
11659
|
-
return addMonths(d, n * 12);
|
|
11660
|
-
}
|
|
11661
|
-
function setTimeOfDay(d, parts) {
|
|
11662
|
-
const r = new Date(d);
|
|
11663
|
-
if (parts.hours !== undefined)
|
|
11664
|
-
r.setHours(parts.hours);
|
|
11665
|
-
if (parts.minutes !== undefined)
|
|
11666
|
-
r.setMinutes(parts.minutes);
|
|
11667
|
-
if (parts.seconds !== undefined)
|
|
11668
|
-
r.setSeconds(parts.seconds);
|
|
11669
|
-
return r;
|
|
11670
|
-
}
|
|
11671
|
-
function clampDate(d, min, max) {
|
|
11672
|
-
if (min && isBefore(d, min))
|
|
11673
|
-
return new Date(min);
|
|
11674
|
-
if (max && isAfter(d, max))
|
|
11675
|
-
return new Date(max);
|
|
11676
|
-
return new Date(d);
|
|
11677
|
-
}
|
|
11678
11743
|
/**
|
|
11679
|
-
*
|
|
11680
|
-
*
|
|
11681
|
-
* non-1 hour / minute / second steps so the committed time always lands on
|
|
11682
|
-
* a slot that exists on the wheel. Overflows roll up: e.g.
|
|
11683
|
-
* 13:58 with step=15 → 14:00. Seconds are zeroed when `enableSeconds` is
|
|
11684
|
-
* false, regardless of step.
|
|
11744
|
+
* Selects the calendar's focused cell, the one tab stop of whichever grid or
|
|
11745
|
+
* year list is on show, for a popover to put focus on as it opens.
|
|
11685
11746
|
*/
|
|
11686
|
-
|
|
11687
|
-
const { incrementHours = 1, incrementMinutes = 1, incrementSeconds = 1, enableSeconds = false, } = opts;
|
|
11688
|
-
const r = new Date(d);
|
|
11689
|
-
let h = r.getHours();
|
|
11690
|
-
let m = r.getMinutes();
|
|
11691
|
-
let s = enableSeconds ? r.getSeconds() : 0;
|
|
11692
|
-
if (enableSeconds && incrementSeconds > 1) {
|
|
11693
|
-
s = Math.round(s / incrementSeconds) * incrementSeconds;
|
|
11694
|
-
if (s >= 60) {
|
|
11695
|
-
s -= 60;
|
|
11696
|
-
m += 1;
|
|
11697
|
-
}
|
|
11698
|
-
}
|
|
11699
|
-
if (incrementMinutes > 1) {
|
|
11700
|
-
m = Math.round(m / incrementMinutes) * incrementMinutes;
|
|
11701
|
-
if (m >= 60) {
|
|
11702
|
-
m -= 60;
|
|
11703
|
-
h += 1;
|
|
11704
|
-
}
|
|
11705
|
-
}
|
|
11706
|
-
if (incrementHours > 1) {
|
|
11707
|
-
h = Math.round(h / incrementHours) * incrementHours;
|
|
11708
|
-
// setHours handles 24+ overflow naturally (rolls forward into next day).
|
|
11709
|
-
}
|
|
11710
|
-
r.setHours(h, m, s, 0);
|
|
11711
|
-
return r;
|
|
11712
|
-
}
|
|
11713
|
-
/**
|
|
11714
|
-
* Build a 6-week × 7-day grid (42 cells) anchored on the month containing
|
|
11715
|
-
* `monthAnchor`. The first cell is the start of the week containing the first
|
|
11716
|
-
* of the month, where the week starts on `firstDayOfWeek`.
|
|
11717
|
-
*/
|
|
11718
|
-
function buildMonthGrid(monthAnchor, firstDayOfWeek = 0) {
|
|
11719
|
-
const monthStart = startOfMonth(monthAnchor);
|
|
11720
|
-
const firstWeekday = monthStart.getDay();
|
|
11721
|
-
const offset = (firstWeekday - firstDayOfWeek + 7) % 7;
|
|
11722
|
-
const gridStart = addDays(monthStart, -offset);
|
|
11723
|
-
const today = startOfDay(new Date());
|
|
11724
|
-
const cells = [];
|
|
11725
|
-
for (let i = 0; i < 42; i++) {
|
|
11726
|
-
const date = addDays(gridStart, i);
|
|
11727
|
-
cells.push({
|
|
11728
|
-
date,
|
|
11729
|
-
inCurrentMonth: date.getMonth() === monthAnchor.getMonth(),
|
|
11730
|
-
isToday: isSameDay(date, today),
|
|
11731
|
-
});
|
|
11732
|
-
}
|
|
11733
|
-
return cells;
|
|
11734
|
-
}
|
|
11735
|
-
|
|
11747
|
+
const CALENDAR_FOCUSED_CELL = '[data-focused="true"], [data-focused-year="true"]';
|
|
11736
11748
|
const BASE_VIEW = {
|
|
11737
11749
|
day: 'days',
|
|
11738
11750
|
month: 'months',
|
|
@@ -11778,20 +11790,32 @@ function monthStep(key, col) {
|
|
|
11778
11790
|
}
|
|
11779
11791
|
}
|
|
11780
11792
|
/**
|
|
11781
|
-
*
|
|
11782
|
-
*
|
|
11783
|
-
*
|
|
11793
|
+
* Whether DOM focus should follow a grid's tab stop as it moves: focus is in
|
|
11794
|
+
* the grid, or fell to <body> while the grid held it last, as it does when
|
|
11795
|
+
* the focused cell leaves the page or becomes disabled. Focus is read in the
|
|
11796
|
+
* grid's own tree, so this holds inside a shadow root too.
|
|
11784
11797
|
*/
|
|
11785
|
-
function
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11798
|
+
function focusFollows(grid, heldLast) {
|
|
11799
|
+
const active = getActiveElementInTree(grid);
|
|
11800
|
+
if (grid.contains(active))
|
|
11801
|
+
return true;
|
|
11802
|
+
return heldLast && active === grid.ownerDocument.body;
|
|
11803
|
+
}
|
|
11804
|
+
/**
|
|
11805
|
+
* The cell nearest index `i` that `disabled` leaves enabled, `i` itself when
|
|
11806
|
+
* it is. At equal distance the later cell wins. With every cell disabled it
|
|
11807
|
+
* stays `i`, as there is nothing to reach.
|
|
11808
|
+
*/
|
|
11809
|
+
function nearestEnabled(i, disabled) {
|
|
11810
|
+
if (!disabled[i])
|
|
11811
|
+
return i;
|
|
11812
|
+
for (let d = 1; d < disabled.length; d++) {
|
|
11813
|
+
if (i + d < disabled.length && !disabled[i + d])
|
|
11814
|
+
return i + d;
|
|
11815
|
+
if (i - d >= 0 && !disabled[i - d])
|
|
11816
|
+
return i - d;
|
|
11793
11817
|
}
|
|
11794
|
-
return
|
|
11818
|
+
return i;
|
|
11795
11819
|
}
|
|
11796
11820
|
/**
|
|
11797
11821
|
* The year list's keyboard when it is the selection surface: arrows move a
|
|
@@ -11863,6 +11887,33 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
11863
11887
|
shouldDisableDate,
|
|
11864
11888
|
unselectableDates,
|
|
11865
11889
|
}), [min, max, shouldDisableDate, unselectableDates]);
|
|
11890
|
+
// The day grid's one tab stop. A disabled button can't take focus, so when
|
|
11891
|
+
// the focused day is disabled the stop moves to the nearest enabled day of
|
|
11892
|
+
// the month on show, and focusing it makes it the focused day. A nearby
|
|
11893
|
+
// month's day is left out, as focusing it would turn the grid to its month.
|
|
11894
|
+
const tabStopDay = React.useMemo(() => {
|
|
11895
|
+
const days = cells.filter(c => c.inCurrentMonth).map(c => c.date);
|
|
11896
|
+
const i = nearestEnabled(focusedDate.getDate() - 1, days.map(isDateUnselectable));
|
|
11897
|
+
return days[i];
|
|
11898
|
+
}, [cells, focusedDate, isDateUnselectable]);
|
|
11899
|
+
// Whether the grid on show holds focus, or held it last before focus went
|
|
11900
|
+
// nowhere: the cell that had it left the page or became disabled. A blur
|
|
11901
|
+
// that takes focus out of the grid, to nowhere included, is the user
|
|
11902
|
+
// leaving, so it clears this. A render that removes or disables the focused
|
|
11903
|
+
// cell sends no blur here: browsers fire none, or fire it inside React's
|
|
11904
|
+
// commit, where React delivers no events.
|
|
11905
|
+
const gridHeldFocusRef = React.useRef(false);
|
|
11906
|
+
const gridFocusHandlers = {
|
|
11907
|
+
onFocus: () => {
|
|
11908
|
+
gridHeldFocusRef.current = true;
|
|
11909
|
+
},
|
|
11910
|
+
onBlur: (e) => {
|
|
11911
|
+
const next = e.relatedTarget;
|
|
11912
|
+
if (!next || !e.currentTarget.contains(next)) {
|
|
11913
|
+
gridHeldFocusRef.current = false;
|
|
11914
|
+
}
|
|
11915
|
+
},
|
|
11916
|
+
};
|
|
11866
11917
|
// Step from `next` by `direction` days until we hit a selectable date,
|
|
11867
11918
|
// skipping past dates blocked by min/max, shouldDisableDate, or
|
|
11868
11919
|
// unselectableDates. If the whole searched range is disabled, focus stays
|
|
@@ -12026,6 +12077,16 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12026
12077
|
// Only the selection surface disables years; as navigation every listed
|
|
12027
12078
|
// year can be visited.
|
|
12028
12079
|
const disabledYears = React.useMemo(() => new Set(isYearGranularity ? yearList.filter(y => isYearUnselectable(y)) : []), [isYearGranularity, yearList, isYearUnselectable]);
|
|
12080
|
+
// The year list's one tab stop, after the month grid's: when the focused
|
|
12081
|
+
// year is disabled the stop moves to the nearest enabled year in the list,
|
|
12082
|
+
// and focusing it makes it the focused year. A year outside the list keeps
|
|
12083
|
+
// the stop, which then marks no option.
|
|
12084
|
+
const tabStopYear = React.useMemo(() => {
|
|
12085
|
+
const i = yearList.indexOf(focusedYear);
|
|
12086
|
+
if (i < 0)
|
|
12087
|
+
return focusedYear;
|
|
12088
|
+
return yearList[nearestEnabled(i, yearList.map(y => disabledYears.has(y)))];
|
|
12089
|
+
}, [yearList, focusedYear, disabledYears]);
|
|
12029
12090
|
// Which of the focused year's months have no selectable day.
|
|
12030
12091
|
const disabledMonths = React.useMemo(() => granularity === 'month'
|
|
12031
12092
|
? Array.from({ length: 12 }, (_, m) => isMonthUnselectable(makeDate(focusedYear, m)))
|
|
@@ -12033,7 +12094,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12033
12094
|
// The month grid's one tab stop. A disabled button can't take focus, so
|
|
12034
12095
|
// when the focused month is disabled the stop moves to the nearest enabled
|
|
12035
12096
|
// month, and focusing it makes it the focused month.
|
|
12036
|
-
const tabStopMonth =
|
|
12097
|
+
const tabStopMonth = nearestEnabled(focusedDate.getMonth(), disabledMonths);
|
|
12037
12098
|
const handleYearSelect = React.useCallback((year) => {
|
|
12038
12099
|
const next = new Date(focusedDate);
|
|
12039
12100
|
next.setFullYear(year);
|
|
@@ -12075,24 +12136,37 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12075
12136
|
e.preventDefault();
|
|
12076
12137
|
moveYearFocus(step[0], step[1]);
|
|
12077
12138
|
}, [isYearGranularity, baseView, focusedYear, yearList, moveYearFocus]);
|
|
12139
|
+
// The day or month grid takes focus as the popover opens on it, or comes
|
|
12140
|
+
// back to it from the year list.
|
|
12078
12141
|
React.useEffect(() => {
|
|
12079
|
-
if (
|
|
12142
|
+
if (!autoFocusCell)
|
|
12080
12143
|
return;
|
|
12081
|
-
|
|
12144
|
+
const grid = view === 'days'
|
|
12145
|
+
? gridRef.current
|
|
12146
|
+
: view === 'months'
|
|
12147
|
+
? monthGridRef.current
|
|
12148
|
+
: null;
|
|
12149
|
+
grid?.querySelector('[data-focused="true"]')?.focus();
|
|
12150
|
+
}, [autoFocusCell, view]);
|
|
12151
|
+
// From then on focus follows the tab stop only while the grid has it, or
|
|
12152
|
+
// has just lost it, so focus on a header button or a time wheel stays put.
|
|
12153
|
+
const tabStopDayTime = tabStopDay.getTime();
|
|
12154
|
+
React.useEffect(() => {
|
|
12155
|
+
const grid = gridRef.current;
|
|
12156
|
+
if (view !== 'days' || !grid)
|
|
12157
|
+
return;
|
|
12158
|
+
if (!focusFollows(grid, gridHeldFocusRef.current))
|
|
12082
12159
|
return;
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
}, [autoFocusCell, focusedDate, view]);
|
|
12086
|
-
// The month grid takes focus when the popover asks for it, and keeps it on
|
|
12087
|
-
// the focused month while the keyboard moves through the grid.
|
|
12160
|
+
grid.querySelector('[data-focused="true"]')?.focus();
|
|
12161
|
+
}, [tabStopDayTime, view]);
|
|
12088
12162
|
React.useEffect(() => {
|
|
12089
12163
|
const grid = monthGridRef.current;
|
|
12090
12164
|
if (view !== 'months' || !grid)
|
|
12091
12165
|
return;
|
|
12092
|
-
if (!
|
|
12166
|
+
if (!focusFollows(grid, gridHeldFocusRef.current))
|
|
12093
12167
|
return;
|
|
12094
12168
|
grid.querySelector('[data-focused="true"]')?.focus();
|
|
12095
|
-
}, [
|
|
12169
|
+
}, [focusedYear, tabStopMonth, view]);
|
|
12096
12170
|
// When the year view opens as navigation, scroll the focused year into
|
|
12097
12171
|
// view.
|
|
12098
12172
|
React.useEffect(() => {
|
|
@@ -12126,15 +12200,15 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12126
12200
|
const list = yearGridRef.current;
|
|
12127
12201
|
if (!isYearGranularity || !list)
|
|
12128
12202
|
return;
|
|
12129
|
-
if (!list.contains(
|
|
12203
|
+
if (!list.contains(getActiveElementInTree(list)))
|
|
12130
12204
|
return;
|
|
12131
12205
|
list.querySelector('[data-focused-year="true"]')?.focus();
|
|
12132
12206
|
}, [isYearGranularity, focusedYear]);
|
|
12133
12207
|
const navArrow = (d) => (jsxRuntime.jsx("svg", { viewBox: "0 0 16 16", width: "14", height: "14", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: d, fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
12134
|
-
return (jsxRuntime.jsxs("div", { className: classNames(calendarClass, className), id: id, children: [jsxRuntime.jsxs("div", { className: headerClass, children: [isYearGranularity ? (jsxRuntime.jsx("span", { className: monthLabelClass, id: labelId, children: monthLabel })) : (jsxRuntime.jsxs("button", { type: "button", className: monthTriggerClass, "aria-haspopup": "listbox", "aria-expanded": view === 'years', onClick: () => setView(v => (v === 'years' ? baseView : 'years')), children: [jsxRuntime.jsx("span", { className: monthLabelClass, id: labelId, "aria-live": "polite", children: monthLabel }), jsxRuntime.jsx("svg", { className: monthCaretClass, viewBox: "0 0 12 12", width: "10", height: "10", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: "M2 4l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })] })), !isYearGranularity && (jsxRuntime.jsxs("div", { className: navGroupClass, children: [jsxRuntime.jsx("button", { type: "button", className: navPrevClass, "aria-label": isDayGranularity ? t.prevMonth : t.prevYear, disabled: prevDisabled || view !== baseView, onClick: () => onFocusedDateChange(prevAnchor), children: navArrow('M10 3l-5 5 5 5') }), jsxRuntime.jsx("button", { type: "button", className: navNextClass, "aria-label": isDayGranularity ? t.nextMonth : t.nextYear, disabled: nextDisabled || view !== baseView, onClick: () => onFocusedDateChange(nextAnchor), children: navArrow('M6 3l5 5-5 5') })] }))] }), view === 'days' && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: dayNamesRowClass, "aria-hidden": "true", children: computedDayNames.map((name, i) => (jsxRuntime.jsx("div", { className: dayNameClass, children: name }, i))) }), jsxRuntime.jsx("div", { ref: gridRef, role: "grid", "aria-labelledby": labelId, className: gridClass, onKeyDown: handleKeyDown, children: cells.map(cell => {
|
|
12208
|
+
return (jsxRuntime.jsxs("div", { className: classNames(calendarClass, className), id: id, children: [jsxRuntime.jsxs("div", { className: headerClass, children: [isYearGranularity ? (jsxRuntime.jsx("span", { className: monthLabelClass, id: labelId, children: monthLabel })) : (jsxRuntime.jsxs("button", { type: "button", className: monthTriggerClass, "aria-haspopup": "listbox", "aria-expanded": view === 'years', onClick: () => setView(v => (v === 'years' ? baseView : 'years')), children: [jsxRuntime.jsx("span", { className: monthLabelClass, id: labelId, "aria-live": "polite", children: monthLabel }), jsxRuntime.jsx("svg", { className: monthCaretClass, viewBox: "0 0 12 12", width: "10", height: "10", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: "M2 4l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })] })), !isYearGranularity && (jsxRuntime.jsxs("div", { className: navGroupClass, children: [jsxRuntime.jsx("button", { type: "button", className: navPrevClass, "aria-label": isDayGranularity ? t.prevMonth : t.prevYear, disabled: prevDisabled || view !== baseView, onClick: () => onFocusedDateChange(prevAnchor), children: navArrow('M10 3l-5 5 5 5') }), jsxRuntime.jsx("button", { type: "button", className: navNextClass, "aria-label": isDayGranularity ? t.nextMonth : t.nextYear, disabled: nextDisabled || view !== baseView, onClick: () => onFocusedDateChange(nextAnchor), children: navArrow('M6 3l5 5-5 5') })] }))] }), view === 'days' && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: dayNamesRowClass, "aria-hidden": "true", children: computedDayNames.map((name, i) => (jsxRuntime.jsx("div", { className: dayNameClass, children: name }, i))) }), jsxRuntime.jsx("div", { ref: gridRef, role: "grid", "aria-labelledby": labelId, className: gridClass, onKeyDown: handleKeyDown, ...gridFocusHandlers, children: cells.map(cell => {
|
|
12135
12209
|
const disabled = isDateUnselectable(cell.date);
|
|
12136
12210
|
const isSelected = !!value && isSameDay(value, cell.date);
|
|
12137
|
-
const isFocused = isSameDay(cell.date,
|
|
12211
|
+
const isFocused = isSameDay(cell.date, tabStopDay);
|
|
12138
12212
|
const otherMonth = !cell.inCurrentMonth;
|
|
12139
12213
|
const cellClass = prefixedClassNames(classPrefix, 'dateinput-cell', {
|
|
12140
12214
|
'is-selected': isSelected,
|
|
@@ -12143,13 +12217,20 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12143
12217
|
'is-other-month': otherMonth,
|
|
12144
12218
|
});
|
|
12145
12219
|
const display = !otherMonth || nearbyMonthDays;
|
|
12146
|
-
return (jsxRuntime.jsx("button", { type: "button", role: "gridcell", tabIndex: isFocused ? 0 : -1, "aria-selected": isSelected, "aria-disabled": disabled, "aria-current": cell.isToday ? 'date' : undefined, "data-focused": isFocused ? 'true' : undefined, disabled: disabled || !display, className: cellClass,
|
|
12220
|
+
return (jsxRuntime.jsx("button", { type: "button", role: "gridcell", tabIndex: isFocused ? 0 : -1, "aria-selected": isSelected, "aria-disabled": disabled, "aria-current": cell.isToday ? 'date' : undefined, "data-focused": isFocused ? 'true' : undefined, disabled: disabled || !display, className: cellClass, onFocus: () => {
|
|
12221
|
+
// Reached by Tab while the focused day is disabled, or
|
|
12222
|
+
// by pointer: keys move on from here. A nearby month's
|
|
12223
|
+
// day waits for its click, which turns the grid.
|
|
12224
|
+
if (!otherMonth && !isSameDay(cell.date, focusedDate)) {
|
|
12225
|
+
onFocusedDateChange(cell.date);
|
|
12226
|
+
}
|
|
12227
|
+
}, onClick: () => {
|
|
12147
12228
|
if (disabled)
|
|
12148
12229
|
return;
|
|
12149
12230
|
onFocusedDateChange(cell.date);
|
|
12150
12231
|
onSelect(cell.date);
|
|
12151
12232
|
}, style: !display ? { visibility: 'hidden' } : undefined, children: cell.date.getDate() }, cell.date.toISOString()));
|
|
12152
|
-
}) })] })), view === 'months' && (jsxRuntime.jsx("div", { ref: monthGridRef, role: "grid", "aria-labelledby": labelId, className: monthsGridClass, onKeyDown: handleMonthKeyDown, children: MONTH_ROWS.map(row => (jsxRuntime.jsx("div", { role: "row", className: monthsRowClass, children: row.map(month => {
|
|
12233
|
+
}) })] })), view === 'months' && (jsxRuntime.jsx("div", { ref: monthGridRef, role: "grid", "aria-labelledby": labelId, className: monthsGridClass, onKeyDown: handleMonthKeyDown, ...gridFocusHandlers, children: MONTH_ROWS.map(row => (jsxRuntime.jsx("div", { role: "row", className: monthsRowClass, children: row.map(month => {
|
|
12153
12234
|
const date = makeDate(focusedYear, month);
|
|
12154
12235
|
const disabled = disabledMonths[month];
|
|
12155
12236
|
const isSelected = !!value && isSameMonth(value, date);
|
|
@@ -12171,7 +12252,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12171
12252
|
onSelect(date);
|
|
12172
12253
|
}, children: monthCellNames[month] }, month));
|
|
12173
12254
|
}) }, row[0]))) })), view === 'years' && (jsxRuntime.jsx("div", { ref: yearGridRef, className: yearsGridClass, role: "listbox", "aria-label": isYearGranularity ? t.chooseYear : monthLabel, onKeyDown: handleYearListKeyDown, children: yearList.map(year => {
|
|
12174
|
-
const isFocused = year ===
|
|
12255
|
+
const isFocused = year === tabStopYear;
|
|
12175
12256
|
const isToday = year === todayYear;
|
|
12176
12257
|
// As navigation the focused year is the current one. As the
|
|
12177
12258
|
// selection surface the value's year is selected and focus roams.
|
|
@@ -12184,7 +12265,13 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12184
12265
|
'is-today': isToday,
|
|
12185
12266
|
'is-disabled': disabled,
|
|
12186
12267
|
});
|
|
12187
|
-
return (jsxRuntime.jsx("button", { type: "button", role: "option", "aria-selected": isSelected, "aria-disabled": isYearGranularity ? disabled : undefined, "aria-current": isYearGranularity && isToday ? 'date' : undefined, "data-focused-year": isFocused ? 'true' : undefined, tabIndex: isFocused ? 0 : -1, disabled: disabled, className: cellCls,
|
|
12268
|
+
return (jsxRuntime.jsx("button", { type: "button", role: "option", "aria-selected": isSelected, "aria-disabled": isYearGranularity ? disabled : undefined, "aria-current": isYearGranularity && isToday ? 'date' : undefined, "data-focused-year": isFocused ? 'true' : undefined, tabIndex: isFocused ? 0 : -1, disabled: disabled, className: cellCls, onFocus: () => {
|
|
12269
|
+
// Reached by Tab while the focused year is disabled, or by
|
|
12270
|
+
// pointer: keys move on from here.
|
|
12271
|
+
if (isYearGranularity && year !== focusedYear) {
|
|
12272
|
+
onFocusedDateChange(clampDate(makeDate(year), min, max));
|
|
12273
|
+
}
|
|
12274
|
+
}, onClick: () => isYearGranularity
|
|
12188
12275
|
? handleYearPick(year)
|
|
12189
12276
|
: handleYearSelect(year), children: year }, year));
|
|
12190
12277
|
}) }))] }));
|
|
@@ -12192,12 +12279,26 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
|
|
|
12192
12279
|
Calendar.displayName = 'Calendar';
|
|
12193
12280
|
|
|
12194
12281
|
const isBrowser = typeof window !== 'undefined';
|
|
12195
|
-
const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'bottom-left', appendToBody = false, className, trapFocus = true, closeOnClickOutside = true, closeOnEscape = true, ariaLabel, ariaLabelledBy, role = 'dialog', id, restoreFocusRef, }) => {
|
|
12282
|
+
const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'bottom-left', appendToBody = false, className, trapFocus = true, closeOnClickOutside = true, closeOnEscape = true, ariaLabel, ariaLabelledBy, role = 'dialog', id, restoreFocusRef, initialFocusSelector, }) => {
|
|
12196
12283
|
const panelRef = React.useRef(null);
|
|
12284
|
+
// Read as the trap turns on, once the panel's content has rendered.
|
|
12285
|
+
const initialFocusRef = React.useMemo(() => initialFocusSelector === undefined
|
|
12286
|
+
? undefined
|
|
12287
|
+
: {
|
|
12288
|
+
get current() {
|
|
12289
|
+
return (panelRef.current?.querySelector(initialFocusSelector) ?? null);
|
|
12290
|
+
},
|
|
12291
|
+
}, [initialFocusSelector]);
|
|
12197
12292
|
const resolved = useAnchoredPosition(anchorRef, panelRef, {
|
|
12198
12293
|
active: isOpen,
|
|
12199
12294
|
position,
|
|
12200
12295
|
fixed: appendToBody,
|
|
12296
|
+
// A portaled panel takes its gap from `--bulma-picker-popover-offset` as
|
|
12297
|
+
// a margin, so its coordinates leave the gap out and `auto` reads it off
|
|
12298
|
+
// the panel. An in-place panel's gap is in its `top` or `bottom`, which
|
|
12299
|
+
// `auto` cannot read, so it keeps the hook's default estimate.
|
|
12300
|
+
offset: appendToBody ? 0 : undefined,
|
|
12301
|
+
styledGap: marginGap,
|
|
12201
12302
|
});
|
|
12202
12303
|
React.useEffect(() => {
|
|
12203
12304
|
if (!isOpen || !closeOnClickOutside || !isBrowser)
|
|
@@ -12231,6 +12332,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
12231
12332
|
}, [isOpen, closeOnEscape, onClose]);
|
|
12232
12333
|
useFocusTrap(panelRef, {
|
|
12233
12334
|
active: isOpen && trapFocus,
|
|
12335
|
+
initialFocusRef,
|
|
12234
12336
|
restoreFocus: restoreFocusRef ?? true,
|
|
12235
12337
|
});
|
|
12236
12338
|
const panelClass = usePrefixedClassNames('picker-popover', {
|
|
@@ -12410,8 +12512,11 @@ function buildSegmentMap(format) {
|
|
|
12410
12512
|
// ---------------------------------------------------------------------------
|
|
12411
12513
|
const wrap$1 = (n, max) => ((n % max) + max) % max;
|
|
12412
12514
|
const clamp = (n, min, max) => Math.max(min, Math.min(max, n));
|
|
12413
|
-
/**
|
|
12414
|
-
|
|
12515
|
+
/**
|
|
12516
|
+
* Number of days in the month of `d` (handles leap Februarys). Built on
|
|
12517
|
+
* `makeDate`, so a year below 100 keeps its own leap rule.
|
|
12518
|
+
*/
|
|
12519
|
+
const daysInMonth = (d) => makeDate(d.getFullYear(), d.getMonth() + 1, 0).getDate();
|
|
12415
12520
|
/**
|
|
12416
12521
|
* Returns a new Date with the given segment incremented (`delta` = +1 or -1).
|
|
12417
12522
|
* Wraps at segment boundaries: year is unbounded, month 0↔11 (in place, no
|
|
@@ -13019,16 +13124,19 @@ const toIsoValue = (d, granularity) => {
|
|
|
13019
13124
|
const month = `${year}-${pad2$1(d.getMonth() + 1)}`;
|
|
13020
13125
|
return granularity === 'month' ? month : `${month}-${pad2$1(d.getDate())}`;
|
|
13021
13126
|
};
|
|
13022
|
-
/**
|
|
13127
|
+
/**
|
|
13128
|
+
* Read a native `type="date"` or `type="month"` value back into a Date. HTML
|
|
13129
|
+
* allows a year of four or more digits, as `toIsoValue` writes one past 9999.
|
|
13130
|
+
*/
|
|
13023
13131
|
const fromIsoValue = (s, granularity) => {
|
|
13024
13132
|
if (granularity === 'month') {
|
|
13025
|
-
const m = /^(\d{4})-(\d{2})$/.exec(s);
|
|
13133
|
+
const m = /^(\d{4,})-(\d{2})$/.exec(s);
|
|
13026
13134
|
return m ? makeDate(Number(m[1]), Number(m[2]) - 1) : null;
|
|
13027
13135
|
}
|
|
13028
|
-
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
|
|
13136
|
+
const m = /^(\d{4,})-(\d{2})-(\d{2})$/.exec(s);
|
|
13029
13137
|
if (!m)
|
|
13030
13138
|
return null;
|
|
13031
|
-
return
|
|
13139
|
+
return makeDate(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
13032
13140
|
};
|
|
13033
13141
|
const DEFAULT_FORMATS = {
|
|
13034
13142
|
day: DEFAULT_DATE_FORMAT,
|
|
@@ -13092,16 +13200,20 @@ const DateInputBase = React.forwardRef((props, ref) => {
|
|
|
13092
13200
|
});
|
|
13093
13201
|
const containerClass = usePrefixedClassNames('dateinput-container');
|
|
13094
13202
|
const triggerClass = usePrefixedClassNames('dateinput-trigger');
|
|
13203
|
+
// What the latest request asked for, so a second request in the same
|
|
13204
|
+
// event sees it before React renders. The callbacks fire here rather than
|
|
13205
|
+
// in a state updater, which StrictMode calls twice, and after the state
|
|
13206
|
+
// is set, so a request a callback makes is the one that lands last.
|
|
13207
|
+
const requestedOpenRef = React.useRef(false);
|
|
13095
13208
|
const setOpen = React.useCallback((next) => {
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13099
|
-
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
});
|
|
13209
|
+
if (requestedOpenRef.current === next)
|
|
13210
|
+
return;
|
|
13211
|
+
requestedOpenRef.current = next;
|
|
13212
|
+
setOpenState(next);
|
|
13213
|
+
if (next)
|
|
13214
|
+
onOpen?.();
|
|
13215
|
+
else
|
|
13216
|
+
onClose?.();
|
|
13105
13217
|
}, [onOpen, onClose]);
|
|
13106
13218
|
// Sync displayed text with value when value changes externally.
|
|
13107
13219
|
React.useEffect(() => {
|
|
@@ -13246,14 +13358,14 @@ const DateInputBase = React.forwardRef((props, ref) => {
|
|
|
13246
13358
|
: t.chooseDate;
|
|
13247
13359
|
const calendar = (jsxRuntime.jsx(Calendar
|
|
13248
13360
|
// A new granularity starts the calendar over on its own view.
|
|
13249
|
-
, { granularity: granularity, value: value, focusedDate: focusedDate, onSelect: handleSelect, onFocusedDateChange: setFocusedDate, min: min, max: max, shouldDisableDate: shouldDisableDate, unselectableDates: unselectableDates, firstDayOfWeek: firstDayOfWeek, locale: locale, dayNames: dayNames, monthNames: monthNames, nearbyMonthDays: nearbyMonthDays, color: color, size: size, id: popoverId
|
|
13361
|
+
, { granularity: granularity, value: value, focusedDate: focusedDate, onSelect: handleSelect, onFocusedDateChange: setFocusedDate, min: min, max: max, shouldDisableDate: shouldDisableDate, unselectableDates: unselectableDates, firstDayOfWeek: firstDayOfWeek, locale: locale, dayNames: dayNames, monthNames: monthNames, nearbyMonthDays: nearbyMonthDays, color: color, size: size, id: `${popoverId}-cal`, autoFocusCell: open, labels: labels }, granularity));
|
|
13250
13362
|
if (inline) {
|
|
13251
13363
|
return (jsxRuntime.jsxs("div", { ...cleanRest, ref: containerRef, className: classNames(containerClass, bulmaHelperClasses, className), children: [calendar, name && (jsxRuntime.jsx("input", { type: "hidden", name: name, form: form, value: value ? toIsoValue(value, granularity) : '', required: required }))] }));
|
|
13252
13364
|
}
|
|
13253
13365
|
return (jsxRuntime.jsxs("div", { ref: containerRef, className: classNames(containerClass, bulmaHelperClasses, className), ...cleanRest, children: [jsxRuntime.jsx("input", { ref: combinedRef, type: "text", role: "combobox", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": popoverId, autoComplete: "off", className: inputClass, value: text, placeholder: placeholder, disabled: disabled, readOnly: inputReadOnlyAttr, name: name, form: form, required: required, id: id, ...inputHandlers }), popover && triggerIcon && (jsxRuntime.jsx("button", { type: "button", className: triggerClass, onClick: () => {
|
|
13254
13366
|
if (canOpen)
|
|
13255
13367
|
setOpen(!open);
|
|
13256
|
-
}, disabled: !canOpen, "aria-label": chooseLabel, "aria-haspopup": "dialog", "aria-controls": popoverId, "aria-expanded": open, tabIndex: canOpen ? 0 : -1, children: jsxRuntime.jsx(Icon, { name: triggerIconName, size: size }) })), popover && (jsxRuntime.jsx(PickerPopover, { isOpen: open, onClose: handlePopoverClose, anchorRef: containerRef, position: position, appendToBody: appendToBody, ariaLabel: chooseLabel, id: popoverId, restoreFocusRef: inputRef, children: calendar }))] }));
|
|
13368
|
+
}, disabled: !canOpen, "aria-label": chooseLabel, "aria-haspopup": "dialog", "aria-controls": popoverId, "aria-expanded": open, tabIndex: canOpen ? 0 : -1, children: jsxRuntime.jsx(Icon, { name: triggerIconName, size: size }) })), popover && (jsxRuntime.jsx(PickerPopover, { isOpen: open, onClose: handlePopoverClose, anchorRef: containerRef, position: position, appendToBody: appendToBody, ariaLabel: chooseLabel, id: popoverId, restoreFocusRef: inputRef, initialFocusSelector: CALENDAR_FOCUSED_CELL, children: calendar }))] }));
|
|
13257
13369
|
});
|
|
13258
13370
|
DateInputBase.displayName = 'DateInputBase';
|
|
13259
13371
|
|
|
@@ -14226,19 +14338,23 @@ const TimeInputBase = React.forwardRef((props, ref) => {
|
|
|
14226
14338
|
const mobileFooterClass = usePrefixedClassNames('timeinput-footer-mobile');
|
|
14227
14339
|
const mobileFooterResetClass = usePrefixedClassNames('timeinput-footer-reset');
|
|
14228
14340
|
const mobileFooterDoneClass = usePrefixedClassNames('timeinput-footer-done');
|
|
14341
|
+
// What the latest request asked for, so a second request in the same
|
|
14342
|
+
// event sees it before React renders. The callbacks fire here rather than
|
|
14343
|
+
// in a state updater, which StrictMode calls twice, and after the state
|
|
14344
|
+
// is set, so a request a callback makes is the one that lands last.
|
|
14345
|
+
const requestedOpenRef = React.useRef(false);
|
|
14229
14346
|
const setOpen = React.useCallback((next) => {
|
|
14230
|
-
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
|
|
14239
|
-
|
|
14240
|
-
|
|
14241
|
-
});
|
|
14347
|
+
if (requestedOpenRef.current === next)
|
|
14348
|
+
return;
|
|
14349
|
+
requestedOpenRef.current = next;
|
|
14350
|
+
setOpenState(next);
|
|
14351
|
+
if (next) {
|
|
14352
|
+
valueAtOpenRef.current = value;
|
|
14353
|
+
onOpen?.();
|
|
14354
|
+
}
|
|
14355
|
+
else {
|
|
14356
|
+
onClose?.();
|
|
14357
|
+
}
|
|
14242
14358
|
}, [onOpen, onClose, value]);
|
|
14243
14359
|
React.useEffect(() => {
|
|
14244
14360
|
setText(value ? formatTime(value, defaultFormat, locale) : '');
|
|
@@ -14394,8 +14510,9 @@ const TimeInput = React.forwardRef(({ label, labelSize, labelProps, horizontal,
|
|
|
14394
14510
|
});
|
|
14395
14511
|
TimeInput.displayName = 'TimeInput';
|
|
14396
14512
|
|
|
14513
|
+
// The year is padded to four digits, as `datetime-local` requires.
|
|
14397
14514
|
const toIsoDateTime = (d, withSeconds) => {
|
|
14398
|
-
const yyyy = d.getFullYear();
|
|
14515
|
+
const yyyy = String(d.getFullYear()).padStart(4, '0');
|
|
14399
14516
|
const mo = String(d.getMonth() + 1).padStart(2, '0');
|
|
14400
14517
|
const dd = String(d.getDate()).padStart(2, '0');
|
|
14401
14518
|
const hh = String(d.getHours()).padStart(2, '0');
|
|
@@ -14408,10 +14525,13 @@ const toIsoDateTime = (d, withSeconds) => {
|
|
|
14408
14525
|
const fromIsoDateTime = (s) => {
|
|
14409
14526
|
// The HTML datetime-local value may carry fractional seconds (the spec
|
|
14410
14527
|
// allows them and some engines normalize to `:ss.sss`); accept and drop.
|
|
14411
|
-
|
|
14528
|
+
// Its year is four or more digits, as `toIsoDateTime` writes one past 9999.
|
|
14529
|
+
const m = /^(\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,3})?)?$/.exec(s);
|
|
14412
14530
|
if (!m)
|
|
14413
14531
|
return null;
|
|
14414
|
-
|
|
14532
|
+
const d = makeDate(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
14533
|
+
d.setHours(Number(m[4]), Number(m[5]), m[6] ? Number(m[6]) : 0, 0);
|
|
14534
|
+
return d;
|
|
14415
14535
|
};
|
|
14416
14536
|
/**
|
|
14417
14537
|
* Raw DateTimeInput — combined input + calendar + time spinner without
|
|
@@ -14498,19 +14618,23 @@ const DateTimeInputBase = React.forwardRef((props, ref) => {
|
|
|
14498
14618
|
const footerResetClass = usePrefixedClassNames('datetimeinput-footer-reset');
|
|
14499
14619
|
const footerDoneClass = usePrefixedClassNames('datetimeinput-footer-done');
|
|
14500
14620
|
const footerTimePillClass = usePrefixedClassNames('datetimeinput-footer-time-pill');
|
|
14621
|
+
// What the latest request asked for, so a second request in the same event
|
|
14622
|
+
// sees it before React renders. The callbacks fire here rather than in a
|
|
14623
|
+
// state updater, which StrictMode calls twice, and after the state is set,
|
|
14624
|
+
// so a request a callback makes is the one that lands last.
|
|
14625
|
+
const requestedOpenRef = React.useRef(false);
|
|
14501
14626
|
const setOpen = React.useCallback((next) => {
|
|
14502
|
-
|
|
14503
|
-
|
|
14504
|
-
|
|
14505
|
-
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
});
|
|
14627
|
+
if (requestedOpenRef.current === next)
|
|
14628
|
+
return;
|
|
14629
|
+
requestedOpenRef.current = next;
|
|
14630
|
+
setOpenState(next);
|
|
14631
|
+
if (next) {
|
|
14632
|
+
valueAtOpenRef.current = value;
|
|
14633
|
+
onOpen?.();
|
|
14634
|
+
}
|
|
14635
|
+
else {
|
|
14636
|
+
onClose?.();
|
|
14637
|
+
}
|
|
14514
14638
|
}, [onOpen, onClose, value]);
|
|
14515
14639
|
React.useEffect(() => {
|
|
14516
14640
|
setText(value ? formatDateTime(value, defaultFormat, locale) : '');
|
|
@@ -14640,7 +14764,7 @@ const DateTimeInputBase = React.forwardRef((props, ref) => {
|
|
|
14640
14764
|
return (jsxRuntime.jsxs("div", { ref: containerRef, className: classNames(containerClass, bulmaHelperClasses, className), ...cleanRest, children: [jsxRuntime.jsx("input", { ref: combinedRef, type: "text", role: "combobox", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": popoverId, autoComplete: "off", className: inputClass, value: text, placeholder: placeholder, disabled: disabled, readOnly: inputReadOnlyAttr, name: name, form: form, required: required, id: id, ...inputHandlers }), popover && triggerIcon && (jsxRuntime.jsx("button", { type: "button", className: triggerClass, onClick: () => {
|
|
14641
14765
|
if (canOpen)
|
|
14642
14766
|
setOpen(!open);
|
|
14643
|
-
}, disabled: !canOpen, "aria-label": t.chooseDateTime, "aria-haspopup": "dialog", "aria-controls": popoverId, "aria-expanded": open, tabIndex: canOpen ? 0 : -1, children: jsxRuntime.jsx(Icon, { name: triggerIconName, size: size }) })), popover && (jsxRuntime.jsx(PickerPopover, { isOpen: open, onClose: () => setOpen(false), anchorRef: containerRef, position: position, appendToBody: appendToBody, ariaLabel: t.chooseDateTime, id: popoverId, restoreFocusRef: inputRef, children: panel }))] }));
|
|
14767
|
+
}, disabled: !canOpen, "aria-label": t.chooseDateTime, "aria-haspopup": "dialog", "aria-controls": popoverId, "aria-expanded": open, tabIndex: canOpen ? 0 : -1, children: jsxRuntime.jsx(Icon, { name: triggerIconName, size: size }) })), popover && (jsxRuntime.jsx(PickerPopover, { isOpen: open, onClose: () => setOpen(false), anchorRef: containerRef, position: position, appendToBody: appendToBody, ariaLabel: t.chooseDateTime, id: popoverId, restoreFocusRef: inputRef, initialFocusSelector: CALENDAR_FOCUSED_CELL, children: panel }))] }));
|
|
14644
14768
|
});
|
|
14645
14769
|
DateTimeInputBase.displayName = 'DateTimeInputBase';
|
|
14646
14770
|
|
|
@@ -15622,31 +15746,57 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
|
|
|
15622
15746
|
// rewrites the shared element in place rather than removing and recreating
|
|
15623
15747
|
// it between the cleanup and the next run.
|
|
15624
15748
|
React.useEffect(() => () => setRootThemeRules(rootOrder, ''), [rootOrder]);
|
|
15625
|
-
//
|
|
15626
|
-
//
|
|
15627
|
-
// `
|
|
15749
|
+
// Bulma names its scheme attribute after the class prefix a stylesheet was
|
|
15750
|
+
// built with (`data-<prefix>theme`), so the prefixed builds read
|
|
15751
|
+
// `data-bestax-theme` and never `data-theme`.
|
|
15752
|
+
const classPrefix = useClassPrefix();
|
|
15753
|
+
// Toggle Bulma's light/dark scheme by writing the theme attribute on the
|
|
15754
|
+
// document root (<html>). This is always global, even on a scoped Theme.
|
|
15755
|
+
// Under a class prefix the prefixed attribute is written as well as
|
|
15756
|
+
// `data-theme`, so whichever build is loaded sees it; with no prefix only
|
|
15757
|
+
// `data-theme` is. `'system'` removes them so Bulma follows the OS
|
|
15758
|
+
// preference.
|
|
15628
15759
|
React.useEffect(() => {
|
|
15629
15760
|
if (colorMode === undefined) {
|
|
15630
15761
|
return;
|
|
15631
15762
|
}
|
|
15632
15763
|
const root = document.documentElement;
|
|
15633
|
-
const
|
|
15634
|
-
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
return () => {
|
|
15642
|
-
if (previous === null) {
|
|
15643
|
-
root.removeAttribute('data-theme');
|
|
15764
|
+
const names = classPrefix
|
|
15765
|
+
? ['data-theme', `data-${classPrefix}theme`]
|
|
15766
|
+
: ['data-theme'];
|
|
15767
|
+
const previous = names.map(name => root.getAttribute(name));
|
|
15768
|
+
for (const name of names) {
|
|
15769
|
+
if (colorMode === 'system') {
|
|
15770
|
+
root.removeAttribute(name);
|
|
15771
|
+
continue;
|
|
15644
15772
|
}
|
|
15645
|
-
|
|
15646
|
-
root.setAttribute(
|
|
15773
|
+
try {
|
|
15774
|
+
root.setAttribute(name, colorMode);
|
|
15775
|
+
}
|
|
15776
|
+
catch {
|
|
15777
|
+
// A class prefix can hold characters an attribute name cannot.
|
|
15778
|
+
// `data-theme` is still written, as it was before the prefixed
|
|
15779
|
+
// attribute existed, but a sheet built with that prefix keeps its
|
|
15780
|
+
// scheme, so say so.
|
|
15781
|
+
warnOnce('Theme:color-mode-prefix', `[bestax-bulma] <Theme colorMode="${colorMode}">: the class ` +
|
|
15782
|
+
`prefix "${classPrefix}" makes ${name} an invalid attribute ` +
|
|
15783
|
+
'name, so only data-theme is written, and a stylesheet built ' +
|
|
15784
|
+
'with that prefix keeps its scheme.');
|
|
15647
15785
|
}
|
|
15786
|
+
}
|
|
15787
|
+
// Restore the previous values when colorMode changes or the component unmounts.
|
|
15788
|
+
return () => {
|
|
15789
|
+
names.forEach((name, i) => {
|
|
15790
|
+
const value = previous[i];
|
|
15791
|
+
if (value === null) {
|
|
15792
|
+
root.removeAttribute(name);
|
|
15793
|
+
}
|
|
15794
|
+
else {
|
|
15795
|
+
root.setAttribute(name, value);
|
|
15796
|
+
}
|
|
15797
|
+
});
|
|
15648
15798
|
};
|
|
15649
|
-
}, [colorMode]);
|
|
15799
|
+
}, [colorMode, classPrefix]);
|
|
15650
15800
|
// For local injection (when isRoot is false), prepare style object for CSS vars
|
|
15651
15801
|
const style = React.useMemo(() => {
|
|
15652
15802
|
if (isRoot) {
|