@lumx/react 4.12.1-next.1 → 4.12.1-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +156 -73
- package/index.js.map +1 -1
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ColorVariant as ColorVariant$1, Size as Size$1, VISUALLY_HIDDEN, Theme as Theme$1, AspectRatio as AspectRatio$1, DOCUMENT, IS_BROWSER as IS_BROWSER$1, Emphasis as Emphasis$1, WINDOW, DIALOG_TRANSITION_DURATION, Orientation as Orientation$1, NOTIFICATION_TRANSITION_DURATION, Kind as Kind$1, Alignment as Alignment$1, ColorPalette as ColorPalette$1 } from '@lumx/core/js/constants';
|
|
1
|
+
import { ColorVariant as ColorVariant$1, Size as Size$1, VISUALLY_HIDDEN, Theme as Theme$1, AspectRatio as AspectRatio$1, DOCUMENT as DOCUMENT$1, IS_BROWSER as IS_BROWSER$1, Emphasis as Emphasis$1, WINDOW, DIALOG_TRANSITION_DURATION, Orientation as Orientation$1, NOTIFICATION_TRANSITION_DURATION, Kind as Kind$1, Alignment as Alignment$1, ColorPalette as ColorPalette$1 } from '@lumx/core/js/constants';
|
|
2
2
|
export * from '@lumx/core/js/constants';
|
|
3
3
|
export * from '@lumx/core/js/types';
|
|
4
4
|
import * as React from 'react';
|
|
@@ -204,6 +204,11 @@ const ColorVariant = {
|
|
|
204
204
|
*/
|
|
205
205
|
const IS_BROWSER = typeof window !== 'undefined' && !window.navigator.userAgent.includes('jsdom');
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Optional global `document` instance (not defined when running SSR).
|
|
209
|
+
*/
|
|
210
|
+
const DOCUMENT = typeof document !== 'undefined' ? document : undefined;
|
|
211
|
+
|
|
207
212
|
function getDefaultExportFromCjs (x) {
|
|
208
213
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
209
214
|
}
|
|
@@ -5060,7 +5065,7 @@ const LISTENERS = makeListenerTowerContext();
|
|
|
5060
5065
|
*/
|
|
5061
5066
|
function useCallbackOnEscape(callback, closeOnEscape = true) {
|
|
5062
5067
|
useEffect(() => {
|
|
5063
|
-
const rootElement = DOCUMENT?.body;
|
|
5068
|
+
const rootElement = DOCUMENT$1?.body;
|
|
5064
5069
|
if (!closeOnEscape || !callback || !rootElement) {
|
|
5065
5070
|
return undefined;
|
|
5066
5071
|
}
|
|
@@ -5275,7 +5280,7 @@ const Tooltip = forwardRef((props, ref) => {
|
|
|
5275
5280
|
...forwardedProps
|
|
5276
5281
|
} = props;
|
|
5277
5282
|
// Disable in SSR.
|
|
5278
|
-
if (!DOCUMENT) {
|
|
5283
|
+
if (!DOCUMENT$1) {
|
|
5279
5284
|
return /*#__PURE__*/jsx(Fragment, {
|
|
5280
5285
|
children: children
|
|
5281
5286
|
});
|
|
@@ -8813,8 +8818,14 @@ const ComboboxOptionMoreInfo$1 = (props, {
|
|
|
8813
8818
|
/** CSS selector listing all tabbable elements. */
|
|
8814
8819
|
const TABBABLE_ELEMENTS_SELECTOR = 'a[href], button, textarea, input:not([type="hidden"]):not([hidden]), [tabindex]';
|
|
8815
8820
|
|
|
8816
|
-
/**
|
|
8817
|
-
|
|
8821
|
+
/**
|
|
8822
|
+
* CSS selector matching elements that should be excluded from focus traversal.
|
|
8823
|
+
*
|
|
8824
|
+
* Note: `aria-disabled` is intentionally NOT in this list — per ARIA semantics, an `aria-disabled` element
|
|
8825
|
+
* remains focusable (and discoverable by assistive tech). To remove an element from the tab order, use
|
|
8826
|
+
* `tabindex="-1"` instead.
|
|
8827
|
+
*/
|
|
8828
|
+
const DISABLED_SELECTOR = '[hidden], [tabindex="-1"], [disabled]:not([disabled="false"])';
|
|
8818
8829
|
|
|
8819
8830
|
const isNotDisabled = element => !element.matches(DISABLED_SELECTOR);
|
|
8820
8831
|
function getFocusableElements(element) {
|
|
@@ -8843,86 +8854,158 @@ function getFirstAndLastFocusable(parentElement) {
|
|
|
8843
8854
|
return {};
|
|
8844
8855
|
}
|
|
8845
8856
|
|
|
8857
|
+
/**
|
|
8858
|
+
* Shared listener tower for focus traps.
|
|
8859
|
+
*
|
|
8860
|
+
* When multiple traps are activated, only the last registered one is active. When it tears down, the previously
|
|
8861
|
+
* registered trap is re-enabled.
|
|
8862
|
+
*/
|
|
8846
8863
|
const FOCUS_TRAPS = makeListenerTowerContext();
|
|
8847
|
-
|
|
8848
8864
|
/**
|
|
8849
8865
|
* Trap 'Tab' focus switch inside the `focusZoneElement`.
|
|
8850
8866
|
*
|
|
8851
|
-
*
|
|
8852
|
-
*
|
|
8867
|
+
* Setup behavior:
|
|
8868
|
+
* 1. Focus `focusElement` if provided and contained in the zone.
|
|
8869
|
+
* 2. Otherwise focus the first focusable descendant.
|
|
8870
|
+
* 3. Otherwise focus the zone element itself (falling back to setting `tabindex="-1"` if needed) so that
|
|
8871
|
+
* keyboard users (especially screen reader users) land inside the trapped region (e.g. an empty dialog).
|
|
8853
8872
|
*
|
|
8854
|
-
*
|
|
8855
|
-
*
|
|
8856
|
-
*
|
|
8873
|
+
* Tab key behavior:
|
|
8874
|
+
* - With at least one focusable descendant: focus cycles between the first and last focusable in the zone.
|
|
8875
|
+
* - With no focusable descendant: Tab is swallowed and focus is restored to the zone element itself.
|
|
8876
|
+
*
|
|
8877
|
+
* Multiple traps stack — only the latest one is active; previous traps re-enable when the latest is torn down.
|
|
8878
|
+
*
|
|
8879
|
+
* @param options Trap configuration.
|
|
8880
|
+
* @param signal AbortSignal used to tear down the trap.
|
|
8857
8881
|
*/
|
|
8858
|
-
function
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8882
|
+
function setupFocusTrap(options, signal) {
|
|
8883
|
+
const {
|
|
8884
|
+
focusZoneElement,
|
|
8885
|
+
focusElement
|
|
8886
|
+
} = options;
|
|
8887
|
+
|
|
8888
|
+
// Body element can be undefined in SSR context.
|
|
8889
|
+
const rootElement = DOCUMENT?.body;
|
|
8890
|
+
if (!rootElement || !focusZoneElement || signal.aborted) {
|
|
8891
|
+
return;
|
|
8892
|
+
}
|
|
8893
|
+
|
|
8894
|
+
// Use the shadow root as focus zone when available.
|
|
8895
|
+
const focusZoneElementOrShadowRoot = focusZoneElement.shadowRoot || focusZoneElement;
|
|
8896
|
+
|
|
8897
|
+
// Track whether we added a `tabindex="-1"` so we can restore the original state on teardown.
|
|
8898
|
+
let addedTabIndex = false;
|
|
8899
|
+
|
|
8900
|
+
/** Make the zone element programmatically focusable (so we can fall back to it). */
|
|
8901
|
+
const ensureZoneIsFocusable = () => {
|
|
8902
|
+
if (!focusZoneElement.hasAttribute('tabindex')) {
|
|
8903
|
+
focusZoneElement.setAttribute('tabindex', '-1');
|
|
8904
|
+
addedTabIndex = true;
|
|
8864
8905
|
}
|
|
8906
|
+
};
|
|
8865
8907
|
|
|
8866
|
-
|
|
8867
|
-
|
|
8908
|
+
/** Focus the zone element itself as a last-resort fallback. */
|
|
8909
|
+
const focusZoneFallback = () => {
|
|
8910
|
+
ensureZoneIsFocusable();
|
|
8911
|
+
focusZoneElement.focus({
|
|
8912
|
+
preventScroll: true
|
|
8913
|
+
});
|
|
8914
|
+
};
|
|
8868
8915
|
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
return;
|
|
8876
|
-
}
|
|
8877
|
-
const focusable = getFirstAndLastFocusable(focusZoneElementOrShadowRoot);
|
|
8916
|
+
// Trap 'Tab' key down focus switch into the focus zone.
|
|
8917
|
+
const trapTabFocusInFocusZone = evt => {
|
|
8918
|
+
if (evt.key !== 'Tab') {
|
|
8919
|
+
return;
|
|
8920
|
+
}
|
|
8921
|
+
const focusable = getFirstAndLastFocusable(focusZoneElementOrShadowRoot);
|
|
8878
8922
|
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
}
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8923
|
+
// Prevent focus switch if no focusable available — pin focus on the zone itself.
|
|
8924
|
+
if (!focusable.first) {
|
|
8925
|
+
evt.preventDefault();
|
|
8926
|
+
focusZoneFallback();
|
|
8927
|
+
return;
|
|
8928
|
+
}
|
|
8929
|
+
const activeElement = focusZoneElement.shadowRoot ? focusZoneElement.shadowRoot.activeElement : DOCUMENT?.activeElement;
|
|
8930
|
+
if (
|
|
8931
|
+
// No previous focus.
|
|
8932
|
+
!activeElement ||
|
|
8933
|
+
// Previous focus is at the end of the focus zone.
|
|
8934
|
+
!evt.shiftKey && activeElement === focusable.last ||
|
|
8935
|
+
// Previous focus is outside the focus zone.
|
|
8936
|
+
!focusZoneElementOrShadowRoot.contains(activeElement)) {
|
|
8937
|
+
focusable.first.focus();
|
|
8938
|
+
evt.preventDefault();
|
|
8939
|
+
return;
|
|
8940
|
+
}
|
|
8941
|
+
if (
|
|
8942
|
+
// Focus order reversed.
|
|
8943
|
+
evt.shiftKey &&
|
|
8944
|
+
// Previous focus is at the start of the focus zone.
|
|
8945
|
+
activeElement === focusable.first) {
|
|
8946
|
+
focusable.last.focus();
|
|
8947
|
+
evt.preventDefault();
|
|
8948
|
+
}
|
|
8949
|
+
};
|
|
8950
|
+
const focusTrap = {
|
|
8951
|
+
enable: () => rootElement.addEventListener('keydown', trapTabFocusInFocusZone),
|
|
8952
|
+
disable: () => rootElement.removeEventListener('keydown', trapTabFocusInFocusZone)
|
|
8953
|
+
};
|
|
8909
8954
|
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8955
|
+
// SETUP: focus initial element.
|
|
8956
|
+
if (focusElement && focusZoneElementOrShadowRoot.contains(focusElement)) {
|
|
8957
|
+
// Focus the given element.
|
|
8958
|
+
focusElement.focus({
|
|
8959
|
+
preventScroll: true
|
|
8960
|
+
});
|
|
8961
|
+
} else {
|
|
8962
|
+
const firstFocusable = getFirstAndLastFocusable(focusZoneElementOrShadowRoot).first;
|
|
8963
|
+
if (firstFocusable) {
|
|
8964
|
+
// Focus the first focusable descendant.
|
|
8965
|
+
firstFocusable.focus({
|
|
8914
8966
|
preventScroll: true
|
|
8915
8967
|
});
|
|
8916
8968
|
} else {
|
|
8917
|
-
//
|
|
8918
|
-
|
|
8919
|
-
preventScroll: true
|
|
8920
|
-
});
|
|
8969
|
+
// No focusable descendant — fall back to the zone itself (e.g. an empty dialog).
|
|
8970
|
+
focusZoneFallback();
|
|
8921
8971
|
}
|
|
8922
|
-
|
|
8972
|
+
}
|
|
8973
|
+
FOCUS_TRAPS.register(focusTrap);
|
|
8923
8974
|
|
|
8924
|
-
|
|
8925
|
-
|
|
8975
|
+
// TEARDOWN.
|
|
8976
|
+
signal.addEventListener('abort', () => {
|
|
8977
|
+
FOCUS_TRAPS.unregister(focusTrap);
|
|
8978
|
+
if (addedTabIndex) {
|
|
8979
|
+
focusZoneElement.removeAttribute('tabindex');
|
|
8980
|
+
}
|
|
8981
|
+
}, {
|
|
8982
|
+
once: true
|
|
8983
|
+
});
|
|
8984
|
+
}
|
|
8985
|
+
|
|
8986
|
+
/**
|
|
8987
|
+
* Trap 'Tab' focus switch inside the `focusZoneElement`.
|
|
8988
|
+
*
|
|
8989
|
+
* If multiple focus traps are activated, only the last one is maintained and when a focus trap closes, the previous one
|
|
8990
|
+
* gets activated again.
|
|
8991
|
+
*
|
|
8992
|
+
* If the zone has no focusable descendant, the zone element itself receives focus (with a fallback `tabindex="-1"`).
|
|
8993
|
+
*
|
|
8994
|
+
* @param focusZoneElement The element in which to trap the focus.
|
|
8995
|
+
* @param focusElement The element to focus when the focus trap is activated (otherwise the first focusable element
|
|
8996
|
+
* will be focused — and finally the zone element itself if no focusable is found).
|
|
8997
|
+
*/
|
|
8998
|
+
function useFocusTrap(focusZoneElement, focusElement) {
|
|
8999
|
+
useEffect(() => {
|
|
9000
|
+
if (!focusZoneElement) {
|
|
9001
|
+
return undefined;
|
|
9002
|
+
}
|
|
9003
|
+
const controller = new AbortController();
|
|
9004
|
+
setupFocusTrap({
|
|
9005
|
+
focusZoneElement,
|
|
9006
|
+
focusElement
|
|
9007
|
+
}, controller.signal);
|
|
9008
|
+
return () => controller.abort();
|
|
8926
9009
|
}, [focusElement, focusZoneElement]);
|
|
8927
9010
|
}
|
|
8928
9011
|
|
|
@@ -9463,7 +9546,7 @@ _InnerPopover.displayName = COMPONENT_NAME$15;
|
|
|
9463
9546
|
*/
|
|
9464
9547
|
const Popover = skipRender(
|
|
9465
9548
|
// Skip render in SSR
|
|
9466
|
-
() => Boolean(DOCUMENT), _InnerPopover);
|
|
9549
|
+
() => Boolean(DOCUMENT$1), _InnerPopover);
|
|
9467
9550
|
Popover.displayName = COMPONENT_NAME$15;
|
|
9468
9551
|
Popover.className = CLASSNAME$14;
|
|
9469
9552
|
Popover.defaultProps = DEFAULT_PROPS$V;
|
|
@@ -11748,7 +11831,7 @@ const DEFAULT_PROPS$P = {
|
|
|
11748
11831
|
* @return React element.
|
|
11749
11832
|
*/
|
|
11750
11833
|
const Dialog = forwardRef((props, ref) => {
|
|
11751
|
-
if (!DOCUMENT) {
|
|
11834
|
+
if (!DOCUMENT$1) {
|
|
11752
11835
|
// Can't render in SSR.
|
|
11753
11836
|
return null;
|
|
11754
11837
|
}
|
|
@@ -14157,7 +14240,7 @@ const Lightbox = forwardRef((props, ref) => {
|
|
|
14157
14240
|
zIndex,
|
|
14158
14241
|
...forwardedProps
|
|
14159
14242
|
} = props;
|
|
14160
|
-
if (!DOCUMENT) {
|
|
14243
|
+
if (!DOCUMENT$1) {
|
|
14161
14244
|
// Can't render in SSR.
|
|
14162
14245
|
return null;
|
|
14163
14246
|
}
|
|
@@ -15116,7 +15199,7 @@ const Notification = forwardRef((props, ref) => {
|
|
|
15116
15199
|
style,
|
|
15117
15200
|
...forwardedProps
|
|
15118
15201
|
} = props;
|
|
15119
|
-
if (!DOCUMENT) {
|
|
15202
|
+
if (!DOCUMENT$1) {
|
|
15120
15203
|
// Can't render in SSR.
|
|
15121
15204
|
return null;
|
|
15122
15205
|
}
|