@inera/ids-react 7.0.1 → 7.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 (39) hide show
  1. package/components/accordion/accordion.d.ts +1 -0
  2. package/components/accordion/accordion.js +2 -1
  3. package/components/dialog/dialog.js +1 -0
  4. package/components/form/check-button/check-button.d.ts +1 -0
  5. package/components/form/check-button/check-button.js +4 -2
  6. package/components/form/checkbox/checkbox-group.d.ts +1 -0
  7. package/components/form/checkbox/checkbox-group.js +17 -28
  8. package/components/form/checkbox/checkbox.d.ts +1 -0
  9. package/components/form/checkbox/checkbox.js +4 -2
  10. package/components/form/form-hooks/useGroupValidity.d.ts +1 -1
  11. package/components/form/form-hooks/useGroupValidity.js +5 -4
  12. package/components/form/input/input.d.ts +1 -0
  13. package/components/form/input/input.js +4 -5
  14. package/components/form/radio/radio.d.ts +1 -0
  15. package/components/form/radio/radio.js +4 -3
  16. package/components/form/radio-button/radio-button.d.ts +1 -0
  17. package/components/form/radio-button/radio-button.js +4 -2
  18. package/components/form/range/range.d.ts +1 -0
  19. package/components/form/range/range.js +4 -2
  20. package/components/form/select/select.d.ts +1 -0
  21. package/components/form/select/select.js +3 -2
  22. package/components/form/select-multiple/select-multiple.d.ts +1 -0
  23. package/components/form/select-multiple/select-multiple.js +7 -3
  24. package/components/form/textarea/textarea.d.ts +1 -0
  25. package/components/form/textarea/textarea.js +3 -2
  26. package/components/form/time/time.d.ts +1 -0
  27. package/components/form/time/time.js +3 -2
  28. package/components/form/toggle/toggle.d.ts +1 -0
  29. package/components/form/toggle/toggle.js +4 -2
  30. package/components/header-1177/header-1177-avatar.js +24 -11
  31. package/components/header-1177/header-1177-nav-item.js +22 -5
  32. package/components/header-1177-admin/header-1177-admin-nav-item.js +21 -6
  33. package/components/header-1177-pro/header-1177-pro-nav-item.js +21 -6
  34. package/components/header-inera/header-inera-nav-item.js +6 -1
  35. package/components/header-inera-admin/header-inera-admin-nav-item.js +6 -1
  36. package/components/link/link.d.ts +3 -3
  37. package/components/puff-list/puff-list-item/puff-list-item.d.ts +1 -0
  38. package/components/puff-list/puff-list-item/puff-list-item.js +9 -7
  39. package/package.json +2 -2
@@ -6,6 +6,7 @@ interface IDSAccordionProps extends React.HTMLAttributes<HTMLDivElement> {
6
6
  level?: 1 | 2;
7
7
  expanded?: boolean;
8
8
  lean?: boolean;
9
+ noBorder?: boolean;
9
10
  onCollapsed?: () => void;
10
11
  onExpanded?: () => void;
11
12
  children?: ReactNode;
@@ -5,7 +5,7 @@ import clsx from 'clsx';
5
5
  import '@inera/ids-design/components/accordion/accordion.css';
6
6
  import { useElementId } from '../utils/hooks/useElementId.js';
7
7
 
8
- const IDSAccordion = ({ headline = "", level = 1, headlineSize = level === 2 ? "s" : "m", expanded = false, lean = false, onCollapsed, onExpanded, children, className, ...props }) => {
8
+ const IDSAccordion = ({ headline = "", level = 1, headlineSize = level === 2 ? "s" : "m", expanded = false, lean = false, noBorder = false, onCollapsed, onExpanded, children, className, ...props }) => {
9
9
  const [isExpanded, setIsExpanded] = useState(expanded);
10
10
  const [isLean, setIsLean] = useState(lean);
11
11
  const [insideIDSCard, setInsideIDSCard] = useState(false);
@@ -63,6 +63,7 @@ const IDSAccordion = ({ headline = "", level = 1, headlineSize = level === 2 ? "
63
63
  };
64
64
  return (jsxs("div", { ref: accordionRef, className: clsx("ids-accordion", {
65
65
  "ids-accordion--expanded": isExpanded,
66
+ "ids-accordion--no-border": noBorder,
66
67
  "ids-accordion--lean": lean || isLean,
67
68
  "ids-accordion--has-children": hasChildren,
68
69
  "ids-accordion--is-child": level === 2
@@ -51,6 +51,7 @@ const IDSDialog = ({ show = false, srClose = "Stäng", dismissible = false, auto
51
51
  const focusable = bodyRef.current?.querySelector(".ids-focus-anchor") ||
52
52
  bodyRef.current?.querySelector("h1, h2, h3, [tabindex]:not([tabindex='-1']), button:not([disabled]), a, input:not([disabled]), textarea:not([disabled])") ||
53
53
  dialogRef.current?.querySelector("button.ids-dialog__header__button");
54
+ console.log("focusable", focusable);
54
55
  focusable?.focus();
55
56
  });
56
57
  }
@@ -1,6 +1,7 @@
1
1
  import { ReactNode, InputHTMLAttributes } from "react";
2
2
  interface IDSCheckButtonProps extends InputHTMLAttributes<HTMLInputElement> {
3
3
  disabled?: boolean;
4
+ focusAnchor?: boolean;
4
5
  children?: ReactNode;
5
6
  }
6
7
  export declare const IDSCheckButton: import("react").ForwardRefExoticComponent<IDSCheckButtonProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -4,11 +4,13 @@ import { forwardRef, useRef, useImperativeHandle } from 'react';
4
4
  import { useElementId } from '../../utils/hooks/useElementId.js';
5
5
  import clsx from 'clsx';
6
6
 
7
- const IDSCheckButton = forwardRef(({ disabled = false, id, children, className, ...props }, ref) => {
7
+ const IDSCheckButton = forwardRef(({ disabled = false, focusAnchor = false, id, children, className, ...props }, ref) => {
8
8
  const fieldId = useElementId(id);
9
9
  const checkButtonRef = useRef(null);
10
10
  useImperativeHandle(ref, () => checkButtonRef.current);
11
- return (jsxs("div", { className: clsx("ids-check-button", { "ids-check-button--disabled": disabled }, className), children: [jsx("input", { id: fieldId, ref: checkButtonRef, className: "ids-check-button__input", type: "checkbox", disabled: disabled, ...props }), jsx("label", { htmlFor: fieldId, className: "ids-check-button__label", children: children })] }));
11
+ return (jsxs("div", { className: clsx("ids-check-button", { "ids-check-button--disabled": disabled }, className), children: [jsx("input", { id: fieldId, ref: checkButtonRef, className: clsx("ids-check-button__input", {
12
+ "ids-focus-anchor": focusAnchor
13
+ }), type: "checkbox", disabled: disabled, ...props }), jsx("label", { htmlFor: fieldId, className: "ids-check-button__label", children: children })] }));
12
14
  });
13
15
  IDSCheckButton.displayName = "IDSCheckButton";
14
16
 
@@ -5,6 +5,7 @@ interface IDSCheckboxGroupProps extends InputHTMLAttributes<HTMLInputElement> {
5
5
  compact?: boolean;
6
6
  tooltip?: ReactNode;
7
7
  block?: boolean;
8
+ noValidation?: boolean;
8
9
  children?: ReactNode;
9
10
  onValidityChange?: (isValid: boolean) => void;
10
11
  setCheckedBoxesCount?: Dispatch<SetStateAction<number>>;
@@ -6,42 +6,31 @@ import { useElementId } from '../../utils/hooks/useElementId.js';
6
6
  import { useGroupValidity } from '../form-hooks/useGroupValidity.js';
7
7
  import clsx from 'clsx';
8
8
 
9
- const IDSCheckboxGroup = ({ legend = "", errorMsg = "", compact = false, block = false, tooltip, children, className, onValidityChange, setCheckedBoxesCount }) => {
9
+ const IDSCheckboxGroup = ({ legend = "", errorMsg = "", compact = false, block = false, noValidation = false, tooltip, children, className, onValidityChange, setCheckedBoxesCount }) => {
10
10
  const groupRef = useRef(null);
11
- const isValid = useGroupValidity(groupRef, "checkbox");
11
+ const isValid = useGroupValidity(groupRef, "checkbox", () => {
12
+ onValidityChange?.(false);
13
+ });
12
14
  const errorMsgId = useElementId();
13
15
  useEffect(() => {
14
- if (onValidityChange) {
15
- onValidityChange(isValid);
16
- }
17
- }, [isValid, onValidityChange]);
18
- useEffect(() => {
19
- const checkboxes = groupRef.current?.querySelectorAll(`input[type="checkbox"]`);
20
- if (!checkboxes.length)
21
- return;
22
- const count = () => {
23
- const checkedCount = groupRef.current?.querySelectorAll('input[type="checkbox"]:checked')?.length ?? 0;
24
- if (setCheckedBoxesCount) {
25
- setCheckedBoxesCount(checkedCount);
26
- }
27
- };
28
- count();
29
- checkboxes.forEach(cb => {
30
- cb.addEventListener("change", count);
31
- });
32
- return () => {
33
- checkboxes.forEach(cb => {
34
- cb.removeEventListener("change", count);
35
- });
36
- };
16
+ const checkedCount = groupRef.current?.querySelectorAll('input[type="checkbox"]:checked')?.length ?? 0;
17
+ setCheckedBoxesCount?.(checkedCount);
37
18
  }, [groupRef]);
19
+ useEffect(() => {
20
+ onValidityChange?.(isValid);
21
+ }, [isValid, onValidityChange]);
38
22
  const clonedChildren = React__default.Children.map(children, child => {
39
- if (React__default.isValidElement(child) &&
40
- child.type.displayName === "IDSCheckbox") {
23
+ if (React__default.isValidElement(child) && child.type.displayName === "IDSCheckbox") {
41
24
  return React__default.cloneElement(child, {
42
25
  key: child.props.id,
43
26
  groupErrorMsgId: errorMsg && errorMsgId,
44
- block: block
27
+ block: block,
28
+ noValidation: noValidation,
29
+ onChange: (e) => {
30
+ child.props.onChange?.(e);
31
+ const checkedCount = groupRef.current?.querySelectorAll('input[type="checkbox"]:checked')?.length ?? 0;
32
+ setCheckedBoxesCount?.(checkedCount);
33
+ }
45
34
  });
46
35
  }
47
36
  return child;
@@ -9,6 +9,7 @@ export interface IDSCheckboxProps extends InputHTMLAttributes<HTMLInputElement>
9
9
  light?: boolean;
10
10
  block?: boolean;
11
11
  compact?: boolean;
12
+ focusAnchor?: boolean;
12
13
  errorMsg?: string | ReactNode;
13
14
  groupErrorMsgId?: string;
14
15
  tooltip?: ReactNode;
@@ -7,7 +7,7 @@ import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
7
7
  import { useInputValidity } from '../form-hooks/useInputValidity.js';
8
8
  import clsx from 'clsx';
9
9
 
10
- const IDSCheckbox = forwardRef(({ invalid = false, disabled = false, required = false, indeterminate = false, noValidation = false, noLabel = false, light = false, block = false, compact = false, errorMsg = "", groupErrorMsgId = "", tooltip, id, children, className, ...props }, ref) => {
10
+ const IDSCheckbox = forwardRef(({ invalid = false, disabled = false, required = false, indeterminate = false, noValidation = false, noLabel = false, light = false, block = false, compact = false, focusAnchor = false, errorMsg = "", groupErrorMsgId = "", tooltip, id, children, className, ...props }, ref) => {
11
11
  const internalRef = useRef(null);
12
12
  const checkboxRef = ref ?? internalRef;
13
13
  const fieldId = useElementId(id);
@@ -42,7 +42,9 @@ const IDSCheckbox = forwardRef(({ invalid = false, disabled = false, required =
42
42
  "ids-checkbox--light": light,
43
43
  "ids-checkbox--block": block,
44
44
  "ids-checkbox--compact": compact
45
- }), children: [jsx("input", { id: fieldId, ref: checkboxRef, type: "checkbox", className: "ids-checkbox__input", "aria-invalid": isInvalid, disabled: disabled, "aria-disabled": disabled, required: required, "aria-required": required, ...props }), jsxs("div", { className: clsx("ids-label-tooltip-wrapper", {
45
+ }), children: [jsx("input", { id: fieldId, ref: checkboxRef, type: "checkbox", className: clsx("ids-checkbox__input", {
46
+ "ids-focus-anchor": focusAnchor
47
+ }), "aria-invalid": isInvalid, disabled: disabled, "aria-disabled": disabled, required: !noValidation && required, "aria-required": !noValidation && required, ...props }), jsxs("div", { className: clsx("ids-label-tooltip-wrapper", {
46
48
  "ids-label-tooltip-wrapper--block": block,
47
49
  "ids-label-tooltip-wrapper--inline": tooltip
48
50
  }), children: [jsx("label", { htmlFor: fieldId, className: clsx("ds-checkbox__label", {
@@ -1 +1 @@
1
- export declare function useGroupValidity(ref: React.RefObject<HTMLFieldSetElement>, type: "checkbox" | "radio"): boolean;
1
+ export declare function useGroupValidity(ref: React.RefObject<HTMLFieldSetElement>, type: "checkbox" | "radio", onInvalid?: () => void): boolean;
@@ -1,6 +1,6 @@
1
1
  import { useState, useRef, useEffect } from 'react';
2
2
 
3
- function useGroupValidity(ref, type) {
3
+ function useGroupValidity(ref, type, onInvalid) {
4
4
  const [isValid, setIsValid] = useState(true);
5
5
  const [hasInteracted, setHasInteracted] = useState(false);
6
6
  const initialized = useRef(false);
@@ -29,6 +29,7 @@ function useGroupValidity(ref, type) {
29
29
  initialized.current = true;
30
30
  }
31
31
  const handleInteraction = () => {
32
+ onInvalid?.();
32
33
  setHasInteracted(true);
33
34
  validate();
34
35
  };
@@ -38,7 +39,7 @@ function useGroupValidity(ref, type) {
38
39
  };
39
40
  inputs.forEach(cb => {
40
41
  cb.addEventListener("invalid", handleInteraction);
41
- cb.addEventListener("change", handleChange);
42
+ cb.addEventListener("input", handleChange);
42
43
  });
43
44
  const form = fieldset.closest("form");
44
45
  const handleSubmit = () => {
@@ -49,11 +50,11 @@ function useGroupValidity(ref, type) {
49
50
  return () => {
50
51
  inputs.forEach(cb => {
51
52
  cb.removeEventListener("invalid", handleInteraction);
52
- cb.removeEventListener("change", handleChange);
53
+ cb.removeEventListener("input", handleChange);
53
54
  });
54
55
  form?.removeEventListener("submit", handleSubmit);
55
56
  };
56
- }, [ref]);
57
+ }, [ref, onInvalid]);
57
58
  return isValid || !hasInteracted;
58
59
  }
59
60
 
@@ -9,6 +9,7 @@ interface IDSInputProps extends InputHTMLAttributes<HTMLInputElement> {
9
9
  disabled?: boolean;
10
10
  invalid?: boolean;
11
11
  required?: boolean;
12
+ focusAnchor?: boolean;
12
13
  noValidation?: boolean;
13
14
  autoSize?: boolean;
14
15
  block?: boolean;
@@ -7,7 +7,7 @@ import { useInputValidity } from '../form-hooks/useInputValidity.js';
7
7
  import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
8
8
  import clsx from 'clsx';
9
9
 
10
- const IDSInput = forwardRef(({ label = "", type = "text", icon = "", hint = "", showSearchLabel = false, errorMsg = "", disabled = false, invalid = false, required = false, noValidation = false, autoSize = false, block = false, light = false, readOnly = false, id, tooltip, submitButton, oldIcon, className, ...props }, ref) => {
10
+ const IDSInput = forwardRef(({ label = "", type = "text", icon = "", hint = "", showSearchLabel = false, errorMsg = "", disabled = false, invalid = false, required = false, noValidation = false, autoSize = false, block = false, light = false, readOnly = false, focusAnchor = false, id, tooltip, submitButton, oldIcon, className, ...props }, ref) => {
11
11
  const fieldId = useElementId(id);
12
12
  const errorMsgId = useElementId();
13
13
  const hintId = useElementId();
@@ -43,9 +43,7 @@ const IDSInput = forwardRef(({ label = "", type = "text", icon = "", hint = "",
43
43
  iconComponent.setAttribute("color", color);
44
44
  iconComponent.setAttribute("color2", color2);
45
45
  };
46
- const renderOldIcon = oldIcon && React__default.isValidElement(oldIcon)
47
- ? React__default.cloneElement(oldIcon, { ref: oldIconRef })
48
- : oldIcon;
46
+ const renderOldIcon = oldIcon && React__default.isValidElement(oldIcon) ? React__default.cloneElement(oldIcon, { ref: oldIconRef }) : oldIcon;
49
47
  return (jsxs("div", { className: clsx("ids-input-component", className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { className: clsx("ids-label", {
50
48
  "ids-label--disabled": disabled || readOnly,
51
49
  "ids-hidden": type === "search" && !showSearchLabel
@@ -54,7 +52,8 @@ const IDSInput = forwardRef(({ label = "", type = "text", icon = "", hint = "",
54
52
  "ids-input__inner-wrapper--search": type === "search"
55
53
  }), children: [jsx("input", { ref: combinedRef, id: fieldId, type: type, readOnly: readOnly, className: clsx("ids-input", {
56
54
  "ids-input--light": light,
57
- "ids-input--invalid": invalid
55
+ "ids-input--invalid": invalid,
56
+ "ids-focus-anchor": focusAnchor
58
57
  }), "aria-invalid": isInvalid, required: required, "aria-required": required, disabled: disabled, "aria-disabled": disabled, ...props }), type === "search" && jsx("span", { className: "ids-input__search-icon" }), icon && jsx("span", { className: `ids-input__icon ids-icon-${icon}` }), oldIcon && jsx("span", { className: "ids-input__icon", children: renderOldIcon })] }), submitButton] }), hint && (jsx("div", { id: hintId, className: "ids-input__hint", children: hint })), isInvalid && errorMsg && (jsx(IDSErrorMessage, { id: errorMsgId, show: true, children: errorMsg }))] }));
59
58
  });
60
59
  IDSInput.displayName = "IDSInput"; // Needed when using forwardRef
@@ -10,6 +10,7 @@ export interface IDSRadioProps extends InputHTMLAttributes<HTMLInputElement> {
10
10
  noValidation?: boolean;
11
11
  light?: boolean;
12
12
  compact?: boolean;
13
+ focusAnchor?: boolean;
13
14
  tooltip?: ReactNode;
14
15
  children?: ReactNode;
15
16
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
@@ -6,7 +6,7 @@ import { useInputValidity } from '../form-hooks/useInputValidity.js';
6
6
  import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
7
7
  import clsx from 'clsx';
8
8
 
9
- const IDSRadio = forwardRef(({ name = "", groupErrorMsgId = "", invalid = false, groupInvalid = false, disabled = false, noValidation = false, light = false, compact = false, tooltip, id, children, className, onChange, ...props }, ref) => {
9
+ const IDSRadio = forwardRef(({ name = "", groupErrorMsgId = "", invalid = false, groupInvalid = false, disabled = false, noValidation = false, light = false, compact = false, focusAnchor = false, tooltip, id, children, className, onChange, ...props }, ref) => {
10
10
  const fieldId = useElementId(id);
11
11
  const radioRef = useRef(null);
12
12
  useImperativeHandle(ref, () => radioRef.current);
@@ -15,8 +15,9 @@ const IDSRadio = forwardRef(({ name = "", groupErrorMsgId = "", invalid = false,
15
15
  useAriaDescribedBy(radioRef, groupErrorMsgId, isInvalid, !!groupErrorMsgId);
16
16
  return (jsxs("div", { className: clsx("ids-radio", {
17
17
  "ids-radio--compact": compact
18
- }, className), children: [jsx("input", { id: fieldId, ref: radioRef, type: "radio", className: clsx({
19
- "ids-input--light": light
18
+ }, className), children: [jsx("input", { id: fieldId, ref: radioRef, type: "radio", className: clsx("ids-radio__input", {
19
+ "ids-input--light": light,
20
+ "ids-focus-anchor": focusAnchor
20
21
  }), disabled: disabled, "aria-disabled": disabled, "aria-invalid": isInvalid, name: name, onChange: onChange, ...props }), jsxs("div", { className: clsx("ids-label-tooltip-wrapper", {
21
22
  "ids-label-tooltip-wrapper--inline": tooltip
22
23
  }), children: [jsx("label", { htmlFor: fieldId, className: clsx("ids-radio__label ids-label", {
@@ -2,6 +2,7 @@ import React, { ReactNode, InputHTMLAttributes } from "react";
2
2
  export interface IDSRadioButtonProps extends InputHTMLAttributes<HTMLInputElement> {
3
3
  name?: string;
4
4
  id?: string;
5
+ focusAnchor?: boolean;
5
6
  icon: string;
6
7
  children?: ReactNode;
7
8
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
@@ -4,11 +4,13 @@ import { forwardRef, useRef, useImperativeHandle } from 'react';
4
4
  import { useElementId } from '../../utils/hooks/useElementId.js';
5
5
  import clsx from 'clsx';
6
6
 
7
- const IDSRadioButton = forwardRef(({ name = "", icon = "user", id, children, onChange, className, ...props }, ref) => {
7
+ const IDSRadioButton = forwardRef(({ name = "", icon = "user", focusAnchor = false, id, children, onChange, className, ...props }, ref) => {
8
8
  const fieldId = useElementId(id);
9
9
  const radioButtonRef = useRef(null);
10
10
  useImperativeHandle(ref, () => radioButtonRef.current);
11
- return (jsxs("div", { className: clsx("ids-radio-button", className), children: [jsx("input", { id: fieldId, ref: radioButtonRef, type: "radio", className: "ids-radio-button__input", name: name, onChange: onChange, ...props }), jsx("label", { htmlFor: fieldId, className: `ids-radio-button__label ids-icon-${icon}`, children: children })] }));
11
+ return (jsxs("div", { className: clsx("ids-radio-button", className), children: [jsx("input", { id: fieldId, ref: radioButtonRef, type: "radio", className: clsx("ids-radio-button__input", {
12
+ "ids-focus-anchor": focusAnchor
13
+ }), name: name, onChange: onChange, ...props }), jsx("label", { htmlFor: fieldId, className: `ids-radio-button__label ids-icon-${icon}`, children: children })] }));
12
14
  });
13
15
  IDSRadioButton.displayName = "IDSRadioButton";
14
16
 
@@ -9,6 +9,7 @@ interface IDSRangeProps extends InputHTMLAttributes<HTMLInputElement> {
9
9
  max?: number;
10
10
  step?: number;
11
11
  disabled?: boolean;
12
+ focusAnchor?: boolean;
12
13
  tooltip?: ReactNode;
13
14
  onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
14
15
  }
@@ -5,7 +5,7 @@ import { useElementId } from '../../utils/hooks/useElementId.js';
5
5
  import '@inera/ids-design/components/form/range/range.css';
6
6
  import clsx from 'clsx';
7
7
 
8
- const IDSRange = forwardRef(({ label = "", value = 0, showTicks = false, interval = 0, min = 0, max = 0, step = 0, disabled = false, tooltip, id, onChange, className, ...props }, ref) => {
8
+ const IDSRange = forwardRef(({ label = "", value = 0, showTicks = false, interval = 0, min = 0, max = 0, step = 0, disabled = false, focusAnchor = false, tooltip, id, onChange, className, ...props }, ref) => {
9
9
  const [internalValue, setInternalValue] = useState(value);
10
10
  const [ticks, setTicks] = useState([]);
11
11
  const fieldId = useElementId(id);
@@ -27,7 +27,9 @@ const IDSRange = forwardRef(({ label = "", value = 0, showTicks = false, interva
27
27
  onChange(ev);
28
28
  }
29
29
  };
30
- return (jsxs("div", { className: clsx("ids-range", className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { htmlFor: fieldId, className: clsx("ids-label", { "ids-label--disabled": disabled }), children: label }), tooltip] }), jsx("input", { id: fieldId, ref: rangeRef, type: "range", className: "ids-range__input", min: min, "aria-valuemin": min, max: max, "aria-valuemax": max, step: step, value: internalValue, "aria-valuenow": internalValue, disabled: disabled, style: { backgroundSize: ((internalValue - min) * 100) / (max - min) + "% 100%" }, onChange: handleOnChange, ...props }), showTicks && (jsx("div", { className: clsx("ids-range-ticks", { "ids-range-ticks--disabled": disabled }), children: ticks.map(tick => (jsx("div", { className: "ids-range-tick", children: tick }, tick))) }))] }));
30
+ return (jsxs("div", { className: clsx("ids-range", className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { htmlFor: fieldId, className: clsx("ids-label", { "ids-label--disabled": disabled }), children: label }), tooltip] }), jsx("input", { id: fieldId, ref: rangeRef, type: "range", className: clsx("ids-range__input", {
31
+ "ids-focus-anchor": focusAnchor
32
+ }), min: min, "aria-valuemin": min, max: max, "aria-valuemax": max, step: step, value: internalValue, "aria-valuenow": internalValue, disabled: disabled, style: { backgroundSize: ((internalValue - min) * 100) / (max - min) + "% 100%" }, onChange: handleOnChange, ...props }), showTicks && (jsx("div", { className: clsx("ids-range-ticks", { "ids-range-ticks--disabled": disabled }), children: ticks.map(tick => (jsx("div", { className: "ids-range-tick", children: tick }, tick))) }))] }));
31
33
  });
32
34
  IDSRange.displayName = "IDSRange";
33
35
 
@@ -7,6 +7,7 @@ interface IDSSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
7
7
  required?: boolean;
8
8
  invalid?: boolean;
9
9
  light?: boolean;
10
+ focusAnchor?: boolean;
10
11
  noValidation?: boolean;
11
12
  tooltip?: ReactNode;
12
13
  children?: ReactNode;
@@ -7,7 +7,7 @@ import { useInputValidity } from '../form-hooks/useInputValidity.js';
7
7
  import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
8
8
  import clsx from 'clsx';
9
9
 
10
- const IDSSelect = forwardRef(({ label = "", srOf = "av", errorMsg = "", disabled = false, required = false, invalid = false, light = false, noValidation = false, tooltip, id, children, className, ...props }, ref) => {
10
+ const IDSSelect = forwardRef(({ label = "", srOf = "av", errorMsg = "", disabled = false, required = false, invalid = false, light = false, noValidation = false, focusAnchor = false, tooltip, id, children, className, ...props }, ref) => {
11
11
  const fieldId = useElementId(id);
12
12
  const errorMsgId = useElementId();
13
13
  const selectRef = useRef(null);
@@ -16,7 +16,8 @@ const IDSSelect = forwardRef(({ label = "", srOf = "av", errorMsg = "", disabled
16
16
  const isInvalid = (invalid || !hasValidValue) && !noValidation;
17
17
  useAriaDescribedBy(selectRef, errorMsgId, isInvalid, !!errorMsg);
18
18
  return (jsxs("div", { className: clsx("ids-select-component", className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { htmlFor: fieldId, className: "ids-label", children: label }), tooltip] }), jsx("div", { className: "ids-select-wrapper", children: jsx("select", { id: fieldId, ref: selectRef, className: clsx("ids-select", {
19
- "ids-input--light": light
19
+ "ids-input--light": light,
20
+ "ids-focus-anchor": focusAnchor
20
21
  }), "aria-invalid": isInvalid, required: required, disabled: disabled, ...props, children: children }) }), isInvalid && errorMsg && (jsx(IDSErrorMessage, { id: errorMsgId, show: true, children: errorMsg }))] }));
21
22
  });
22
23
  IDSSelect.displayName = "IDSSelect";
@@ -10,6 +10,7 @@ interface IDSSelectMultipleProps extends React.InputHTMLAttributes<HTMLInputElem
10
10
  errorMsg?: string;
11
11
  invalid?: boolean;
12
12
  light?: boolean;
13
+ focusAnchor?: boolean;
13
14
  noValidation?: boolean;
14
15
  tooltip?: ReactNode;
15
16
  children?: ReactNode;
@@ -7,7 +7,7 @@ import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
7
7
  import '@inera/ids-design/components/form/select-multiple/select-multiple.css';
8
8
  import clsx from 'clsx';
9
9
 
10
- const IDSSelectMultiple = ({ label = "", selectedLabel = "vald", selectedLabelPlural = "valda", placeholder = "", maxHeight = "", expanded = false, errorMsg = "", invalid = false, light = false, noValidation = false, tooltip, id, children, className, ...props }) => {
10
+ const IDSSelectMultiple = ({ label = "", selectedLabel = "vald", selectedLabelPlural = "valda", placeholder = "", maxHeight = "", expanded = false, errorMsg = "", invalid = false, light = false, noValidation = false, focusAnchor = false, tooltip, id, children, className, ...props }) => {
11
11
  const [checkboxListInvalid, setCheckboxListInvalid] = useState(false);
12
12
  const [checkedBoxesCount, setCheckedBoxesCount] = useState(0);
13
13
  const [value, setValue] = useState(placeholder);
@@ -52,10 +52,14 @@ const IDSSelectMultiple = ({ label = "", selectedLabel = "vald", selectedLabelPl
52
52
  };
53
53
  }, [isExpanded]);
54
54
  return (jsxs("div", { className: clsx("ids-select-multiple-component", className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { id: labelId, htmlFor: inputId, className: "ids-label", children: label }), tooltip] }), jsx("div", { className: "ids-select-multiple-wrapper", children: jsx("input", { id: inputId, "aria-labelledby": labelId, ref: selectMultipleRef, type: "button", className: clsx("ids-select-multiple__select", {
55
- "ids-input--light": light
55
+ "ids-input--light": light,
56
+ "ids-focus-anchor": focusAnchor
56
57
  }), "aria-expanded": isExpanded, "aria-invalid": invalid || checkboxListInvalid, placeholder: placeholder, value: value, onClick: () => setIsExpanded(!isExpanded), ...props }) }), jsx("div", { className: "ids-select-multiple__dropdown__wrapper", children: jsx("div", { className: clsx("ids-select-multiple__dropdown", {
57
58
  "ids-select-multiple__dropdown--show": isExpanded
58
- }), children: jsx("div", { className: "ids-select-multiple__dropdown__inner", style: { maxHeight: maxHeight }, children: jsx(IDSCheckboxGroup, { block: true, errorMsg: errorMsg, setCheckedBoxesCount: setCheckedBoxesCount, onValidityChange: (isValid) => {
59
+ }), children: jsx("div", { className: "ids-select-multiple__dropdown__inner", style: { maxHeight: maxHeight }, children: jsx(IDSCheckboxGroup, { block: true, noValidation: noValidation, errorMsg: errorMsg, setCheckedBoxesCount: setCheckedBoxesCount, onValidityChange: (isValid) => {
60
+ if (!isValid) {
61
+ setIsExpanded(true);
62
+ }
59
63
  setCheckboxListInvalid(!isValid);
60
64
  }, children: children }) }) }) })] }));
61
65
  };
@@ -11,6 +11,7 @@ interface IDSTextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
11
11
  noResize?: boolean;
12
12
  block?: boolean;
13
13
  light?: boolean;
14
+ focusAnchor?: boolean;
14
15
  readOnly?: boolean;
15
16
  tooltip?: ReactNode;
16
17
  }
@@ -7,7 +7,7 @@ import { useInputValidity } from '../form-hooks/useInputValidity.js';
7
7
  import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
8
8
  import clsx from 'clsx';
9
9
 
10
- const IDSTextarea = forwardRef(({ label = "", hint, errorMsg = "", disabled = false, invalid = false, required = false, noValidation = false, autoSize = false, noResize = false, block = false, light = false, readOnly = false, id, tooltip, className, ...props }, ref) => {
10
+ const IDSTextarea = forwardRef(({ label = "", hint, errorMsg = "", disabled = false, invalid = false, required = false, noValidation = false, autoSize = false, noResize = false, block = false, light = false, readOnly = false, focusAnchor = false, id, tooltip, className, ...props }, ref) => {
11
11
  const fieldId = useElementId(id);
12
12
  const errorMsgId = useElementId();
13
13
  const hintId = useElementId();
@@ -23,7 +23,8 @@ const IDSTextarea = forwardRef(({ label = "", hint, errorMsg = "", disabled = fa
23
23
  }, className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { className: clsx("ids-label", {
24
24
  "ids-label--disabled": disabled || readOnly
25
25
  }), htmlFor: fieldId, children: label }), tooltip && tooltip] }), jsx("textarea", { ref: textareaRef, id: fieldId, className: clsx("ids-textarea__textarea", {
26
- "ids-input--light": light
26
+ "ids-input--light": light,
27
+ "ids-focus-anchor": focusAnchor
27
28
  }), "aria-invalid": isInvalid, required: required, "aria-required": required, disabled: disabled, "aria-disabled": disabled, readOnly: readOnly, ...props }), hint && (jsx("div", { id: hintId, className: "ids-input__hint", children: hint })), isInvalid && errorMsg && (jsx(IDSErrorMessage, { id: errorMsgId, show: true, children: errorMsg }))] }));
28
29
  });
29
30
  IDSTextarea.displayName = "IDSTextarea";
@@ -6,6 +6,7 @@ interface IDSTimeProps extends InputHTMLAttributes<HTMLInputElement> {
6
6
  invalid?: boolean;
7
7
  required?: boolean;
8
8
  noValidation?: boolean;
9
+ focusAnchor?: boolean;
9
10
  light?: boolean;
10
11
  tooltip?: ReactNode;
11
12
  }
@@ -7,7 +7,7 @@ import { useInputValidity } from '../form-hooks/useInputValidity.js';
7
7
  import { useAriaDescribedBy } from '../form-hooks/useAriaDescribedBy.js';
8
8
  import clsx from 'clsx';
9
9
 
10
- const IDSTime = forwardRef(({ label = "", errorMsg = "", disabled = false, invalid = false, required = false, noValidation = false, light = false, id, tooltip, className, ...props }, ref) => {
10
+ const IDSTime = forwardRef(({ label = "", errorMsg = "", disabled = false, invalid = false, required = false, noValidation = false, light = false, focusAnchor = false, id, tooltip, className, ...props }, ref) => {
11
11
  const fieldId = useElementId(id);
12
12
  const errorMsgId = useElementId();
13
13
  const inputRef = useRef(null);
@@ -18,7 +18,8 @@ const IDSTime = forwardRef(({ label = "", errorMsg = "", disabled = false, inval
18
18
  return (jsxs("div", { className: clsx("ids-time-component", className), children: [jsxs("div", { className: "ids-label-tooltip-wrapper", children: [jsx("label", { className: clsx("ids-label", {
19
19
  "ids-label--disabled": disabled
20
20
  }), htmlFor: fieldId, children: label }), tooltip] }), jsx("div", { className: "ids-time", children: jsx("div", { className: "ids-time__input-wrapper", children: jsx("input", { ref: inputRef, id: fieldId, type: "time", className: clsx("ids-time__input", {
21
- "ids-input--light": light
21
+ "ids-input--light": light,
22
+ "ids-focus-anchor": focusAnchor
22
23
  }), "aria-invalid": isInvalid, required: required, "aria-required": required, disabled: disabled, "aria-disabled": disabled, ...props }) }) }), isInvalid && errorMsg && (jsx(IDSErrorMessage, { id: errorMsgId, show: true, children: errorMsg }))] }));
23
24
  });
24
25
  IDSTime.displayName = "IDSTime";
@@ -2,6 +2,7 @@ import { ReactNode, InputHTMLAttributes } from "react";
2
2
  interface IDSToggleProps extends InputHTMLAttributes<HTMLInputElement> {
3
3
  disabled?: boolean;
4
4
  tooltip?: ReactNode;
5
+ focusAnchor?: boolean;
5
6
  children?: ReactNode;
6
7
  }
7
8
  export declare const IDSToggle: import("react").ForwardRefExoticComponent<IDSToggleProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -4,11 +4,13 @@ import { forwardRef, useRef, useImperativeHandle } from 'react';
4
4
  import { useElementId } from '../../utils/hooks/useElementId.js';
5
5
  import clsx from 'clsx';
6
6
 
7
- const IDSToggle = forwardRef(({ disabled = false, tooltip, id, children, className, ...props }, ref) => {
7
+ const IDSToggle = forwardRef(({ disabled = false, focusAnchor = false, tooltip, id, children, className, ...props }, ref) => {
8
8
  const fieldId = useElementId(id);
9
9
  const toggleRef = useRef(null);
10
10
  useImperativeHandle(ref, () => toggleRef.current);
11
- return (jsx(Fragment, { children: jsxs("div", { className: clsx("ids-toggle", className), children: [jsx("input", { id: fieldId, ref: toggleRef, className: "ids-toggle__input", type: "checkbox", disabled: disabled, ...props }), jsxs("div", { className: "ids-label-tooltip-wrapper ids-label-tooltip-wrapper--inline", children: [jsx("label", { htmlFor: fieldId, className: "ids-toggle__label ids-label ids-label--clickable", children: children }), tooltip] })] }) }));
11
+ return (jsx(Fragment, { children: jsxs("div", { className: clsx("ids-toggle", className), children: [jsx("input", { id: fieldId, ref: toggleRef, className: clsx("ids-toggle__input", {
12
+ "ids-focus-anchor": focusAnchor
13
+ }), type: "checkbox", disabled: disabled, ...props }), jsxs("div", { className: "ids-label-tooltip-wrapper ids-label-tooltip-wrapper--inline", children: [jsx("label", { htmlFor: fieldId, className: "ids-toggle__label ids-label ids-label--clickable", children: children }), tooltip] })] }) }));
12
14
  });
13
15
  IDSToggle.displayName = "IDSToggle";
14
16
 
@@ -6,27 +6,40 @@ import clsx from 'clsx';
6
6
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
7
7
 
8
8
  const IDSHeader1177Avatar = ({ username, agent, links, persistent = false }) => {
9
- const [expanded, setExpanded] = useState(false);
9
+ const [isExpanded, setIsExpanded] = useState(false);
10
10
  const headerContext = useHeaderContext();
11
- const avatarRef = useRef(null);
12
- const toggleExpanded = () => setExpanded(prev => !prev);
11
+ const containerRef = useRef(null);
12
+ const dropdownRef = useRef(null);
13
+ const toggleExpanded = () => setIsExpanded(prev => !prev);
13
14
  const handleClickOutside = (event) => {
14
- if (!persistent && avatarRef.current && !avatarRef.current.contains(event.target)) {
15
- setExpanded(false);
15
+ if (!persistent && containerRef.current && !containerRef.current.contains(event.target)) {
16
+ setIsExpanded(false);
16
17
  }
17
18
  };
18
19
  useEffect(() => {
19
- document.addEventListener("click", handleClickOutside);
20
+ document.addEventListener("mousedown", handleClickOutside);
20
21
  return () => {
21
- document.removeEventListener("click", handleClickOutside);
22
+ document.removeEventListener("mousedown", handleClickOutside);
22
23
  };
23
24
  }, [persistent]);
24
- return (jsx("div", { ref: avatarRef, className: clsx("ids-header-1177-avatar", {
25
+ const handleLinkClick = () => {
26
+ setIsExpanded(false);
27
+ };
28
+ useEffect(() => {
29
+ if (!persistent && isExpanded) {
30
+ const links = dropdownRef.current?.querySelectorAll("a") || [];
31
+ links.forEach(link => link.addEventListener("click", handleLinkClick));
32
+ return () => {
33
+ links.forEach(link => link.removeEventListener("click", handleLinkClick));
34
+ };
35
+ }
36
+ }, [isExpanded, persistent]);
37
+ return (jsx("div", { ref: containerRef, className: clsx("ids-header-1177-avatar", {
25
38
  "ids-header-1177-avatar--unresponsive": headerContext?.unresponsive
26
39
  }), children: jsxs("div", { className: "ids-header-1177-avatar__dropdown-wrapper", children: [jsx("button", { className: clsx("ids-header-1177-avatar__button", {
27
- "ids-header-1177-avatar__button--expanded": expanded
28
- }), onClick: toggleExpanded, "aria-controls": "ids-header-1177-avatar__dropdown", "aria-expanded": expanded, children: jsx("div", { className: "ids-header-1177-avatar-content__name", title: username, children: username }) }), jsxs("div", { id: "ids-header-1177-avatar__dropdown", className: clsx("ids-header-1177-avatar__dropdown", {
29
- "ids-header-1177-avatar__dropdown--expanded": expanded
40
+ "ids-header-1177-avatar__button--expanded": isExpanded
41
+ }), onClick: toggleExpanded, "aria-controls": "ids-header-1177-avatar__dropdown", "aria-expanded": isExpanded, children: jsx("div", { className: "ids-header-1177-avatar-content__name", title: username, children: username }) }), jsxs("div", { ref: dropdownRef, id: "ids-header-1177-avatar__dropdown", className: clsx("ids-header-1177-avatar__dropdown", {
42
+ "ids-header-1177-avatar__dropdown--expanded": isExpanded
30
43
  }), children: [agent && jsx("div", { className: "ids-header-1177-avatar__agent", children: agent }), agent && links && jsx("div", { className: "ids-header-1177-avatar__menu-separator" }), links && jsx("div", { className: "ids-header-1177-avatar__menu-links", children: links })] })] }) }));
31
44
  };
32
45
 
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import React__default, { useState } from 'react';
3
+ import React__default, { useState, useRef, useEffect } from 'react';
4
4
  import '@inera/ids-design/components/header-1177/header-1177-nav-item.css';
5
5
  import clsx from 'clsx';
6
6
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
@@ -8,18 +8,35 @@ import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
8
8
  const IDSHeader1177NavItem = ({ active = false, expanded = false, label = "", col1, col2, col3, col4, children }) => {
9
9
  const [isExpanded, setIsExpanded] = useState(expanded);
10
10
  const headerContext = useHeaderContext();
11
- const toggleExpanded = () => setIsExpanded(prev => !prev);
11
+ const containerRef = useRef(null);
12
+ const handleClickOutside = (event) => {
13
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
14
+ setIsExpanded(false);
15
+ }
16
+ };
17
+ useEffect(() => {
18
+ setIsExpanded(expanded);
19
+ document.addEventListener("click", handleClickOutside);
20
+ return () => {
21
+ document.removeEventListener("click", handleClickOutside);
22
+ };
23
+ }, [expanded]);
12
24
  const renderLink = (link) => {
13
25
  return React__default.isValidElement(link)
14
- ? React__default.cloneElement(link, { activeIcon: true, block: true, colorPreset: 2 })
26
+ ? React__default.cloneElement(link, {
27
+ activeIcon: true,
28
+ block: true,
29
+ colorPreset: 2,
30
+ onClick: () => setIsExpanded(false)
31
+ })
15
32
  : link;
16
33
  };
17
34
  const renderDropdown = () => isExpanded && (jsx("div", { id: "ids-header-1177-nav-dropdown", className: "ids-header-1177__items__item-dropdown", children: jsxs("div", { className: "ids-header-1177__items__item-dropdown__content", children: [jsx("div", { className: "ids-header-1177__items__item-dropdown__content-col-1", children: col1?.map((link, idx) => (jsx("div", { className: "ids-header-1177__items-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177__items__item-dropdown__content-col-2", children: col2?.map((link, idx) => (jsx("div", { className: "ids-header-1177__items-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177__items__item-dropdown__content-col-3", children: col3?.map((link, idx) => (jsx("div", { className: "ids-header-1177__items-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177__items__item-dropdown__content-col-4", children: col4 })] }) }));
18
- return (jsxs("div", { className: clsx("ids-header-1177__nav-item", {
35
+ return (jsxs("div", { ref: containerRef, className: clsx("ids-header-1177__nav-item", {
19
36
  "ids-header-1177__nav-item--unresponsive": headerContext?.unresponsive,
20
37
  "ids-header-1177__nav-item--fluid": headerContext?.fluid,
21
38
  "ids-header-1177__nav-item--active": active || isExpanded
22
- }), children: [label ? (jsx("button", { onClick: toggleExpanded, "aria-expanded": isExpanded, "aria-controls": "ids-header-1177-nav-dropdown", children: label })) : (children), renderDropdown()] }));
39
+ }), children: [label ? (jsx("button", { onClick: () => setIsExpanded(prev => !prev), "aria-expanded": isExpanded, "aria-controls": "ids-header-1177-nav-dropdown", children: label })) : (children), renderDropdown()] }));
23
40
  };
24
41
 
25
42
  export { IDSHeader1177NavItem };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import React__default, { useState } from 'react';
3
+ import React__default, { useState, useRef, useEffect } from 'react';
4
4
  import '@inera/ids-design/components/header-1177-admin/header-1177-admin-nav-item.css';
5
5
  import clsx from 'clsx';
6
6
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
@@ -8,20 +8,35 @@ import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
8
8
  const IDSHeader1177AdminNavItem = ({ active = false, expanded = false, label = "", col1, col2, col3, col4, children }) => {
9
9
  const headerContext = useHeaderContext();
10
10
  const [isExpanded, setIsExpanded] = useState(expanded);
11
- const toggleExpanded = () => {
12
- setIsExpanded(prev => !prev);
11
+ const containerRef = useRef(null);
12
+ const handleClickOutside = (event) => {
13
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
14
+ setIsExpanded(false);
15
+ }
13
16
  };
17
+ useEffect(() => {
18
+ setIsExpanded(expanded);
19
+ document.addEventListener("click", handleClickOutside);
20
+ return () => {
21
+ document.removeEventListener("click", handleClickOutside);
22
+ };
23
+ }, [expanded]);
14
24
  const renderLink = (link) => {
15
25
  return React__default.isValidElement(link)
16
- ? React__default.cloneElement(link, { activeIcon: true, block: true, colorPreset: 1 })
26
+ ? React__default.cloneElement(link, {
27
+ activeIcon: true,
28
+ block: true,
29
+ colorPreset: 1,
30
+ onClick: () => setIsExpanded(false)
31
+ })
17
32
  : link;
18
33
  };
19
34
  const renderDropdown = () => isExpanded && (jsx("div", { style: { marginTop: "0.125rem" }, id: "ids-header-1177-admin-nav-dropdown", className: "ids-header-1177-admin__items__item-dropdown", children: jsxs("div", { className: "ids-header-1177-admin__items__item-dropdown__content", children: [jsx("div", { className: "ids-header-1177-admin__items__item-dropdown__content-col-1", children: col1?.map((link, idx) => (jsx("div", { className: "ids-header-1177-admin__items-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177-admin__items__item-dropdown__content-col-2", children: col2?.map((link, idx) => (jsx("div", { className: "ids-header-1177-admin__items-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177-admin__items__item-dropdown__content-col-3", children: col3?.map((link, idx) => (jsx("div", { className: "ids-header-1177-admin__items-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177-admin__items__item-dropdown__content-col-4", children: col4?.map((link, idx) => (jsx("div", { className: "ids-header-1177-admin__items-wrapper", children: renderLink(link) }, idx))) })] }) }));
20
- return (jsxs("div", { className: clsx("ids-header-1177-admin__nav-item", {
35
+ return (jsxs("div", { ref: containerRef, className: clsx("ids-header-1177-admin__nav-item", {
21
36
  "ids-header-1177-admin__nav-item--fluid": headerContext?.fluid,
22
37
  "ids-header-1177-admin__nav-item--unresponsive": headerContext?.unresponsive,
23
38
  "ids-header-1177-admin__nav-item--active": active || isExpanded
24
- }), children: [label ? (jsx("button", { onClick: toggleExpanded, "aria-expanded": isExpanded, "aria-controls": "ids-header-1177-admin-nav-dropdown", children: label })) : (children), renderDropdown()] }));
39
+ }), children: [label ? (jsx("button", { onClick: () => setIsExpanded(prev => !prev), "aria-expanded": isExpanded, "aria-controls": "ids-header-1177-admin-nav-dropdown", children: label })) : (children), renderDropdown()] }));
25
40
  };
26
41
 
27
42
  export { IDSHeader1177AdminNavItem };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import React__default, { useState } from 'react';
3
+ import React__default, { useState, useRef, useEffect } from 'react';
4
4
  import '@inera/ids-design/components/header-1177-pro/header-1177-pro-nav-item.css';
5
5
  import clsx from 'clsx';
6
6
  import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
@@ -8,18 +8,33 @@ import { useHeaderContext } from '../utils/contexts/HeaderContext.js';
8
8
  const IDSHeader1177ProNavItem = ({ active = false, expanded = false, label = "", col1, col2, col3, col4, children }) => {
9
9
  const headerContext = useHeaderContext();
10
10
  const [isExpanded, setIsExpanded] = useState(expanded);
11
- const toggleExpanded = () => {
12
- setIsExpanded(prev => !prev);
11
+ const containerRef = useRef(null);
12
+ const handleClickOutside = (event) => {
13
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
14
+ setIsExpanded(false);
15
+ }
13
16
  };
17
+ useEffect(() => {
18
+ setIsExpanded(expanded);
19
+ document.addEventListener("click", handleClickOutside);
20
+ return () => {
21
+ document.removeEventListener("click", handleClickOutside);
22
+ };
23
+ }, [expanded]);
14
24
  const renderItem = () => {
15
25
  if (label) {
16
- return (jsx("button", { onClick: toggleExpanded, "aria-expanded": isExpanded, "aria-controls": "ids-dropdown", children: label }));
26
+ return (jsx("button", { onClick: () => setIsExpanded(prev => !prev), "aria-expanded": isExpanded, "aria-controls": "ids-dropdown", children: label }));
17
27
  }
18
28
  return children;
19
29
  };
20
30
  const renderLink = (link) => {
21
31
  return React__default.isValidElement(link)
22
- ? React__default.cloneElement(link, { activeIcon: true, block: true, colorPreset: 1 })
32
+ ? React__default.cloneElement(link, {
33
+ activeIcon: true,
34
+ block: true,
35
+ colorPreset: 1,
36
+ onClick: () => setIsExpanded(false)
37
+ })
23
38
  : link;
24
39
  };
25
40
  const renderDropdown = () => {
@@ -27,7 +42,7 @@ const IDSHeader1177ProNavItem = ({ active = false, expanded = false, label = "",
27
42
  return null;
28
43
  return (jsx("div", { id: "ids-dropdown", className: "ids-header-1177-pro__items__item-dropdown", children: jsxs("div", { className: "ids-header-1177-pro__items__item-dropdown__content", children: [jsx("div", { className: "ids-header-1177-pro__items__item-dropdown__content-col-1", children: col1?.map((link, idx) => (jsx("div", { className: "ids-header-1177-pro__item-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177-pro__items__item-dropdown__content-col-2", children: col2?.map((link, idx) => (jsx("div", { className: "ids-header-1177-pro__item-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177-pro__items__item-dropdown__content-col-3", children: col3?.map((link, idx) => (jsx("div", { className: "ids-header-1177-pro__item-wrapper", children: renderLink(link) }, idx))) }), jsx("div", { className: "ids-header-1177-pro__items__item-dropdown__content-col-4", children: col4?.map((link, idx) => (jsx("div", { className: "ids-header-1177-pro__item-wrapper", children: renderLink(link) }, idx))) })] }) }));
29
44
  };
30
- return (jsxs("div", { className: clsx("ids-header-1177-pro__nav-item", {
45
+ return (jsxs("div", { ref: containerRef, className: clsx("ids-header-1177-pro__nav-item", {
31
46
  "ids-header-1177-pro__nav-item--unresponsive": headerContext?.unresponsive,
32
47
  "ids-header-1177-pro__nav-item--active": active || isExpanded
33
48
  }), children: [renderItem(), renderDropdown()] }));
@@ -38,7 +38,12 @@ const IDSHeaderIneraNavItem = ({ label = "", active = false, expanded = false, n
38
38
  };
39
39
  const renderLink = (link) => {
40
40
  return React__default.isValidElement(link)
41
- ? React__default.cloneElement(link, { activeIcon: true, block: true, colorPreset: 2 })
41
+ ? React__default.cloneElement(link, {
42
+ activeIcon: true,
43
+ block: true,
44
+ colorPreset: 2,
45
+ onClick: () => setIsExpanded(false)
46
+ })
42
47
  : link;
43
48
  };
44
49
  const renderContent = () => {
@@ -38,7 +38,12 @@ const IDSHeaderIneraAdminNavItem = ({ label = "", active = false, expanded = fal
38
38
  };
39
39
  const renderLink = (link) => {
40
40
  return React__default.isValidElement(link)
41
- ? React__default.cloneElement(link, { activeIcon: true, block: true, colorPreset: 2 })
41
+ ? React__default.cloneElement(link, {
42
+ activeIcon: true,
43
+ block: true,
44
+ colorPreset: 2,
45
+ onClick: () => setIsExpanded(false)
46
+ })
42
47
  : link;
43
48
  };
44
49
  const renderContent = () => {
@@ -1,6 +1,6 @@
1
- import React, { ReactNode, HTMLAttributes, ReactElement } from "react";
1
+ import React, { ReactNode, ReactElement, AnchorHTMLAttributes } from "react";
2
2
  export type ColorPreset = 0 | 1 | 2 | 3 | 4;
3
- export interface IDSLinkProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
3
+ export interface IDSLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "children"> {
4
4
  colorPreset?: ColorPreset;
5
5
  light?: boolean;
6
6
  active?: boolean;
@@ -12,6 +12,6 @@ export interface IDSLinkProps extends Omit<HTMLAttributes<HTMLElement>, "childre
12
12
  large?: boolean;
13
13
  focusAnchor?: boolean;
14
14
  endItem?: ReactNode;
15
- children: ReactElement;
15
+ children?: ReactElement;
16
16
  }
17
17
  export declare const IDSLink: React.FC<IDSLinkProps>;
@@ -2,6 +2,7 @@ import React, { ReactNode } from "react";
2
2
  import "@inera/ids-design/components/puff-list/puff-list.css";
3
3
  interface IDSPuffListItemProps extends React.HTMLAttributes<HTMLDivElement> {
4
4
  headline?: ReactNode;
5
+ headlineLevel?: 2 | 3 | 4 | 5;
5
6
  headlineLink?: React.ReactElement;
6
7
  itemLink?: React.ReactElement;
7
8
  date?: Date | null;
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { useState, useEffect, isValidElement, cloneElement } from 'react';
3
+ import React__default, { useState, useEffect, isValidElement, cloneElement } from 'react';
4
4
  import '@inera/ids-design/components/puff-list/puff-list.css';
5
5
  import { getMonthAsSweText, getDayAsText } from '../../utils/utils.js';
6
6
  import clsx from 'clsx';
7
7
 
8
- const IDSPuffListItem = ({ headline = "", headlineLink, itemLink, date = null, year = "", month = -1, monthLabel = "", day = -1, time = "", noContent = false, noMargin = false, dateLabel, className, children }) => {
8
+ const IDSPuffListItem = ({ headline = "", headlineLevel = 2, headlineLink, itemLink, date = null, year = "", month = -1, monthLabel = "", day = -1, time = "", noContent = false, noMargin = false, dateLabel, className, children }) => {
9
9
  const [presentedDate, setPresentedDate] = useState(date);
10
10
  useEffect(() => {
11
11
  if (date) {
@@ -31,14 +31,16 @@ const IDSPuffListItem = ({ headline = "", headlineLink, itemLink, date = null, y
31
31
  "ids-puff-list-item--no-margin": noMargin
32
32
  });
33
33
  const renderHeadline = () => {
34
+ const level = [2, 3, 4, 5].includes(headlineLevel) ? headlineLevel : 2;
35
+ const Tag = `h${level}`;
34
36
  const headlineContent = jsx(Fragment, { children: headline });
35
37
  if (headlineLink && isValidElement(headlineLink)) {
36
- return (jsx("h2", { className: getHeaderClass(noMargin), children: cloneElement(headlineLink, {
37
- ...headlineLink.props,
38
- children: headlineContent
39
- }) }));
38
+ return React__default.createElement(Tag, { className: getHeaderClass(noMargin) }, cloneElement(headlineLink, {
39
+ ...headlineLink.props,
40
+ children: headlineContent
41
+ }));
40
42
  }
41
- return jsx("h2", { className: getHeaderClass(noMargin), children: headline });
43
+ return React__default.createElement(Tag, { className: getHeaderClass(noMargin) }, headline);
42
44
  };
43
45
  const renderBody = () => {
44
46
  const content = (jsxs(Fragment, { children: [renderHeadline(), !noContent && jsx("div", { className: "ids-puff-list-item__body", children: children })] }));
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@inera/ids-react",
3
- "version": "7.0.1",
3
+ "version": "7.1.1",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
6
  "react": "*"
7
7
  },
8
8
  "dependencies": {
9
- "@inera/ids-core": "7.0.x",
9
+ "@inera/ids-core": "7.1.x",
10
10
  "@lit-labs/react": "^1.1.0",
11
11
  "clsx": "*"
12
12
  },