@lumx/react 4.12.1-alpha.0 → 4.12.1-next.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.
Files changed (3) hide show
  1. package/index.js +73 -156
  2. package/index.js.map +1 -1
  3. 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 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';
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';
2
2
  export * from '@lumx/core/js/constants';
3
3
  export * from '@lumx/core/js/types';
4
4
  import * as React from 'react';
@@ -204,11 +204,6 @@ 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
-
212
207
  function getDefaultExportFromCjs (x) {
213
208
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
214
209
  }
@@ -5065,7 +5060,7 @@ const LISTENERS = makeListenerTowerContext();
5065
5060
  */
5066
5061
  function useCallbackOnEscape(callback, closeOnEscape = true) {
5067
5062
  useEffect(() => {
5068
- const rootElement = DOCUMENT$1?.body;
5063
+ const rootElement = DOCUMENT?.body;
5069
5064
  if (!closeOnEscape || !callback || !rootElement) {
5070
5065
  return undefined;
5071
5066
  }
@@ -5280,7 +5275,7 @@ const Tooltip = forwardRef((props, ref) => {
5280
5275
  ...forwardedProps
5281
5276
  } = props;
5282
5277
  // Disable in SSR.
5283
- if (!DOCUMENT$1) {
5278
+ if (!DOCUMENT) {
5284
5279
  return /*#__PURE__*/jsx(Fragment, {
5285
5280
  children: children
5286
5281
  });
@@ -8818,14 +8813,8 @@ const ComboboxOptionMoreInfo$1 = (props, {
8818
8813
  /** CSS selector listing all tabbable elements. */
8819
8814
  const TABBABLE_ELEMENTS_SELECTOR = 'a[href], button, textarea, input:not([type="hidden"]):not([hidden]), [tabindex]';
8820
8815
 
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"])';
8816
+ /** CSS selector matching element that are disabled (should not receive focus). */
8817
+ const DISABLED_SELECTOR = '[hidden], [tabindex="-1"], [disabled]:not([disabled="false"]), [aria-disabled]:not([aria-disabled="false"])';
8829
8818
 
8830
8819
  const isNotDisabled = element => !element.matches(DISABLED_SELECTOR);
8831
8820
  function getFocusableElements(element) {
@@ -8854,158 +8843,86 @@ function getFirstAndLastFocusable(parentElement) {
8854
8843
  return {};
8855
8844
  }
8856
8845
 
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
- */
8863
8846
  const FOCUS_TRAPS = makeListenerTowerContext();
8847
+
8864
8848
  /**
8865
8849
  * Trap 'Tab' focus switch inside the `focusZoneElement`.
8866
8850
  *
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).
8872
- *
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.
8851
+ * If multiple focus trap are activated, only the last one is maintained and when a focus trap closes, the previous one
8852
+ * gets activated again.
8878
8853
  *
8879
- * @param options Trap configuration.
8880
- * @param signal AbortSignal used to tear down the trap.
8854
+ * @param focusZoneElement The element in which to trap the focus.
8855
+ * @param focusElement The element to focus when the focus trap is activated (otherwise the first focusable element
8856
+ * will be focused).
8881
8857
  */
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;
8858
+ function useFocusTrap(focusZoneElement, focusElement) {
8859
+ useEffect(() => {
8860
+ // Body element can be undefined in SSR context.
8861
+ const rootElement = DOCUMENT?.body;
8862
+ if (!rootElement || !focusZoneElement) {
8863
+ return undefined;
8905
8864
  }
8906
- };
8907
8865
 
8908
- /** Focus the zone element itself as a last-resort fallback. */
8909
- const focusZoneFallback = () => {
8910
- ensureZoneIsFocusable();
8911
- focusZoneElement.focus({
8912
- preventScroll: true
8913
- });
8914
- };
8866
+ // Use the shadow root as focusZoneElement when available
8867
+ const focusZoneElementOrShadowRoot = focusZoneElement.shadowRoot || focusZoneElement;
8915
8868
 
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);
8869
+ // Trap 'Tab' key down focus switch into the focus zone.
8870
+ const trapTabFocusInFocusZone = evt => {
8871
+ const {
8872
+ key
8873
+ } = evt;
8874
+ if (key !== 'Tab') {
8875
+ return;
8876
+ }
8877
+ const focusable = getFirstAndLastFocusable(focusZoneElementOrShadowRoot);
8922
8878
 
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
- };
8879
+ // Prevent focus switch if no focusable available.
8880
+ if (!focusable.first) {
8881
+ evt.preventDefault();
8882
+ return;
8883
+ }
8884
+ const activeElement = focusZoneElement.shadowRoot ? focusZoneElement.shadowRoot.activeElement : document.activeElement;
8885
+ if (
8886
+ // No previous focus
8887
+ !activeElement ||
8888
+ // Previous focus is at the end of the focus zone.
8889
+ !evt.shiftKey && activeElement === focusable.last ||
8890
+ // Previous focus is outside the focus zone
8891
+ !focusZoneElementOrShadowRoot.contains(activeElement)) {
8892
+ focusable.first.focus();
8893
+ evt.preventDefault();
8894
+ return;
8895
+ }
8896
+ if (
8897
+ // Focus order reversed
8898
+ evt.shiftKey &&
8899
+ // Previous focus is at the start of the focus zone.
8900
+ activeElement === focusable.first) {
8901
+ focusable.last.focus();
8902
+ evt.preventDefault();
8903
+ }
8904
+ };
8905
+ const focusTrap = {
8906
+ enable: () => rootElement.addEventListener('keydown', trapTabFocusInFocusZone),
8907
+ disable: () => rootElement.removeEventListener('keydown', trapTabFocusInFocusZone)
8908
+ };
8954
8909
 
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({
8910
+ // SETUP:
8911
+ if (focusElement && focusZoneElementOrShadowRoot.contains(focusElement)) {
8912
+ // Focus the given element.
8913
+ focusElement.focus({
8966
8914
  preventScroll: true
8967
8915
  });
8968
8916
  } else {
8969
- // No focusable descendant fall back to the zone itself (e.g. an empty dialog).
8970
- focusZoneFallback();
8971
- }
8972
- }
8973
- FOCUS_TRAPS.register(focusTrap);
8974
-
8975
- // TEARDOWN.
8976
- signal.addEventListener('abort', () => {
8977
- FOCUS_TRAPS.unregister(focusTrap);
8978
- if (addedTabIndex) {
8979
- focusZoneElement.removeAttribute('tabindex');
8917
+ // Focus the first focusable element in the zone.
8918
+ getFirstAndLastFocusable(focusZoneElementOrShadowRoot).first?.focus({
8919
+ preventScroll: true
8920
+ });
8980
8921
  }
8981
- }, {
8982
- once: true
8983
- });
8984
- }
8922
+ FOCUS_TRAPS.register(focusTrap);
8985
8923
 
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();
8924
+ // TEARDOWN:
8925
+ return () => FOCUS_TRAPS.unregister(focusTrap);
9009
8926
  }, [focusElement, focusZoneElement]);
9010
8927
  }
9011
8928
 
@@ -9546,7 +9463,7 @@ _InnerPopover.displayName = COMPONENT_NAME$15;
9546
9463
  */
9547
9464
  const Popover = skipRender(
9548
9465
  // Skip render in SSR
9549
- () => Boolean(DOCUMENT$1), _InnerPopover);
9466
+ () => Boolean(DOCUMENT), _InnerPopover);
9550
9467
  Popover.displayName = COMPONENT_NAME$15;
9551
9468
  Popover.className = CLASSNAME$14;
9552
9469
  Popover.defaultProps = DEFAULT_PROPS$V;
@@ -11831,7 +11748,7 @@ const DEFAULT_PROPS$P = {
11831
11748
  * @return React element.
11832
11749
  */
11833
11750
  const Dialog = forwardRef((props, ref) => {
11834
- if (!DOCUMENT$1) {
11751
+ if (!DOCUMENT) {
11835
11752
  // Can't render in SSR.
11836
11753
  return null;
11837
11754
  }
@@ -14240,7 +14157,7 @@ const Lightbox = forwardRef((props, ref) => {
14240
14157
  zIndex,
14241
14158
  ...forwardedProps
14242
14159
  } = props;
14243
- if (!DOCUMENT$1) {
14160
+ if (!DOCUMENT) {
14244
14161
  // Can't render in SSR.
14245
14162
  return null;
14246
14163
  }
@@ -15199,7 +15116,7 @@ const Notification = forwardRef((props, ref) => {
15199
15116
  style,
15200
15117
  ...forwardedProps
15201
15118
  } = props;
15202
- if (!DOCUMENT$1) {
15119
+ if (!DOCUMENT) {
15203
15120
  // Can't render in SSR.
15204
15121
  return null;
15205
15122
  }