@inera/ids-react 9.0.4 → 9.1.0

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 +10 -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
@@ -2,6 +2,7 @@ import { ChangeEvent, ReactNode, FieldsetHTMLAttributes } from "react";
2
2
  interface IDSRadioGroupProps extends FieldsetHTMLAttributes<HTMLFieldSetElement> {
3
3
  name?: string;
4
4
  legend?: string;
5
+ hideLegend?: boolean;
5
6
  errorMsg?: string | ReactNode;
6
7
  compact?: boolean;
7
8
  invalid?: boolean;
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import clsx from 'clsx';
3
3
 
4
4
  function IDSRadioButtonGroupBase({ legend, groupRef, tooltip, children, className, ...props }) {
5
- return (jsxs("fieldset", { ref: groupRef, className: clsx("ids-form-group__fieldset", className), ...props, children: [legend ? (jsx("div", { className: "ids-label-wrapper", children: jsxs("legend", { children: [legend, tooltip && jsx("span", { className: "ids-legend__tooltip", children: tooltip })] }) })) : null, children] }));
5
+ return (jsxs("fieldset", { ref: groupRef, className: clsx("ids-form-group__fieldset", "ids-form-group__fieldset--compact", className), ...props, children: [legend ? (jsx("div", { className: "ids-label-wrapper", children: jsxs("legend", { children: [legend, tooltip && jsx("span", { className: "ids-legend__tooltip", children: tooltip })] }) })) : null, children] }));
6
6
  }
7
7
 
8
8
  export { IDSRadioButtonGroupBase };
@@ -11,6 +11,6 @@ export interface IDSSelectProps extends SelectHTMLAttributes<HTMLSelectElement>
11
11
  tooltip?: ReactNode;
12
12
  dataTestId?: string;
13
13
  noValidation?: boolean;
14
- validatesOnBlur?: boolean;
14
+ validationOnBlur?: boolean;
15
15
  }
16
16
  export declare const IDSSelect: import("react").ForwardRefExoticComponent<IDSSelectProps & import("react").RefAttributes<HTMLSelectElement>>;
@@ -4,10 +4,10 @@ import { forwardRef, useId, useRef, useImperativeHandle } from 'react';
4
4
  import { useInputValidity } from '../form-hooks/useInputValidity.js';
5
5
  import { IDSSelectBase } from './select-base.js';
6
6
 
7
- const IDSSelect = forwardRef(({ id, label, errorMsg, disabled, required, light, focusAnchor, tooltip, dataTestId, invalid = false, noValidation = false, validatesOnBlur = false, children, style, ...props }, ref) => {
7
+ const IDSSelect = forwardRef(({ id, label, errorMsg, disabled, required, light, focusAnchor, tooltip, dataTestId, invalid = false, noValidation = false, validationOnBlur = false, children, style, ...props }, ref) => {
8
8
  const errorMsgId = `select-error-${useId()}`;
9
9
  const selectRef = useRef(null);
10
- const hasValidValue = useInputValidity(selectRef, validatesOnBlur);
10
+ const hasValidValue = useInputValidity(selectRef, validationOnBlur);
11
11
  const isInvalid = (invalid || !hasValidValue) && !noValidation;
12
12
  useImperativeHandle(ref, () => selectRef.current);
13
13
  return (jsx(IDSSelectBase, { ...props, style: style, id: id, label: label, errorMsg: errorMsg, disabled: disabled, required: required, light: light, focusAnchor: focusAnchor, tooltip: tooltip, dataTestId: dataTestId, selectRef: selectRef, errorMsgId: errorMsgId, invalid: isInvalid, children: children }));
@@ -1,7 +1,7 @@
1
1
  import { CSSProperties, ReactNode } from "react";
2
2
  export interface IDSSelectMultipleBaseProps {
3
- labelId?: string;
4
3
  id?: string;
4
+ labelId?: string;
5
5
  label?: string;
6
6
  expanded?: boolean;
7
7
  placeholder?: string;
@@ -16,7 +16,7 @@ export interface IDSSelectMultipleBaseProps {
16
16
  light?: boolean;
17
17
  focusAnchor?: boolean;
18
18
  tooltip?: ReactNode;
19
- inputRef?: React.Ref<HTMLInputElement>;
19
+ buttonRef?: React.Ref<HTMLButtonElement>;
20
20
  componentRef?: React.Ref<HTMLDivElement>;
21
21
  children?: ReactNode;
22
22
  className?: string;
@@ -24,4 +24,4 @@ export interface IDSSelectMultipleBaseProps {
24
24
  client?: boolean;
25
25
  style?: CSSProperties;
26
26
  }
27
- export declare function IDSSelectMultipleBase({ labelId, id, label, placeholder, numbCheckedBoxes, selectedLabel, selectedLabelPlural, expanded, maxHeight, tooltip, componentRef, inputRef, invalid, disabled, ariaDisabled, light, focusAnchor, className, children, style, onClick, client }: IDSSelectMultipleBaseProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function IDSSelectMultipleBase({ id, labelId, label, placeholder, numbCheckedBoxes, selectedLabel, selectedLabelPlural, expanded, maxHeight, tooltip, componentRef, buttonRef, invalid, disabled, ariaDisabled, light, focusAnchor, className, children, style, onClick, client }: IDSSelectMultipleBaseProps): import("react/jsx-runtime").JSX.Element;
@@ -2,10 +2,9 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSSelectMultipleBase({ labelId, id, label, placeholder, numbCheckedBoxes, selectedLabel = "vald", selectedLabelPlural = "valda", expanded, maxHeight = "8.125rem", tooltip, componentRef, inputRef, invalid, disabled, ariaDisabled, light, focusAnchor, className, children, style, onClick, client = false }) {
6
- const reactId = useId();
7
- const inputId = !!id ? id : `select-multiple-base-${reactId}`;
8
- const inputLabelId = !!labelId ? labelId : `select-multiple-base-label-${reactId}`;
5
+ function IDSSelectMultipleBase({ id, labelId, label, placeholder, numbCheckedBoxes, selectedLabel = "vald", selectedLabelPlural = "valda", expanded, maxHeight = "", tooltip, componentRef, buttonRef, invalid, disabled, ariaDisabled, light, focusAnchor, className, children, style, onClick, client = false }) {
6
+ const defaultLabelId = !!labelId ? labelId : `select-multiple-base-label-${useId()}`;
7
+ const dropdownId = `select-multiple-dropdown-${useId()}`;
9
8
  const displayedValue = numbCheckedBoxes === 0 || !numbCheckedBoxes
10
9
  ? placeholder
11
10
  : numbCheckedBoxes === 1
@@ -16,10 +15,10 @@ function IDSSelectMultipleBase({ labelId, id, label, placeholder, numbCheckedBox
16
15
  onClick
17
16
  }
18
17
  : {};
19
- return (jsxs("div", { className: clsx("ids-select-multiple", className), ref: componentRef, style: style, children: [label && (jsxs("div", { className: "ids-label-wrapper ids-label-wrapper--margin-bottom", children: [jsx("label", { id: inputLabelId, htmlFor: inputId, className: clsx("ids-label", { "ids-label--disabled": disabled || ariaDisabled }), children: label }), tooltip && jsx("span", { className: "ids-label__tooltip", children: tooltip })] })), jsx("div", { className: "ids-select-multiple__select-wrapper", children: jsx("input", { id: inputId, ref: inputRef, "aria-labelledby": inputLabelId, type: "button", className: clsx("ids-select-multiple__select", {
18
+ return (jsxs("div", { className: clsx("ids-select-multiple", className), ref: componentRef, style: style, children: [label && (jsxs("div", { className: "ids-label-wrapper ids-label-wrapper--margin-bottom", children: [jsx("label", { id: defaultLabelId, className: clsx("ids-label", { "ids-label--disabled": disabled || ariaDisabled }), children: label }), tooltip && jsx("span", { className: "ids-label__tooltip", children: tooltip })] })), jsx("div", { className: "ids-select-multiple__select-wrapper", children: jsx("button", { ref: buttonRef, id: id, "aria-labelledby": defaultLabelId, type: "button", "aria-haspopup": "dialog", "aria-controls": dropdownId, className: clsx("ids-select-multiple__select", {
20
19
  "ids-input--light": light,
21
20
  "ids-focus-anchor": focusAnchor
22
- }), "aria-expanded": expanded, disabled: disabled, "aria-disabled": ariaDisabled, "aria-invalid": invalid, placeholder: placeholder, defaultValue: displayedValue, ...clickHandler }) }), jsx("div", { className: "ids-select-multiple__dropdown-wrapper", children: jsx("div", { className: clsx("ids-select-multiple__dropdown", {
21
+ }), disabled: disabled, "aria-disabled": ariaDisabled, "aria-expanded": expanded, "aria-invalid": invalid, ...clickHandler, children: displayedValue }) }), jsx("div", { className: "ids-select-multiple__dropdown-wrapper", id: dropdownId, children: jsx("div", { className: clsx("ids-select-multiple__dropdown", {
23
22
  "ids-select-multiple__dropdown--expanded": expanded
24
23
  }), children: jsx("div", { className: "ids-select-multiple__dropdown-scroll-area", style: { maxHeight }, children: children }) }) })] }));
25
24
  }
@@ -19,7 +19,7 @@ export interface IDSSelectMultipleProps extends HTMLAttributes<HTMLDivElement> {
19
19
  dataTestId?: string;
20
20
  id?: string;
21
21
  }
22
- export declare function IDSSelectMultiple({ invalid, noValidation, expanded, errorMsg, ariaDisabled, style, children, ...props }: IDSSelectMultipleProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function IDSSelectMultiple({ label, id, invalid, noValidation, expanded, errorMsg, ariaDisabled, style, children, ...props }: IDSSelectMultipleProps): import("react/jsx-runtime").JSX.Element;
23
23
  export declare namespace IDSSelectMultiple {
24
24
  var displayName: string;
25
25
  }
@@ -3,12 +3,18 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSSelectMultipleBase } from './select-multiple-base.js';
5
5
  import { IDSCheckboxGroup } from '../checkbox/checkbox-group.js';
6
+ import { useClickOutside } from '../../utils/hooks/useClickOutside.js';
7
+ import { useEsc } from '../../utils/hooks/useEsc.js';
6
8
 
7
- function IDSSelectMultiple({ invalid = false, noValidation = false, expanded = false, errorMsg, ariaDisabled = false, style, children, ...props }) {
9
+ function IDSSelectMultiple({ label, id, invalid = false, noValidation = false, expanded = false, errorMsg, ariaDisabled = false, style, children, ...props }) {
8
10
  const [isExpanded, setIsExpanded] = useState(expanded);
9
11
  const [checkboxListInvalid, setCheckboxListInvalid] = useState(false);
10
- const inputRef = useRef(null);
12
+ const buttonRef = useRef(null);
11
13
  const componentRef = useRef(null);
14
+ useClickOutside([componentRef, buttonRef], () => {
15
+ setIsExpanded(false);
16
+ });
17
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded);
12
18
  useEffect(() => {
13
19
  if (checkboxListInvalid && !noValidation)
14
20
  setIsExpanded(true);
@@ -16,28 +22,9 @@ function IDSSelectMultiple({ invalid = false, noValidation = false, expanded = f
16
22
  useEffect(() => {
17
23
  setIsExpanded(expanded);
18
24
  }, [expanded]);
19
- useEffect(() => {
20
- const handleClickOutside = (event) => {
21
- if (componentRef.current && !componentRef.current.contains(event.target)) {
22
- setIsExpanded(false);
23
- }
24
- };
25
- const handleKeyDown = (event) => {
26
- if (event.key === "Escape")
27
- setIsExpanded(false);
28
- };
29
- document.addEventListener("click", handleClickOutside);
30
- document.addEventListener("focusin", handleClickOutside);
31
- document.addEventListener("keydown", handleKeyDown);
32
- return () => {
33
- document.removeEventListener("click", handleClickOutside);
34
- document.removeEventListener("focusin", handleClickOutside);
35
- document.removeEventListener("keydown", handleKeyDown);
36
- };
37
- }, []);
38
- return (jsx(IDSSelectMultipleBase, { ...props, style: style, client: true, componentRef: componentRef, inputRef: inputRef, invalid: invalid || (!noValidation && checkboxListInvalid), expanded: isExpanded, ariaDisabled: ariaDisabled, onClick: () => {
25
+ return (jsx(IDSSelectMultipleBase, { ...props, style: style, client: true, label: label, id: id, componentRef: componentRef, buttonRef: buttonRef, invalid: invalid || (!noValidation && checkboxListInvalid), expanded: isExpanded, ariaDisabled: ariaDisabled, onClick: () => {
39
26
  setIsExpanded(!isExpanded);
40
- }, children: jsx(IDSCheckboxGroup, { block: true, noValidation: noValidation, errorMsg: errorMsg, onValidityChange: (isValid) => {
27
+ }, children: jsx(IDSCheckboxGroup, { block: true, legend: label, hideLegend: true, noValidation: noValidation, errorMsg: errorMsg, onValidityChange: (isValid) => {
41
28
  if (!isValid && !noValidation) {
42
29
  setIsExpanded(true);
43
30
  setCheckboxListInvalid(!isValid);
@@ -6,7 +6,7 @@ export interface IDSTextareaProps extends React.TextareaHTMLAttributes<HTMLTextA
6
6
  invalid?: boolean;
7
7
  required?: boolean;
8
8
  noValidation?: boolean;
9
- validatesOnBlur?: boolean;
9
+ validationOnBlur?: boolean;
10
10
  autoSize?: boolean;
11
11
  noResize?: boolean;
12
12
  block?: boolean;
@@ -4,10 +4,10 @@ import { forwardRef, useRef, useImperativeHandle } from 'react';
4
4
  import { useInputValidity } from '../form-hooks/useInputValidity.js';
5
5
  import { IDSTextareaBase } from './textarea-base.js';
6
6
 
7
- const IDSTextarea = forwardRef(({ id, label, hint, errorMsg, disabled = false, invalid = false, required = false, noValidation = false, validatesOnBlur = false, autoSize = false, noResize = false, block = false, light = false, readOnly = false, focusAnchor = false, dataTestId = "", tooltip, className, children, style, ...props }, ref) => {
7
+ const IDSTextarea = forwardRef(({ id, label, hint, errorMsg, disabled = false, invalid = false, required = false, noValidation = false, validationOnBlur = false, autoSize = false, noResize = false, block = false, light = false, readOnly = false, focusAnchor = false, dataTestId = "", tooltip, className, children, style, ...props }, ref) => {
8
8
  const textareaRef = useRef(null);
9
9
  useImperativeHandle(ref, () => textareaRef.current);
10
- const isValid = useInputValidity(textareaRef, validatesOnBlur);
10
+ const isValid = useInputValidity(textareaRef, validationOnBlur);
11
11
  const isInvalid = (invalid || !isValid) && !noValidation;
12
12
  return (jsx(IDSTextareaBase, { id: id, label: label, hint: hint, errorMsg: errorMsg, invalid: isInvalid, required: required, disabled: disabled, readOnly: readOnly, light: light, autoSize: autoSize, noResize: noResize, block: block, focusAnchor: focusAnchor, tooltip: tooltip, className: className, textareaRef: textareaRef, dataTestId: dataTestId, style: style, ...props }));
13
13
  });
@@ -11,6 +11,6 @@ export interface IDSTimeProps extends React.InputHTMLAttributes<HTMLInputElement
11
11
  errorMsgId?: string;
12
12
  dataTestId?: string;
13
13
  noValidation?: boolean;
14
- validatesOnBlur?: boolean;
14
+ validationOnBlur?: boolean;
15
15
  }
16
16
  export declare const IDSTime: import("react").ForwardRefExoticComponent<IDSTimeProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -4,10 +4,10 @@ import { forwardRef, useRef, useImperativeHandle } from 'react';
4
4
  import { useInputValidity } from '../form-hooks/useInputValidity.js';
5
5
  import { IDSTimeBase } from './time-base.js';
6
6
 
7
- const IDSTime = forwardRef(({ invalid = false, noValidation = false, validatesOnBlur = false, style, ...props }, ref) => {
7
+ const IDSTime = forwardRef(({ invalid = false, noValidation = false, validationOnBlur = false, style, ...props }, ref) => {
8
8
  const inputRef = useRef(null);
9
9
  useImperativeHandle(ref, () => inputRef.current);
10
- const hasValidValue = useInputValidity(inputRef, validatesOnBlur);
10
+ const hasValidValue = useInputValidity(inputRef, validationOnBlur);
11
11
  const isInvalid = (invalid || !hasValidValue) && !noValidation;
12
12
  return jsx(IDSTimeBase, { invalid: isInvalid, inputRef: inputRef, ...props, style: style });
13
13
  });
@@ -3,5 +3,5 @@ import { IDSGlobalAlertBase } from "./global-alert-base";
3
3
  interface IDSGlobalAlertProps extends Omit<React.ComponentProps<typeof IDSGlobalAlertBase>, "onToggle" | "contentId"> {
4
4
  onCollapsedChange?: (isCollapsed: boolean) => void;
5
5
  }
6
- export declare function IDSGlobalAlert({ collapsed, onCollapsedChange, ...props }: IDSGlobalAlertProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function IDSGlobalAlert({ collapsed, onCollapsedChange, className, ...props }: IDSGlobalAlertProps): import("react/jsx-runtime").JSX.Element;
7
7
  export {};
@@ -3,7 +3,7 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { IDSGlobalAlertBase } from './global-alert-base.js';
5
5
 
6
- function IDSGlobalAlert({ collapsed = false, onCollapsedChange, ...props }) {
6
+ function IDSGlobalAlert({ collapsed = false, onCollapsedChange, className, ...props }) {
7
7
  const [isCollapsed, setIsCollapsed] = useState(collapsed);
8
8
  useEffect(() => {
9
9
  setIsCollapsed(collapsed);
@@ -13,7 +13,7 @@ function IDSGlobalAlert({ collapsed = false, onCollapsedChange, ...props }) {
13
13
  setIsCollapsed(newState);
14
14
  onCollapsedChange?.(newState);
15
15
  };
16
- return jsx(IDSGlobalAlertBase, { ...props, client: true, collapsed: isCollapsed, onToggle: handleToggleCollapse });
16
+ return (jsx(IDSGlobalAlertBase, { ...props, client: true, collapsed: isCollapsed, onToggle: handleToggleCollapse, className: className }));
17
17
  }
18
18
 
19
19
  export { IDSGlobalAlert };
@@ -11,6 +11,7 @@ interface IDSHeader1177AvatarBaseProps extends HTMLAttributes<HTMLDivElement> {
11
11
  onToggle?: () => void;
12
12
  componentRef?: React.RefObject<HTMLDivElement>;
13
13
  menuRef?: React.RefObject<HTMLDivElement>;
14
+ buttonRef?: React.RefObject<HTMLButtonElement>;
14
15
  }
15
- export declare function IDSHeader1177AvatarBase({ username, agent, children, unresponsive, expanded, hide, noMobileMenu, client, onToggle, componentRef, menuRef, ...props }: IDSHeader1177AvatarBaseProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function IDSHeader1177AvatarBase({ username, agent, children, unresponsive, expanded, hide, noMobileMenu, client, onToggle, componentRef, menuRef, buttonRef, ...props }: IDSHeader1177AvatarBaseProps): import("react/jsx-runtime").JSX.Element;
16
17
  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 IDSHeader1177AvatarBase({ username, agent, children, unresponsive = false, expanded = false, hide = false, noMobileMenu = false, client = false, onToggle, componentRef, menuRef, ...props }) {
5
+ function IDSHeader1177AvatarBase({ username, agent, children, unresponsive = false, expanded = false, hide = false, noMobileMenu = false, client = false, onToggle, componentRef, menuRef, buttonRef, ...props }) {
6
6
  if (hide)
7
7
  return null;
8
8
  const menuId = `header-1177-avatar-menu${useId()}`;
@@ -13,7 +13,7 @@ function IDSHeader1177AvatarBase({ username, agent, children, unresponsive = fal
13
13
  : {};
14
14
  return (jsx("div", { ref: componentRef, className: clsx("ids-header-1177-avatar", {
15
15
  "ids-header-1177-avatar--unresponsive": unresponsive
16
- }), style: { flexGrow: username ? "1" : "0" }, ...props, children: username && (jsx("div", { className: "ids-header-1177-avatar__avatar", children: jsxs("div", { className: "ids-header-1177-avatar__menu-wrapper", children: [jsx("button", { className: clsx("ids-header-1177-avatar__button", {
16
+ }), style: { flexGrow: username ? "1" : "0" }, ...props, children: username && (jsx("div", { className: "ids-header-1177-avatar__avatar", children: jsxs("div", { className: "ids-header-1177-avatar__menu-wrapper", children: [jsx("button", { ref: buttonRef, className: clsx("ids-header-1177-avatar__button", {
17
17
  "ids-header-1177-avatar__button--expanded": expanded
18
18
  }), ...toggleHandler, "aria-controls": menuId, "aria-expanded": expanded, children: jsx("div", { className: "ids-header-1177-avatar__name", title: username, children: username }) }), jsxs("div", { ref: menuRef, id: menuId, className: clsx("ids-header-1177-avatar__menu", {
19
19
  "ids-header-1177-avatar__menu--expanded": expanded,
@@ -3,6 +3,9 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
5
5
  import { IDSHeader1177AvatarBase } from './header-1177-avatar-base.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 IDSHeader1177Avatar({ expanded = false, persistent = false, children, ...props }) {
8
11
  const headerContext = useHeaderContext();
@@ -11,30 +14,14 @@ function IDSHeader1177Avatar({ expanded = false, persistent = false, children, .
11
14
  const [isExpanded, setIsExpanded] = useState(expanded);
12
15
  const componentRef = useRef(null);
13
16
  const menuRef = useRef(null);
17
+ const buttonRef = useRef(null);
18
+ useFocusTrap(componentRef.current, isExpanded && !persistent);
19
+ useClickOutside([componentRef, buttonRef], () => {
20
+ if (!persistent)
21
+ setIsExpanded(false);
22
+ });
23
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
14
24
  const toggleAvatar = () => setIsExpanded(prev => !prev);
15
- // Close when clicking outside
16
- useEffect(() => {
17
- const handleClickOutside = (event) => {
18
- if (!persistent && componentRef.current && !componentRef.current.contains(event.target)) {
19
- setIsExpanded(false);
20
- }
21
- };
22
- document.addEventListener("mousedown", handleClickOutside);
23
- return () => document.removeEventListener("mousedown", handleClickOutside);
24
- }, [persistent]);
25
- // Escape should also close
26
- useEffect(() => {
27
- if (!isExpanded || persistent)
28
- return;
29
- const handleKeyDown = (event) => {
30
- if (event.key === "Escape") {
31
- event.preventDefault();
32
- setIsExpanded(false);
33
- }
34
- };
35
- document.addEventListener("keydown", handleKeyDown);
36
- return () => document.removeEventListener("keydown", handleKeyDown);
37
- }, [isExpanded, persistent]);
38
25
  // Clicking a link inside dropdown closes it
39
26
  useEffect(() => {
40
27
  if (!persistent && isExpanded) {
@@ -44,7 +31,7 @@ function IDSHeader1177Avatar({ expanded = false, persistent = false, children, .
44
31
  }
45
32
  }, [isExpanded, persistent]);
46
33
  const closeOnClick = () => setIsExpanded(false);
47
- return (jsx(IDSHeader1177AvatarBase, { ...props, client: true, expanded: isExpanded, onToggle: toggleAvatar, componentRef: componentRef, menuRef: menuRef, unresponsive: headerContext?.unresponsive ?? false, children: children }));
34
+ return (jsx(IDSHeader1177AvatarBase, { ...props, client: true, expanded: isExpanded, onToggle: toggleAvatar, componentRef: componentRef, menuRef: menuRef, buttonRef: buttonRef, unresponsive: headerContext?.unresponsive ?? false, children: children }));
48
35
  }
49
36
 
50
37
  export { IDSHeader1177Avatar };
@@ -6,7 +6,8 @@ interface IDSHeader1177MenuMobileBaseProps extends HTMLAttributes<HTMLDivElement
6
6
  onToggleMenu?: () => void;
7
7
  componentRef?: React.Ref<HTMLDivElement>;
8
8
  menuRef?: React.Ref<HTMLDivElement>;
9
+ buttonRef?: React.Ref<HTMLButtonElement>;
9
10
  client?: boolean;
10
11
  }
11
- export declare function IDSHeader1177MenuMobileBase({ srLabel, expanded, unresponsive, children, onToggleMenu, client, componentRef, menuRef, ...props }: IDSHeader1177MenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function IDSHeader1177MenuMobileBase({ srLabel, expanded, unresponsive, children, onToggleMenu, client, componentRef, menuRef, buttonRef, ...props }: IDSHeader1177MenuMobileBaseProps): import("react/jsx-runtime").JSX.Element;
12
13
  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 IDSHeader1177MenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive, children, onToggleMenu, client = false, componentRef, menuRef, ...props }) {
5
+ function IDSHeader1177MenuMobileBase({ srLabel = "Meny", expanded = false, unresponsive, children, onToggleMenu, client = false, componentRef, menuRef, buttonRef, ...props }) {
6
6
  const menuId = `header-1177-menu-${useId()}`;
7
7
  const toggleHandler = client && onToggleMenu
8
8
  ? {
@@ -12,7 +12,7 @@ function IDSHeader1177MenuMobileBase({ srLabel = "Meny", expanded = false, unres
12
12
  return (jsxs("div", { ...props, className: clsx("ids-header-1177-menu-mobile", {
13
13
  "ids-header-1177-menu-mobile--expanded": expanded,
14
14
  "ids-header-1177-menu-mobile--unresponsive": unresponsive
15
- }), ref: componentRef, children: [jsx("button", { ...toggleHandler, "aria-expanded": expanded, "aria-label": srLabel, "aria-controls": menuId, className: "ids-header-1177-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("span", { className: "ids-hamburger__lines" }) }) }), expanded && (jsx("div", { id: menuId, className: "ids-header-1177-menu-mobile__items", ref: menuRef, children: children }))] }));
15
+ }), ref: componentRef, children: [jsx("button", { ...toggleHandler, ref: buttonRef, "aria-expanded": expanded, "aria-label": srLabel, "aria-controls": menuId, className: "ids-header-1177-menu-mobile__button", children: jsx("div", { className: "ids-hamburger", children: jsx("span", { className: "ids-hamburger__lines" }) }) }), expanded && (jsx("div", { id: menuId, className: "ids-header-1177-menu-mobile__items", ref: menuRef, children: children }))] }));
16
16
  }
17
17
 
18
18
  export { IDSHeader1177MenuMobileBase };
@@ -3,12 +3,20 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { IDSHeader1177MenuMobileBase } from './header-1177-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 IDSHeader1177MenuMobile({ 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);
11
13
  const menuRef = useRef(null);
14
+ const buttonRef = useRef(null);
15
+ useClickOutside([componentRef, buttonRef], () => {
16
+ if (!persistent)
17
+ setIsExpanded(false);
18
+ });
19
+ useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
12
20
  const handleLinkClick = () => {
13
21
  setIsExpanded(false);
14
22
  onClosed?.();
@@ -35,20 +43,7 @@ function IDSHeader1177MenuMobile({ srLabel = "Meny", persistent = false, expande
35
43
  return () => links.forEach(link => link.removeEventListener("click", handleLinkClick));
36
44
  }
37
45
  }, [isExpanded, persistent]);
38
- // Escape should also close
39
- useEffect(() => {
40
- if (!isExpanded || persistent)
41
- return;
42
- const handleKeyDown = (event) => {
43
- if (event.key === "Escape") {
44
- event.preventDefault();
45
- setIsExpanded(false);
46
- }
47
- };
48
- document.addEventListener("keydown", handleKeyDown);
49
- return () => document.removeEventListener("keydown", handleKeyDown);
50
- }, [isExpanded, persistent]);
51
- return (jsx(IDSHeader1177MenuMobileBase, { ...props, client: true, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef, menuRef: menuRef, children: children }));
46
+ return (jsx(IDSHeader1177MenuMobileBase, { ...props, client: true, srLabel: srLabel, expanded: isExpanded, unresponsive: headerContext?.unresponsive, onToggleMenu: toggleMenu, componentRef: componentRef, menuRef: menuRef, buttonRef: buttonRef, children: children }));
52
47
  }
53
48
 
54
49
  export { IDSHeader1177MenuMobile };
@@ -11,8 +11,9 @@ export interface IDSHeader1177NavItemBaseProps extends HTMLAttributes<HTMLElemen
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
  client?: boolean;
17
18
  }
18
- export declare function IDSHeader1177NavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, client, unresponsive, ...props }: IDSHeader1177NavItemBaseProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function IDSHeader1177NavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, client, unresponsive, ...props }: IDSHeader1177NavItemBaseProps): 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 IDSHeader1177NavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, onToggleItem, client = false, unresponsive, ...props }) {
5
+ function IDSHeader1177NavItemBase({ label, active, expanded, children, col1, col2, col3, col4, componentRef, menuRef, buttonRef, onToggleItem, client = false, unresponsive, ...props }) {
6
6
  const itemId = `header-1177-item-${useId()}`;
7
7
  const toggleHandler = client && onToggleItem
8
8
  ? {
@@ -13,7 +13,7 @@ function IDSHeader1177NavItemBase({ label, active, expanded, children, col1, col
13
13
  "ids-header-1177-nav-item--unresponsive": unresponsive,
14
14
  "ids-header-1177-nav-item--expanded": expanded,
15
15
  "ids-header-1177-nav-item--active": active
16
- }), ...props, children: [!!label && (jsx("button", { "aria-controls": itemId, "aria-expanded": expanded, className: "ids-header-1177-nav-item__button", ...toggleHandler, children: label })), !label && (jsx("span", { className: "ids-header-1177-nav-item__link", children: React.Children.map(children, child => {
16
+ }), ...props, children: [!!label && (jsx("button", { "aria-controls": itemId, ref: buttonRef, "aria-expanded": expanded, className: "ids-header-1177-nav-item__button", ...toggleHandler, children: label })), !label && (jsx("span", { className: "ids-header-1177-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 { IDSHeader1177NavItemBase } from './header-1177-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 IDSHeader1177NavItem({ 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 IDSHeader1177NavItem({ 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(IDSHeader1177NavItemBase, { ...props, client: true, expanded: isExpanded, unresponsive: headerContext?.unresponsive, componentRef: componentRef, menuRef: menuRef, onToggleItem: () => setIsExpanded((prev) => !prev) }));
47
+ return (jsx(IDSHeader1177NavItemBase, { ...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 { IDSHeader1177NavItem };
@@ -8,7 +8,8 @@ interface IDSHeader1177AdminAvatarBaseProps extends HTMLAttributes<HTMLElement>
8
8
  onToggleAvatar?: () => void;
9
9
  componentRef?: React.RefObject<HTMLDivElement>;
10
10
  menuRef?: React.RefObject<HTMLDivElement>;
11
+ buttonRef?: React.RefObject<HTMLButtonElement>;
11
12
  client?: boolean;
12
13
  }
13
- export declare function IDSHeader1177AdminAvatarBase({ username, unit, expanded, unresponsive, hide, componentRef, menuRef, children, onToggleAvatar, client, ...props }: IDSHeader1177AdminAvatarBaseProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function IDSHeader1177AdminAvatarBase({ username, unit, expanded, unresponsive, hide, componentRef, menuRef, buttonRef, children, onToggleAvatar, client, ...props }: IDSHeader1177AdminAvatarBaseProps): import("react/jsx-runtime").JSX.Element;
14
15
  export {};
@@ -2,7 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { useId } from 'react';
3
3
  import clsx from 'clsx';
4
4
 
5
- function IDSHeader1177AdminAvatarBase({ username, unit, expanded = false, unresponsive = false, hide = false, componentRef, menuRef, children, onToggleAvatar, client = false, ...props }) {
5
+ function IDSHeader1177AdminAvatarBase({ username, unit, expanded = false, unresponsive = false, hide = false, componentRef, menuRef, buttonRef, children, onToggleAvatar, client = false, ...props }) {
6
6
  if (hide)
7
7
  return null;
8
8
  const menuId = `header-1177-admin-avatar-menu-${useId()}`;
@@ -13,7 +13,7 @@ function IDSHeader1177AdminAvatarBase({ username, unit, expanded = false, unresp
13
13
  : {};
14
14
  return (jsx("div", { ref: componentRef, className: clsx("ids-header-1177-admin-avatar", {
15
15
  "ids-header-1177-admin-avatar--unresponsive": unresponsive
16
- }), ...props, children: jsxs("div", { className: "ids-header-1177-admin-avatar__menu-wrapper", children: [jsxs("button", { className: clsx("ids-header-1177-admin-avatar__button", {
16
+ }), ...props, children: jsxs("div", { className: "ids-header-1177-admin-avatar__menu-wrapper", children: [jsxs("button", { ref: buttonRef, className: clsx("ids-header-1177-admin-avatar__button", {
17
17
  "ids-header-1177-admin-avatar__button--expanded": expanded
18
18
  }), ...toggleHandler, "aria-controls": menuId, "aria-expanded": expanded, children: [jsx("div", { className: "ids-header-1177-admin-avatar__name", title: username, children: username }), unit && (jsxs(Fragment, { children: [jsx("span", { className: "ids-header-1177-admin-avatar__unit-separator", children: "|" }), jsx("span", { className: "ids-header-1177-admin-avatar__unit", title: unit, children: unit })] }))] }), jsx("div", { ref: menuRef, id: menuId, className: clsx("ids-header-1177-admin-avatar__menu", {
19
19
  "ids-header-1177-admin-avatar__menu--expanded": expanded
@@ -8,6 +8,7 @@ interface IDSHeader1177AdminAvatarMobileBaseProps extends HTMLAttributes<HTMLEle
8
8
  client?: boolean;
9
9
  onToggleAvatar?: () => void;
10
10
  componentRef?: React.Ref<HTMLDivElement>;
11
+ buttonRef?: React.Ref<HTMLButtonElement>;
11
12
  }
12
- export declare function IDSHeader1177AdminAvatarMobileBase({ username, unit, children, unresponsive, expanded, hide, client, onToggleAvatar, componentRef, ...props }: IDSHeader1177AdminAvatarMobileBaseProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function IDSHeader1177AdminAvatarMobileBase({ username, unit, children, unresponsive, expanded, hide, client, onToggleAvatar, componentRef, buttonRef, ...props }: IDSHeader1177AdminAvatarMobileBaseProps): 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 IDSHeader1177AdminAvatarMobileBase({ username, unit, children, unresponsive, expanded = false, hide = false, client = false, onToggleAvatar, componentRef, ...props }) {
5
+ function IDSHeader1177AdminAvatarMobileBase({ username, unit, children, unresponsive, expanded = false, hide = false, client = false, onToggleAvatar, componentRef, buttonRef, ...props }) {
6
6
  if (hide)
7
7
  return null;
8
8
  const menuId = `header-1177-admin-avatar-mobile-menu-${useId()}`;
@@ -15,7 +15,7 @@ function IDSHeader1177AdminAvatarMobileBase({ username, unit, children, unrespon
15
15
  "ids-header-1177-admin-avatar-mobile--unresponsive": unresponsive
16
16
  }), ref: componentRef, ...props, children: jsxs("div", { className: "ids-header-1177-admin-avatar-mobile__menu-wrapper", children: [jsx("button", { className: clsx("ids-header-1177-admin-avatar-mobile__button", {
17
17
  "ids-header-1177-admin-avatar-mobile__button--expanded": expanded
18
- }), ...toggleHandler, "aria-controls": menuId, "aria-expanded": expanded, children: jsx("div", { className: "ids-header-1177-admin-avatar-mobile-content__name", title: username, children: username }) }), jsxs("div", { id: menuId, className: clsx("ids-header-1177-admin-avatar-mobile__menu", {
18
+ }), ...toggleHandler, ref: buttonRef, "aria-controls": menuId, "aria-expanded": expanded, children: jsx("div", { className: "ids-header-1177-admin-avatar-mobile-content__name", title: username, children: username }) }), jsxs("div", { id: menuId, className: clsx("ids-header-1177-admin-avatar-mobile__menu", {
19
19
  "ids-header-1177-admin-avatar-mobile__menu--expanded": expanded
20
20
  }), children: [unit && jsx("div", { className: "ids-header-1177-admin-avatar-mobile__unit", children: unit }), jsx("div", { className: "ids-header-1177-admin-avatar-mobile__menu-links", children: children })] })] }) }));
21
21
  }
@@ -1,38 +1,26 @@
1
1
  "use client";
2
2
  import { jsx } from 'react/jsx-runtime';
3
- import { useId, useState, useRef, useEffect } from 'react';
3
+ import { useId, useState, useRef } from 'react';
4
4
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
5
5
  import { IDSHeader1177AdminAvatarMobileBase } from './header-1177-admin-avatar-mobile-base.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 IDSHeader1177AdminAvatarMobile({ username = "", unit = "", expanded = false, persistent = false, children, ...props }) {
8
11
  useId();
9
12
  const [isExpanded, setIsExpanded] = useState(expanded);
10
13
  const headerContext = useHeaderContext();
11
14
  const componentRef = useRef(null);
15
+ const buttonRef = useRef(null);
16
+ useFocusTrap(componentRef.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
- useEffect(() => {
14
- const handleClickOutside = (event) => {
15
- if (!persistent && componentRef.current && !componentRef.current.contains(event.target)) {
16
- setIsExpanded(false);
17
- }
18
- };
19
- document.addEventListener("click", handleClickOutside);
20
- return () => document.removeEventListener("click", handleClickOutside);
21
- }, [persistent]);
22
- // Escape should also close
23
- useEffect(() => {
24
- if (!isExpanded || persistent)
25
- return;
26
- const handleKeyDown = (event) => {
27
- if (event.key === "Escape") {
28
- event.preventDefault();
29
- setIsExpanded(false);
30
- }
31
- };
32
- document.addEventListener("keydown", handleKeyDown);
33
- return () => document.removeEventListener("keydown", handleKeyDown);
34
- }, [isExpanded, persistent]);
35
- return (jsx(IDSHeader1177AdminAvatarMobileBase, { ...props, client: true, username: username, unit: unit, unresponsive: headerContext?.unresponsive ?? false, hide: headerContext?.hideAvatar, expanded: isExpanded, onToggleAvatar: toggleAvatar, componentRef: componentRef, children: children }));
23
+ return (jsx(IDSHeader1177AdminAvatarMobileBase, { ...props, client: true, username: username, unit: unit, unresponsive: headerContext?.unresponsive ?? false, hide: headerContext?.hideAvatar, expanded: isExpanded, onToggleAvatar: toggleAvatar, componentRef: componentRef, buttonRef: buttonRef, children: children }));
36
24
  }
37
25
 
38
26
  export { IDSHeader1177AdminAvatarMobile };