@allxsmith/bestax-bulma 5.34.3 → 5.35.1
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/esm/components/Loading.js +19 -6
- package/dist/esm/components/Loading.js.map +1 -1
- package/dist/esm/components/Menu.js +5 -1
- package/dist/esm/components/Menu.js.map +1 -1
- package/dist/esm/components/Modal.js +22 -31
- package/dist/esm/components/Modal.js.map +1 -1
- package/dist/esm/components/Sidebar.js +19 -6
- package/dist/esm/components/Sidebar.js.map +1 -1
- package/dist/esm/components/Steps.js +14 -13
- package/dist/esm/components/Steps.js.map +1 -1
- package/dist/esm/components/Toast.js +18 -7
- package/dist/esm/components/Toast.js.map +1 -1
- package/dist/esm/components/Tooltip.js +3 -3
- package/dist/esm/components/Tooltip.js.map +1 -1
- package/dist/esm/elements/Icon.js +2 -3
- package/dist/esm/elements/Icon.js.map +1 -1
- package/dist/esm/elements/Loader.js +2 -1
- package/dist/esm/elements/Loader.js.map +1 -1
- package/dist/esm/form/Checkbox.js +37 -4
- package/dist/esm/form/Checkbox.js.map +1 -1
- package/dist/esm/form/Slider.js +5 -4
- package/dist/esm/form/Slider.js.map +1 -1
- package/dist/esm/form/Taginput.js +21 -9
- package/dist/esm/form/Taginput.js.map +1 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js +9 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js +5 -4
- package/dist/esm/form/_pickerInternals/useSegmentedEntry.js.map +1 -1
- package/dist/esm/helpers/overlayStack.js +49 -0
- package/dist/esm/helpers/overlayStack.js.map +1 -0
- package/dist/esm/helpers/useLatestRef.js +33 -0
- package/dist/esm/helpers/useLatestRef.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +242 -95
- package/dist/index.cjs.map +1 -1
- package/dist/types/components/Loading.d.ts +13 -3
- package/dist/types/components/Menu.d.ts +14 -1
- package/dist/types/components/Modal.d.ts +7 -4
- package/dist/types/components/Sidebar.d.ts +8 -3
- package/dist/types/components/Steps.d.ts +27 -5
- package/dist/types/components/Toast.d.ts +5 -2
- package/dist/types/elements/Icon.d.ts +6 -1
- package/dist/types/elements/Loader.d.ts +2 -1
- package/dist/types/form/Checkbox.d.ts +11 -0
- package/dist/types/form/Taginput.d.ts +5 -1
- package/dist/types/helpers/overlayStack.d.ts +15 -0
- package/dist/types/helpers/useLatestRef.d.ts +21 -0
- package/dist/types-cjs/components/Loading.d.ts +13 -3
- package/dist/types-cjs/components/Menu.d.ts +14 -1
- package/dist/types-cjs/components/Modal.d.ts +7 -4
- package/dist/types-cjs/components/Sidebar.d.ts +8 -3
- package/dist/types-cjs/components/Steps.d.ts +27 -5
- package/dist/types-cjs/components/Toast.d.ts +5 -2
- package/dist/types-cjs/elements/Icon.d.ts +6 -1
- package/dist/types-cjs/elements/Loader.d.ts +2 -1
- package/dist/types-cjs/form/Checkbox.d.ts +11 -0
- package/dist/types-cjs/form/Taginput.d.ts +5 -1
- package/dist/types-cjs/helpers/overlayStack.d.ts +15 -0
- package/dist/types-cjs/helpers/useLatestRef.d.ts +21 -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/components/_steps.scss +96 -38
- package/src/scss/elements/_loader.scss +20 -0
- package/src/scss/form/_checkbox.scss +28 -1
package/dist/index.cjs
CHANGED
|
@@ -3199,8 +3199,7 @@ function getIconClasses(library, name, variant, features) {
|
|
|
3199
3199
|
*/
|
|
3200
3200
|
const Icon = ({ className, textColor, bgColor, name, library, variant, features, libraryFeatures, // Deprecated but maintained for backward compatibility
|
|
3201
3201
|
size, ariaLabel, style, icon, // Capture and exclude the deprecated 'icon' prop from DOM
|
|
3202
|
-
color
|
|
3203
|
-
containerClassName, children, ...restProps }) => {
|
|
3202
|
+
color, containerClassName, children, ...restProps }) => {
|
|
3204
3203
|
// Get the default icon library from context, fallback to 'fa' if not set
|
|
3205
3204
|
const defaultLibrary = useIconLibrary();
|
|
3206
3205
|
/**
|
|
@@ -3208,7 +3207,7 @@ containerClassName, children, ...restProps }) => {
|
|
|
3208
3207
|
* Note: variant, features, and libraryFeatures are excluded from props spread
|
|
3209
3208
|
*/
|
|
3210
3209
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
3211
|
-
color: textColor,
|
|
3210
|
+
color: textColor ?? color,
|
|
3212
3211
|
backgroundColor: bgColor,
|
|
3213
3212
|
...restProps,
|
|
3214
3213
|
});
|
|
@@ -3581,6 +3580,10 @@ const STRIP_FROM_NON_LINK = { href: true };
|
|
|
3581
3580
|
/**
|
|
3582
3581
|
* MenuItem supports `as` prop for custom link components, e.g., react-router-dom Link.
|
|
3583
3582
|
*
|
|
3583
|
+
* Give each item an `href`, a router link through `as`, or `as="button"`, so
|
|
3584
|
+
* the keyboard can reach it: an `<a>` with no `href` isn't a link, and Tab
|
|
3585
|
+
* skips it.
|
|
3586
|
+
*
|
|
3584
3587
|
* @function
|
|
3585
3588
|
* @param {MenuItemProps} props - Props for the MenuItem component.
|
|
3586
3589
|
* @param {React.Ref} ref - Forwarded ref to the inner element `as` renders.
|
|
@@ -3623,7 +3626,7 @@ const MenuItem = React.forwardRef(function MenuItem(itemProps, ref) {
|
|
|
3623
3626
|
labelChildren.push(child);
|
|
3624
3627
|
}
|
|
3625
3628
|
});
|
|
3626
|
-
return (jsxRuntime.jsxs("li", { className: className, "data-testid": testId, style: style, id: id, title: title, role: role, tabIndex: tabIndex, children: [jsxRuntime.jsx(Component, { ref: ref, className: itemClass || undefined, ...linkProps, ...(Component === 'button'
|
|
3629
|
+
return (jsxRuntime.jsxs("li", { className: className, "data-testid": testId, style: style, id: id, title: title, role: role, tabIndex: tabIndex, children: [jsxRuntime.jsx(Component, { ref: ref, className: itemClass || undefined, "aria-current": active ? 'page' : undefined, ...linkProps, ...(Component === 'button'
|
|
3627
3630
|
? { type: buttonType(linkProps.type) }
|
|
3628
3631
|
: {}), children: labelChildren }), nestedMenuLists] }));
|
|
3629
3632
|
});
|
|
@@ -3834,12 +3837,85 @@ function Portal({ children, container, disabled = false }) {
|
|
|
3834
3837
|
return reactDom.createPortal(children, resolveContainer(container));
|
|
3835
3838
|
}
|
|
3836
3839
|
|
|
3840
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
3841
|
+
/**
|
|
3842
|
+
* `useLayoutEffect` in the browser, `useEffect` on the server. A server
|
|
3843
|
+
* render runs neither, and React 18 warns about a layout effect there, so the
|
|
3844
|
+
* server is handed the plain one.
|
|
3845
|
+
*/
|
|
3846
|
+
const useClientLayoutEffect = typeof document === 'undefined' ? React.useEffect : React.useLayoutEffect;
|
|
3847
|
+
|
|
3848
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
3849
|
+
/**
|
|
3850
|
+
* A ref holding the value from the latest commit, for a listener that is
|
|
3851
|
+
* added once and calls whatever callback it was given most recently.
|
|
3852
|
+
*
|
|
3853
|
+
* A listener whose effect lists a callback prop is taken off and added again
|
|
3854
|
+
* each time that callback changes, and an inline one changes on every render
|
|
3855
|
+
* of its parent. React can render between two listeners for the same
|
|
3856
|
+
* keypress, and the browser runs neither a listener taken off mid-event nor
|
|
3857
|
+
* one added mid-event, so that keypress never reaches it (#1088). Reading the
|
|
3858
|
+
* callback from here keeps the listener where it is.
|
|
3859
|
+
*
|
|
3860
|
+
* It's updated before paint on each commit, so a listener reads the props of
|
|
3861
|
+
* the render that's on screen.
|
|
3862
|
+
*
|
|
3863
|
+
* @function useLatestRef
|
|
3864
|
+
* @param value - The value to keep current, usually a callback prop.
|
|
3865
|
+
* @returns A ref whose `current` is `value` as of the latest commit.
|
|
3866
|
+
*/
|
|
3867
|
+
function useLatestRef(value) {
|
|
3868
|
+
const ref = React.useRef(value);
|
|
3869
|
+
useClientLayoutEffect(() => {
|
|
3870
|
+
ref.current = value;
|
|
3871
|
+
});
|
|
3872
|
+
return ref;
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
3837
3876
|
/**
|
|
3838
|
-
*
|
|
3839
|
-
*
|
|
3840
|
-
*
|
|
3877
|
+
* The open overlays that take part, in the order they opened. Only the last
|
|
3878
|
+
* one answers Escape, so one keypress closes one layer. It's touched in
|
|
3879
|
+
* effects and key handlers, never while rendering, so a server render leaves
|
|
3880
|
+
* it alone.
|
|
3841
3881
|
*/
|
|
3842
|
-
const
|
|
3882
|
+
const openOverlays = [];
|
|
3883
|
+
/**
|
|
3884
|
+
* Holds this overlay's place in the stack of open overlays while `inStack`
|
|
3885
|
+
* is true, from the effect after it opens until the one after it closes.
|
|
3886
|
+
* Opening again puts it back on top.
|
|
3887
|
+
*
|
|
3888
|
+
* Returns a check for whether nothing in the stack sits above this overlay,
|
|
3889
|
+
* to call when a key arrives. For an overlay in the stack, that's whether it
|
|
3890
|
+
* is the topmost one. For one that isn't, such as a Toast, it's whether no
|
|
3891
|
+
* overlay is open at all.
|
|
3892
|
+
*
|
|
3893
|
+
* @function useOverlayStack
|
|
3894
|
+
* @param inStack - Whether this overlay is open and takes part.
|
|
3895
|
+
* @returns A check for whether nothing in the stack is above this overlay.
|
|
3896
|
+
*/
|
|
3897
|
+
function useOverlayStack(inStack) {
|
|
3898
|
+
const tokenRef = React.useRef({});
|
|
3899
|
+
React.useEffect(() => {
|
|
3900
|
+
if (!inStack)
|
|
3901
|
+
return undefined;
|
|
3902
|
+
const token = tokenRef.current;
|
|
3903
|
+
openOverlays.push(token);
|
|
3904
|
+
// Takes out this effect's own push. Should it ever be gone already, the
|
|
3905
|
+
// stack is left as it is: splicing at -1 would take the topmost overlay
|
|
3906
|
+
// instead.
|
|
3907
|
+
return () => {
|
|
3908
|
+
const index = openOverlays.indexOf(token);
|
|
3909
|
+
if (index !== -1)
|
|
3910
|
+
openOverlays.splice(index, 1);
|
|
3911
|
+
};
|
|
3912
|
+
}, [inStack]);
|
|
3913
|
+
return React.useCallback(() => {
|
|
3914
|
+
const top = openOverlays[openOverlays.length - 1];
|
|
3915
|
+
return top === undefined || top === tokenRef.current;
|
|
3916
|
+
}, []);
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3843
3919
|
/**
|
|
3844
3920
|
* Controls the modal can hand focus to. Disabled and hidden controls are
|
|
3845
3921
|
* excluded because `focus()` on them is a no-op, which would leave focus
|
|
@@ -3998,8 +4074,8 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
3998
4074
|
// Cleanup returned by a consumer's callback ref on attach, held until detach.
|
|
3999
4075
|
const consumerCleanupRef = React.useRef(null);
|
|
4000
4076
|
// The forwarded ref and the internal one must both see the node: focus
|
|
4001
|
-
// management
|
|
4002
|
-
//
|
|
4077
|
+
// management and the Tab trap read `modalRootRef`, while consumers expect
|
|
4078
|
+
// their own ref to resolve.
|
|
4003
4079
|
const combinedRef = React.useCallback((node) => {
|
|
4004
4080
|
modalRootRef.current =
|
|
4005
4081
|
node;
|
|
@@ -4030,7 +4106,6 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
4030
4106
|
}
|
|
4031
4107
|
}, [ref]);
|
|
4032
4108
|
const previouslyFocusedRef = React.useRef(null);
|
|
4033
|
-
const stackTokenRef = React.useRef({});
|
|
4034
4109
|
const generatedTitleId = React.useId();
|
|
4035
4110
|
// A portal has no server-rendered counterpart, so the server render and the
|
|
4036
4111
|
// hydrating render have to stay inline and only move into the portal once
|
|
@@ -4048,35 +4123,31 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
4048
4123
|
// to it moves nothing, so it mustn't re-run the effects either.
|
|
4049
4124
|
const defaultTarget = isPortaled && portal === true ? portalContainer : undefined;
|
|
4050
4125
|
useScrollLock(isModalActive && lockScroll);
|
|
4051
|
-
//
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
};
|
|
4062
|
-
}, [isModalActive]);
|
|
4063
|
-
// Escape closes; Tab cycles inside the modal. Both only for the topmost one.
|
|
4064
|
-
// The listener is on `window`, where a keydown arrives last, so everything
|
|
4065
|
-
// inside the modal has had the key first: React's handlers, and `document`
|
|
4066
|
-
// listeners added after this one, such as a date picker's.
|
|
4126
|
+
// Read when Escape arrives, so the key listener stays put while `onClose`
|
|
4127
|
+
// changes identity, as an inline one does on every render of the parent.
|
|
4128
|
+
const onCloseRef = useLatestRef(onClose);
|
|
4129
|
+
// Joins the stack of open overlays, so the keyboard handling below only
|
|
4130
|
+
// applies while this modal is the topmost overlay.
|
|
4131
|
+
const isTopmost = useOverlayStack(isModalActive);
|
|
4132
|
+
// Escape closes; Tab cycles inside the modal. Both only for the topmost
|
|
4133
|
+
// overlay. The listener is on `window`, where a keydown arrives last, so
|
|
4134
|
+
// everything inside the modal has had the key first: React's handlers, and
|
|
4135
|
+
// `document` listeners added after this one, such as a date picker's.
|
|
4067
4136
|
React.useEffect(() => {
|
|
4068
4137
|
if (!isModalActive)
|
|
4069
4138
|
return undefined;
|
|
4070
4139
|
const handleKeyDown = (e) => {
|
|
4071
4140
|
const node = modalRootRef.current;
|
|
4072
|
-
if (
|
|
4141
|
+
if (!isTopmost())
|
|
4073
4142
|
return;
|
|
4074
|
-
}
|
|
4075
4143
|
if (e.key === 'Escape') {
|
|
4076
4144
|
// Something inside already used this Escape, such as a popup that
|
|
4077
|
-
// closed on it, so the modal waits for the next one.
|
|
4078
|
-
|
|
4079
|
-
|
|
4145
|
+
// closed on it, so the modal waits for the next one. One the modal
|
|
4146
|
+
// closes on is marked used, so nothing under it acts on it as well.
|
|
4147
|
+
if (closeOnEscape && onCloseRef.current && !e.defaultPrevented) {
|
|
4148
|
+
e.preventDefault();
|
|
4149
|
+
onCloseRef.current();
|
|
4150
|
+
}
|
|
4080
4151
|
return;
|
|
4081
4152
|
}
|
|
4082
4153
|
// A trap inside the modal, such as a portaled popup's, already moved
|
|
@@ -4108,7 +4179,7 @@ const ModalRoot = React.forwardRef(function ModalRoot({ active, isActive, onClos
|
|
|
4108
4179
|
};
|
|
4109
4180
|
window.addEventListener('keydown', handleKeyDown);
|
|
4110
4181
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
4111
|
-
}, [isModalActive, closeOnEscape,
|
|
4182
|
+
}, [isModalActive, closeOnEscape, onCloseRef, isTopmost]);
|
|
4112
4183
|
// Move focus into the modal on open, restore it on close
|
|
4113
4184
|
React.useEffect(() => {
|
|
4114
4185
|
if (!isModalActive)
|
|
@@ -5105,14 +5176,6 @@ const Tabs = withSubComponents(TabsComponent, {
|
|
|
5105
5176
|
Content: TabsContent,
|
|
5106
5177
|
}, 'Tabs');
|
|
5107
5178
|
|
|
5108
|
-
// INTERNAL: deliberately not exported from src/index.ts.
|
|
5109
|
-
/**
|
|
5110
|
-
* `useLayoutEffect` in the browser, `useEffect` on the server. A server
|
|
5111
|
-
* render runs neither, and React 18 warns about a layout effect there, so the
|
|
5112
|
-
* server is handed the plain one.
|
|
5113
|
-
*/
|
|
5114
|
-
const useClientLayoutEffect = typeof document === 'undefined' ? React.useEffect : React.useLayoutEffect;
|
|
5115
|
-
|
|
5116
5179
|
// Not exported, so it stays out of the public API that `index.ts` re-exports.
|
|
5117
5180
|
const SegmentedControlContext = React.createContext(null);
|
|
5118
5181
|
/** Sorts mounted segments into the order they sit in the page. */
|
|
@@ -5387,19 +5450,30 @@ const Loading = ({ active = false, isFullPage = false, size, color, canCancel =
|
|
|
5387
5450
|
onCancel();
|
|
5388
5451
|
}
|
|
5389
5452
|
};
|
|
5453
|
+
// Read when Escape arrives, so the key listener stays put while `onCancel`
|
|
5454
|
+
// changes identity, as an inline one does on every render of the parent.
|
|
5455
|
+
const onCancelRef = useLatestRef(onCancel);
|
|
5456
|
+
const hasOnCancel = Boolean(onCancel);
|
|
5457
|
+
// A full-page loading covers everything under it, so it joins the stack of
|
|
5458
|
+
// open overlays and takes Escape from them while it's on top. One inside a
|
|
5459
|
+
// container covers only that, so it stays out and waits, as a Toast does,
|
|
5460
|
+
// until no overlay is open.
|
|
5461
|
+
const isTopmost = useOverlayStack(active && isFullPage);
|
|
5390
5462
|
// Handle escape key. On `window`, as in `Modal`, so a popup on the page has
|
|
5391
|
-
// had the key first, and an Escape it closed on doesn't cancel as well.
|
|
5463
|
+
// had the key first, and an Escape it closed on doesn't cancel as well. One
|
|
5464
|
+
// that cancels is marked used, so nothing under it acts on it too.
|
|
5392
5465
|
React.useEffect(() => {
|
|
5393
|
-
if (!active || !canCancel || !
|
|
5466
|
+
if (!active || !canCancel || !hasOnCancel)
|
|
5394
5467
|
return;
|
|
5395
5468
|
const handleKeyDown = (e) => {
|
|
5396
|
-
if (e.key
|
|
5397
|
-
|
|
5398
|
-
|
|
5469
|
+
if (e.key !== 'Escape' || e.defaultPrevented || !isTopmost())
|
|
5470
|
+
return;
|
|
5471
|
+
e.preventDefault();
|
|
5472
|
+
onCancelRef.current?.();
|
|
5399
5473
|
};
|
|
5400
5474
|
window.addEventListener('keydown', handleKeyDown);
|
|
5401
5475
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
5402
|
-
}, [active, canCancel,
|
|
5476
|
+
}, [active, canCancel, hasOnCancel, onCancelRef, isTopmost]);
|
|
5403
5477
|
// Prevent body scroll when full page loading is active. Ref-counted through
|
|
5404
5478
|
// the shared helper so an overlapping Modal/Dialog/Sidebar doesn't have its
|
|
5405
5479
|
// lock cleared when this one releases.
|
|
@@ -6672,10 +6746,9 @@ const Tooltip = ({ label, content, position = 'top', color, size, active: always
|
|
|
6672
6746
|
openOnTap,
|
|
6673
6747
|
});
|
|
6674
6748
|
// Auto-placement and overflow correction
|
|
6675
|
-
|
|
6749
|
+
useClientLayoutEffect(() => {
|
|
6676
6750
|
const isAuto = position === 'auto';
|
|
6677
6751
|
if (!isAuto) {
|
|
6678
|
-
// eslint-disable-next-line react-hooks/set-state-in-effect -- layout measurement effect resolves placement
|
|
6679
6752
|
setResolvedPosition(position);
|
|
6680
6753
|
}
|
|
6681
6754
|
const showing = isVisible || alwaysActive;
|
|
@@ -7657,23 +7730,21 @@ const Step = ({ isActive = false, isCompleted = false, label, icon, clickable =
|
|
|
7657
7730
|
const stepsMarkerClass = usePrefixedClassNames('steps-marker');
|
|
7658
7731
|
const stepsContentClass = usePrefixedClassNames('steps-content');
|
|
7659
7732
|
const stepsTitleClass = usePrefixedClassNames('steps-title');
|
|
7660
|
-
const handleClick = () => {
|
|
7661
|
-
if (clickable && onClick) {
|
|
7662
|
-
onClick();
|
|
7663
|
-
}
|
|
7664
|
-
};
|
|
7665
|
-
const handleKeyDown = (e) => {
|
|
7666
|
-
if (clickable && onClick && (e.key === 'Enter' || e.key === ' ')) {
|
|
7667
|
-
e.preventDefault();
|
|
7668
|
-
onClick();
|
|
7669
|
-
}
|
|
7670
|
-
};
|
|
7671
7733
|
const resolvedCompletedIcon = completedIcon === undefined ? '\u2713' : completedIcon;
|
|
7672
7734
|
const markerContent = icon ||
|
|
7673
7735
|
(isCompleted && resolvedCompletedIcon
|
|
7674
7736
|
? resolvedCompletedIcon
|
|
7675
7737
|
: (stepNumber ?? null));
|
|
7676
|
-
|
|
7738
|
+
const title = label || children;
|
|
7739
|
+
const current = isActive ? 'step' : undefined;
|
|
7740
|
+
// The step stays a list item, and a real button inside it does the
|
|
7741
|
+
// clicking, so the list keeps its items and the button brings Enter, Space
|
|
7742
|
+
// and focus with it. A button holds only phrasing content, so its parts
|
|
7743
|
+
// are spans.
|
|
7744
|
+
if (clickable) {
|
|
7745
|
+
return (jsxRuntime.jsx("li", { className: stepClasses, ...rest, children: jsxRuntime.jsxs("button", { type: "button", className: stepsLinkClass, onClick: () => onClick?.(), "aria-current": current, children: [jsxRuntime.jsx("span", { className: stepsMarkerClass, children: markerContent }), title && (jsxRuntime.jsx("span", { className: stepsContentClass, children: jsxRuntime.jsx("span", { className: stepsTitleClass, children: title }) }))] }) }));
|
|
7746
|
+
}
|
|
7747
|
+
return (jsxRuntime.jsx("li", { className: stepClasses, "aria-current": current, ...rest, children: jsxRuntime.jsxs("div", { className: stepsLinkClass, children: [jsxRuntime.jsx("span", { className: stepsMarkerClass, children: markerContent }), title && (jsxRuntime.jsx("div", { className: stepsContentClass, children: jsxRuntime.jsx("p", { className: stepsTitleClass, children: title }) }))] }) }));
|
|
7677
7748
|
};
|
|
7678
7749
|
/**
|
|
7679
7750
|
* The `Steps` component provides a multi-step progress indicator for wizard flows, checkout processes, or any multi-step workflow.
|
|
@@ -7754,7 +7825,10 @@ const StepsComponent = ({ value = 0, items, size, color, hasMarker = true, anima
|
|
|
7754
7825
|
}
|
|
7755
7826
|
return null;
|
|
7756
7827
|
};
|
|
7757
|
-
|
|
7828
|
+
// role="list" restates the <ul>'s own role, for Safari: WebKit stops
|
|
7829
|
+
// exposing a list as one once its markers are hidden, as they are here,
|
|
7830
|
+
// unless the role is set explicitly.
|
|
7831
|
+
return (jsxRuntime.jsxs("div", { className: combinedClasses, ...rest, children: [jsxRuntime.jsx("ul", { className: listClasses, role: "list", children: renderSteps() }), hasNavigation && (jsxRuntime.jsxs("div", { className: stepsNavigationClass, children: [jsxRuntime.jsx("button", { type: "button", className: prevButtonClass, disabled: value === 0, onClick: onPrev ?? (() => onStepClick?.(value - 1)), children: prevLabel ?? 'Previous' }), jsxRuntime.jsx("button", { type: "button", className: nextButtonClass, disabled: value === totalSteps - 1, onClick: onNext ?? (() => onStepClick?.(value + 1)), children: nextLabel ?? 'Next' })] }))] }));
|
|
7758
7832
|
};
|
|
7759
7833
|
// Attach Step as static property
|
|
7760
7834
|
const Steps = withSubComponents(StepsComponent, { Step }, 'Steps');
|
|
@@ -7808,19 +7882,29 @@ const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left',
|
|
|
7808
7882
|
handleClose();
|
|
7809
7883
|
}
|
|
7810
7884
|
}, [overlayClose, handleClose]);
|
|
7885
|
+
// Read when Escape arrives, so the key listener stays put while
|
|
7886
|
+
// `onClose` changes identity, as an inline one does on every render of
|
|
7887
|
+
// the parent.
|
|
7888
|
+
const handleCloseRef = useLatestRef(handleClose);
|
|
7889
|
+
// Joins the stack of open overlays, so the sidebar answers Escape only
|
|
7890
|
+
// while it's the topmost overlay, and keeps it from those under it.
|
|
7891
|
+
const isTopmost = useOverlayStack(isOpen);
|
|
7892
|
+
const closesOnEscape = escapeClose && canCancel && Boolean(onClose);
|
|
7811
7893
|
// Handle escape key. On `window`, as in `Modal`, so everything inside the
|
|
7812
|
-
// sidebar has had the key first, and an Escape it used is left to it.
|
|
7894
|
+
// sidebar has had the key first, and an Escape it used is left to it. One
|
|
7895
|
+
// the sidebar closes on is marked used, so nothing under it acts on it.
|
|
7813
7896
|
React.useEffect(() => {
|
|
7814
|
-
if (!isOpen || !
|
|
7897
|
+
if (!isOpen || !closesOnEscape)
|
|
7815
7898
|
return;
|
|
7816
7899
|
const handleKeyDown = (e) => {
|
|
7817
|
-
if (e.key
|
|
7818
|
-
|
|
7819
|
-
|
|
7900
|
+
if (e.key !== 'Escape' || e.defaultPrevented || !isTopmost())
|
|
7901
|
+
return;
|
|
7902
|
+
e.preventDefault();
|
|
7903
|
+
handleCloseRef.current();
|
|
7820
7904
|
};
|
|
7821
7905
|
window.addEventListener('keydown', handleKeyDown);
|
|
7822
7906
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
7823
|
-
}, [isOpen,
|
|
7907
|
+
}, [isOpen, closesOnEscape, handleCloseRef, isTopmost]);
|
|
7824
7908
|
// Prevent body scroll when sidebar is open. Ref-counted through the shared
|
|
7825
7909
|
// helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.
|
|
7826
7910
|
useScrollLock(isOpen && overlay);
|
|
@@ -7830,6 +7914,7 @@ const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left',
|
|
|
7830
7914
|
React.useEffect(() => {
|
|
7831
7915
|
if (isOpen) {
|
|
7832
7916
|
const element = sidebarRef.current;
|
|
7917
|
+
/* istanbul ignore else: the aside renders whenever the sidebar is open, so its ref is set before this runs */
|
|
7833
7918
|
if (element) {
|
|
7834
7919
|
// Focus first focusable element or sidebar itself
|
|
7835
7920
|
const focusable = element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
@@ -8038,14 +8123,19 @@ const Toast = React.forwardRef(({ message, type = 'default', actionType, positio
|
|
|
8038
8123
|
onAction?.();
|
|
8039
8124
|
handleClose();
|
|
8040
8125
|
}, [onAction, handleClose]);
|
|
8126
|
+
// Read when the timer runs out, a click lands outside or Escape arrives,
|
|
8127
|
+
// so the timer and the listeners stay put while `onClose` changes
|
|
8128
|
+
// identity, as an inline one does on every render of the parent. An
|
|
8129
|
+
// inline one from `ToastContainer` changes whenever another toast shows.
|
|
8130
|
+
const handleCloseRef = useLatestRef(handleClose);
|
|
8041
8131
|
React.useEffect(() => {
|
|
8042
8132
|
if (indefinite || duration === 0 || isPaused)
|
|
8043
8133
|
return undefined;
|
|
8044
8134
|
const timer = setTimeout(() => {
|
|
8045
|
-
|
|
8135
|
+
handleCloseRef.current();
|
|
8046
8136
|
}, duration);
|
|
8047
8137
|
return () => clearTimeout(timer);
|
|
8048
|
-
}, [duration, indefinite, isPaused,
|
|
8138
|
+
}, [duration, indefinite, isPaused, handleCloseRef]);
|
|
8049
8139
|
const handleMouseEnter = React.useCallback(() => {
|
|
8050
8140
|
if (pauseOnHover) {
|
|
8051
8141
|
setIsPaused(true);
|
|
@@ -8063,7 +8153,7 @@ const Toast = React.forwardRef(({ message, type = 'default', actionType, positio
|
|
|
8063
8153
|
return undefined;
|
|
8064
8154
|
const handleDocumentClick = (e) => {
|
|
8065
8155
|
if (toastRef.current && !isEventInside(e, toastRef.current)) {
|
|
8066
|
-
|
|
8156
|
+
handleCloseRef.current();
|
|
8067
8157
|
}
|
|
8068
8158
|
};
|
|
8069
8159
|
const raf = requestAnimationFrame(() => {
|
|
@@ -8073,20 +8163,24 @@ const Toast = React.forwardRef(({ message, type = 'default', actionType, positio
|
|
|
8073
8163
|
cancelAnimationFrame(raf);
|
|
8074
8164
|
document.removeEventListener('click', handleDocumentClick);
|
|
8075
8165
|
};
|
|
8076
|
-
}, [dismissible, isVisible,
|
|
8166
|
+
}, [dismissible, isVisible, handleCloseRef]);
|
|
8167
|
+
// A toast is a notification rather than a layer, so it never joins the
|
|
8168
|
+
// stack of open overlays. Asking the stack tells it whether one is open:
|
|
8169
|
+
// an Escape while one is belongs to the overlay, and the toast waits.
|
|
8170
|
+
const isTopmost = useOverlayStack(false);
|
|
8077
8171
|
// On `window`, as in `Modal`, so a popup on the page has had the key
|
|
8078
8172
|
// first, and an Escape it closed on doesn't dismiss the toast as well.
|
|
8079
8173
|
React.useEffect(() => {
|
|
8080
8174
|
if (!cancelable || !isVisible)
|
|
8081
8175
|
return undefined;
|
|
8082
8176
|
const handleKeyDown = (e) => {
|
|
8083
|
-
if (e.key === 'Escape' && !e.defaultPrevented) {
|
|
8084
|
-
|
|
8177
|
+
if (e.key === 'Escape' && !e.defaultPrevented && isTopmost()) {
|
|
8178
|
+
handleCloseRef.current();
|
|
8085
8179
|
}
|
|
8086
8180
|
};
|
|
8087
8181
|
window.addEventListener('keydown', handleKeyDown);
|
|
8088
8182
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
8089
|
-
}, [cancelable, isVisible,
|
|
8183
|
+
}, [cancelable, isVisible, handleCloseRef, isTopmost]);
|
|
8090
8184
|
const containerClasses = usePrefixedClassNames('toast-container', {
|
|
8091
8185
|
[`is-${position}`]: true,
|
|
8092
8186
|
});
|
|
@@ -9754,7 +9848,8 @@ const ListItem = ({ className, textColor, bgColor, children, ...props }) => {
|
|
|
9754
9848
|
*
|
|
9755
9849
|
* The ring is `1em` square, so `textSize` scales it. It is exposed as an
|
|
9756
9850
|
* indeterminate `progressbar` named by `ariaLabel`, and it stops spinning
|
|
9757
|
-
* under `prefers-reduced-motion: reduce` while staying drawn.
|
|
9851
|
+
* under `prefers-reduced-motion: reduce` while staying drawn. In forced
|
|
9852
|
+
* colors its two open sides take the page's color, so it stays open.
|
|
9758
9853
|
*
|
|
9759
9854
|
* @function
|
|
9760
9855
|
* @param {LoaderProps} props - Props for the Loader component.
|
|
@@ -10835,11 +10930,39 @@ const checkboxSizes = ['small', 'normal', 'medium', 'large'];
|
|
|
10835
10930
|
* <Checkbox value="vue">Vue</Checkbox>
|
|
10836
10931
|
* </Checkboxes>
|
|
10837
10932
|
*/
|
|
10838
|
-
const Checkbox = React.forwardRef(({ color, size, className, children, textColor, disabled, name, value, checked, defaultChecked, onChange, ...props }, ref) => {
|
|
10933
|
+
const Checkbox = React.forwardRef(({ color, size, className, children, textColor, disabled, name, value, checked, defaultChecked, indeterminate, onChange, ...props }, ref) => {
|
|
10839
10934
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
10840
10935
|
color: textColor,
|
|
10841
10936
|
...props,
|
|
10842
10937
|
});
|
|
10938
|
+
// The input's own ref, merged with the forwarded one. A callback ref's
|
|
10939
|
+
// return value is handed back, so React 19 still runs a cleanup it
|
|
10940
|
+
// returns, as it did when the ref went straight to the input.
|
|
10941
|
+
const inputRef = React.useRef(null);
|
|
10942
|
+
const setRefs = React.useCallback((node) => {
|
|
10943
|
+
inputRef.current = node;
|
|
10944
|
+
if (typeof ref === 'function')
|
|
10945
|
+
return ref(node);
|
|
10946
|
+
if (ref)
|
|
10947
|
+
ref.current = node;
|
|
10948
|
+
}, [ref]);
|
|
10949
|
+
// `indeterminate` is a DOM property with no attribute, so it's set on the
|
|
10950
|
+
// input rather than rendered. A click clears it, so this runs after every
|
|
10951
|
+
// render, not only when the prop changes. Unset, it's left alone, so a
|
|
10952
|
+
// ref that sets the property itself still works. `ownsIndeterminateRef`
|
|
10953
|
+
// remembers that this component set it, so a prop that goes back to unset
|
|
10954
|
+
// clears the dash once instead of leaving it on.
|
|
10955
|
+
const ownsIndeterminateRef = React.useRef(false);
|
|
10956
|
+
useClientLayoutEffect(() => {
|
|
10957
|
+
if (indeterminate !== undefined) {
|
|
10958
|
+
ownsIndeterminateRef.current = true;
|
|
10959
|
+
inputRef.current.indeterminate = indeterminate;
|
|
10960
|
+
}
|
|
10961
|
+
else if (ownsIndeterminateRef.current) {
|
|
10962
|
+
ownsIndeterminateRef.current = false;
|
|
10963
|
+
inputRef.current.indeterminate = false;
|
|
10964
|
+
}
|
|
10965
|
+
});
|
|
10843
10966
|
// Inherit name + selection state from a surrounding <Checkboxes> group.
|
|
10844
10967
|
// Local props always win over the group (explicit > implicit).
|
|
10845
10968
|
const group = useCheckboxesGroup();
|
|
@@ -10854,6 +10977,10 @@ const Checkbox = React.forwardRef(({ color, size, className, children, textColor
|
|
|
10854
10977
|
// the controlled/uncontrolled React warning.
|
|
10855
10978
|
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
10856
10979
|
const handleChange = React.useCallback((e) => {
|
|
10980
|
+
// The click that got here cleared the mixed state. Put it back now,
|
|
10981
|
+
// since a click that changes no state renders nothing to do it.
|
|
10982
|
+
if (indeterminate !== undefined)
|
|
10983
|
+
e.target.indeterminate = indeterminate;
|
|
10857
10984
|
// Local onChange always fires.
|
|
10858
10985
|
onChange?.(e);
|
|
10859
10986
|
// Dispatch to the group additionally — toggle this value's
|
|
@@ -10868,7 +10995,7 @@ const Checkbox = React.forwardRef(({ color, size, className, children, textColor
|
|
|
10868
10995
|
: currentArr.filter(v => v !== valStr);
|
|
10869
10996
|
group.onChange(next);
|
|
10870
10997
|
}
|
|
10871
|
-
}, [onChange, group, value]);
|
|
10998
|
+
}, [onChange, group, value, indeterminate]);
|
|
10872
10999
|
const mainClass = usePrefixedClassNames('styled-checkbox', 'checkbox', {
|
|
10873
11000
|
[`is-${color}`]: color && checkboxColors.includes(color),
|
|
10874
11001
|
[`is-${size}`]: size && checkboxSizes.includes(size),
|
|
@@ -10876,7 +11003,7 @@ const Checkbox = React.forwardRef(({ color, size, className, children, textColor
|
|
|
10876
11003
|
const checkboxClass = classNames(mainClass, bulmaHelperClasses, className);
|
|
10877
11004
|
const checkClass = usePrefixedClassNames('check');
|
|
10878
11005
|
const controlLabelClass = usePrefixedClassNames('control-label');
|
|
10879
|
-
return (jsxRuntime.jsxs("label", { className: checkboxClass, children: [jsxRuntime.jsx("input", { ref:
|
|
11006
|
+
return (jsxRuntime.jsxs("label", { className: checkboxClass, children: [jsxRuntime.jsx("input", { ref: setRefs, type: "checkbox", disabled: disabled, name: effectiveName, value: value, checked: effectiveChecked, defaultChecked: effectiveDefaultChecked, onChange: handleChange, ...rest }), jsxRuntime.jsx("span", { className: checkClass }), children && jsxRuntime.jsx("span", { className: controlLabelClass, children: children })] }));
|
|
10880
11007
|
});
|
|
10881
11008
|
Checkbox.displayName = 'Checkbox';
|
|
10882
11009
|
|
|
@@ -12029,7 +12156,7 @@ const Slider = React.forwardRef((props, ref) => {
|
|
|
12029
12156
|
}
|
|
12030
12157
|
return 0;
|
|
12031
12158
|
}, []);
|
|
12032
|
-
|
|
12159
|
+
useClientLayoutEffect(() => {
|
|
12033
12160
|
if (isVertical)
|
|
12034
12161
|
return;
|
|
12035
12162
|
const wrapper = wrapperRef.current;
|
|
@@ -12043,7 +12170,7 @@ const Slider = React.forwardRef((props, ref) => {
|
|
|
12043
12170
|
setNudgeX(computeNudge(el, wrapper, pct, thumbPx));
|
|
12044
12171
|
}
|
|
12045
12172
|
});
|
|
12046
|
-
|
|
12173
|
+
useClientLayoutEffect(() => {
|
|
12047
12174
|
if (isVertical || !range)
|
|
12048
12175
|
return;
|
|
12049
12176
|
const wrapper = wrapperRef.current;
|
|
@@ -12057,7 +12184,7 @@ const Slider = React.forwardRef((props, ref) => {
|
|
|
12057
12184
|
}
|
|
12058
12185
|
});
|
|
12059
12186
|
// Detect vertical tooltip overflow: flip to left if no space on right
|
|
12060
|
-
|
|
12187
|
+
useClientLayoutEffect(() => {
|
|
12061
12188
|
if (!isVertical)
|
|
12062
12189
|
return;
|
|
12063
12190
|
const wrapper = wrapperRef.current;
|
|
@@ -13089,7 +13216,10 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
13089
13216
|
const hasInput = inputValue || (openOnFocus && isActive);
|
|
13090
13217
|
return matchesInput && notAlreadyAdded && hasInput;
|
|
13091
13218
|
});
|
|
13092
|
-
|
|
13219
|
+
// At the tag limit the input isn't rendered, so the list isn't either.
|
|
13220
|
+
// Left open it would be a listbox with no combobox, offering tags past
|
|
13221
|
+
// the limit (#1087).
|
|
13222
|
+
const listOpen = isActive && !isMaxReached && filteredData.length > 0;
|
|
13093
13223
|
// keepFirst: auto-highlight first item when dropdown opens
|
|
13094
13224
|
React.useEffect(() => {
|
|
13095
13225
|
if (keepFirst && isActive && filteredData.length > 0) {
|
|
@@ -13103,17 +13233,16 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
13103
13233
|
}
|
|
13104
13234
|
onChange?.(newTags);
|
|
13105
13235
|
}, [controlledValue, onChange]);
|
|
13106
|
-
// Add tag
|
|
13236
|
+
// Add a tag from text typed, pasted or picked from the list. Only
|
|
13237
|
+
// `createTag` turns it into an object.
|
|
13107
13238
|
const addTag = React.useCallback((value) => {
|
|
13108
13239
|
if (disabled || readonly)
|
|
13109
13240
|
return;
|
|
13110
|
-
const
|
|
13111
|
-
const displayValue = typeof tagValue === 'string' ? tagValue : getDisplayValue(tagValue);
|
|
13241
|
+
const displayValue = value.trim();
|
|
13112
13242
|
if (!displayValue)
|
|
13113
13243
|
return;
|
|
13114
|
-
//
|
|
13115
|
-
|
|
13116
|
-
return;
|
|
13244
|
+
// No maxTags check: only the input and the list call this, neither
|
|
13245
|
+
// renders at the limit, and each call adds at most one tag.
|
|
13117
13246
|
// Check duplicates
|
|
13118
13247
|
if (!allowDuplicates) {
|
|
13119
13248
|
const exists = tags.some(tag => getDisplayValue(tag).toLowerCase() === displayValue.toLowerCase());
|
|
@@ -13130,7 +13259,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
13130
13259
|
if (beforeAdding && !beforeAdding(displayValue))
|
|
13131
13260
|
return;
|
|
13132
13261
|
// createTag transformation
|
|
13133
|
-
const finalTag = createTag ? createTag(displayValue) :
|
|
13262
|
+
const finalTag = createTag ? createTag(displayValue) : displayValue;
|
|
13134
13263
|
const newTags = [...tags, finalTag];
|
|
13135
13264
|
updateTags(newTags);
|
|
13136
13265
|
onAdd?.(finalTag);
|
|
@@ -13143,7 +13272,6 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
13143
13272
|
disabled,
|
|
13144
13273
|
readonly,
|
|
13145
13274
|
tags,
|
|
13146
|
-
maxTags,
|
|
13147
13275
|
allowDuplicates,
|
|
13148
13276
|
allowNew,
|
|
13149
13277
|
data,
|
|
@@ -13209,6 +13337,17 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
13209
13337
|
setIsActive(true);
|
|
13210
13338
|
}
|
|
13211
13339
|
}, [openOnFocus, data.length, disabled]);
|
|
13340
|
+
// Reaching the limit dismisses the list the way a click outside does, so
|
|
13341
|
+
// making room later doesn't bring it back unasked. It's an effect, not a
|
|
13342
|
+
// step in addTag, because a click on a suggestion also bubbles to the
|
|
13343
|
+
// container, whose focus on the input reopens the list in that same
|
|
13344
|
+
// event, and because a controlled value can reach the limit too.
|
|
13345
|
+
React.useEffect(() => {
|
|
13346
|
+
if (isMaxReached) {
|
|
13347
|
+
setIsActive(false);
|
|
13348
|
+
setHighlightedIndex(-1);
|
|
13349
|
+
}
|
|
13350
|
+
}, [isMaxReached]);
|
|
13212
13351
|
// Handle click outside
|
|
13213
13352
|
React.useEffect(() => {
|
|
13214
13353
|
if (!isActive)
|
|
@@ -15167,7 +15306,15 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
15167
15306
|
left: resolved.left,
|
|
15168
15307
|
}
|
|
15169
15308
|
: {};
|
|
15170
|
-
const panel = (jsxRuntime.jsx("div", { ref: panelRef, id: id, role: role, "aria-modal": role === 'dialog' ? 'false' : undefined, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, tabIndex: -1, className: classNames(panelClass, className), style: style,
|
|
15309
|
+
const panel = (jsxRuntime.jsx("div", { ref: panelRef, id: id, role: role, "aria-modal": role === 'dialog' ? 'false' : undefined, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, tabIndex: -1, className: classNames(panelClass, className), style: style,
|
|
15310
|
+
// An Escape pressed in the panel is the picker's, and the listener
|
|
15311
|
+
// above claims it. A Dropdown menu around the picker hears Escape in a
|
|
15312
|
+
// React handler, which runs before that listener, so the panel claims
|
|
15313
|
+
// it here too, where React gets to it first.
|
|
15314
|
+
onKeyDown: e => {
|
|
15315
|
+
if (closeOnEscape && e.key === 'Escape')
|
|
15316
|
+
e.preventDefault();
|
|
15317
|
+
}, children: children }));
|
|
15171
15318
|
if (appendToBody && isBrowser) {
|
|
15172
15319
|
return reactDom.createPortal(panel, resolveContainer());
|
|
15173
15320
|
}
|
|
@@ -15581,7 +15728,7 @@ function useSegmentedEntry(params) {
|
|
|
15581
15728
|
// any of its effects, so a focus in between is the one handed back.
|
|
15582
15729
|
const closingRef = React.useRef(false);
|
|
15583
15730
|
const wasOpenRef = React.useRef(isOpen);
|
|
15584
|
-
|
|
15731
|
+
useClientLayoutEffect(() => {
|
|
15585
15732
|
if (wasOpenRef.current && !isOpen) {
|
|
15586
15733
|
closingRef.current = true;
|
|
15587
15734
|
if (!value && isUntouched(text))
|
|
@@ -15611,12 +15758,12 @@ function useSegmentedEntry(params) {
|
|
|
15611
15758
|
setText(formatFn(next, format, locale));
|
|
15612
15759
|
}, [commitValue, isAllowed, formatFn, format, locale, setText]);
|
|
15613
15760
|
// After every segment-driven change, restore the system selection on the
|
|
15614
|
-
// active segment's char range.
|
|
15761
|
+
// active segment's char range. A layout effect runs synchronously before
|
|
15615
15762
|
// paint so the user never sees a stray caret position. We deliberately do
|
|
15616
15763
|
// NOT gate on document.activeElement: when openOnFocus is true the focus
|
|
15617
15764
|
// trap moves focus into the popover, but setSelectionRange on an unfocused
|
|
15618
15765
|
// input is well-defined and the selection becomes visible on refocus.
|
|
15619
|
-
|
|
15766
|
+
useClientLayoutEffect(() => {
|
|
15620
15767
|
if (activeSegmentIdx === null || !segmentMap || !inputRef.current)
|
|
15621
15768
|
return;
|
|
15622
15769
|
const seg = segmentMap.segments[activeSegmentIdx];
|