@inera/ids-react 9.0.4 → 9.1.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 (111) hide show
  1. package/components/alert/alert.js +1 -1
  2. package/components/breadcrumbs/breadcrumbs.js +1 -1
  3. package/components/card/card.d.ts +2 -2
  4. package/components/card/card.js +4 -15
  5. package/components/data-pagination/data-pagination.d.ts +6 -6
  6. package/components/data-pagination/data-pagination.js +1 -1
  7. package/components/dialog/dialog-base.js +1 -1
  8. package/components/dialog/dialog.js +12 -13
  9. package/components/dropdown/dropdown-base.js +2 -2
  10. package/components/dropdown/dropdown.js +1 -0
  11. package/components/footer-1177-pro/footer-1177-pro.d.ts +2 -1
  12. package/components/footer-1177-pro/footer-1177-pro.js +2 -2
  13. package/components/form/checkbox/checkbox-group-base.d.ts +2 -1
  14. package/components/form/checkbox/checkbox-group-base.js +4 -2
  15. package/components/form/checkbox/checkbox-group.d.ts +1 -0
  16. package/components/form/checkbox/checkbox-group.js +4 -3
  17. package/components/form/datepicker/datepicker.d.ts +2 -1
  18. package/components/form/datepicker/datepicker.js +60 -37
  19. package/components/form/input/input-base.d.ts +2 -1
  20. package/components/form/input/input-base.js +11 -10
  21. package/components/form/input/input.d.ts +1 -0
  22. package/components/form/radio/radio-group-base.d.ts +2 -1
  23. package/components/form/radio/radio-group-base.js +4 -2
  24. package/components/form/radio/radio-group.d.ts +1 -0
  25. package/components/form/radio-button/radio-button-group-base.js +1 -1
  26. package/components/form/select/select.d.ts +1 -1
  27. package/components/form/select/select.js +2 -2
  28. package/components/form/select-multiple/select-multiple-base.d.ts +3 -3
  29. package/components/form/select-multiple/select-multiple-base.js +5 -6
  30. package/components/form/select-multiple/select-multiple.d.ts +1 -1
  31. package/components/form/select-multiple/select-multiple.js +10 -23
  32. package/components/form/textarea/textarea.d.ts +1 -1
  33. package/components/form/textarea/textarea.js +2 -2
  34. package/components/form/time/time.d.ts +1 -1
  35. package/components/form/time/time.js +2 -2
  36. package/components/{alert → global-alert}/global-alert.d.ts +1 -1
  37. package/components/{alert → global-alert}/global-alert.js +2 -2
  38. package/components/header-1177/header-1177-avatar-base.d.ts +2 -1
  39. package/components/header-1177/header-1177-avatar-base.js +2 -2
  40. package/components/header-1177/header-1177-avatar.js +11 -24
  41. package/components/header-1177/header-1177-menu-mobile-base.d.ts +2 -1
  42. package/components/header-1177/header-1177-menu-mobile-base.js +2 -2
  43. package/components/header-1177/header-1177-menu-mobile.js +9 -14
  44. package/components/header-1177/header-1177-nav-item-base.d.ts +2 -1
  45. package/components/header-1177/header-1177-nav-item-base.js +2 -2
  46. package/components/header-1177/header-1177-nav-item.js +8 -14
  47. package/components/header-1177-admin/header-1177-admin-avatar-base.d.ts +2 -1
  48. package/components/header-1177-admin/header-1177-admin-avatar-base.js +2 -2
  49. package/components/header-1177-admin/header-1177-admin-avatar-mobile-base.d.ts +2 -1
  50. package/components/header-1177-admin/header-1177-admin-avatar-mobile-base.js +2 -2
  51. package/components/header-1177-admin/header-1177-admin-avatar-mobile.js +12 -24
  52. package/components/header-1177-admin/header-1177-admin-avatar.js +11 -14
  53. package/components/header-1177-admin/header-1177-admin-menu-mobile-base.d.ts +2 -1
  54. package/components/header-1177-admin/header-1177-admin-menu-mobile-base.js +2 -2
  55. package/components/header-1177-admin/header-1177-admin-menu-mobile.js +9 -14
  56. package/components/header-1177-admin/header-1177-admin-nav-item-base.d.ts +2 -1
  57. package/components/header-1177-admin/header-1177-admin-nav-item-base.js +2 -2
  58. package/components/header-1177-admin/header-1177-admin-nav-item.js +8 -14
  59. package/components/header-1177-pro/header-1177-pro-avatar-base.d.ts +2 -1
  60. package/components/header-1177-pro/header-1177-pro-avatar-base.js +2 -2
  61. package/components/header-1177-pro/header-1177-pro-avatar-mobile-base.d.ts +2 -1
  62. package/components/header-1177-pro/header-1177-pro-avatar-mobile-base.js +2 -2
  63. package/components/header-1177-pro/header-1177-pro-avatar-mobile.js +12 -24
  64. package/components/header-1177-pro/header-1177-pro-avatar.js +11 -14
  65. package/components/header-1177-pro/header-1177-pro-menu-mobile-base.d.ts +2 -1
  66. package/components/header-1177-pro/header-1177-pro-menu-mobile-base.js +2 -2
  67. package/components/header-1177-pro/header-1177-pro-menu-mobile.js +9 -14
  68. package/components/header-1177-pro/header-1177-pro-nav-item-base.d.ts +2 -1
  69. package/components/header-1177-pro/header-1177-pro-nav-item-base.js +2 -2
  70. package/components/header-1177-pro/header-1177-pro-nav-item.js +8 -14
  71. package/components/header-inera/header-inera-menu-mobile-base.d.ts +2 -1
  72. package/components/header-inera/header-inera-menu-mobile-base.js +2 -2
  73. package/components/header-inera/header-inera-menu-mobile.js +9 -14
  74. package/components/header-inera/header-inera-nav-item-base.d.ts +2 -1
  75. package/components/header-inera/header-inera-nav-item-base.js +2 -2
  76. package/components/header-inera/header-inera-nav-item.js +8 -14
  77. package/components/header-inera-admin/header-inera-admin-avatar-base.d.ts +2 -1
  78. package/components/header-inera-admin/header-inera-admin-avatar-base.js +2 -2
  79. package/components/header-inera-admin/header-inera-admin-avatar-mobile-base.d.ts +2 -1
  80. package/components/header-inera-admin/header-inera-admin-avatar-mobile-base.js +2 -2
  81. package/components/header-inera-admin/header-inera-admin-avatar-mobile.js +12 -24
  82. package/components/header-inera-admin/header-inera-admin-avatar.js +12 -16
  83. package/components/header-inera-admin/header-inera-admin-menu-mobile-base.d.ts +2 -1
  84. package/components/header-inera-admin/header-inera-admin-menu-mobile-base.js +2 -2
  85. package/components/header-inera-admin/header-inera-admin-menu-mobile.js +9 -14
  86. package/components/header-inera-admin/header-inera-admin-nav-item-base.d.ts +2 -1
  87. package/components/header-inera-admin/header-inera-admin-nav-item-base.js +2 -2
  88. package/components/header-inera-admin/header-inera-admin-nav-item.js +8 -14
  89. package/components/popover/popover-content.js +1 -1
  90. package/components/popover/popover.js +1 -1
  91. package/components/puff-list/puff-list-item-header.js +1 -1
  92. package/components/puff-list/puff-list-item.js +1 -1
  93. package/components/side-panel/side-panel-base.js +6 -2
  94. package/components/side-panel/side-panel.js +50 -41
  95. package/components/stepper/step-base.js +1 -1
  96. package/components/stepper/step.js +12 -10
  97. package/components/tabs/tab-panel.js +1 -1
  98. package/components/tabs/tab.js +1 -1
  99. package/components/tabs/tabs.d.ts +1 -1
  100. package/components/tabs/tabs.js +8 -4
  101. package/components/utils/hooks/useClickOutside.d.ts +10 -0
  102. package/components/utils/hooks/useClickOutside.js +31 -0
  103. package/components/utils/hooks/useEsc.d.ts +10 -0
  104. package/components/utils/hooks/useEsc.js +27 -0
  105. package/components/utils/hooks/useHasFocusableChildren.d.ts +2 -0
  106. package/components/utils/hooks/useHasFocusableChildren.js +25 -0
  107. package/index.d.ts +2 -2
  108. package/index.js +2 -2
  109. package/package.json +2 -2
  110. /package/components/{alert → global-alert}/global-alert-base.d.ts +0 -0
  111. /package/components/{alert → global-alert}/global-alert-base.js +0 -0
@@ -3,12 +3,22 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177AdminAvatarBase } from './header-1177-admin-avatar-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useFocusTrap } from '../utils/hooks/useFocusTrap.js';
7
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
8
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
9
 
7
10
  function IDSHeader1177AdminAvatar({ username, unit, expanded = false, children, persistent = false, ...props }) {
8
11
  const [isExpanded, setIsExpanded] = useState(expanded);
9
12
  const headerContext = useHeaderContext();
10
13
  const componentRef = useRef(null);
11
14
  const menuRef = useRef(null);
15
+ const buttonRef = useRef(null);
16
+ useFocusTrap(menuRef.current, isExpanded && !persistent);
17
+ useClickOutside([componentRef, buttonRef], () => {
18
+ if (!persistent)
19
+ setIsExpanded(false);
20
+ });
21
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
12
22
  const toggleAvatar = () => setIsExpanded(prev => !prev);
13
23
  const handleClickOutside = (event) => {
14
24
  if (!persistent && componentRef.current && !componentRef.current.contains(event.target)) {
@@ -29,20 +39,7 @@ function IDSHeader1177AdminAvatar({ username, unit, expanded = false, children,
29
39
  };
30
40
  }
31
41
  }, [isExpanded, persistent]);
32
- // Escape should also close
33
- useEffect(() => {
34
- if (!isExpanded || persistent)
35
- return;
36
- const handleKeyDown = (event) => {
37
- if (event.key === "Escape") {
38
- event.preventDefault();
39
- setIsExpanded(false);
40
- }
41
- };
42
- document.addEventListener("keydown", handleKeyDown);
43
- return () => document.removeEventListener("keydown", handleKeyDown);
44
- }, [isExpanded, persistent]);
45
- return (jsx(IDSHeader1177AdminAvatarBase, { ...props, client: true, username: username, unit: unit, children: children, componentRef: componentRef, menuRef: menuRef, expanded: isExpanded, unresponsive: headerContext?.unresponsive, hide: headerContext?.hideAvatar, onToggleAvatar: toggleAvatar }));
42
+ return (jsx(IDSHeader1177AdminAvatarBase, { ...props, client: true, username: username, unit: unit, children: children, componentRef: componentRef, menuRef: menuRef, buttonRef: buttonRef, expanded: isExpanded, unresponsive: headerContext?.unresponsive, hide: headerContext?.hideAvatar, onToggleAvatar: toggleAvatar }));
46
43
  }
47
44
 
48
45
  export { IDSHeader1177AdminAvatar };
@@ -6,6 +6,7 @@ interface IDSHeader1177AdminMenuMobileBaseProps extends HTMLAttributes<HTMLEleme
6
6
  client?: boolean;
7
7
  onToggleMenu?: () => void;
8
8
  componentRef?: React.RefObject<HTMLDivElement>;
9
+ buttonRef?: React.RefObject<HTMLButtonElement>;
9
10
  }
10
- export declare function IDSHeader1177AdminMenuMobileBase({ srLabel, expanded, unresponsive, client, children, componentRef, onToggleMenu, ...props }: IDSHeader1177AdminMenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function IDSHeader1177AdminMenuMobileBase({ srLabel, expanded, unresponsive, client, children, componentRef, buttonRef, onToggleMenu, ...props }: IDSHeader1177AdminMenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
11
12
  export {};
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeader1177AdminMenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive = false, client = false, children, componentRef, onToggleMenu, ...props }) {
5
+ function IDSHeader1177AdminMenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive = false, client = false, children, componentRef, buttonRef, onToggleMenu, ...props }) {
6
6
  const menuId = `header-1177-admin-menu-${useId()}`;
7
7
  const toggleHandler = client && onToggleMenu
8
8
  ? {
@@ -12,7 +12,7 @@ function IDSHeader1177AdminMenuMobileBase({ srLabel = "Meny", expanded = false,
12
12
  return (jsxs("div", { className: clsx("ids-header-1177-admin-menu-mobile", {
13
13
  "ids-header-1177-admin-menu-mobile--expanded": expanded,
14
14
  "ids-header-1177-admin-menu-mobile--unresponsive": unresponsive
15
- }), ref: componentRef, ...props, children: [jsx("button", { ...toggleHandler, "aria-expanded": expanded, "aria-label": srLabel, "aria-controls": menuId, className: "ids-header-1177-admin-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("span", { className: "ids-hamburger__lines" }) }) }), jsx("div", { id: menuId, className: "ids-header-1177-admin-menu-mobile__items", children: children })] }));
15
+ }), ref: componentRef, ...props, children: [jsx("button", { ...toggleHandler, ref: buttonRef, "aria-expanded": expanded, "aria-label": srLabel, "aria-controls": menuId, className: "ids-header-1177-admin-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("span", { className: "ids-hamburger__lines" }) }) }), jsx("div", { id: menuId, className: "ids-header-1177-admin-menu-mobile__items", children: children })] }));
16
16
  }
17
17
 
18
18
  export { IDSHeader1177AdminMenuMobileBase };
@@ -3,11 +3,19 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177AdminMenuMobileBase } from './header-1177-admin-menu-mobile-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
7
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
8
 
7
9
  function IDSHeader1177AdminMenuMobile({ srLabel = "Meny", persistent = false, expanded = false, children, onExpanded, onClosed, ...props }) {
8
10
  const [isExpanded, setIsExpanded] = useState(expanded);
9
11
  const headerContext = useHeaderContext();
10
12
  const componentRef = useRef(null);
13
+ const buttonRef = useRef(null);
14
+ useClickOutside([componentRef, buttonRef], () => {
15
+ if (!persistent)
16
+ setIsExpanded(false);
17
+ });
18
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
11
19
  const handleLinkClick = () => {
12
20
  setIsExpanded(false);
13
21
  onClosed?.();
@@ -36,20 +44,7 @@ function IDSHeader1177AdminMenuMobile({ srLabel = "Meny", persistent = false, ex
36
44
  setIsExpanded(next);
37
45
  next ? onExpanded?.() : onClosed?.();
38
46
  };
39
- // Escape should also close
40
- useEffect(() => {
41
- if (!isExpanded || persistent)
42
- return;
43
- const handleKeyDown = (event) => {
44
- if (event.key === "Escape") {
45
- event.preventDefault();
46
- setIsExpanded(false);
47
- }
48
- };
49
- document.addEventListener("keydown", handleKeyDown);
50
- return () => document.removeEventListener("keydown", handleKeyDown);
51
- }, [isExpanded, persistent]);
52
- return (jsx(IDSHeader1177AdminMenuMobileBase, { ...props, client: true, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef, children: children }));
47
+ return (jsx(IDSHeader1177AdminMenuMobileBase, { ...props, client: true, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef, buttonRef: buttonRef, children: children }));
53
48
  }
54
49
 
55
50
  export { IDSHeader1177AdminMenuMobile };
@@ -11,8 +11,9 @@ export interface IDSHeader1177AdminNavItemBaseProps extends HTMLAttributes<HTMLE
11
11
  col4?: ReactElement<IDSLinkProps>[];
12
12
  componentRef?: React.Ref<HTMLDivElement>;
13
13
  menuRef?: React.Ref<HTMLDivElement>;
14
+ buttonRef?: React.Ref<HTMLButtonElement>;
14
15
  onToggleItem?: () => void;
15
16
  unresponsive?: boolean;
16
17
  client?: boolean;
17
18
  }
18
- export declare function IDSHeader1177AdminNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, unresponsive, client, ...props }: IDSHeader1177AdminNavItemBaseProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function IDSHeader1177AdminNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, unresponsive, client, ...props }: IDSHeader1177AdminNavItemBaseProps): import("react/jsx-runtime").JSX.Element;
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import React, { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeader1177AdminNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, unresponsive, client = false, ...props }) {
5
+ function IDSHeader1177AdminNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, unresponsive, client = false, ...props }) {
6
6
  const itemId = `header-1177-admin-item-${useId()}`;
7
7
  const toggleHandler = client && onToggleItem
8
8
  ? {
@@ -13,7 +13,7 @@ function IDSHeader1177AdminNavItemBase({ label, active, expanded, children, col1
13
13
  "ids-header-1177-admin-nav-item--unresponsive": unresponsive,
14
14
  "ids-header-1177-admin-nav-item--expanded": expanded,
15
15
  "ids-header-1177-admin-nav-item--active": active
16
- }), children: [!!label && (jsx("button", { "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-1177-admin-nav-item__button", ...toggleHandler, children: label })), !label && (jsx("span", { className: "ids-header-1177-admin-nav-item__link", children: React.Children.map(children, child => {
16
+ }), children: [!!label && (jsx("button", { ref: buttonRef, "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-1177-admin-nav-item__button", ...toggleHandler, children: label })), !label && (jsx("span", { className: "ids-header-1177-admin-nav-item__link", children: React.Children.map(children, child => {
17
17
  if (!React.isValidElement(child))
18
18
  return child;
19
19
  const element = child;
@@ -3,12 +3,19 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177AdminNavItemBase } from './header-1177-admin-nav-item-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
7
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
8
 
7
9
  function IDSHeader1177AdminNavItem({ expanded = false, ...props }) {
8
10
  const headerContext = useHeaderContext();
9
11
  const [isExpanded, setIsExpanded] = useState(expanded);
10
12
  const componentRef = useRef(null);
11
13
  const menuRef = useRef(null);
14
+ const buttonRef = useRef(null);
15
+ useClickOutside([componentRef, buttonRef], () => {
16
+ setIsExpanded(false);
17
+ });
18
+ useEsc(() => setIsExpanded(false), buttonRef);
12
19
  useEffect(() => {
13
20
  setIsExpanded(expanded);
14
21
  }, [expanded]);
@@ -37,20 +44,7 @@ function IDSHeader1177AdminNavItem({ expanded = false, ...props }) {
37
44
  menuEl.addEventListener("click", onMenuClick);
38
45
  return () => menuEl.removeEventListener("click", onMenuClick);
39
46
  }, [menuRef]);
40
- // Escape should also close
41
- useEffect(() => {
42
- if (!isExpanded)
43
- return;
44
- const handleKeyDown = (event) => {
45
- if (event.key === "Escape") {
46
- event.preventDefault();
47
- setIsExpanded(false);
48
- }
49
- };
50
- document.addEventListener("keydown", handleKeyDown);
51
- return () => document.removeEventListener("keydown", handleKeyDown);
52
- }, [isExpanded]);
53
- return (jsx(IDSHeader1177AdminNavItemBase, { ...props, client: true, expanded: isExpanded, unresponsive: headerContext?.unresponsive, componentRef: componentRef, menuRef: menuRef, onToggleItem: () => setIsExpanded((prev) => !prev) }));
47
+ return (jsx(IDSHeader1177AdminNavItemBase, { ...props, client: true, expanded: isExpanded, unresponsive: headerContext?.unresponsive, componentRef: componentRef, menuRef: menuRef, buttonRef: buttonRef, onToggleItem: () => setIsExpanded((prev) => !prev) }));
54
48
  }
55
49
 
56
50
  export { IDSHeader1177AdminNavItem };
@@ -8,7 +8,8 @@ export interface IDSHeader1177ProAvatarBaseProps extends HTMLAttributes<HTMLElem
8
8
  onToggleAvatar?: () => void;
9
9
  componentRef?: React.RefObject<HTMLDivElement>;
10
10
  menuRef?: React.RefObject<HTMLDivElement>;
11
+ buttonRef?: React.RefObject<HTMLButtonElement>;
11
12
  unresponsive?: boolean;
12
13
  client?: boolean;
13
14
  }
14
- export declare function IDSHeader1177ProAvatarBase({ username, unit, children, expanded, hide, client, onToggleAvatar, componentRef, menuRef, unresponsive }: IDSHeader1177ProAvatarBaseProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function IDSHeader1177ProAvatarBase({ username, unit, children, expanded, hide, client, onToggleAvatar, componentRef, menuRef, buttonRef, unresponsive }: IDSHeader1177ProAvatarBaseProps): import("react/jsx-runtime").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import clsx from 'clsx';
3
3
  import { useId } from 'react';
4
4
 
5
- function IDSHeader1177ProAvatarBase({ username, unit, children, expanded = false, hide = false, client = false, onToggleAvatar, componentRef, menuRef, unresponsive }) {
5
+ function IDSHeader1177ProAvatarBase({ username, unit, children, expanded = false, hide = false, client = false, onToggleAvatar, componentRef, menuRef, buttonRef, unresponsive }) {
6
6
  if (hide)
7
7
  return null;
8
8
  const menuId = `header-1177-pro-avatar-menu-${useId()}`;
@@ -15,7 +15,7 @@ function IDSHeader1177ProAvatarBase({ username, unit, children, expanded = false
15
15
  "ids-header-1177-pro-avatar--unresponsive": unresponsive
16
16
  }), children: jsxs("div", { className: "ids-header-1177-pro-avatar__menu-wrapper", children: [jsxs("button", { className: clsx("ids-header-1177-pro-avatar__button", {
17
17
  "ids-header-1177-pro-avatar__button--expanded": expanded
18
- }), ...toggleHandler, "aria-controls": menuId, "aria-expanded": expanded, children: [jsx("div", { className: "ids-header-1177-pro-avatar__name", title: username, children: username }), unit && (jsx("div", { className: "ids-header-1177-pro-avatar__unit", title: unit, children: unit }))] }), jsx("div", { ref: menuRef || undefined, id: menuId, className: clsx("ids-header-1177-pro-avatar__menu", {
18
+ }), ...toggleHandler, ref: buttonRef, "aria-controls": menuId, "aria-expanded": expanded, children: [jsx("div", { className: "ids-header-1177-pro-avatar__name", title: username, children: username }), unit && (jsx("div", { className: "ids-header-1177-pro-avatar__unit", title: unit, children: unit }))] }), jsx("div", { ref: menuRef || undefined, id: menuId, className: clsx("ids-header-1177-pro-avatar__menu", {
19
19
  "ids-header-1177-pro-avatar__menu--expanded": expanded
20
20
  }), children: children && jsx("div", { className: "ids-header-1177-pro-avatar__menu-links", children: children }) })] }) }));
21
21
  }
@@ -8,6 +8,7 @@ interface IDSHeader1177ProAvatarMobileBaseProps extends HTMLAttributes<HTMLDivEl
8
8
  onToggleMenu?: () => void;
9
9
  client?: ReactNode;
10
10
  componentRef?: React.RefObject<HTMLDivElement>;
11
+ buttonRef?: React.RefObject<HTMLButtonElement>;
11
12
  }
12
- export declare function IDSHeader1177ProAvatarMobileBase({ username, unit, expanded, hide, unresponsive, componentRef, children, client, onToggleMenu, ...props }: IDSHeader1177ProAvatarMobileBaseProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function IDSHeader1177ProAvatarMobileBase({ username, unit, expanded, hide, unresponsive, componentRef, buttonRef, children, client, onToggleMenu, ...props }: IDSHeader1177ProAvatarMobileBaseProps): import("react/jsx-runtime").JSX.Element;
13
14
  export {};
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeader1177ProAvatarMobileBase({ username = "", unit = "", expanded, hide, unresponsive, componentRef, children, client = false, onToggleMenu, ...props }) {
5
+ function IDSHeader1177ProAvatarMobileBase({ username = "", unit = "", expanded, hide, unresponsive, componentRef, buttonRef, children, client = false, onToggleMenu, ...props }) {
6
6
  if (hide)
7
7
  return null;
8
8
  const menuId = `header-1177-pro-avatar-mobile-menu-${useId()}`;
@@ -14,7 +14,7 @@ function IDSHeader1177ProAvatarMobileBase({ username = "", unit = "", expanded,
14
14
  return (jsx("div", { ref: componentRef, className: clsx("ids-header-1177-pro-avatar-mobile", {
15
15
  "ids-header-1177-pro-avatar-mobile--logged-in": !!username,
16
16
  "ids-header-1177-pro-avatar-mobile--unresponsive": unresponsive
17
- }), ...props, children: jsxs("div", { className: "ids-header-1177-pro-avatar-mobile__menu-wrapper", children: [jsx("button", { ...toggleHandler, className: clsx("ids-header-1177-pro-avatar-mobile__button", {
17
+ }), ...props, children: jsxs("div", { className: "ids-header-1177-pro-avatar-mobile__menu-wrapper", children: [jsx("button", { ...toggleHandler, ref: buttonRef, className: clsx("ids-header-1177-pro-avatar-mobile__button", {
18
18
  "ids-header-1177-pro-avatar-mobile__button--expanded": expanded
19
19
  }), "aria-controls": menuId, "aria-expanded": expanded, children: jsx("div", { className: "ids-header-1177-pro-avatar-mobile-content__name", title: username, children: username }) }), jsxs("div", { id: menuId, className: clsx("ids-header-1177-pro-avatar-mobile__menu", {
20
20
  "ids-header-1177-pro-avatar-mobile__menu--expanded": expanded
@@ -1,37 +1,25 @@
1
1
  "use client";
2
2
  import { jsx } from 'react/jsx-runtime';
3
- import { useState, useRef, useEffect } from 'react';
3
+ import { useState, useRef } from 'react';
4
4
  import { IDSHeader1177ProAvatarMobileBase } from './header-1177-pro-avatar-mobile-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useFocusTrap } from '../utils/hooks/useFocusTrap.js';
7
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
8
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
9
 
7
10
  function IDSHeader1177ProAvatarMobile({ username = "", unit = "", expanded = false, persistent = false, children, ...props }) {
8
11
  const [isExpanded, setIsExpanded] = useState(expanded);
9
12
  const headerContext = useHeaderContext();
10
13
  const componentRef = useRef(null);
11
- const toggleAvatar = () => setIsExpanded(prev => !prev);
12
- const handleClickOutside = (event) => {
13
- if (!persistent && componentRef.current && !componentRef.current.contains(event.target)) {
14
+ const buttonRef = useRef(null);
15
+ useFocusTrap(componentRef.current, isExpanded && !persistent);
16
+ useClickOutside([componentRef, buttonRef], () => {
17
+ if (!persistent)
14
18
  setIsExpanded(false);
15
- }
16
- };
17
- useEffect(() => {
18
- document.addEventListener("click", handleClickOutside);
19
- return () => document.removeEventListener("click", handleClickOutside);
20
- }, [persistent]);
21
- // Escape should also close
22
- useEffect(() => {
23
- if (!isExpanded || persistent)
24
- return;
25
- const handleKeyDown = (event) => {
26
- if (event.key === "Escape") {
27
- event.preventDefault();
28
- setIsExpanded(false);
29
- }
30
- };
31
- document.addEventListener("keydown", handleKeyDown);
32
- return () => document.removeEventListener("keydown", handleKeyDown);
33
- }, [isExpanded, persistent]);
34
- return (jsx(IDSHeader1177ProAvatarMobileBase, { ...props, client: true, username: username, unit: unit, expanded: isExpanded, componentRef: componentRef, onToggleMenu: username ? toggleAvatar : undefined, hide: headerContext?.hideAvatar ?? false, unresponsive: headerContext?.unresponsive ?? false, children: children }));
19
+ });
20
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
21
+ const toggleAvatar = () => setIsExpanded(prev => !prev);
22
+ return (jsx(IDSHeader1177ProAvatarMobileBase, { ...props, client: true, username: username, unit: unit, expanded: isExpanded, componentRef: componentRef, buttonRef: buttonRef, onToggleMenu: username ? toggleAvatar : undefined, hide: headerContext?.hideAvatar ?? false, unresponsive: headerContext?.unresponsive ?? false, children: children }));
35
23
  }
36
24
 
37
25
  export { IDSHeader1177ProAvatarMobile };
@@ -3,6 +3,9 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useId, useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177ProAvatarBase } from './header-1177-pro-avatar-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useFocusTrap } from '../utils/hooks/useFocusTrap.js';
7
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
8
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
9
 
7
10
  function IDSHeader1177ProAvatar({ username, unit, expanded = false, children, persistent = false, ...props }) {
8
11
  useId();
@@ -10,6 +13,13 @@ function IDSHeader1177ProAvatar({ username, unit, expanded = false, children, pe
10
13
  const headerContext = useHeaderContext();
11
14
  const componentRef = useRef(null);
12
15
  const menuRef = useRef(null);
16
+ const buttonRef = useRef(null);
17
+ useFocusTrap(componentRef.current, isExpanded && !persistent);
18
+ useClickOutside([componentRef, buttonRef], () => {
19
+ if (!persistent)
20
+ setIsExpanded(false);
21
+ });
22
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
13
23
  const toggleAvatar = () => setIsExpanded(prev => !prev);
14
24
  useEffect(() => {
15
25
  const clickOutsideHandler = (e) => {
@@ -30,20 +40,7 @@ function IDSHeader1177ProAvatar({ username, unit, expanded = false, children, pe
30
40
  };
31
41
  }
32
42
  }, [isExpanded, persistent]);
33
- // Escape should also close
34
- useEffect(() => {
35
- if (!isExpanded || persistent)
36
- return;
37
- const handleKeyDown = (event) => {
38
- if (event.key === "Escape") {
39
- event.preventDefault();
40
- setIsExpanded(false);
41
- }
42
- };
43
- document.addEventListener("keydown", handleKeyDown);
44
- return () => document.removeEventListener("keydown", handleKeyDown);
45
- }, [isExpanded, persistent]);
46
- return (jsx(IDSHeader1177ProAvatarBase, { ...props, client: true, username: username, unit: unit, children: children, componentRef: componentRef, menuRef: menuRef, expanded: isExpanded, onToggleAvatar: toggleAvatar, unresponsive: headerContext?.unresponsive ?? false, hide: headerContext?.hideAvatar ?? false }));
43
+ return (jsx(IDSHeader1177ProAvatarBase, { ...props, client: true, username: username, unit: unit, children: children, componentRef: componentRef, menuRef: menuRef, buttonRef: buttonRef, expanded: isExpanded, onToggleAvatar: toggleAvatar, unresponsive: headerContext?.unresponsive ?? false, hide: headerContext?.hideAvatar ?? false }));
47
44
  }
48
45
 
49
46
  export { IDSHeader1177ProAvatar };
@@ -6,6 +6,7 @@ interface IDSHeader1177ProMenuMobileBaseProps extends HTMLAttributes<HTMLElement
6
6
  onToggleMenu?: () => void;
7
7
  client?: boolean;
8
8
  componentRef?: React.RefObject<HTMLDivElement>;
9
+ buttonRef?: React.RefObject<HTMLButtonElement>;
9
10
  }
10
- export declare function IDSHeader1177ProMenuMobileBase({ srLabel, expanded, unresponsive, children, componentRef, client, onToggleMenu, ...props }: IDSHeader1177ProMenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function IDSHeader1177ProMenuMobileBase({ srLabel, expanded, unresponsive, children, componentRef, buttonRef, client, onToggleMenu, ...props }: IDSHeader1177ProMenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
11
12
  export {};
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeader1177ProMenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive = false, children, componentRef, client = false, onToggleMenu, ...props }) {
5
+ function IDSHeader1177ProMenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive = false, children, componentRef, buttonRef, client = false, onToggleMenu, ...props }) {
6
6
  const menuId = `header-1177-pro-menu-${useId()}`;
7
7
  const toggleHandler = client && onToggleMenu
8
8
  ? {
@@ -12,7 +12,7 @@ function IDSHeader1177ProMenuMobileBase({ srLabel = "Meny", expanded = false, un
12
12
  return (jsxs("div", { className: clsx("ids-header-1177-pro-menu-mobile", {
13
13
  "ids-header-1177-pro-menu-mobile--expanded": expanded,
14
14
  "ids-header-1177-pro-menu-mobile--unresponsive": unresponsive
15
- }), ref: componentRef, ...props, children: [jsx("button", { ...toggleHandler, "aria-expanded": expanded, "aria-label": srLabel, "aria-controls": menuId, className: "ids-header-1177-pro-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("span", { className: "ids-hamburger__lines" }) }) }), jsx("div", { id: menuId, className: "ids-header-1177-pro-menu-mobile__items", children: children })] }));
15
+ }), ref: componentRef, ...props, children: [jsx("button", { ...toggleHandler, ref: buttonRef, "aria-expanded": expanded, "aria-label": srLabel, "aria-controls": menuId, className: "ids-header-1177-pro-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("span", { className: "ids-hamburger__lines" }) }) }), jsx("div", { id: menuId, className: "ids-header-1177-pro-menu-mobile__items", children: children })] }));
16
16
  }
17
17
 
18
18
  export { IDSHeader1177ProMenuMobileBase };
@@ -3,11 +3,19 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177ProMenuMobileBase } from './header-1177-pro-menu-mobile-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
7
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
8
 
7
9
  function IDSHeader1177ProMenuMobile({ srLabel = "Meny", persistent = false, expanded = false, children, onExpanded, onClosed, ...props }) {
8
10
  const [isExpanded, setIsExpanded] = useState(expanded);
9
11
  const headerContext = useHeaderContext();
10
12
  const componentRef = useRef(null);
13
+ const buttonRef = useRef(null);
14
+ useClickOutside([componentRef, buttonRef], () => {
15
+ if (!persistent)
16
+ setIsExpanded(false);
17
+ });
18
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
11
19
  const handleLinkClick = () => {
12
20
  setIsExpanded(false);
13
21
  onClosed?.();
@@ -36,20 +44,7 @@ function IDSHeader1177ProMenuMobile({ srLabel = "Meny", persistent = false, expa
36
44
  setIsExpanded(next);
37
45
  next ? onExpanded?.() : onClosed?.();
38
46
  };
39
- // Escape should also close
40
- useEffect(() => {
41
- if (!isExpanded || persistent)
42
- return;
43
- const handleKeyDown = (event) => {
44
- if (event.key === "Escape") {
45
- event.preventDefault();
46
- setIsExpanded(false);
47
- }
48
- };
49
- document.addEventListener("keydown", handleKeyDown);
50
- return () => document.removeEventListener("keydown", handleKeyDown);
51
- }, [isExpanded, persistent]);
52
- return (jsx(IDSHeader1177ProMenuMobileBase, { ...props, client: true, children: children, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef }));
47
+ return (jsx(IDSHeader1177ProMenuMobileBase, { ...props, client: true, children: children, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef, buttonRef: buttonRef }));
53
48
  }
54
49
 
55
50
  export { IDSHeader1177ProMenuMobile };
@@ -11,8 +11,9 @@ export interface IDSHeader1177ProNavItemBaseProps extends HTMLAttributes<HTMLEle
11
11
  col4?: ReactElement<IDSLinkProps>[];
12
12
  componentRef?: React.Ref<HTMLDivElement>;
13
13
  menuRef?: React.Ref<HTMLDivElement>;
14
+ buttonRef?: React.Ref<HTMLButtonElement>;
14
15
  onToggleItem?: () => void;
15
16
  unresponsive?: boolean;
16
17
  client?: boolean;
17
18
  }
18
- export declare function IDSHeader1177ProNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, unresponsive, client, ...props }: IDSHeader1177ProNavItemBaseProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function IDSHeader1177ProNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, unresponsive, client, ...props }: IDSHeader1177ProNavItemBaseProps): import("react/jsx-runtime").JSX.Element;
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import React, { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeader1177ProNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, unresponsive, client = false, ...props }) {
5
+ function IDSHeader1177ProNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, unresponsive, client = false, ...props }) {
6
6
  const itemId = `header-1177-pro-item-${useId()}`;
7
7
  const toggleHandler = client && onToggleItem
8
8
  ? {
@@ -13,7 +13,7 @@ function IDSHeader1177ProNavItemBase({ label, active, expanded, children, col1,
13
13
  "ids-header-1177-pro-nav-item--unresponsive": unresponsive,
14
14
  "ids-header-1177-pro-nav-item--expanded": expanded,
15
15
  "ids-header-1177-pro-nav-item--active": active
16
- }), ...props, children: [!!label && (jsx("button", { "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-1177-pro-nav-item__button", ...toggleHandler, children: label })), !label && (jsx("span", { className: "ids-header-1177-pro-nav-item__link", children: React.Children.map(children, child => {
16
+ }), ...props, children: [!!label && (jsx("button", { ref: buttonRef, "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-1177-pro-nav-item__button", ...toggleHandler, children: label })), !label && (jsx("span", { className: "ids-header-1177-pro-nav-item__link", children: React.Children.map(children, child => {
17
17
  if (!React.isValidElement(child))
18
18
  return child;
19
19
  const element = child;
@@ -3,12 +3,19 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177ProNavItemBase } from './header-1177-pro-nav-item-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
7
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
8
 
7
9
  function IDSHeader1177ProNavItem({ expanded = false, ...props }) {
8
10
  const headerContext = useHeaderContext();
9
11
  const [isExpanded, setIsExpanded] = useState(expanded);
10
12
  const componentRef = useRef(null);
11
13
  const menuRef = useRef(null);
14
+ const buttonRef = useRef(null);
15
+ useClickOutside([componentRef, buttonRef], () => {
16
+ setIsExpanded(false);
17
+ });
18
+ useEsc(() => setIsExpanded(false), buttonRef);
12
19
  useEffect(() => {
13
20
  setIsExpanded(expanded);
14
21
  }, [expanded]);
@@ -37,20 +44,7 @@ function IDSHeader1177ProNavItem({ expanded = false, ...props }) {
37
44
  menuEl.addEventListener("click", onMenuClick);
38
45
  return () => menuEl.removeEventListener("click", onMenuClick);
39
46
  }, [menuRef]);
40
- // Escape should also close
41
- useEffect(() => {
42
- if (!isExpanded)
43
- return;
44
- const handleKeyDown = (event) => {
45
- if (event.key === "Escape") {
46
- event.preventDefault();
47
- setIsExpanded(false);
48
- }
49
- };
50
- document.addEventListener("keydown", handleKeyDown);
51
- return () => document.removeEventListener("keydown", handleKeyDown);
52
- }, [isExpanded]);
53
- return (jsx(IDSHeader1177ProNavItemBase, { ...props, client: true, expanded: isExpanded, unresponsive: headerContext?.unresponsive, componentRef: componentRef, menuRef: menuRef, onToggleItem: () => setIsExpanded((prev) => !prev) }));
47
+ return (jsx(IDSHeader1177ProNavItemBase, { ...props, client: true, expanded: isExpanded, unresponsive: headerContext?.unresponsive, componentRef: componentRef, menuRef: menuRef, buttonRef: buttonRef, onToggleItem: () => setIsExpanded((prev) => !prev) }));
54
48
  }
55
49
 
56
50
  export { IDSHeader1177ProNavItem };
@@ -5,7 +5,8 @@ interface IDSHeaderIneraMenuMobileBaseProps extends HTMLAttributes<HTMLElement>
5
5
  unresponsive?: boolean;
6
6
  onToggleMenu?: () => void;
7
7
  componentRef?: React.RefObject<HTMLDivElement>;
8
+ buttonRef?: React.RefObject<HTMLButtonElement>;
8
9
  client?: boolean;
9
10
  }
10
- export declare function IDSHeaderIneraMenuMobileBase({ srLabel, expanded, unresponsive, children, componentRef, onToggleMenu, client, ...props }: IDSHeaderIneraMenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function IDSHeaderIneraMenuMobileBase({ srLabel, expanded, unresponsive, children, componentRef, buttonRef, onToggleMenu, client, ...props }: IDSHeaderIneraMenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
11
12
  export {};
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeaderIneraMenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive = false, children, componentRef, onToggleMenu, client = false, ...props }) {
5
+ function IDSHeaderIneraMenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive = false, children, componentRef, buttonRef, onToggleMenu, client = false, ...props }) {
6
6
  const menuId = `header-inera-menu-${useId()}`;
7
7
  const toggleHandler = client && onToggleMenu
8
8
  ? {
@@ -12,7 +12,7 @@ function IDSHeaderIneraMenuMobileBase({ srLabel = "Meny", expanded = false, unre
12
12
  return (jsxs("div", { ...props, ref: componentRef, className: clsx("ids-header-inera-menu-mobile", {
13
13
  "ids-header-inera-menu-mobile--expanded": expanded,
14
14
  "ids-header-inera-menu-mobile--unresponsive": unresponsive
15
- }), children: [jsx("button", { "aria-label": srLabel, "aria-expanded": expanded, "aria-controls": menuId, ...toggleHandler, className: "ids-header-inera-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("div", { className: "ids-hamburger__lines" }) }) }), jsx("div", { id: menuId, className: "ids-header-inera-menu-mobile__items", children: children })] }));
15
+ }), children: [jsx("button", { "aria-label": srLabel, "aria-expanded": expanded, "aria-controls": menuId, ...toggleHandler, ref: buttonRef, className: "ids-header-inera-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("div", { className: "ids-hamburger__lines" }) }) }), jsx("div", { id: menuId, className: "ids-header-inera-menu-mobile__items", children: children })] }));
16
16
  }
17
17
 
18
18
  export { IDSHeaderIneraMenuMobileBase };
@@ -3,11 +3,19 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeaderIneraMenuMobileBase } from './header-inera-menu-mobile-base.js';
5
5
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
6
+ import { useClickOutside } from '../utils/hooks/useClickOutside.js';
7
+ import { useEsc } from '../utils/hooks/useEsc.js';
6
8
 
7
9
  function IDSHeaderIneraMenuMobile({ srLabel = "Meny", expanded = false, persistent = false, children, onExpanded, onClosed, ...props }) {
8
10
  const [isExpanded, setIsExpanded] = useState(expanded);
9
11
  const headerContext = useHeaderContext();
10
12
  const componentRef = useRef(null);
13
+ const buttonRef = useRef(null);
14
+ useClickOutside([componentRef, buttonRef], () => {
15
+ if (!persistent)
16
+ setIsExpanded(false);
17
+ });
18
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
11
19
  const handleLinkClick = () => {
12
20
  setIsExpanded(false);
13
21
  onClosed?.();
@@ -36,20 +44,7 @@ function IDSHeaderIneraMenuMobile({ srLabel = "Meny", expanded = false, persiste
36
44
  setIsExpanded(next);
37
45
  next ? onExpanded?.() : onClosed?.();
38
46
  };
39
- // Escape should also close
40
- useEffect(() => {
41
- if (!isExpanded || persistent)
42
- return;
43
- const handleKeyDown = (event) => {
44
- if (event.key === "Escape") {
45
- event.preventDefault();
46
- setIsExpanded(false);
47
- }
48
- };
49
- document.addEventListener("keydown", handleKeyDown);
50
- return () => document.removeEventListener("keydown", handleKeyDown);
51
- }, [isExpanded, persistent]);
52
- return (jsx(IDSHeaderIneraMenuMobileBase, { ...props, client: true, children: children, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef }));
47
+ return (jsx(IDSHeaderIneraMenuMobileBase, { ...props, client: true, children: children, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef, buttonRef: buttonRef }));
53
48
  }
54
49
 
55
50
  export { IDSHeaderIneraMenuMobile };
@@ -11,7 +11,8 @@ export interface IDSHeaderIneraNavItemBaseProps extends HTMLAttributes<HTMLEleme
11
11
  col4?: React.ReactNode;
12
12
  componentRef?: React.Ref<HTMLDivElement>;
13
13
  menuRef?: React.Ref<HTMLDivElement>;
14
+ buttonRef?: React.Ref<HTMLButtonElement>;
14
15
  onToggleItem?: () => void;
15
16
  unresponsive?: boolean;
16
17
  }
17
- export declare function IDSHeaderIneraNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, unresponsive, ...props }: IDSHeaderIneraNavItemBaseProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function IDSHeaderIneraNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, unresponsive, ...props }: IDSHeaderIneraNavItemBaseProps): import("react/jsx-runtime").JSX.Element;
@@ -2,13 +2,13 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import React, { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeaderIneraNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, unresponsive, ...props }) {
5
+ function IDSHeaderIneraNavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, unresponsive, ...props }) {
6
6
  const itemId = `header-inera-item-${useId()}`;
7
7
  return (jsxs("div", { ...props, ref: componentRef, className: clsx("ids-header-inera-nav-item", {
8
8
  "ids-header-inera-nav-item--unresponsive": unresponsive,
9
9
  "ids-header-inera-nav-item--expanded": expanded,
10
10
  "ids-header-inera-nav-item--active": active
11
- }), children: [!!label && (jsx("button", { "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-inera-nav-item__button", onClick: onToggleItem, children: label })), !label && (jsx("span", { className: "ids-header-inera-nav-item__link", children: React.Children.map(children, child => {
11
+ }), children: [!!label && (jsx("button", { ref: buttonRef, "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-inera-nav-item__button", onClick: onToggleItem, children: label })), !label && (jsx("span", { className: "ids-header-inera-nav-item__link", children: React.Children.map(children, child => {
12
12
  if (!React.isValidElement(child))
13
13
  return child;
14
14
  const element = child;