@allxsmith/bestax-bulma 5.26.4 → 5.26.6
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 +445 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +445 -55
- package/dist/index.esm.js.map +1 -1
- package/dist/types/form/Autocomplete.d.ts +6 -1
- package/dist/types/form/DateInput.d.ts +8 -4
- package/dist/types/form/DateTimeInput.d.ts +8 -4
- package/dist/types/form/FormContext.d.ts +47 -0
- package/dist/types/form/Input.d.ts +4 -3
- package/dist/types/form/Numberinput.d.ts +11 -1
- package/dist/types/form/Select.d.ts +4 -3
- package/dist/types/form/TextArea.d.ts +4 -3
- package/dist/types/form/TimeInput.d.ts +8 -4
- package/dist/types/form/_pickerInternals/Calendar.d.ts +45 -4
- package/dist/types/form/_pickerInternals/dateUtils.d.ts +17 -0
- package/dist/types/form/_pickerInternals/pickerTypes.d.ts +14 -0
- package/dist/types/helpers/statusRegion.d.ts +1 -0
- package/dist/types-cjs/form/Autocomplete.d.ts +6 -1
- package/dist/types-cjs/form/DateInput.d.ts +8 -4
- package/dist/types-cjs/form/DateTimeInput.d.ts +8 -4
- package/dist/types-cjs/form/FormContext.d.ts +47 -0
- package/dist/types-cjs/form/Input.d.ts +4 -3
- package/dist/types-cjs/form/Numberinput.d.ts +11 -1
- package/dist/types-cjs/form/Select.d.ts +4 -3
- package/dist/types-cjs/form/TextArea.d.ts +4 -3
- package/dist/types-cjs/form/TimeInput.d.ts +8 -4
- package/dist/types-cjs/form/_pickerInternals/Calendar.d.ts +45 -4
- package/dist/types-cjs/form/_pickerInternals/dateUtils.d.ts +17 -0
- package/dist/types-cjs/form/_pickerInternals/pickerTypes.d.ts +14 -0
- package/dist/types-cjs/helpers/statusRegion.d.ts +1 -0
- 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/form/_dateinput.scss +8 -1
- package/src/scss/form/_taginput.scss +8 -0
package/dist/index.cjs
CHANGED
|
@@ -7691,7 +7691,8 @@ const noAnnouncementsYet = {
|
|
|
7691
7691
|
// the accessibility tree. The declarations match the `extras-sr-only` mixin,
|
|
7692
7692
|
// plus `clip-path`, which replaces the deprecated `clip`. The negative margin
|
|
7693
7693
|
// pulls the 1px box back inside the page, so it doesn't add scrollable
|
|
7694
|
-
// overflow.
|
|
7694
|
+
// overflow. Exported for other live regions that must work without a
|
|
7695
|
+
// stylesheet, such as a range calendar's.
|
|
7695
7696
|
const visuallyHidden = {
|
|
7696
7697
|
position: 'absolute',
|
|
7697
7698
|
width: '1px',
|
|
@@ -8821,6 +8822,58 @@ const rendersOwnField = (component, options) => {
|
|
|
8821
8822
|
(moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.'));
|
|
8822
8823
|
return true;
|
|
8823
8824
|
};
|
|
8825
|
+
// Each Control-level prop, with what takes its place on the `Control` the
|
|
8826
|
+
// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to
|
|
8827
|
+
// what each wrapper hands its own `Control`, so a new Control-level prop
|
|
8828
|
+
// fails there until it is added here and passed to `rendersOwnControl`.
|
|
8829
|
+
const ON_CONTROL = {
|
|
8830
|
+
isLoading: 'isLoading',
|
|
8831
|
+
iconLeft: 'iconLeft',
|
|
8832
|
+
iconLeftName: 'iconLeftName',
|
|
8833
|
+
iconLeftSize: 'iconLeftSize',
|
|
8834
|
+
iconRight: 'iconRight',
|
|
8835
|
+
iconRightName: 'iconRightName',
|
|
8836
|
+
iconRightSize: 'iconRightSize',
|
|
8837
|
+
hasIconsLeft: 'hasIconsLeft',
|
|
8838
|
+
hasIconsRight: 'hasIconsRight',
|
|
8839
|
+
isExpanded: 'isExpanded',
|
|
8840
|
+
controlSize: 'size (for controlSize)',
|
|
8841
|
+
controlClassName: 'className (for controlClassName)',
|
|
8842
|
+
};
|
|
8843
|
+
/**
|
|
8844
|
+
* Whether a convenience form wrapper renders a `Control` of its own (#921):
|
|
8845
|
+
* only outside one. Inside a `Control` it renders none, so the props it
|
|
8846
|
+
* would have handed its own do nothing there, and it warns in development,
|
|
8847
|
+
* naming them and saying to set them on that `Control`. An `inline` picker
|
|
8848
|
+
* renders none in either place, so it drops them inside a `Control` or not,
|
|
8849
|
+
* and its warning says so without pointing at a `Control`. A wrapper passes
|
|
8850
|
+
* only the props it hands its own `Control`, as the caller gave them, so a
|
|
8851
|
+
* default the wrapper fills in itself never warns. A prop counts when it is
|
|
8852
|
+
* truthy, since a falsy one would change nothing on a `Control` either.
|
|
8853
|
+
* Internal; not part of the public API.
|
|
8854
|
+
*/
|
|
8855
|
+
const rendersOwnControl = (component, options) => {
|
|
8856
|
+
if (!options.inline && !options.insideControl)
|
|
8857
|
+
return true;
|
|
8858
|
+
const held = Object.keys(ON_CONTROL).filter(prop => options[prop]);
|
|
8859
|
+
if (held.length === 0)
|
|
8860
|
+
return false;
|
|
8861
|
+
const names = held.join(' ');
|
|
8862
|
+
const doNothing = held.length > 1 ? 'those props do' : 'that prop does';
|
|
8863
|
+
if (options.inline) {
|
|
8864
|
+
warnOnce(`${component}:Control-props-inline:${held.join('+')}`, `[bestax-bulma] <${component} inline ${names}> renders no <Control> ` +
|
|
8865
|
+
`in inline mode, inside a <Control> or not, so ${doNothing} ` +
|
|
8866
|
+
`nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +
|
|
8867
|
+
`inline picker.`);
|
|
8868
|
+
}
|
|
8869
|
+
else {
|
|
8870
|
+
warnOnce(`${component}:Control-props-in-Control:${held.join('+')}`, `[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +
|
|
8871
|
+
`no <Control> of its own, so ${doNothing} nothing there. Set ` +
|
|
8872
|
+
`${listOf(held.map(prop => ON_CONTROL[prop]))} on that <Control> ` +
|
|
8873
|
+
`instead.`);
|
|
8874
|
+
}
|
|
8875
|
+
return false;
|
|
8876
|
+
};
|
|
8824
8877
|
const FieldLabelIdContext = React.createContext(undefined);
|
|
8825
8878
|
/**
|
|
8826
8879
|
* The id a labeled Field wants its single composed control to adopt (#495).
|
|
@@ -8955,6 +9008,7 @@ Checkbox.displayName = 'Checkbox';
|
|
|
8955
9008
|
// picker bases read it so their right-side launcher gives way to that
|
|
8956
9009
|
// spinner, which sits at the same edge, whether the `Control` is the one a
|
|
8957
9010
|
// convenience input renders or one the caller wrapped around the input.
|
|
9011
|
+
// Autocomplete reads it for its clear button, which sits there too.
|
|
8958
9012
|
// Kept apart from `FormContext`, whose contexts only report whether a `Field`
|
|
8959
9013
|
// or `Control` is present. Internal; not part of the public API.
|
|
8960
9014
|
const ControlLoadingContext = React.createContext(false);
|
|
@@ -9284,7 +9338,7 @@ const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message
|
|
|
9284
9338
|
}), [name, groupActive, currentValue, handleChange]);
|
|
9285
9339
|
const checkboxesElement = (jsxRuntime.jsx("div", { className: wrapperClass, role: "group", "aria-labelledby": ariaLabelledBy, ...rest, children: jsxRuntime.jsx(CheckboxesProvider, { value: ctx, children: children }) }));
|
|
9286
9340
|
let content = checkboxesElement;
|
|
9287
|
-
if (
|
|
9341
|
+
if (rendersOwnControl('Checkboxes', { insideControl })) {
|
|
9288
9342
|
content = jsxRuntime.jsx(Control, { children: content });
|
|
9289
9343
|
}
|
|
9290
9344
|
if (ownField) {
|
|
@@ -9524,7 +9578,7 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
|
|
|
9524
9578
|
}), [name, groupActive, currentValue, handleChange]);
|
|
9525
9579
|
const radiosElement = (jsxRuntime.jsx("div", { className: wrapperClass, role: "radiogroup", "aria-labelledby": ariaLabelledBy, ...rest, children: jsxRuntime.jsx(RadiosProvider, { value: ctx, children: children }) }));
|
|
9526
9580
|
let content = radiosElement;
|
|
9527
|
-
if (
|
|
9581
|
+
if (rendersOwnControl('Radios', { insideControl })) {
|
|
9528
9582
|
content = jsxRuntime.jsx(Control, { children: content });
|
|
9529
9583
|
}
|
|
9530
9584
|
if (ownField) {
|
|
@@ -10058,7 +10112,7 @@ const Slider = React.forwardRef((props, ref) => {
|
|
|
10058
10112
|
'--slider-output-arrow-offset': `calc(50% - ${nudgeXHigh}px)`,
|
|
10059
10113
|
}, children: formatValue(currentRange[1]) })), renderTicks()] }));
|
|
10060
10114
|
let content = sliderElement;
|
|
10061
|
-
if (
|
|
10115
|
+
if (rendersOwnControl('Slider', { insideControl })) {
|
|
10062
10116
|
content = jsxRuntime.jsx(Control, { children: content });
|
|
10063
10117
|
}
|
|
10064
10118
|
if (ownField) {
|
|
@@ -10089,7 +10143,11 @@ const Numberinput = React.forwardRef(({
|
|
|
10089
10143
|
// Field props
|
|
10090
10144
|
label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, value: controlledValue, defaultValue, min, max, step = 1, size, color, inputColor, controlsPosition = 'both', controlsRounded = false, compact = false, bare, variant = 'plusminus', disabled = false, editable = true, isLoading = false, exponential = false, onChange, className, ...props }, ref) => {
|
|
10091
10145
|
const insideField = useInsideField();
|
|
10092
|
-
|
|
10146
|
+
// Bare controls join their parent's row, which a `.control` does not lay
|
|
10147
|
+
// out. `useInsideControl()` is true anywhere below a `Control`, even past
|
|
10148
|
+
// a `Field` nested in it, so there they keep their own row as well.
|
|
10149
|
+
const insideControl = useInsideControl();
|
|
10150
|
+
const effectiveBare = bare ?? (insideField && !insideControl);
|
|
10093
10151
|
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
10094
10152
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
10095
10153
|
const inputRef = React.useRef(null);
|
|
@@ -10502,7 +10560,7 @@ const Rate = React.forwardRef(({ label, labelSize, labelProps, horizontal, messa
|
|
|
10502
10560
|
const rateCustomTextClass = usePrefixedClassNames('rate-custom-text');
|
|
10503
10561
|
const rateElement = (jsxRuntime.jsxs("div", { ref: ref, className: combinedClasses, role: "radiogroup", "aria-label": ariaLabelledBy ? undefined : 'Rating', "aria-labelledby": ariaLabelledBy, "aria-valuenow": currentValue, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuetext": ariaValueText, tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, ...rest, children: [jsxRuntime.jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsxRuntime.jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsxRuntime.jsx("span", { className: rateTextClass, children: text }), customText && (jsxRuntime.jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsxRuntime.jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
|
|
10504
10562
|
let content = rateElement;
|
|
10505
|
-
if (
|
|
10563
|
+
if (rendersOwnControl('Rate', { insideControl })) {
|
|
10506
10564
|
content = jsxRuntime.jsx(Control, { children: content });
|
|
10507
10565
|
}
|
|
10508
10566
|
if (ownField) {
|
|
@@ -10741,7 +10799,11 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
10741
10799
|
[`is-${messageColor}`]: !!messageColor,
|
|
10742
10800
|
});
|
|
10743
10801
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|
|
10744
|
-
|
|
10802
|
+
// The clear button gives way to a spinner at its spot: the `loading` one,
|
|
10803
|
+
// or that of a loading Control this sits in.
|
|
10804
|
+
const controlLoading = useControlLoading();
|
|
10805
|
+
const showClear = clearable && !!inputValue && !disabled && !loading && !controlLoading;
|
|
10806
|
+
const autocompleteElement = (jsxRuntime.jsxs("div", { ref: containerRef, className: combinedClasses, ...rest, children: [jsxRuntime.jsxs("div", { className: controlClasses, children: [jsxRuntime.jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: placeholder, disabled: disabled, name: name, form: form, required: required, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, role: "combobox", "aria-expanded": isActive, "aria-haspopup": "listbox", "aria-autocomplete": "list", autoComplete: "off" }), showClear && (jsxRuntime.jsx("button", { type: "button", className: iconRightClickableClass, onClick: handleClear, "aria-label": "Clear", children: jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "16", height: "16", "aria-hidden": "true", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })), loading && (jsxRuntime.jsx("span", { className: iconRightClass, children: jsxRuntime.jsx("span", { className: loaderClass }) }))] }), isActive && (filteredData.length > 0 || empty) && (jsxRuntime.jsx("div", { className: dropdownMenuClasses, children: jsxRuntime.jsxs("div", { ref: dropdownRef, className: dropdownContentClass, style: { maxHeight: `${maxHeight}px`, overflowY: 'auto' }, role: "listbox", onScroll: handleDropdownScroll, children: [header && jsxRuntime.jsx("div", { className: dropdownHeaderClass, children: header }), filteredData.length > 0 ? (filteredData.map((item, index) => {
|
|
10745
10807
|
const isDisabled = typeof item !== 'string' && item.disabled;
|
|
10746
10808
|
const isHighlighted = index === highlightedIndex;
|
|
10747
10809
|
const itemClasses = prefixedClassNames(classPrefix, 'dropdown-item', {
|
|
@@ -11249,6 +11311,21 @@ fieldClassName, controlClassName,
|
|
|
11249
11311
|
horizontal,
|
|
11250
11312
|
fieldClassName,
|
|
11251
11313
|
});
|
|
11314
|
+
const ownControl = rendersOwnControl('Input', {
|
|
11315
|
+
insideControl,
|
|
11316
|
+
isLoading,
|
|
11317
|
+
iconLeft,
|
|
11318
|
+
iconLeftName,
|
|
11319
|
+
iconLeftSize,
|
|
11320
|
+
iconRight,
|
|
11321
|
+
iconRightName,
|
|
11322
|
+
iconRightSize,
|
|
11323
|
+
hasIconsLeft,
|
|
11324
|
+
hasIconsRight,
|
|
11325
|
+
isExpanded,
|
|
11326
|
+
controlSize,
|
|
11327
|
+
controlClassName,
|
|
11328
|
+
});
|
|
11252
11329
|
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
11253
11330
|
label,
|
|
11254
11331
|
id: inputProps.id,
|
|
@@ -11259,7 +11336,7 @@ fieldClassName, controlClassName,
|
|
|
11259
11336
|
[`is-${messageColor}`]: !!messageColor,
|
|
11260
11337
|
});
|
|
11261
11338
|
let content = jsxRuntime.jsx(InputBase, { ref: ref, id: controlId, ...inputProps });
|
|
11262
|
-
if (
|
|
11339
|
+
if (ownControl) {
|
|
11263
11340
|
content = (jsxRuntime.jsx(Control, { iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
11264
11341
|
}
|
|
11265
11342
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|
|
@@ -11307,6 +11384,17 @@ fieldClassName, controlClassName,
|
|
|
11307
11384
|
horizontal,
|
|
11308
11385
|
fieldClassName,
|
|
11309
11386
|
});
|
|
11387
|
+
// Not isLoading, which the select draws itself and so keeps in a Control.
|
|
11388
|
+
const ownControl = rendersOwnControl('Select', {
|
|
11389
|
+
insideControl,
|
|
11390
|
+
iconLeft,
|
|
11391
|
+
iconLeftName,
|
|
11392
|
+
iconLeftSize,
|
|
11393
|
+
hasIconsLeft,
|
|
11394
|
+
isExpanded,
|
|
11395
|
+
controlSize,
|
|
11396
|
+
controlClassName,
|
|
11397
|
+
});
|
|
11310
11398
|
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
11311
11399
|
label,
|
|
11312
11400
|
id: selectProps.id,
|
|
@@ -11317,7 +11405,7 @@ fieldClassName, controlClassName,
|
|
|
11317
11405
|
[`is-${messageColor}`]: !!messageColor,
|
|
11318
11406
|
});
|
|
11319
11407
|
let content = jsxRuntime.jsx(SelectBase, { ref: ref, id: controlId, ...selectProps });
|
|
11320
|
-
if (
|
|
11408
|
+
if (ownControl) {
|
|
11321
11409
|
content = (jsxRuntime.jsx(Control, { iconLeft: iconLeft, iconLeftName: iconLeftName, iconLeftSize: iconLeftSize, hasIconsLeft: hasIconsLeft, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
11322
11410
|
}
|
|
11323
11411
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|
|
@@ -11366,6 +11454,12 @@ fieldClassName, controlClassName,
|
|
|
11366
11454
|
horizontal,
|
|
11367
11455
|
fieldClassName,
|
|
11368
11456
|
});
|
|
11457
|
+
const ownControl = rendersOwnControl('TextArea', {
|
|
11458
|
+
insideControl,
|
|
11459
|
+
isLoading: controlIsLoading,
|
|
11460
|
+
controlSize,
|
|
11461
|
+
controlClassName,
|
|
11462
|
+
});
|
|
11369
11463
|
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
11370
11464
|
label,
|
|
11371
11465
|
id: textAreaProps.id,
|
|
@@ -11376,7 +11470,7 @@ fieldClassName, controlClassName,
|
|
|
11376
11470
|
[`is-${messageColor}`]: !!messageColor,
|
|
11377
11471
|
});
|
|
11378
11472
|
let content = jsxRuntime.jsx(TextAreaBase, { ref: ref, id: controlId, ...textAreaProps });
|
|
11379
|
-
if (
|
|
11473
|
+
if (ownControl) {
|
|
11380
11474
|
content = (jsxRuntime.jsx(Control, { isLoading: controlIsLoading, size: controlSize, className: controlClassName, children: content }));
|
|
11381
11475
|
}
|
|
11382
11476
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|
|
@@ -11395,6 +11489,9 @@ const DEFAULT_PICKER_LABELS = {
|
|
|
11395
11489
|
chooseDate: 'Choose date',
|
|
11396
11490
|
chooseMonth: 'Choose month',
|
|
11397
11491
|
chooseYear: 'Choose year',
|
|
11492
|
+
rangeStart: 'Start date',
|
|
11493
|
+
rangeEnd: 'End date',
|
|
11494
|
+
rangePreviewEnd: 'Choose as end date',
|
|
11398
11495
|
hours: 'hours',
|
|
11399
11496
|
minutes: 'minutes',
|
|
11400
11497
|
seconds: 'seconds',
|
|
@@ -11547,6 +11644,43 @@ function isPeriodUnselectable(d, granularity, c) {
|
|
|
11547
11644
|
}
|
|
11548
11645
|
return true;
|
|
11549
11646
|
}
|
|
11647
|
+
/**
|
|
11648
|
+
* The most days {@link canCloseRange} walks between a range's ends, about
|
|
11649
|
+
* eleven years. A span it would have to walk further is refused, so the check
|
|
11650
|
+
* stays cheap enough to run on every pointer move over the calendar.
|
|
11651
|
+
*/
|
|
11652
|
+
const RANGE_WALK_LIMIT = 4000;
|
|
11653
|
+
/**
|
|
11654
|
+
* Whether `end` can close a range that opens on `start`: it falls on `start`'s
|
|
11655
|
+
* day or later, and, unless `allowDisabled`, no day between the two is one
|
|
11656
|
+
* that `shouldDisableDate` or `unselectableDates` disables. Checking that
|
|
11657
|
+
* walks the days between, so with either rule set and `allowDisabled` off,
|
|
11658
|
+
* an `end` more than {@link RANGE_WALK_LIMIT} days after `start` is refused.
|
|
11659
|
+
* The ends' own constraints, `min` and `max` among them, are the caller's to
|
|
11660
|
+
* check. Bounds can't fall between two days inside them, so they are not
|
|
11661
|
+
* walked.
|
|
11662
|
+
*/
|
|
11663
|
+
function canCloseRange(start, end, c, allowDisabled = false) {
|
|
11664
|
+
const first = startOfDay(start);
|
|
11665
|
+
const last = startOfDay(end).getTime();
|
|
11666
|
+
if (last < first.getTime())
|
|
11667
|
+
return false;
|
|
11668
|
+
if (allowDisabled)
|
|
11669
|
+
return true;
|
|
11670
|
+
const blocks = {
|
|
11671
|
+
shouldDisableDate: c.shouldDisableDate,
|
|
11672
|
+
unselectableDates: c.unselectableDates,
|
|
11673
|
+
};
|
|
11674
|
+
if (!blocks.shouldDisableDate && !blocks.unselectableDates?.length) {
|
|
11675
|
+
return true;
|
|
11676
|
+
}
|
|
11677
|
+
for (let d = addDays(first, 1), walked = 1; d.getTime() < last; d = addDays(d, 1), walked++) {
|
|
11678
|
+
if (walked >= RANGE_WALK_LIMIT || isDayUnselectable(d, blocks)) {
|
|
11679
|
+
return false;
|
|
11680
|
+
}
|
|
11681
|
+
}
|
|
11682
|
+
return true;
|
|
11683
|
+
}
|
|
11550
11684
|
function addDays(d, n) {
|
|
11551
11685
|
const r = new Date(d);
|
|
11552
11686
|
r.setDate(r.getDate() + n);
|
|
@@ -12007,11 +12141,34 @@ function nearestEnabled(i, disabled) {
|
|
|
12007
12141
|
}
|
|
12008
12142
|
return i;
|
|
12009
12143
|
}
|
|
12144
|
+
/** The long, spoken form of a date in a range's announcement. */
|
|
12145
|
+
const LONG_DATE = {
|
|
12146
|
+
year: 'numeric',
|
|
12147
|
+
month: 'long',
|
|
12148
|
+
day: 'numeric',
|
|
12149
|
+
};
|
|
12150
|
+
/** A range's pending start: its start, when it has no end. */
|
|
12151
|
+
const pendingStartOf = (range) => range?.[0] && !range[1] ? startOfDay(range[0]) : null;
|
|
12010
12152
|
/**
|
|
12011
|
-
* The
|
|
12012
|
-
*
|
|
12013
|
-
*
|
|
12014
|
-
|
|
12153
|
+
* The day grid's cells in rows of a week. A grid owns rows and rows own
|
|
12154
|
+
* cells, so assistive technology can walk the grid, and the selection it
|
|
12155
|
+
* reports, row by row.
|
|
12156
|
+
*/
|
|
12157
|
+
const inWeeks = (days, className) => {
|
|
12158
|
+
const rows = [];
|
|
12159
|
+
for (let i = 0; i < days.length; i += 7) {
|
|
12160
|
+
rows.push(jsxRuntime.jsx("div", { role: "row", className: className, children: days.slice(i, i + 7) }, i));
|
|
12161
|
+
}
|
|
12162
|
+
return rows;
|
|
12163
|
+
};
|
|
12164
|
+
/** A date's time, or `null` for none, for comparing ranges by value. */
|
|
12165
|
+
const timeOf = (d) => d ? d.getTime() : null;
|
|
12166
|
+
/**
|
|
12167
|
+
* The year list's keyboard, as the selection surface and as navigation:
|
|
12168
|
+
* arrows move a year or a row, Home/End go to the list's ends. Returns the
|
|
12169
|
+
* year to start from and the direction to search past disabled years, or
|
|
12170
|
+
* `null` for a key the list doesn't handle. As navigation no year is
|
|
12171
|
+
* disabled, so only the year is used.
|
|
12015
12172
|
*/
|
|
12016
12173
|
function yearStep(key, focusedYear, years) {
|
|
12017
12174
|
switch (key) {
|
|
@@ -12031,7 +12188,7 @@ function yearStep(key, focusedYear, years) {
|
|
|
12031
12188
|
return null;
|
|
12032
12189
|
}
|
|
12033
12190
|
}
|
|
12034
|
-
const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minProp, max, shouldDisableDate, unselectableDates, firstDayOfWeek = 0, locale, dayNames, monthNames, nearbyMonthDays = true, color, size, className, id, autoFocusCell = false, labels, yearsRange, granularity = 'day', }) => {
|
|
12191
|
+
const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minProp, max, shouldDisableDate, unselectableDates, firstDayOfWeek = 0, locale, dayNames, monthNames, nearbyMonthDays = true, color, size, className, id, autoFocusCell = false, labels, yearsRange, granularity = 'day', range, onRangeSelect, allowDisabledInRange = false, }) => {
|
|
12035
12192
|
const gridRef = React.useRef(null);
|
|
12036
12193
|
const monthGridRef = React.useRef(null);
|
|
12037
12194
|
const yearGridRef = React.useRef(null);
|
|
@@ -12088,6 +12245,80 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12088
12245
|
const i = nearestEnabled(focusedDate.getDate() - 1, days.map(isDateUnselectable));
|
|
12089
12246
|
return days[i];
|
|
12090
12247
|
}, [cells, focusedDate, isDateUnselectable]);
|
|
12248
|
+
// ----- Range mode -----
|
|
12249
|
+
// Range props come only with the day grid.
|
|
12250
|
+
const rangeMode = !!range;
|
|
12251
|
+
const valuePending = pendingStartOf(range);
|
|
12252
|
+
// The start of a range being picked, which nothing outside the calendar
|
|
12253
|
+
// sees until its end is picked too. It starts as the range's own pending
|
|
12254
|
+
// start, and starts there again whenever the range changes from outside.
|
|
12255
|
+
const [anchor, setAnchor] = React.useState(valuePending);
|
|
12256
|
+
const rangeKey = `${timeOf(range?.[0])}|${timeOf(range?.[1])}`;
|
|
12257
|
+
const [anchorRangeKey, setAnchorRangeKey] = React.useState(rangeKey);
|
|
12258
|
+
if (anchorRangeKey !== rangeKey) {
|
|
12259
|
+
setAnchorRangeKey(rangeKey);
|
|
12260
|
+
setAnchor(valuePending);
|
|
12261
|
+
}
|
|
12262
|
+
// The day under the pointer while a start is pending, which the preview
|
|
12263
|
+
// runs to ahead of the focused day.
|
|
12264
|
+
const [hoverDate, setHoverDate] = React.useState(null);
|
|
12265
|
+
// Whether `day` can end a range that starts on `start`. The start is
|
|
12266
|
+
// checked as well as the end: one the range brought, or one picked before
|
|
12267
|
+
// the bounds or the disabled days changed, can rule itself out.
|
|
12268
|
+
const canEndAt = React.useCallback((start, day) => !isDateUnselectable(start) &&
|
|
12269
|
+
!isDateUnselectable(day) &&
|
|
12270
|
+
canCloseRange(start, day, { shouldDisableDate, unselectableDates }, allowDisabledInRange), [
|
|
12271
|
+
isDateUnselectable,
|
|
12272
|
+
shouldDisableDate,
|
|
12273
|
+
unselectableDates,
|
|
12274
|
+
allowDisabledInRange,
|
|
12275
|
+
]);
|
|
12276
|
+
// A pick in range mode: the end of the pending range when it can be one,
|
|
12277
|
+
// and otherwise the start of a new one.
|
|
12278
|
+
const pickRangeDay = React.useCallback((picked) => {
|
|
12279
|
+
const day = startOfDay(picked);
|
|
12280
|
+
// The pointer is on the picked day, which the focused day now is too.
|
|
12281
|
+
setHoverDate(null);
|
|
12282
|
+
if (anchor && canEndAt(anchor, day)) {
|
|
12283
|
+
setAnchor(null);
|
|
12284
|
+
onRangeSelect?.(anchor, day);
|
|
12285
|
+
}
|
|
12286
|
+
else {
|
|
12287
|
+
setAnchor(day);
|
|
12288
|
+
}
|
|
12289
|
+
}, [anchor, canEndAt, onRangeSelect]);
|
|
12290
|
+
// What the grid shows: while a start is pending, the range from it to the
|
|
12291
|
+
// hovered or focused day, when that day can end it; otherwise the
|
|
12292
|
+
// committed range.
|
|
12293
|
+
let shownStart;
|
|
12294
|
+
let shownEnd;
|
|
12295
|
+
if (anchor) {
|
|
12296
|
+
shownStart = anchor;
|
|
12297
|
+
const previewEnd = startOfDay(hoverDate ?? focusedDate);
|
|
12298
|
+
shownEnd = canEndAt(anchor, previewEnd) ? previewEnd : null;
|
|
12299
|
+
}
|
|
12300
|
+
else {
|
|
12301
|
+
shownStart = range?.[0] ? startOfDay(range[0]) : null;
|
|
12302
|
+
shownEnd = range?.[1] ? startOfDay(range[1]) : null;
|
|
12303
|
+
}
|
|
12304
|
+
const previewing = !!anchor;
|
|
12305
|
+
// Ids for the words that name a range's ends and state the whole range.
|
|
12306
|
+
const reactId = React.useId();
|
|
12307
|
+
const rangeIdBase = id ?? `calendar-${reactId}`;
|
|
12308
|
+
const rangeStartId = `${rangeIdBase}-range-start`;
|
|
12309
|
+
const rangeEndId = `${rangeIdBase}-range-end`;
|
|
12310
|
+
const rangePreviewEndId = `${rangeIdBase}-range-preview-end`;
|
|
12311
|
+
const rangeSummaryId = `${rangeIdBase}-range-summary`;
|
|
12312
|
+
// The selection in words: the pending start alone, or the committed ends.
|
|
12313
|
+
const selectedEnd = previewing ? null : shownEnd;
|
|
12314
|
+
const rangeSummary = [
|
|
12315
|
+
shownStart &&
|
|
12316
|
+
`${t.rangeStart}: ${formatDate(shownStart, LONG_DATE, locale)}`,
|
|
12317
|
+
selectedEnd &&
|
|
12318
|
+
`${t.rangeEnd}: ${formatDate(selectedEnd, LONG_DATE, locale)}`,
|
|
12319
|
+
]
|
|
12320
|
+
.filter(Boolean)
|
|
12321
|
+
.join(', ');
|
|
12091
12322
|
// Whether the grid on show holds focus, or held it last before focus went
|
|
12092
12323
|
// nowhere: the cell that had it left the page or became disabled. A blur
|
|
12093
12324
|
// that takes focus out of the grid, to nowhere included, is the user
|
|
@@ -12125,6 +12356,8 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12125
12356
|
}
|
|
12126
12357
|
}, [min, max, isDateUnselectable, onFocusedDateChange]);
|
|
12127
12358
|
const handleKeyDown = React.useCallback((e) => {
|
|
12359
|
+
// A key takes the range preview back from the pointer.
|
|
12360
|
+
setHoverDate(null);
|
|
12128
12361
|
switch (e.key) {
|
|
12129
12362
|
case 'ArrowLeft':
|
|
12130
12363
|
e.preventDefault();
|
|
@@ -12167,11 +12400,35 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12167
12400
|
case 'Enter':
|
|
12168
12401
|
case ' ':
|
|
12169
12402
|
e.preventDefault();
|
|
12170
|
-
if (
|
|
12171
|
-
|
|
12403
|
+
if (isDateUnselectable(focusedDate))
|
|
12404
|
+
break;
|
|
12405
|
+
if (rangeMode)
|
|
12406
|
+
pickRangeDay(focusedDate);
|
|
12407
|
+
else
|
|
12408
|
+
onSelect?.(focusedDate);
|
|
12409
|
+
break;
|
|
12410
|
+
case 'Escape':
|
|
12411
|
+
// A pending pick is taken back, and the key stops there so a
|
|
12412
|
+
// popover around the calendar stays open. The range's own pending
|
|
12413
|
+
// start is no pick, so Escape goes on to close the popover.
|
|
12414
|
+
if (anchor && !(valuePending && isSameDay(anchor, valuePending))) {
|
|
12415
|
+
e.preventDefault();
|
|
12416
|
+
e.stopPropagation();
|
|
12417
|
+
setAnchor(valuePending);
|
|
12418
|
+
}
|
|
12172
12419
|
break;
|
|
12173
12420
|
}
|
|
12174
|
-
}, [
|
|
12421
|
+
}, [
|
|
12422
|
+
focusedDate,
|
|
12423
|
+
firstDayOfWeek,
|
|
12424
|
+
isDateUnselectable,
|
|
12425
|
+
moveFocus,
|
|
12426
|
+
onSelect,
|
|
12427
|
+
rangeMode,
|
|
12428
|
+
pickRangeDay,
|
|
12429
|
+
anchor,
|
|
12430
|
+
valuePending,
|
|
12431
|
+
]);
|
|
12175
12432
|
// The month grid's counterpart to `moveFocus`: step by `direction` months
|
|
12176
12433
|
// until a month with a selectable day turns up, stopping at the min/max
|
|
12177
12434
|
// months. The cap guards against a range with nothing selectable.
|
|
@@ -12194,7 +12451,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12194
12451
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
12195
12452
|
e.preventDefault();
|
|
12196
12453
|
if (!isMonthUnselectable(focusedDate)) {
|
|
12197
|
-
onSelect(startOfMonth(focusedDate));
|
|
12454
|
+
onSelect?.(startOfMonth(focusedDate));
|
|
12198
12455
|
}
|
|
12199
12456
|
return;
|
|
12200
12457
|
}
|
|
@@ -12220,6 +12477,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12220
12477
|
const dayNamesRowClass = usePrefixedClassNames('dateinput-day-names');
|
|
12221
12478
|
const dayNameClass = usePrefixedClassNames('dateinput-day-name');
|
|
12222
12479
|
const gridClass = usePrefixedClassNames('dateinput-grid');
|
|
12480
|
+
const weekClass = usePrefixedClassNames('dateinput-week');
|
|
12223
12481
|
const monthsGridClass = usePrefixedClassNames('dateinput-months-grid');
|
|
12224
12482
|
const monthsRowClass = usePrefixedClassNames('dateinput-months-row');
|
|
12225
12483
|
const yearsGridClass = usePrefixedClassNames('dateinput-years-grid');
|
|
@@ -12272,8 +12530,8 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12272
12530
|
// year is disabled the stop moves to the nearest enabled year in the list,
|
|
12273
12531
|
// and focusing it makes it the focused year. As the selection surface a
|
|
12274
12532
|
// year outside a non-empty list starts from the end it fell past, so the
|
|
12275
|
-
// list keeps a stop. As navigation
|
|
12276
|
-
//
|
|
12533
|
+
// list keeps a stop. As navigation it marks the current year, so a year
|
|
12534
|
+
// outside the list marks no option, and the stop itself follows focus.
|
|
12277
12535
|
const tabStopYear = React.useMemo(() => {
|
|
12278
12536
|
let i = yearList.indexOf(focusedYear);
|
|
12279
12537
|
if (i < 0) {
|
|
@@ -12283,6 +12541,12 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12283
12541
|
}
|
|
12284
12542
|
return yearList[nearestEnabled(i, yearList.map(y => disabledYears.has(y)))];
|
|
12285
12543
|
}, [yearList, focusedYear, disabledYears, isYearGranularity]);
|
|
12544
|
+
// As navigation the keys move focus and leave the focused year alone, so
|
|
12545
|
+
// the tab stop goes to the year that last had focus while it is listed,
|
|
12546
|
+
// and Tab leaves the list rather than landing on the current year. It
|
|
12547
|
+
// starts over each time the list opens.
|
|
12548
|
+
const [navYear, setNavYear] = React.useState(null);
|
|
12549
|
+
const listTabStop = navYear !== null && yearList.includes(navYear) ? navYear : tabStopYear;
|
|
12286
12550
|
// Which of the focused year's months have no selectable day.
|
|
12287
12551
|
const disabledMonths = React.useMemo(() => granularity === 'month'
|
|
12288
12552
|
? Array.from({ length: 12 }, (_, m) => isMonthUnselectable(makeDate(focusedYear, m)))
|
|
@@ -12303,7 +12567,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12303
12567
|
// pick and `onSelect` gets the year's first day.
|
|
12304
12568
|
const handleYearPick = React.useCallback((year) => {
|
|
12305
12569
|
onFocusedDateChange(clampDate(makeDate(year), min, max));
|
|
12306
|
-
onSelect(makeDate(year));
|
|
12570
|
+
onSelect?.(makeDate(year));
|
|
12307
12571
|
}, [min, max, onFocusedDateChange, onSelect]);
|
|
12308
12572
|
// Step through the listed years from `year` by `direction`, skipping
|
|
12309
12573
|
// disabled ones, and focus the first selectable year found.
|
|
@@ -12318,19 +12582,39 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12318
12582
|
}
|
|
12319
12583
|
}, [yearList, disabledYears, onFocusedDateChange, min, max]);
|
|
12320
12584
|
const handleYearListKeyDown = React.useCallback((e) => {
|
|
12321
|
-
if (
|
|
12322
|
-
|
|
12323
|
-
if (
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12585
|
+
if (isYearGranularity) {
|
|
12586
|
+
const step = yearStep(e.key, focusedYear, yearList);
|
|
12587
|
+
if (!step)
|
|
12588
|
+
return;
|
|
12589
|
+
e.preventDefault();
|
|
12590
|
+
moveYearFocus(step[0], step[1]);
|
|
12327
12591
|
return;
|
|
12328
12592
|
}
|
|
12329
|
-
|
|
12593
|
+
// As navigation, Escape returns to the grid the list was opened from.
|
|
12594
|
+
// It stops there, so a popover around the calendar stays open and only
|
|
12595
|
+
// a second Escape closes it.
|
|
12596
|
+
if (e.key === 'Escape') {
|
|
12597
|
+
e.preventDefault();
|
|
12598
|
+
e.stopPropagation();
|
|
12599
|
+
setView(baseView);
|
|
12600
|
+
return;
|
|
12601
|
+
}
|
|
12602
|
+
// The other keys move DOM focus from the year that has it and leave
|
|
12603
|
+
// the focused date alone. The list is centred on that date, so moving
|
|
12604
|
+
// it would reflow the list under the keyboard, and it would turn the
|
|
12605
|
+
// grid behind the list before anything is picked. Enter or Space
|
|
12606
|
+
// clicks the year with focus, which picks it.
|
|
12607
|
+
const options = Array.from(e.currentTarget.querySelectorAll('[role="option"]'));
|
|
12608
|
+
const from = options.indexOf(e.target);
|
|
12609
|
+
if (from < 0)
|
|
12610
|
+
return;
|
|
12611
|
+
const step = yearStep(e.key, yearList[from], yearList);
|
|
12330
12612
|
if (!step)
|
|
12331
12613
|
return;
|
|
12332
12614
|
e.preventDefault();
|
|
12333
|
-
|
|
12615
|
+
// The listed years run one apart, so a year's index is its distance
|
|
12616
|
+
// from the first. A step past either end finds no option.
|
|
12617
|
+
options[step[0] - yearList[0]]?.focus();
|
|
12334
12618
|
}, [isYearGranularity, baseView, focusedYear, yearList, moveYearFocus]);
|
|
12335
12619
|
// The day or month grid takes focus as the popover opens on it, or comes
|
|
12336
12620
|
// back to it from the year list.
|
|
@@ -12401,32 +12685,72 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12401
12685
|
list.querySelector('[data-focused-year="true"]')?.focus();
|
|
12402
12686
|
}, [isYearGranularity, focusedYear]);
|
|
12403
12687
|
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" }) }));
|
|
12404
|
-
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: () =>
|
|
12688
|
+
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: () => {
|
|
12689
|
+
setNavYear(null);
|
|
12690
|
+
setView(v => (v === 'years' ? baseView : 'years'));
|
|
12691
|
+
}, 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, "aria-multiselectable": rangeMode || undefined, "aria-describedby": rangeMode ? rangeSummaryId : undefined, className: gridClass, onKeyDown: handleKeyDown, onMouseLeave: anchor ? () => setHoverDate(null) : undefined, ...gridFocusHandlers, children: inWeeks(cells.map(cell => {
|
|
12405
12692
|
const disabled = isDateUnselectable(cell.date);
|
|
12406
|
-
const
|
|
12693
|
+
const time = cell.date.getTime();
|
|
12694
|
+
// Range mode. The range on show has its ends filled. Every day
|
|
12695
|
+
// of a committed range is selected, and of a preview only the
|
|
12696
|
+
// start.
|
|
12697
|
+
const isStart = time === timeOf(shownStart);
|
|
12698
|
+
const isEnd = time === timeOf(shownEnd);
|
|
12699
|
+
const inRange = !!shownStart &&
|
|
12700
|
+
!!shownEnd &&
|
|
12701
|
+
time > shownStart.getTime() &&
|
|
12702
|
+
time < shownEnd.getTime();
|
|
12703
|
+
// The day a pending range would end on, which isn't picked
|
|
12704
|
+
// yet, so its description says what picking it does.
|
|
12705
|
+
const isPreviewEnd = previewing && isEnd && !isStart;
|
|
12706
|
+
const isPreview = (previewing && inRange) || isPreviewEnd;
|
|
12707
|
+
const isFilled = rangeMode
|
|
12708
|
+
? isStart || (isEnd && !previewing)
|
|
12709
|
+
: !!value && isSameDay(value, cell.date);
|
|
12710
|
+
const isSelected = isFilled || (rangeMode && !previewing && inRange);
|
|
12711
|
+
const describedBy = rangeMode
|
|
12712
|
+
? [
|
|
12713
|
+
isStart && rangeStartId,
|
|
12714
|
+
isEnd && !previewing && rangeEndId,
|
|
12715
|
+
isPreviewEnd && rangePreviewEndId,
|
|
12716
|
+
]
|
|
12717
|
+
.filter(Boolean)
|
|
12718
|
+
.join(' ') || undefined
|
|
12719
|
+
: undefined;
|
|
12407
12720
|
const isFocused = isSameDay(cell.date, tabStopDay);
|
|
12408
12721
|
const otherMonth = !cell.inCurrentMonth;
|
|
12409
12722
|
const cellClass = prefixedClassNames(classPrefix, 'dateinput-cell', {
|
|
12410
|
-
'is-selected':
|
|
12723
|
+
'is-selected': isFilled,
|
|
12724
|
+
'is-range-start': isStart,
|
|
12725
|
+
'is-range-end': isEnd,
|
|
12726
|
+
'is-in-range': inRange,
|
|
12727
|
+
'is-preview': isPreview,
|
|
12411
12728
|
'is-today': cell.isToday,
|
|
12412
12729
|
'is-disabled': disabled,
|
|
12413
12730
|
'is-other-month': otherMonth,
|
|
12414
12731
|
});
|
|
12415
12732
|
const display = !otherMonth || nearbyMonthDays;
|
|
12416
|
-
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: () => {
|
|
12733
|
+
return (jsxRuntime.jsx("button", { type: "button", role: "gridcell", tabIndex: isFocused ? 0 : -1, "aria-selected": isSelected, "aria-disabled": disabled, "aria-current": cell.isToday ? 'date' : undefined, "aria-describedby": describedBy, "data-focused": isFocused ? 'true' : undefined, disabled: disabled || !display, className: cellClass, onFocus: () => {
|
|
12417
12734
|
// Reached by Tab while the focused day is disabled, or
|
|
12418
12735
|
// by pointer: keys move on from here. A nearby month's
|
|
12419
12736
|
// day waits for its click, which turns the grid.
|
|
12420
12737
|
if (!otherMonth && !isSameDay(cell.date, focusedDate)) {
|
|
12421
12738
|
onFocusedDateChange(cell.date);
|
|
12422
12739
|
}
|
|
12423
|
-
},
|
|
12740
|
+
},
|
|
12741
|
+
// Mouseover rather than mouseenter: browsers send it to a
|
|
12742
|
+
// disabled day as well, where React holds mouseenter back,
|
|
12743
|
+
// so the preview leaves the last enabled day behind.
|
|
12744
|
+
onMouseOver: anchor ? () => setHoverDate(cell.date) : undefined, onClick: () => {
|
|
12424
12745
|
if (disabled)
|
|
12425
12746
|
return;
|
|
12426
12747
|
onFocusedDateChange(cell.date);
|
|
12427
|
-
|
|
12748
|
+
if (rangeMode)
|
|
12749
|
+
pickRangeDay(cell.date);
|
|
12750
|
+
else
|
|
12751
|
+
onSelect?.(cell.date);
|
|
12428
12752
|
}, style: !display ? { visibility: 'hidden' } : undefined, children: cell.date.getDate() }, cell.date.toISOString()));
|
|
12429
|
-
}) })] })), 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 => {
|
|
12753
|
+
}), weekClass) })] })), 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 => {
|
|
12430
12754
|
const date = makeDate(focusedYear, month);
|
|
12431
12755
|
const disabled = disabledMonths[month];
|
|
12432
12756
|
const isSelected = !!value && isSameMonth(value, date);
|
|
@@ -12445,7 +12769,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12445
12769
|
}
|
|
12446
12770
|
}, onClick: () => {
|
|
12447
12771
|
onFocusedDateChange(clampDate(date, min, max));
|
|
12448
|
-
onSelect(date);
|
|
12772
|
+
onSelect?.(date);
|
|
12449
12773
|
}, children: monthCellNames[month] }, month));
|
|
12450
12774
|
}) }, 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 => {
|
|
12451
12775
|
const isFocused = year === tabStopYear;
|
|
@@ -12461,16 +12785,22 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
|
|
|
12461
12785
|
'is-today': isToday,
|
|
12462
12786
|
'is-disabled': disabled,
|
|
12463
12787
|
});
|
|
12464
|
-
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:
|
|
12465
|
-
//
|
|
12466
|
-
//
|
|
12467
|
-
|
|
12788
|
+
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: year === listTabStop ? 0 : -1, disabled: disabled, className: cellCls, onFocus: () => {
|
|
12789
|
+
// As navigation, focusing a year by key or pointer only
|
|
12790
|
+
// moves the tab stop: nothing is committed until a year is
|
|
12791
|
+
// picked. As the selection surface it is reached by Tab
|
|
12792
|
+
// while the focused year is disabled, or by pointer, and
|
|
12793
|
+
// keys move on from here.
|
|
12794
|
+
if (!isYearGranularity) {
|
|
12795
|
+
setNavYear(year);
|
|
12796
|
+
}
|
|
12797
|
+
else if (year !== focusedYear) {
|
|
12468
12798
|
onFocusedDateChange(clampDate(makeDate(year), min, max));
|
|
12469
12799
|
}
|
|
12470
12800
|
}, onClick: () => isYearGranularity
|
|
12471
12801
|
? handleYearPick(year)
|
|
12472
12802
|
: handleYearSelect(year), children: year }, year));
|
|
12473
|
-
}) }))] }));
|
|
12803
|
+
}) })), rangeMode && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { id: rangeStartId, hidden: true, children: t.rangeStart }), jsxRuntime.jsx("span", { id: rangeEndId, hidden: true, children: t.rangeEnd }), jsxRuntime.jsx("span", { id: rangePreviewEndId, hidden: true, children: t.rangePreviewEnd }), jsxRuntime.jsx("div", { id: rangeSummaryId, "aria-live": "polite", style: visuallyHidden, children: rangeSummary })] }))] }));
|
|
12474
12804
|
};
|
|
12475
12805
|
Calendar.displayName = 'Calendar';
|
|
12476
12806
|
|
|
@@ -13610,7 +13940,8 @@ DateInputBase.displayName = 'DateInputBase';
|
|
|
13610
13940
|
* shouldDisableDate={d => d.getDay() === 0 || d.getDay() === 6}
|
|
13611
13941
|
* />
|
|
13612
13942
|
*/
|
|
13613
|
-
const DateInput = React.forwardRef((
|
|
13943
|
+
const DateInput = React.forwardRef((props, ref) => {
|
|
13944
|
+
const { label, labelSize, labelProps, horizontal, iconLeft, iconRight, iconLeftName = 'calendar', iconRightName, iconLeftSize, iconRightSize, hasIconsLeft, hasIconsRight, isLoading, isExpanded, controlSize, message, messageColor, fieldClassName, controlClassName, ...baseProps } = props;
|
|
13614
13945
|
const insideField = useInsideField();
|
|
13615
13946
|
const insideControl = useInsideControl();
|
|
13616
13947
|
const ownField = rendersOwnField('DateInput', {
|
|
@@ -13621,6 +13952,27 @@ const DateInput = React.forwardRef(({ label, labelSize, labelProps, horizontal,
|
|
|
13621
13952
|
horizontal,
|
|
13622
13953
|
fieldClassName,
|
|
13623
13954
|
});
|
|
13955
|
+
// Inline mode renders a bare calendar with no input, so the Control's
|
|
13956
|
+
// icon-left container has nothing to anchor to, and it renders no
|
|
13957
|
+
// Control inside one or out. The left icon is checked as the caller
|
|
13958
|
+
// passed it: the default glyph is this component's own choice, not a
|
|
13959
|
+
// prop the caller set.
|
|
13960
|
+
const ownControl = rendersOwnControl('DateInput', {
|
|
13961
|
+
insideControl,
|
|
13962
|
+
inline: baseProps.inline,
|
|
13963
|
+
isLoading,
|
|
13964
|
+
iconLeft,
|
|
13965
|
+
iconLeftName: props.iconLeftName,
|
|
13966
|
+
iconLeftSize,
|
|
13967
|
+
iconRight,
|
|
13968
|
+
iconRightName,
|
|
13969
|
+
iconRightSize,
|
|
13970
|
+
hasIconsLeft,
|
|
13971
|
+
hasIconsRight,
|
|
13972
|
+
isExpanded,
|
|
13973
|
+
controlSize,
|
|
13974
|
+
controlClassName,
|
|
13975
|
+
});
|
|
13624
13976
|
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
13625
13977
|
label,
|
|
13626
13978
|
id: baseProps.id,
|
|
@@ -13634,9 +13986,7 @@ const DateInput = React.forwardRef(({ label, labelSize, labelProps, horizontal,
|
|
|
13634
13986
|
// The base hides its launcher while the Control it sits in is loading,
|
|
13635
13987
|
// whether that is the one rendered below or an enclosing one.
|
|
13636
13988
|
let content = (jsxRuntime.jsx(DateInputBase, { ref: ref, id: controlId, ...baseProps }));
|
|
13637
|
-
|
|
13638
|
-
// icon-left container has nothing to anchor to. Skip the Control wrap.
|
|
13639
|
-
if (!insideControl && !baseProps.inline) {
|
|
13989
|
+
if (ownControl) {
|
|
13640
13990
|
content = (jsxRuntime.jsx(Control, { iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft || !!iconLeftName, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
13641
13991
|
}
|
|
13642
13992
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|
|
@@ -14718,7 +15068,8 @@ TimeInputBase.displayName = 'TimeInputBase';
|
|
|
14718
15068
|
* @example
|
|
14719
15069
|
* <TimeInput label="Slot" hourFormat="12" incrementMinutes={15} />
|
|
14720
15070
|
*/
|
|
14721
|
-
const TimeInput = React.forwardRef((
|
|
15071
|
+
const TimeInput = React.forwardRef((props, ref) => {
|
|
15072
|
+
const { label, labelSize, labelProps, horizontal, iconLeft, iconRight, iconLeftName = 'clock', iconRightName, iconLeftSize, iconRightSize, hasIconsLeft, hasIconsRight, isLoading, isExpanded, controlSize, message, messageColor, fieldClassName, controlClassName, ...baseProps } = props;
|
|
14722
15073
|
const insideField = useInsideField();
|
|
14723
15074
|
const insideControl = useInsideControl();
|
|
14724
15075
|
const ownField = rendersOwnField('TimeInput', {
|
|
@@ -14729,6 +15080,27 @@ const TimeInput = React.forwardRef(({ label, labelSize, labelProps, horizontal,
|
|
|
14729
15080
|
horizontal,
|
|
14730
15081
|
fieldClassName,
|
|
14731
15082
|
});
|
|
15083
|
+
// Inline mode renders a bare panel with no input, so the Control's
|
|
15084
|
+
// icon-left container has nothing to anchor to, and it renders no
|
|
15085
|
+
// Control inside one or out. The left icon is checked as the caller
|
|
15086
|
+
// passed it: the default glyph is this component's own choice, not a
|
|
15087
|
+
// prop the caller set.
|
|
15088
|
+
const ownControl = rendersOwnControl('TimeInput', {
|
|
15089
|
+
insideControl,
|
|
15090
|
+
inline: baseProps.inline,
|
|
15091
|
+
isLoading,
|
|
15092
|
+
iconLeft,
|
|
15093
|
+
iconLeftName: props.iconLeftName,
|
|
15094
|
+
iconLeftSize,
|
|
15095
|
+
iconRight,
|
|
15096
|
+
iconRightName,
|
|
15097
|
+
iconRightSize,
|
|
15098
|
+
hasIconsLeft,
|
|
15099
|
+
hasIconsRight,
|
|
15100
|
+
isExpanded,
|
|
15101
|
+
controlSize,
|
|
15102
|
+
controlClassName,
|
|
15103
|
+
});
|
|
14732
15104
|
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
14733
15105
|
label,
|
|
14734
15106
|
id: baseProps.id,
|
|
@@ -14742,9 +15114,7 @@ const TimeInput = React.forwardRef(({ label, labelSize, labelProps, horizontal,
|
|
|
14742
15114
|
// The base hides its launcher while the Control it sits in is loading,
|
|
14743
15115
|
// whether that is the one rendered below or an enclosing one.
|
|
14744
15116
|
let content = (jsxRuntime.jsx(TimeInputBase, { ref: ref, id: controlId, ...baseProps }));
|
|
14745
|
-
|
|
14746
|
-
// icon-left container has nothing to anchor to. Skip the Control wrap.
|
|
14747
|
-
if (!insideControl && !baseProps.inline) {
|
|
15117
|
+
if (ownControl) {
|
|
14748
15118
|
content = (jsxRuntime.jsx(Control, { iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft || !!iconLeftName, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
14749
15119
|
}
|
|
14750
15120
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|
|
@@ -15039,7 +15409,8 @@ DateTimeInputBase.displayName = 'DateTimeInputBase';
|
|
|
15039
15409
|
* shouldDisableDate={d => d.getDay() === 0 || d.getDay() === 6}
|
|
15040
15410
|
* />
|
|
15041
15411
|
*/
|
|
15042
|
-
const DateTimeInput = React.forwardRef((
|
|
15412
|
+
const DateTimeInput = React.forwardRef((props, ref) => {
|
|
15413
|
+
const { label, labelSize, labelProps, horizontal, iconLeft, iconRight, iconLeftName = 'calendar-alt', iconRightName, iconLeftSize, iconRightSize, hasIconsLeft, hasIconsRight, isLoading, isExpanded, controlSize, message, messageColor, fieldClassName, controlClassName, ...baseProps } = props;
|
|
15043
15414
|
const insideField = useInsideField();
|
|
15044
15415
|
const insideControl = useInsideControl();
|
|
15045
15416
|
const ownField = rendersOwnField('DateTimeInput', {
|
|
@@ -15050,6 +15421,27 @@ const DateTimeInput = React.forwardRef(({ label, labelSize, labelProps, horizont
|
|
|
15050
15421
|
horizontal,
|
|
15051
15422
|
fieldClassName,
|
|
15052
15423
|
});
|
|
15424
|
+
// Inline mode renders a bare picker with no input, so the Control's
|
|
15425
|
+
// icon-left container has nothing to anchor to, and it renders no
|
|
15426
|
+
// Control inside one or out. The left icon is checked as the caller
|
|
15427
|
+
// passed it: the default glyph is this component's own choice, not a
|
|
15428
|
+
// prop the caller set.
|
|
15429
|
+
const ownControl = rendersOwnControl('DateTimeInput', {
|
|
15430
|
+
insideControl,
|
|
15431
|
+
inline: baseProps.inline,
|
|
15432
|
+
isLoading,
|
|
15433
|
+
iconLeft,
|
|
15434
|
+
iconLeftName: props.iconLeftName,
|
|
15435
|
+
iconLeftSize,
|
|
15436
|
+
iconRight,
|
|
15437
|
+
iconRightName,
|
|
15438
|
+
iconRightSize,
|
|
15439
|
+
hasIconsLeft,
|
|
15440
|
+
hasIconsRight,
|
|
15441
|
+
isExpanded,
|
|
15442
|
+
controlSize,
|
|
15443
|
+
controlClassName,
|
|
15444
|
+
});
|
|
15053
15445
|
const { controlId, fieldLabelProps } = useAutoLabelId({
|
|
15054
15446
|
label,
|
|
15055
15447
|
id: baseProps.id,
|
|
@@ -15063,9 +15455,7 @@ const DateTimeInput = React.forwardRef(({ label, labelSize, labelProps, horizont
|
|
|
15063
15455
|
// The base hides its launcher while the Control it sits in is loading,
|
|
15064
15456
|
// whether that is the one rendered below or an enclosing one.
|
|
15065
15457
|
let content = (jsxRuntime.jsx(DateTimeInputBase, { ref: ref, id: controlId, ...baseProps }));
|
|
15066
|
-
|
|
15067
|
-
// icon-left container has nothing to anchor to. Skip the Control wrap.
|
|
15068
|
-
if (!insideControl && !baseProps.inline) {
|
|
15458
|
+
if (ownControl) {
|
|
15069
15459
|
content = (jsxRuntime.jsx(Control, { iconLeft: iconLeft, iconRight: iconRight, iconLeftName: iconLeftName, iconRightName: iconRightName, iconLeftSize: iconLeftSize, iconRightSize: iconRightSize, hasIconsLeft: hasIconsLeft || !!iconLeftName, hasIconsRight: hasIconsRight, isLoading: isLoading, isExpanded: isExpanded, size: controlSize, className: controlClassName, children: content }));
|
|
15070
15460
|
}
|
|
15071
15461
|
const messageEl = message ? jsxRuntime.jsx("p", { className: helpClass, children: message }) : null;
|