@inera/ids-react 9.1.0 → 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/components/dialog/dialog.js +1 -1
  2. package/components/form/datepicker/datepicker.d.ts +1 -1
  3. package/components/form/datepicker/datepicker.js +8 -7
  4. package/components/form/input/input-base.js +4 -3
  5. package/components/form/select-multiple/select-multiple.js +1 -1
  6. package/components/header-1177/header-1177-avatar.js +1 -1
  7. package/components/header-1177/header-1177-menu-mobile.js +1 -1
  8. package/components/header-1177/header-1177-nav-item.js +1 -1
  9. package/components/header-1177-admin/header-1177-admin-avatar-mobile.js +1 -1
  10. package/components/header-1177-admin/header-1177-admin-avatar.js +1 -1
  11. package/components/header-1177-admin/header-1177-admin-menu-mobile.js +1 -1
  12. package/components/header-1177-admin/header-1177-admin-nav-item.js +1 -1
  13. package/components/header-1177-pro/header-1177-pro-avatar-mobile.js +1 -1
  14. package/components/header-1177-pro/header-1177-pro-avatar.js +1 -1
  15. package/components/header-1177-pro/header-1177-pro-menu-mobile.js +1 -1
  16. package/components/header-1177-pro/header-1177-pro-nav-item.js +1 -1
  17. package/components/header-inera/header-inera-menu-mobile.js +1 -1
  18. package/components/header-inera/header-inera-nav-item.js +1 -1
  19. package/components/header-inera-admin/header-inera-admin-avatar-mobile.js +1 -1
  20. package/components/header-inera-admin/header-inera-admin-avatar.js +1 -1
  21. package/components/header-inera-admin/header-inera-admin-menu-mobile.js +1 -1
  22. package/components/header-inera-admin/header-inera-admin-nav-item.js +1 -1
  23. package/components/side-panel/side-panel.js +1 -1
  24. package/components/utils/hooks/useEsc.d.ts +1 -9
  25. package/components/utils/hooks/useEsc.js +14 -17
  26. package/package.json +1 -1
@@ -11,7 +11,7 @@ function IDSDialog({ show = false, srClose = "Stäng", headline, width, maxWidth
11
11
  const dialogRef = useRef(null);
12
12
  const scrollAreaRef = useRef(null);
13
13
  useFocusTrap(dialogRef.current, isVisible && !noFocusTrap);
14
- useEsc(() => setIsVisible(false), triggerRef, isVisible && !persistent);
14
+ useEsc(() => setIsVisible(false), triggerRef, dialogRef, isVisible && !persistent);
15
15
  // Sync prop show
16
16
  useEffect(() => {
17
17
  setIsVisible(show);
@@ -34,4 +34,4 @@ export interface IDSDatePickerProps extends Omit<React.HTMLAttributes<HTMLElemen
34
34
  onClose?: () => void;
35
35
  onDayClick?: (date: Date, modifiers: Modifiers, e: React.MouseEvent) => void;
36
36
  }
37
- export declare function IDSDatePicker({ label, value, light, placeholder, errorMsg, missingDateErrorMsg, invalidDateErrorMsg, calendarHeader, srOpenText, srCloseText, validationOnBlur, defaultMonth, startMonth, endMonth, noValidation, disabled, required, invalid, tooltip, disableNavigation, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }: IDSDatePickerProps): import("react/jsx-runtime").JSX.Element;
37
+ export declare function IDSDatePicker({ label, id, value, light, placeholder, errorMsg, missingDateErrorMsg, invalidDateErrorMsg, calendarHeader, srOpenText, srCloseText, validationOnBlur, defaultMonth, startMonth, endMonth, noValidation, disabled, required, invalid, tooltip, disableNavigation, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }: IDSDatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useRef, useId, useState, useEffect } from 'react';
2
+ import { useId, useRef, useState, useEffect } from 'react';
3
3
  import { isValid, getWeek, format, subMonths, addMonths } from 'date-fns';
4
4
  import { sv } from 'react-day-picker/locale';
5
5
  import clsx from 'clsx';
@@ -24,7 +24,12 @@ const getPrevMonthYear = (date) => {
24
24
  const getNextMonthYear = (date) => {
25
25
  return `${getSweMonth(addMonths(date, 1))} ${getSweYear(addMonths(date, 1))}`;
26
26
  };
27
- function IDSDatePicker({ label, value, light = false, placeholder = "åååå-mm-dd", errorMsg = "", missingDateErrorMsg = "Datum saknas", invalidDateErrorMsg = "Ogiltigt datum", calendarHeader = "Välj datum", srOpenText = "Öppna kalendern", srCloseText = "Stäng kalendern", validationOnBlur = false, defaultMonth, startMonth = new Date(1900, 0, 1), endMonth = new Date(2050, 0, 1), noValidation = false, disabled = false, required = false, invalid = false, tooltip, disableNavigation = false, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }) {
27
+ function IDSDatePicker({ label, id, value, light = false, placeholder = "åååå-mm-dd", errorMsg = "", missingDateErrorMsg = "Datum saknas", invalidDateErrorMsg = "Ogiltigt datum", calendarHeader = "Välj datum", srOpenText = "Öppna kalendern", srCloseText = "Stäng kalendern", validationOnBlur = false, defaultMonth, startMonth = new Date(1900, 0, 1), endMonth = new Date(2050, 0, 1), noValidation = false, disabled = false, required = false, invalid = false, tooltip, disableNavigation = false, modifiers, focusedDay, onChange, onFocus, onBlur, onOpen, onClose, onDayClick, className, ...props }) {
28
+ const reactId = useId();
29
+ const dialogId = `datepicker-dialog-${reactId}`;
30
+ const headerId = `datepicker-header-${reactId}`;
31
+ const inputId = id ?? `datepicker-input-${reactId}`;
32
+ const errorMsgId = `datepicker-error-${reactId}`;
28
33
  const shortWeek = "v.";
29
34
  const inputRef = useRef(null);
30
35
  const triggerRef = useRef(null);
@@ -32,10 +37,6 @@ function IDSDatePicker({ label, value, light = false, placeholder = "åååå-mm
32
37
  const headerRef = useRef(null);
33
38
  const prevMonthButtonRef = useRef(null);
34
39
  const nextMonthButtonRef = useRef(null);
35
- const dialogId = `datepicker-dialog-${useId()}`;
36
- const headerId = `datepicker-header-${useId()}`;
37
- const inputId = props.id || `datepicker-input-${useId()}`;
38
- const errorMsgId = `datepicker-error-${useId()}`;
39
40
  const [isDialogOpen, setIsDialogOpen] = useState(false);
40
41
  const [inputValue, setInputValue] = useState(value);
41
42
  const initialSelectedDate = !!value ? createNewDate(value) : undefined;
@@ -234,7 +235,7 @@ function IDSDatePicker({ label, value, light = false, placeholder = "åååå-mm
234
235
  WeekNumberHeader: props => (jsx(WeekNumberHeader, { ...props, className: "ids-datepicker__week-number-header", children: shortWeek })),
235
236
  MonthsDropdown: props => (jsx(MonthsDropdown, { ...props, disabled: disableNavigation, className: "ids-datepicker__month-select" })),
236
237
  YearsDropdown: props => (jsx(YearsDropdown, { ...props, disabled: disableNavigation, className: "ids-datepicker__year-select" }))
237
- }, startMonth: startMonth, endMonth: endMonth, month: month, onMonthChange: setMonth, defaultMonth: defaultMonth, selected: selectedDate, onSelect: handleDayPickerSelect, onDayClick: onDayClick })] })] }), (showError || showInvalidError || showMissingError) && (jsxs(IDSErrorMessage, { id: errorMsgId, show: true, children: [showInvalidError && !showMissingError && invalidDateErrorMsg, showMissingError && missingDateErrorMsg, showError && errorMsg] }))] }));
238
+ }, startMonth: startMonth, endMonth: endMonth, month: month, onMonthChange: setMonth, defaultMonth: defaultMonth, selected: selectedDate, onSelect: handleDayPickerSelect, onDayClick: onDayClick })] })] }), (showError || showInvalidError || showMissingError) && (jsxs(IDSErrorMessage, { id: errorMsgId, show: true, children: [showInvalidError && !showMissingError && !showError && invalidDateErrorMsg, showMissingError && !showInvalidError && !showError && missingDateErrorMsg, showError && errorMsg] }))] }));
238
239
  }
239
240
 
240
241
  export { IDSDatePicker };
@@ -19,9 +19,10 @@ function IDSInputBase({ label, type = "text", icon, hint, unit, showSearchLabel
19
19
  }
20
20
  : {};
21
21
  ariaHandler["aria-label"] = type === "search" && !showSearchLabel ? label : "";
22
- return (jsxs(Fragment, { children: [jsxs("div", { className: clsx("ids-input", { "ids-input--search": type === "search", "ids-inout--icon": !!icon }, className), "data-testid": dataTestId, style: style, children: [jsxs("div", { className: "ids-input__wrapper", children: [label && (jsxs("div", { className: "ids-label-wrapper ids-label-wrapper--margin-bottom", children: [jsx("label", { className: clsx("ids-label", {
23
- "ids-label--disabled": disabled || readOnly,
24
- "ids-hidden": type === "search" && !showSearchLabel
22
+ return (jsxs(Fragment, { children: [jsxs("div", { className: clsx("ids-input", { "ids-input--search": type === "search", "ids-input--icon": !!icon }, className), "data-testid": dataTestId, style: style, children: [jsxs("div", { className: "ids-input__wrapper", children: [label && (jsxs("div", { className: clsx("ids-label-wrapper", "ids-label-wrapper--margin-bottom", {
23
+ "ids-hidden": type === "search" && !showSearchLabel
24
+ }), children: [jsx("label", { className: clsx("ids-label", {
25
+ "ids-label--disabled": disabled || readOnly
25
26
  }), htmlFor: inputId, children: label }), tooltip && jsx("span", { className: "ids-label__tooltip", children: tooltip })] })), jsxs("div", { className: "ids-input__unit-wrapper", children: [jsxs("div", { className: "ids-input__input-wrapper", children: [type === "search" && jsx("span", { className: "ids-input__search-icon" }), jsx("input", { ref: inputRef, id: inputId, type: type, readOnly: readOnly, className: clsx("ids-input__input", {
26
27
  "ids-input--light": light,
27
28
  "ids-input--invalid": invalid,
@@ -14,7 +14,7 @@ function IDSSelectMultiple({ label, id, invalid = false, noValidation = false, e
14
14
  useClickOutside([componentRef, buttonRef], () => {
15
15
  setIsExpanded(false);
16
16
  });
17
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded);
17
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded);
18
18
  useEffect(() => {
19
19
  if (checkboxListInvalid && !noValidation)
20
20
  setIsExpanded(true);
@@ -20,7 +20,7 @@ function IDSHeader1177Avatar({ expanded = false, persistent = false, children, .
20
20
  if (!persistent)
21
21
  setIsExpanded(false);
22
22
  });
23
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
23
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
24
24
  const toggleAvatar = () => setIsExpanded(prev => !prev);
25
25
  // Clicking a link inside dropdown closes it
26
26
  useEffect(() => {
@@ -16,7 +16,7 @@ function IDSHeader1177MenuMobile({ srLabel = "Meny", persistent = false, expande
16
16
  if (!persistent)
17
17
  setIsExpanded(false);
18
18
  });
19
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
19
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
20
20
  const handleLinkClick = () => {
21
21
  setIsExpanded(false);
22
22
  onClosed?.();
@@ -15,7 +15,7 @@ function IDSHeader1177NavItem({ expanded = false, ...props }) {
15
15
  useClickOutside([componentRef, buttonRef], () => {
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef);
19
19
  useEffect(() => {
20
20
  setIsExpanded(expanded);
21
21
  }, [expanded]);
@@ -18,7 +18,7 @@ function IDSHeader1177AdminAvatarMobile({ username = "", unit = "", expanded = f
18
18
  if (!persistent)
19
19
  setIsExpanded(false);
20
20
  });
21
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
21
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
22
22
  const toggleAvatar = () => setIsExpanded(prev => !prev);
23
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 }));
24
24
  }
@@ -18,7 +18,7 @@ function IDSHeader1177AdminAvatar({ username, unit, expanded = false, children,
18
18
  if (!persistent)
19
19
  setIsExpanded(false);
20
20
  });
21
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
21
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
22
22
  const toggleAvatar = () => setIsExpanded(prev => !prev);
23
23
  const handleClickOutside = (event) => {
24
24
  if (!persistent && componentRef.current && !componentRef.current.contains(event.target)) {
@@ -15,7 +15,7 @@ function IDSHeader1177AdminMenuMobile({ srLabel = "Meny", persistent = false, ex
15
15
  if (!persistent)
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
19
19
  const handleLinkClick = () => {
20
20
  setIsExpanded(false);
21
21
  onClosed?.();
@@ -15,7 +15,7 @@ function IDSHeader1177AdminNavItem({ expanded = false, ...props }) {
15
15
  useClickOutside([componentRef, buttonRef], () => {
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef);
19
19
  useEffect(() => {
20
20
  setIsExpanded(expanded);
21
21
  }, [expanded]);
@@ -17,7 +17,7 @@ function IDSHeader1177ProAvatarMobile({ username = "", unit = "", expanded = fal
17
17
  if (!persistent)
18
18
  setIsExpanded(false);
19
19
  });
20
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
20
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
21
21
  const toggleAvatar = () => setIsExpanded(prev => !prev);
22
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 }));
23
23
  }
@@ -19,7 +19,7 @@ function IDSHeader1177ProAvatar({ username, unit, expanded = false, children, pe
19
19
  if (!persistent)
20
20
  setIsExpanded(false);
21
21
  });
22
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
22
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
23
23
  const toggleAvatar = () => setIsExpanded(prev => !prev);
24
24
  useEffect(() => {
25
25
  const clickOutsideHandler = (e) => {
@@ -15,7 +15,7 @@ function IDSHeader1177ProMenuMobile({ srLabel = "Meny", persistent = false, expa
15
15
  if (!persistent)
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
19
19
  const handleLinkClick = () => {
20
20
  setIsExpanded(false);
21
21
  onClosed?.();
@@ -15,7 +15,7 @@ function IDSHeader1177ProNavItem({ expanded = false, ...props }) {
15
15
  useClickOutside([componentRef, buttonRef], () => {
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef);
19
19
  useEffect(() => {
20
20
  setIsExpanded(expanded);
21
21
  }, [expanded]);
@@ -15,7 +15,7 @@ function IDSHeaderIneraMenuMobile({ srLabel = "Meny", expanded = false, persiste
15
15
  if (!persistent)
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
19
19
  const handleLinkClick = () => {
20
20
  setIsExpanded(false);
21
21
  onClosed?.();
@@ -15,7 +15,7 @@ function IDSHeaderIneraNavItem({ expanded = false, ...props }) {
15
15
  useClickOutside([componentRef, buttonRef], () => {
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef);
19
19
  useEffect(() => {
20
20
  setIsExpanded(expanded);
21
21
  }, [expanded]);
@@ -19,7 +19,7 @@ function IDSHeaderIneraAdminAvatarMobile({ username = "", unit = "", expanded =
19
19
  if (!persistent)
20
20
  setIsExpanded(false);
21
21
  });
22
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
22
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
23
23
  const toggleAvatar = () => setIsExpanded(prev => !prev);
24
24
  return (jsx(IDSHeaderIneraAdminAvatarMobileBase, { ...props, client: true, username: username, unit: unit, expanded: isExpanded, componentRef: componentRef, buttonRef: buttonRef, onToggleMenu: username ? toggleAvatar : undefined, unresponsive: headerContext?.unresponsive ?? false, children: children }));
25
25
  }
@@ -20,7 +20,7 @@ function IDSHeaderIneraAdminAvatar({ username, unit, expanded = false, children,
20
20
  if (!persistent)
21
21
  setIsExpanded(false);
22
22
  });
23
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
23
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
24
24
  const toggleAvatar = () => setIsExpanded(prev => !prev);
25
25
  useEffect(() => {
26
26
  const clickOutsideHandler = (e) => {
@@ -15,7 +15,7 @@ function IDSHeaderIneraAdminMenuMobile({ srLabel = "Meny", expanded = false, per
15
15
  if (!persistent)
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef, isExpanded && !persistent);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef, isExpanded && !persistent);
19
19
  const handleLinkClick = () => {
20
20
  setIsExpanded(false);
21
21
  onClosed?.();
@@ -15,7 +15,7 @@ function IDSHeaderIneraAdminNavItem({ expanded = false, ...props }) {
15
15
  useClickOutside([componentRef, buttonRef], () => {
16
16
  setIsExpanded(false);
17
17
  });
18
- useEsc(() => setIsExpanded(false), buttonRef);
18
+ useEsc(() => setIsExpanded(false), buttonRef, componentRef);
19
19
  useEffect(() => {
20
20
  setIsExpanded(expanded);
21
21
  }, [expanded]);
@@ -10,7 +10,7 @@ function IDSSidePanel({ show, menu = false, noScrollAreaFocus = false, srLabel =
10
10
  const isControlled = show !== undefined;
11
11
  const [internalShow, setInternalShow] = useState(false);
12
12
  const visible = isControlled ? show : internalShow;
13
- useEsc(() => setVisible(false), hamburgerRef, internalShow);
13
+ useEsc(() => setVisible(false), hamburgerRef, internalRef, internalShow);
14
14
  const setVisible = (value) => {
15
15
  if (!isControlled) {
16
16
  setInternalShow(value);
@@ -1,10 +1,2 @@
1
1
  import { RefObject } from "react";
2
- /**
3
- * useEsc
4
- * Calls the provided callback when Escape is pressed and optionally focuses a ref
5
- *
6
- * @param callback - function to call when Escape is pressed
7
- * @param focusRef - optional ref to focus when Escape is pressed
8
- * @param enabled - optional, defaults to true; disables listener when false
9
- */
10
- export declare function useEsc(callback: () => void, focusRef?: RefObject<HTMLElement>, enabled?: boolean): void;
2
+ export declare function useEsc(callback: (event: KeyboardEvent) => void, focusRef?: RefObject<HTMLElement>, containerRef?: RefObject<HTMLElement>, enabled?: boolean): void;
@@ -1,27 +1,24 @@
1
1
  import { useEffect } from 'react';
2
2
 
3
- /**
4
- * useEsc
5
- * Calls the provided callback when Escape is pressed and optionally focuses a ref
6
- *
7
- * @param callback - function to call when Escape is pressed
8
- * @param focusRef - optional ref to focus when Escape is pressed
9
- * @param enabled - optional, defaults to true; disables listener when false
10
- */
11
- function useEsc(callback, focusRef, enabled = true) {
3
+ function useEsc(callback, focusRef, containerRef, enabled = true) {
12
4
  useEffect(() => {
13
5
  if (!enabled)
14
6
  return;
15
- const handleKeyDown = (event) => {
16
- if (event.key === "Escape") {
17
- event.preventDefault();
18
- callback();
19
- focusRef?.current?.focus();
7
+ const handler = (event) => {
8
+ if (event.key !== "Escape")
9
+ return;
10
+ if (containerRef?.current) {
11
+ const active = document.activeElement;
12
+ if (!containerRef.current.contains(active))
13
+ return;
20
14
  }
15
+ event.preventDefault();
16
+ callback(event);
17
+ focusRef?.current?.focus();
21
18
  };
22
- document.addEventListener("keydown", handleKeyDown);
23
- return () => document.removeEventListener("keydown", handleKeyDown);
24
- }, [callback, focusRef, enabled]);
19
+ document.addEventListener("keydown", handler);
20
+ return () => document.removeEventListener("keydown", handler);
21
+ }, [callback, focusRef, enabled, containerRef]);
25
22
  }
26
23
 
27
24
  export { useEsc };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inera/ids-react",
3
- "version": "9.1.0",
3
+ "version": "9.1.2",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
6
  "react": "*",