@fremtind/jokul 0.69.0 → 0.69.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 (70) hide show
  1. package/build/cjs/components/expander/types.d.cts +2 -2
  2. package/build/cjs/components/select/Select.cjs.map +1 -1
  3. package/build/cjs/components/toggle-switch/usePillStyles.cjs.map +1 -1
  4. package/build/cjs/components/toggle-switch/usePillStyles.d.cts +1 -1
  5. package/build/cjs/hooks/useAnimatedHeight/types.d.cts +3 -3
  6. package/build/cjs/hooks/useAnimatedHeight/useAnimatedHeightBetween.cjs.map +1 -1
  7. package/build/cjs/hooks/useAnimatedHeight/useAutoAnimateHeight.cjs.map +1 -1
  8. package/build/cjs/hooks/useAnimatedHeight/useAutoAnimateHeight.d.cts +2 -2
  9. package/build/cjs/hooks/useClickOutside/useClickOutside.cjs.map +1 -1
  10. package/build/cjs/hooks/useClickOutside/useClickOutside.d.cts +1 -1
  11. package/build/cjs/hooks/useFocusOutside/useFocusOutside.cjs.map +1 -1
  12. package/build/cjs/hooks/useFocusOutside/useFocusOutside.d.cts +1 -1
  13. package/build/cjs/hooks/useKeyListener/useKeyListener.cjs.map +1 -1
  14. package/build/cjs/hooks/useKeyListener/useKeyListener.d.cts +1 -1
  15. package/build/cjs/hooks/useListNavigation/useListNavigation.cjs.map +1 -1
  16. package/build/cjs/hooks/useListNavigation/useListNavigation.d.cts +1 -1
  17. package/build/cjs/hooks/useMutationObserver/useMutationObserver.cjs.map +1 -1
  18. package/build/cjs/hooks/useMutationObserver/useMutationObserver.d.cts +1 -1
  19. package/build/cjs/hooks/useScrollIntoView/useScrollIntoView.cjs.map +1 -1
  20. package/build/cjs/hooks/useScrollIntoView/useScrollIntoView.d.cts +1 -1
  21. package/build/es/components/expander/types.d.ts +2 -2
  22. package/build/es/components/select/Select.js.map +1 -1
  23. package/build/es/components/toggle-switch/usePillStyles.d.ts +1 -1
  24. package/build/es/components/toggle-switch/usePillStyles.js.map +1 -1
  25. package/build/es/hooks/useAnimatedHeight/types.d.ts +3 -3
  26. package/build/es/hooks/useAnimatedHeight/useAnimatedHeightBetween.js.map +1 -1
  27. package/build/es/hooks/useAnimatedHeight/useAutoAnimateHeight.d.ts +2 -2
  28. package/build/es/hooks/useAnimatedHeight/useAutoAnimateHeight.js.map +1 -1
  29. package/build/es/hooks/useClickOutside/useClickOutside.d.ts +1 -1
  30. package/build/es/hooks/useClickOutside/useClickOutside.js.map +1 -1
  31. package/build/es/hooks/useFocusOutside/useFocusOutside.d.ts +1 -1
  32. package/build/es/hooks/useFocusOutside/useFocusOutside.js.map +1 -1
  33. package/build/es/hooks/useKeyListener/useKeyListener.d.ts +1 -1
  34. package/build/es/hooks/useKeyListener/useKeyListener.js.map +1 -1
  35. package/build/es/hooks/useListNavigation/useListNavigation.d.ts +1 -1
  36. package/build/es/hooks/useListNavigation/useListNavigation.js.map +1 -1
  37. package/build/es/hooks/useMutationObserver/useMutationObserver.d.ts +1 -1
  38. package/build/es/hooks/useMutationObserver/useMutationObserver.js.map +1 -1
  39. package/build/es/hooks/useScrollIntoView/useScrollIntoView.d.ts +1 -1
  40. package/build/es/hooks/useScrollIntoView/useScrollIntoView.js.map +1 -1
  41. package/build/jokul.css +1 -1
  42. package/package.json +664 -664
  43. package/styles/components/checkbox/checkbox.css +4 -4
  44. package/styles/components/checkbox/checkbox.min.css +1 -1
  45. package/styles/components/checkbox-panel/checkbox-panel.css +2 -2
  46. package/styles/components/checkbox-panel/checkbox-panel.min.css +1 -1
  47. package/styles/components/countdown/countdown.css +2 -2
  48. package/styles/components/countdown/countdown.min.css +1 -1
  49. package/styles/components/feedback/feedback.css +2 -2
  50. package/styles/components/feedback/feedback.min.css +1 -1
  51. package/styles/components/input-group/input-group.css +2 -2
  52. package/styles/components/input-group/input-group.min.css +1 -1
  53. package/styles/components/loader/loader.css +6 -6
  54. package/styles/components/loader/loader.min.css +1 -1
  55. package/styles/components/loader/skeleton-loader.css +5 -5
  56. package/styles/components/loader/skeleton-loader.min.css +1 -1
  57. package/styles/components/message/message.css +2 -2
  58. package/styles/components/message/message.min.css +1 -1
  59. package/styles/components/progress-bar/progress-bar.css +1 -1
  60. package/styles/components/progress-bar/progress-bar.min.css +1 -1
  61. package/styles/components/radio-button/radio-button.css +2 -2
  62. package/styles/components/radio-button/radio-button.min.css +1 -1
  63. package/styles/components/radio-panel/radio-panel.css +2 -2
  64. package/styles/components/radio-panel/radio-panel.min.css +1 -1
  65. package/styles/components/system-message/system-message.css +2 -2
  66. package/styles/components/system-message/system-message.min.css +1 -1
  67. package/styles/components/toast/toast.css +4 -4
  68. package/styles/components/toast/toast.min.css +1 -1
  69. package/styles/styles.css +36 -36
  70. package/styles/styles.min.css +1 -1
@@ -19,7 +19,7 @@ export type ExpanderComponent = <ElementType extends React.ElementType = "button
19
19
  export type ExpandableContext = {
20
20
  open: boolean;
21
21
  onToggle: () => void;
22
- onTransitionStart: (isOpening: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement>) => void;
23
- onTransitionEnd: (isOpen: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement>) => void;
22
+ onTransitionStart: (isOpening: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement | null>) => void;
23
+ onTransitionEnd: (isOpen: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement | null>) => void;
24
24
  setExpanderHeight: (height: number) => void;
25
25
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Select.cjs","sources":["../../../../src/components/select/Select.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport React, {\n type CSSProperties,\n type FocusEvent,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAnimatedHeight } from \"../../hooks/useAnimatedHeight/useAnimatedHeight.js\";\nimport { useId } from \"../../hooks/useId/useId.js\";\nimport { useListNavigation } from \"../../hooks/useListNavigation/useListNavigation.js\";\nimport { usePreviousValue } from \"../../hooks/usePreviousValue/usePreviousValue.js\";\nimport { type ValuePair, getValuePair } from \"../../utilities/valuePair.js\";\nimport { ArrowVerticalAnimated } from \"../icon/icons/animated/ArrowVerticalAnimated.js\";\nimport { InputGroup } from \"../input-group/InputGroup.js\";\nimport type { PopupTipProps } from \"../tooltip/types.js\";\nimport { focusSelected, toLower } from \"./select-utils.js\";\nimport type { SelectProps } from \"./types.js\";\n\nconst noop = () => {\n return;\n};\n\ninterface Option extends ValuePair {\n visible: boolean;\n}\n\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(\n (props, forwardedSelectRef) => {\n const {\n id,\n name,\n items,\n value,\n label,\n labelProps,\n onChange,\n onBlur,\n onFocus,\n className,\n helpLabel,\n errorLabel,\n invalid,\n searchable = false,\n inline = false,\n defaultPrompt = \"Velg\",\n density,\n width,\n maxShownOptions = 5,\n style,\n tooltip,\n ...rest\n } = props;\n\n const listId = useId(id || \"jkl-select\", { generateSuffix: !id });\n const labelId = `${listId}_label`;\n const buttonId = `${listId}_button`;\n const searchInputId = `${listId}_search-input`;\n\n const [dropdownIsShown, setShown] = useState(false);\n const toggleListVisibility = useCallback(() => {\n setShown((previousValue) => !previousValue);\n }, []);\n\n /// Søk\n\n const isSearchable = Boolean(searchable);\n const showSearchInputField = isSearchable && dropdownIsShown;\n const [searchValue, setSearchValue] = useState(\"\");\n const searchFn = useCallback(\n (item: ValuePair) => {\n if (\n item.label.toLowerCase().includes(searchValue.toLowerCase())\n ) {\n return true;\n }\n\n if (typeof searchable === \"function\") {\n return searchable(searchValue, item);\n }\n\n return false;\n },\n [searchable, searchValue],\n );\n const visibleItems: Option[] = useMemo(\n () =>\n items.map(getValuePair).map((item) => {\n const visible =\n !isSearchable || searchValue === \"\" || searchFn(item);\n return { ...item, visible };\n }),\n [items, isSearchable, searchValue, searchFn],\n );\n const valueIsInItems: boolean = useMemo(() => {\n if (typeof value === \"undefined\") {\n return false;\n }\n return items.some((item) =>\n typeof item === \"string\"\n ? item === value\n : item.value === value,\n );\n }, [value, items]);\n\n /// Valg av <option>\n\n const [selectedValue, setSelectedValue] = useState<string>(\n valueIsInItems && value !== undefined ? value : \"\",\n );\n const hasSelectedValue = selectedValue !== \"\";\n const selectedValueLabel = useMemo(\n () =>\n visibleItems.find((item) => item.value === selectedValue)\n ?.label || defaultPrompt,\n [visibleItems, selectedValue, defaultPrompt],\n );\n\n const selectRef = useRef<HTMLSelectElement | null>(null);\n // Hjelpefunksjon som gjør det enklere å forwarde refen og å bruke den selv internt\n const unifiedSelectRef = useCallback(\n (instance: HTMLSelectElement | null) => {\n selectRef.current = instance;\n if (forwardedSelectRef) {\n if (typeof forwardedSelectRef === \"function\") {\n forwardedSelectRef(instance);\n } else {\n forwardedSelectRef.current = instance;\n }\n }\n if (instance) {\n setSelectedValue(instance.value);\n }\n },\n [forwardedSelectRef],\n );\n\n const previousValue = usePreviousValue(value);\n useEffect(() => {\n if (value === previousValue) {\n return;\n }\n if (typeof value === \"undefined\" || !valueIsInItems) {\n setSelectedValue(\"\");\n } else {\n setSelectedValue(value);\n }\n }, [value, previousValue, valueIsInItems]);\n\n const selectOption = useCallback(\n (item: Option) => {\n const nextValue = item.value;\n setSearchValue(\"\");\n setSelectedValue(nextValue);\n toggleListVisibility();\n buttonRef.current?.focus();\n },\n [toggleListVisibility],\n );\n\n // La komponenten rendre <select> med den valgte verdien før onChange trigges, slik at\n // react-hook-form@>7.41.1 behandler feltet som at det har en verdi.\n const previousSelectedValue = usePreviousValue(selectedValue);\n useEffect(() => {\n // previousSelectedValue er undefined på første render, men da vil vi ikke ha en onChange uansett\n if (\n typeof previousSelectedValue === \"undefined\" ||\n previousSelectedValue === selectedValue ||\n selectedValue === value\n ) {\n return;\n }\n if (onChange) {\n onChange({\n type: \"change\",\n target: { name, value: selectedValue },\n });\n }\n if (selectRef.current) {\n selectRef.current.dispatchEvent(\n new Event(\"change\", { bubbles: true }),\n );\n }\n }, [onChange, name, value, selectedValue, previousSelectedValue]);\n\n /// Fokushåndtering\n\n const componentRootElementRef = useRef<HTMLDivElement>(null);\n const focusInsideRef = useRef(false);\n const searchFieldRef = useRef<HTMLInputElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleFocusPlacement = useCallback(\n (isOpen: boolean, ref: RefObject<HTMLElement>) => {\n if (isOpen && !isSearchable) {\n const listElement = ref.current;\n if (listElement) {\n focusSelected(listElement, selectedValue);\n }\n } else if (isOpen) {\n if (searchFieldRef.current) {\n searchFieldRef.current.focus();\n }\n } else {\n if (focusInsideRef.current && buttonRef.current) {\n buttonRef.current.focus();\n }\n }\n },\n [isSearchable, selectedValue],\n );\n\n const [dropdownRef] = useAnimatedHeight<HTMLDivElement>(\n dropdownIsShown,\n {\n onFirstVisible: handleFocusPlacement,\n onTransitionEnd: handleFocusPlacement,\n },\n );\n\n useListNavigation({ ref: dropdownRef });\n\n const close = useCallback(() => {\n if (isSearchable) {\n setSearchValue(\"\");\n }\n if (onBlur) {\n onBlur({\n type: \"blur\",\n target: { name, value: selectedValue },\n });\n selectRef.current?.dispatchEvent(\n new Event(\"focusout\", { bubbles: true }),\n );\n }\n focusInsideRef.current = false;\n setShown(false);\n }, [onBlur, isSearchable, name, selectedValue]);\n\n const handleBlur = useCallback(\n (e: FocusEvent<HTMLButtonElement | HTMLInputElement>) => {\n const componentRootElement = componentRootElementRef.current;\n // There are known issues in Firefox when using \"relatedTarget\" in onBlur events:\n // https://github.com/facebook/react/issues/2011\n // This might be fixed in react 17. Se issue above.\n const nextFocusIsInsideComponent =\n componentRootElement?.contains(e.relatedTarget as Node);\n if (!nextFocusIsInsideComponent) {\n close();\n }\n },\n [close],\n );\n\n const handleFocus = useCallback(() => {\n if (!focusInsideRef.current) {\n if (onFocus) {\n onFocus({\n type: \"change\",\n target: { name, value: selectedValue },\n });\n }\n focusInsideRef.current = true;\n }\n }, [onFocus, selectedValue, name]);\n\n const handleMouseOver = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n // Ved mouseOver på options flytter vi fokus til dem for å unngå \"dobbel fokus\"\n // der det ser ut som to forskjellige elementer er fokusert/hovered samtidig\n (e.target as HTMLButtonElement).focus({ preventScroll: true });\n },\n [],\n );\n\n // Handle focus and blur of hidden select element\n useEffect(() => {\n const select = selectRef.current;\n const searchField = searchFieldRef.current;\n const button = buttonRef.current;\n const componentRootElement = componentRootElementRef.current;\n\n select?.addEventListener(\"focus\", () => {\n showSearchInputField ? searchField?.focus() : button?.focus();\n });\n select?.addEventListener(\"blur\", function (this, ev) {\n componentRootElement?.contains(ev.relatedTarget as Node) &&\n ev.preventDefault();\n });\n\n return () => {\n select?.removeEventListener(\"focus\", () => {\n showSearchInputField\n ? searchField?.focus()\n : button?.focus();\n });\n select?.removeEventListener(\"blur\", function (this, ev) {\n componentRootElement?.contains(ev.relatedTarget as Node) &&\n ev.preventDefault();\n });\n };\n }, [showSearchInputField]);\n\n /// Tastaturnavigasjon\n\n // Add support for opening dropdown with arrowkey down as expected from native select\n // onKeyDown to stop ArrowDown from scrolling the page\n const handleOnKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (\n (e.key === \"ArrowDown\" || e.key === \" \") &&\n !dropdownIsShown\n ) {\n e.preventDefault();\n e.stopPropagation();\n setShown(true);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n setShown(false);\n }\n },\n [dropdownIsShown],\n );\n\n // onKeyDown to stop ArrowDown from scrolling the page\n const handleSearchOnKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n e.stopPropagation();\n\n const listElement = dropdownRef.current;\n if (listElement) {\n if (isSearchable) {\n // Flytt fokus til det første elementet i listen, ikke det forrige valgte.\n // Ved endring i filter er det ikke gitt at vi ønsker å ta utgangspunkt i\n // den valgte verdien.\n focusSelected(listElement, undefined);\n } else {\n focusSelected(listElement, selectedValue);\n }\n }\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n setShown(false);\n } else if (e.key === \"Tab\" && !e.shiftKey) {\n const listElement = dropdownRef.current;\n if (listElement) {\n e.preventDefault();\n e.stopPropagation();\n focusSelected(listElement, selectedValue);\n }\n } else if (e.key === \"Enter\" && dropdownIsShown) {\n // Should not propagate Enter keyevent because form might submit\n e.preventDefault();\n e.stopPropagation();\n }\n },\n [dropdownRef, selectedValue, isSearchable, dropdownIsShown],\n );\n\n // onKeyDown so this Tab listener isn't triggered by tabbing from search field to option\n const handleOptionOnKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Tab\") {\n e.preventDefault();\n e.stopPropagation();\n\n if (e.shiftKey && searchFieldRef.current) {\n searchFieldRef.current.focus();\n } else if (buttonRef.current) {\n // Mimic behaviour of Firefox and native select, where Tab selects the current item and closes the menu\n setSelectedValue(e.currentTarget.value);\n setShown(false);\n buttonRef.current.focus();\n }\n } else if (e.key === \"ArrowUp\") {\n if (dropdownRef.current && searchFieldRef.current) {\n // Can't be based on index since the first item might be filtered out\n const firstVisible = dropdownRef.current.querySelector(\n '[role=\"option\"]:not([hidden])',\n );\n if (\n e.currentTarget.id === firstVisible?.id &&\n searchFieldRef.current\n ) {\n searchFieldRef.current.focus();\n }\n }\n }\n },\n [dropdownRef],\n );\n\n // Add support for closing the dropdown with Escape like native select. Unfortunately, Escape does not trigger the button onKeyDown.\n useEffect(() => {\n const handleEscape = (e: globalThis.KeyboardEvent) => {\n if (e.key === \"Escape\" && dropdownIsShown) {\n setShown(false);\n }\n };\n if (typeof window !== \"undefined\" && dropdownIsShown) {\n window.addEventListener(\"keydown\", handleEscape);\n }\n return () => {\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"keydown\", handleEscape);\n }\n };\n }, [dropdownIsShown]);\n\n return (\n <>\n <select\n name={name}\n tabIndex={-1}\n data-testid=\"jkl-native-select\"\n className=\"jkl-sr-only\"\n aria-hidden\n ref={unifiedSelectRef}\n value={selectedValue}\n onChange={noop} // React complains unless we give an onChange handler. This is technically a read-only field, but readOnly isn't an option here.\n >\n <option value=\"\" />{\" \"}\n {/* Tom option må være et valg, ellers vil <select> alltid ha en value */}\n {visibleItems.map((item) => (\n <option\n key={`${listId}-opt-${item.value}`}\n hidden={!item.visible}\n value={item.value}\n >\n {item.label}\n </option>\n ))}\n </select>\n <InputGroup\n ref={componentRootElementRef}\n data-testid=\"jkl-select\"\n className={clsx(\"jkl-select\", className, {\n \"jkl-select--inline\": inline,\n \"jkl-select--open\":\n dropdownIsShown &&\n visibleItems.some((item) => item.visible),\n \"jkl-select--no-value\": !hasSelectedValue,\n \"jkl-select--invalid\": !!errorLabel || invalid,\n })}\n tooltip={\n tooltip && React.isValidElement<PopupTipProps>(tooltip)\n ? React.cloneElement(tooltip, {\n triggerProps: {\n ...tooltip.props.triggerProps,\n onFocus: (\n e: FocusEvent<HTMLButtonElement>,\n ) => {\n tooltip.props.triggerProps?.onFocus?.(\n e,\n );\n close();\n },\n },\n })\n : null\n }\n {...rest}\n id={isSearchable ? searchInputId : buttonId}\n style={\n {\n \"--jkl-select-max-shown-options\": maxShownOptions,\n ...style,\n } as CSSProperties\n }\n density={density}\n label={label}\n labelProps={{\n id: labelId,\n srOnly: inline,\n ...labelProps,\n htmlFor: isSearchable ? searchInputId : buttonId,\n }}\n helpLabel={helpLabel}\n errorLabel={errorLabel}\n render={({\n \"aria-invalid\": ariaInvalid,\n ...inputProps\n }) => (\n <div\n className=\"jkl-select__outer-wrapper\"\n style={{ width }}\n >\n {isSearchable && (\n <input\n {...inputProps}\n aria-invalid={ariaInvalid}\n id={searchInputId}\n hidden={!showSearchInputField}\n ref={searchFieldRef}\n placeholder=\"Søk\"\n value={searchValue}\n onChange={(e) =>\n setSearchValue(e.target.value)\n }\n data-testid=\"jkl-select__search-input\"\n className=\"jkl-select__search-input\"\n aria-autocomplete=\"list\"\n aria-activedescendant={\n hasSelectedValue\n ? `${listId}__${toLower(\n selectedValue,\n )}`\n : undefined\n }\n aria-controls={listId}\n aria-expanded={dropdownIsShown}\n role=\"combobox\"\n onKeyDown={handleSearchOnKeyDown}\n onBlur={handleBlur}\n onFocus={handleFocus}\n onClick={(e) => {\n e.stopPropagation();\n }}\n />\n )}\n {/* eslint-disable-next-line jsx-a11y/role-supports-aria-props */}\n <button\n // Nei dette er ikke i henhold til speccen, men VoiceOver leser den likevel og det er oppførselen vi ønsker\n aria-invalid={ariaInvalid}\n {...inputProps}\n id={buttonId}\n ref={buttonRef}\n hidden={showSearchInputField}\n type=\"button\"\n name={`${name}-btn`}\n className={clsx(\"jkl-select__button\", {\n \"jkl-select__button--active-value\":\n !!selectedValue,\n })}\n data-testid=\"jkl-select__button\"\n aria-label={`${\n selectedValueLabel || \"Velg\"\n },${label}`}\n aria-expanded={dropdownIsShown}\n aria-controls={listId}\n onBlur={handleBlur}\n onFocus={handleFocus}\n onKeyDown={handleOnKeyDown}\n onClick={toggleListVisibility}\n onMouseDown={(e) => {\n // Workaround for en Safari-bug hvor e.relatedTarget er null i onBlur\n // https://twitter.com/MilesSorce/status/1278762360669265925\n e.preventDefault();\n buttonRef.current?.focus();\n }}\n >\n {selectedValueLabel}\n </button>\n <div\n id={listId}\n ref={dropdownRef}\n // biome-ignore lint/a11y/useSemanticElements: Vi reimplementerer select\n role=\"listbox\"\n className=\"jkl-select__options-menu\"\n hidden={\n !dropdownIsShown ||\n visibleItems.every((item) => !item.visible)\n }\n aria-labelledby={labelId}\n tabIndex={-1}\n data-focus=\"controlled\" // lar oss styre markering av valg vha focus\n >\n {visibleItems.map((item, i) =>\n // Det er viktig at vi _fjerner_ elementer som ikke er synlige fra DOMen for at tastaturnavigasjon skal fungere.\n // For eksempel, hvis vi har elementene Apple, Samsung og LG i den rekkefølgen og søker etter \"l\"\n // vil Samsung ikke synes. Om vi bare setter hidden-attributtet på Samsung vil ArrowDown fra Apple ikke fungere.\n // Dette lar seg ikke gjenskape i en enhetstest med JSDOM + user-events, og Cypress lukker Select\n // ved første {downArrow} ¯\\_(ツ)_/¯. Så please test scenariet over manuelt om dette skaper trøbbel for deg.\n item.visible ? (\n <button\n key={`${listId}-${item.value}`}\n hidden={!item.visible}\n type=\"button\"\n id={`${listId}__${toLower(\n item.value,\n )}`}\n className=\"jkl-select__option\"\n data-testid=\"jkl-select__option\"\n aria-selected={\n item.value === selectedValue\n }\n // biome-ignore lint/a11y/useSemanticElements: Vi reimplementerer select\n role=\"option\"\n value={item.value}\n data-testautoid={`jkl-select__option-${i}`}\n onBlur={handleBlur}\n onFocus={handleFocus}\n onKeyDown={handleOptionOnKeyDown}\n onClick={(e) => {\n e.preventDefault();\n selectOption(item);\n }}\n onMouseOver={handleMouseOver}\n >\n {item.label}\n {item.description ? (\n <span className=\"jkl-select__option-description\">\n {item.description}\n </span>\n ) : null}\n </button>\n ) : null,\n )}\n </div>\n <ArrowVerticalAnimated\n variant=\"medium\"\n pointingDown={!dropdownIsShown}\n className=\"jkl-select__arrow\"\n />\n </div>\n )}\n />\n </>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n"],"names":["noop","Select","forwardRef","props","forwardedSelectRef","id","name","items","value","label","labelProps","onChange","onBlur","onFocus","className","helpLabel","errorLabel","invalid","searchable","inline","defaultPrompt","density","width","maxShownOptions","style","tooltip","rest","listId","useId","generateSuffix","labelId","buttonId","searchInputId","dropdownIsShown","setShown","useState","toggleListVisibility","useCallback","previousValue","isSearchable","showSearchInputField","searchValue","setSearchValue","searchFn","item","toLowerCase","includes","visibleItems","useMemo","map","getValuePair","visible","valueIsInItems","some","selectedValue","setSelectedValue","hasSelectedValue","selectedValueLabel","_a","find","selectRef","useRef","unifiedSelectRef","instance","current","usePreviousValue","useEffect","selectOption","nextValue","buttonRef","focus","previousSelectedValue","type","target","dispatchEvent","Event","bubbles","componentRootElementRef","focusInsideRef","searchFieldRef","handleFocusPlacement","isOpen","ref","listElement","focusSelected","dropdownRef","useAnimatedHeight","onFirstVisible","onTransitionEnd","useListNavigation","close","handleBlur","e","componentRootElement","contains","relatedTarget","handleFocus","handleMouseOver","preventScroll","select","searchField","button","addEventListener","ev","preventDefault","removeEventListener","handleOnKeyDown","key","stopPropagation","handleSearchOnKeyDown","shiftKey","handleOptionOnKeyDown","currentTarget","firstVisible","querySelector","handleEscape","window","jsxs","Fragment","children","tabIndex","jsx","hidden","InputGroup","clsx","React","isValidElement","cloneElement","triggerProps","_b","call","srOnly","htmlFor","render","ariaInvalid","inputProps","placeholder","toLower","role","onKeyDown","onClick","onMouseDown","every","i","onMouseOver","description","ArrowVerticalAnimated","variant","pointingDown","displayName"],"mappings":"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"}
1
+ {"version":3,"file":"Select.cjs","sources":["../../../../src/components/select/Select.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport React, {\n type CSSProperties,\n type FocusEvent,\n forwardRef,\n type KeyboardEvent,\n type MouseEvent,\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { useAnimatedHeight } from \"../../hooks/useAnimatedHeight/useAnimatedHeight.js\";\nimport { useId } from \"../../hooks/useId/useId.js\";\nimport { useListNavigation } from \"../../hooks/useListNavigation/useListNavigation.js\";\nimport { usePreviousValue } from \"../../hooks/usePreviousValue/usePreviousValue.js\";\nimport { type ValuePair, getValuePair } from \"../../utilities/valuePair.js\";\nimport { ArrowVerticalAnimated } from \"../icon/icons/animated/ArrowVerticalAnimated.js\";\nimport { InputGroup } from \"../input-group/InputGroup.js\";\nimport type { PopupTipProps } from \"../tooltip/types.js\";\nimport { focusSelected, toLower } from \"./select-utils.js\";\nimport type { SelectProps } from \"./types.js\";\n\nconst noop = () => {\n return;\n};\n\ninterface Option extends ValuePair {\n visible: boolean;\n}\n\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(\n (props, forwardedSelectRef) => {\n const {\n id,\n name,\n items,\n value,\n label,\n labelProps,\n onChange,\n onBlur,\n onFocus,\n className,\n helpLabel,\n errorLabel,\n invalid,\n searchable = false,\n inline = false,\n defaultPrompt = \"Velg\",\n density,\n width,\n maxShownOptions = 5,\n style,\n tooltip,\n ...rest\n } = props;\n\n const listId = useId(id || \"jkl-select\", { generateSuffix: !id });\n const labelId = `${listId}_label`;\n const buttonId = `${listId}_button`;\n const searchInputId = `${listId}_search-input`;\n\n const [dropdownIsShown, setShown] = useState(false);\n const toggleListVisibility = useCallback(() => {\n setShown((previousValue) => !previousValue);\n }, []);\n\n /// Søk\n\n const isSearchable = Boolean(searchable);\n const showSearchInputField = isSearchable && dropdownIsShown;\n const [searchValue, setSearchValue] = useState(\"\");\n const searchFn = useCallback(\n (item: ValuePair) => {\n if (\n item.label.toLowerCase().includes(searchValue.toLowerCase())\n ) {\n return true;\n }\n\n if (typeof searchable === \"function\") {\n return searchable(searchValue, item);\n }\n\n return false;\n },\n [searchable, searchValue],\n );\n const visibleItems: Option[] = useMemo(\n () =>\n items.map(getValuePair).map((item) => {\n const visible =\n !isSearchable || searchValue === \"\" || searchFn(item);\n return { ...item, visible };\n }),\n [items, isSearchable, searchValue, searchFn],\n );\n const valueIsInItems: boolean = useMemo(() => {\n if (typeof value === \"undefined\") {\n return false;\n }\n return items.some((item) =>\n typeof item === \"string\"\n ? item === value\n : item.value === value,\n );\n }, [value, items]);\n\n /// Valg av <option>\n\n const [selectedValue, setSelectedValue] = useState<string>(\n valueIsInItems && value !== undefined ? value : \"\",\n );\n const hasSelectedValue = selectedValue !== \"\";\n const selectedValueLabel = useMemo(\n () =>\n visibleItems.find((item) => item.value === selectedValue)\n ?.label || defaultPrompt,\n [visibleItems, selectedValue, defaultPrompt],\n );\n\n const selectRef = useRef<HTMLSelectElement | null>(null);\n // Hjelpefunksjon som gjør det enklere å forwarde refen og å bruke den selv internt\n const unifiedSelectRef = useCallback(\n (instance: HTMLSelectElement | null) => {\n selectRef.current = instance;\n if (forwardedSelectRef) {\n if (typeof forwardedSelectRef === \"function\") {\n forwardedSelectRef(instance);\n } else {\n forwardedSelectRef.current = instance;\n }\n }\n if (instance) {\n setSelectedValue(instance.value);\n }\n },\n [forwardedSelectRef],\n );\n\n const previousValue = usePreviousValue(value);\n useEffect(() => {\n if (value === previousValue) {\n return;\n }\n if (typeof value === \"undefined\" || !valueIsInItems) {\n setSelectedValue(\"\");\n } else {\n setSelectedValue(value);\n }\n }, [value, previousValue, valueIsInItems]);\n\n const selectOption = useCallback(\n (item: Option) => {\n const nextValue = item.value;\n setSearchValue(\"\");\n setSelectedValue(nextValue);\n toggleListVisibility();\n buttonRef.current?.focus();\n },\n [toggleListVisibility],\n );\n\n // La komponenten rendre <select> med den valgte verdien før onChange trigges, slik at\n // react-hook-form@>7.41.1 behandler feltet som at det har en verdi.\n const previousSelectedValue = usePreviousValue(selectedValue);\n useEffect(() => {\n // previousSelectedValue er undefined på første render, men da vil vi ikke ha en onChange uansett\n if (\n typeof previousSelectedValue === \"undefined\" ||\n previousSelectedValue === selectedValue ||\n selectedValue === value\n ) {\n return;\n }\n if (onChange) {\n onChange({\n type: \"change\",\n target: { name, value: selectedValue },\n });\n }\n if (selectRef.current) {\n selectRef.current.dispatchEvent(\n new Event(\"change\", { bubbles: true }),\n );\n }\n }, [onChange, name, value, selectedValue, previousSelectedValue]);\n\n /// Fokushåndtering\n\n const componentRootElementRef = useRef<HTMLDivElement>(null);\n const focusInsideRef = useRef(false);\n const searchFieldRef = useRef<HTMLInputElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleFocusPlacement = useCallback(\n (isOpen: boolean, ref: RefObject<HTMLElement | null>) => {\n if (isOpen && !isSearchable) {\n const listElement = ref.current;\n if (listElement) {\n focusSelected(listElement, selectedValue);\n }\n } else if (isOpen) {\n if (searchFieldRef.current) {\n searchFieldRef.current.focus();\n }\n } else {\n if (focusInsideRef.current && buttonRef.current) {\n buttonRef.current.focus();\n }\n }\n },\n [isSearchable, selectedValue],\n );\n\n const [dropdownRef] = useAnimatedHeight<HTMLDivElement>(\n dropdownIsShown,\n {\n onFirstVisible: handleFocusPlacement,\n onTransitionEnd: handleFocusPlacement,\n },\n );\n\n useListNavigation({ ref: dropdownRef });\n\n const close = useCallback(() => {\n if (isSearchable) {\n setSearchValue(\"\");\n }\n if (onBlur) {\n onBlur({\n type: \"blur\",\n target: { name, value: selectedValue },\n });\n selectRef.current?.dispatchEvent(\n new Event(\"focusout\", { bubbles: true }),\n );\n }\n focusInsideRef.current = false;\n setShown(false);\n }, [onBlur, isSearchable, name, selectedValue]);\n\n const handleBlur = useCallback(\n (e: FocusEvent<HTMLButtonElement | HTMLInputElement>) => {\n const componentRootElement = componentRootElementRef.current;\n // There are known issues in Firefox when using \"relatedTarget\" in onBlur events:\n // https://github.com/facebook/react/issues/2011\n // This might be fixed in react 17. Se issue above.\n const nextFocusIsInsideComponent =\n componentRootElement?.contains(e.relatedTarget as Node);\n if (!nextFocusIsInsideComponent) {\n close();\n }\n },\n [close],\n );\n\n const handleFocus = useCallback(() => {\n if (!focusInsideRef.current) {\n if (onFocus) {\n onFocus({\n type: \"change\",\n target: { name, value: selectedValue },\n });\n }\n focusInsideRef.current = true;\n }\n }, [onFocus, selectedValue, name]);\n\n const handleMouseOver = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n // Ved mouseOver på options flytter vi fokus til dem for å unngå \"dobbel fokus\"\n // der det ser ut som to forskjellige elementer er fokusert/hovered samtidig\n (e.target as HTMLButtonElement).focus({ preventScroll: true });\n },\n [],\n );\n\n // Handle focus and blur of hidden select element\n useEffect(() => {\n const select = selectRef.current;\n const searchField = searchFieldRef.current;\n const button = buttonRef.current;\n const componentRootElement = componentRootElementRef.current;\n\n select?.addEventListener(\"focus\", () => {\n showSearchInputField ? searchField?.focus() : button?.focus();\n });\n select?.addEventListener(\"blur\", function (this, ev) {\n componentRootElement?.contains(ev.relatedTarget as Node) &&\n ev.preventDefault();\n });\n\n return () => {\n select?.removeEventListener(\"focus\", () => {\n showSearchInputField\n ? searchField?.focus()\n : button?.focus();\n });\n select?.removeEventListener(\"blur\", function (this, ev) {\n componentRootElement?.contains(ev.relatedTarget as Node) &&\n ev.preventDefault();\n });\n };\n }, [showSearchInputField]);\n\n /// Tastaturnavigasjon\n\n // Add support for opening dropdown with arrowkey down as expected from native select\n // onKeyDown to stop ArrowDown from scrolling the page\n const handleOnKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (\n (e.key === \"ArrowDown\" || e.key === \" \") &&\n !dropdownIsShown\n ) {\n e.preventDefault();\n e.stopPropagation();\n setShown(true);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n setShown(false);\n }\n },\n [dropdownIsShown],\n );\n\n // onKeyDown to stop ArrowDown from scrolling the page\n const handleSearchOnKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n e.stopPropagation();\n\n const listElement = dropdownRef.current;\n if (listElement) {\n if (isSearchable) {\n // Flytt fokus til det første elementet i listen, ikke det forrige valgte.\n // Ved endring i filter er det ikke gitt at vi ønsker å ta utgangspunkt i\n // den valgte verdien.\n focusSelected(listElement, undefined);\n } else {\n focusSelected(listElement, selectedValue);\n }\n }\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n setShown(false);\n } else if (e.key === \"Tab\" && !e.shiftKey) {\n const listElement = dropdownRef.current;\n if (listElement) {\n e.preventDefault();\n e.stopPropagation();\n focusSelected(listElement, selectedValue);\n }\n } else if (e.key === \"Enter\" && dropdownIsShown) {\n // Should not propagate Enter keyevent because form might submit\n e.preventDefault();\n e.stopPropagation();\n }\n },\n [dropdownRef, selectedValue, isSearchable, dropdownIsShown],\n );\n\n // onKeyDown so this Tab listener isn't triggered by tabbing from search field to option\n const handleOptionOnKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Tab\") {\n e.preventDefault();\n e.stopPropagation();\n\n if (e.shiftKey && searchFieldRef.current) {\n searchFieldRef.current.focus();\n } else if (buttonRef.current) {\n // Mimic behaviour of Firefox and native select, where Tab selects the current item and closes the menu\n setSelectedValue(e.currentTarget.value);\n setShown(false);\n buttonRef.current.focus();\n }\n } else if (e.key === \"ArrowUp\") {\n if (dropdownRef.current && searchFieldRef.current) {\n // Can't be based on index since the first item might be filtered out\n const firstVisible = dropdownRef.current.querySelector(\n '[role=\"option\"]:not([hidden])',\n );\n if (\n e.currentTarget.id === firstVisible?.id &&\n searchFieldRef.current\n ) {\n searchFieldRef.current.focus();\n }\n }\n }\n },\n [dropdownRef],\n );\n\n // Add support for closing the dropdown with Escape like native select. Unfortunately, Escape does not trigger the button onKeyDown.\n useEffect(() => {\n const handleEscape = (e: globalThis.KeyboardEvent) => {\n if (e.key === \"Escape\" && dropdownIsShown) {\n setShown(false);\n }\n };\n if (typeof window !== \"undefined\" && dropdownIsShown) {\n window.addEventListener(\"keydown\", handleEscape);\n }\n return () => {\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"keydown\", handleEscape);\n }\n };\n }, [dropdownIsShown]);\n\n return (\n <>\n <select\n name={name}\n tabIndex={-1}\n data-testid=\"jkl-native-select\"\n className=\"jkl-sr-only\"\n aria-hidden\n ref={unifiedSelectRef}\n value={selectedValue}\n onChange={noop} // React complains unless we give an onChange handler. This is technically a read-only field, but readOnly isn't an option here.\n >\n <option value=\"\" />{\" \"}\n {/* Tom option må være et valg, ellers vil <select> alltid ha en value */}\n {visibleItems.map((item) => (\n <option\n key={`${listId}-opt-${item.value}`}\n hidden={!item.visible}\n value={item.value}\n >\n {item.label}\n </option>\n ))}\n </select>\n <InputGroup\n ref={componentRootElementRef}\n data-testid=\"jkl-select\"\n className={clsx(\"jkl-select\", className, {\n \"jkl-select--inline\": inline,\n \"jkl-select--open\":\n dropdownIsShown &&\n visibleItems.some((item) => item.visible),\n \"jkl-select--no-value\": !hasSelectedValue,\n \"jkl-select--invalid\": !!errorLabel || invalid,\n })}\n tooltip={\n tooltip && React.isValidElement<PopupTipProps>(tooltip)\n ? React.cloneElement(tooltip, {\n triggerProps: {\n ...tooltip.props.triggerProps,\n onFocus: (\n e: FocusEvent<HTMLButtonElement>,\n ) => {\n tooltip.props.triggerProps?.onFocus?.(\n e,\n );\n close();\n },\n },\n })\n : null\n }\n {...rest}\n id={isSearchable ? searchInputId : buttonId}\n style={\n {\n \"--jkl-select-max-shown-options\": maxShownOptions,\n ...style,\n } as CSSProperties\n }\n density={density}\n label={label}\n labelProps={{\n id: labelId,\n srOnly: inline,\n ...labelProps,\n htmlFor: isSearchable ? searchInputId : buttonId,\n }}\n helpLabel={helpLabel}\n errorLabel={errorLabel}\n render={({\n \"aria-invalid\": ariaInvalid,\n ...inputProps\n }) => (\n <div\n className=\"jkl-select__outer-wrapper\"\n style={{ width }}\n >\n {isSearchable && (\n <input\n {...inputProps}\n aria-invalid={ariaInvalid}\n id={searchInputId}\n hidden={!showSearchInputField}\n ref={searchFieldRef}\n placeholder=\"Søk\"\n value={searchValue}\n onChange={(e) =>\n setSearchValue(e.target.value)\n }\n data-testid=\"jkl-select__search-input\"\n className=\"jkl-select__search-input\"\n aria-autocomplete=\"list\"\n aria-activedescendant={\n hasSelectedValue\n ? `${listId}__${toLower(\n selectedValue,\n )}`\n : undefined\n }\n aria-controls={listId}\n aria-expanded={dropdownIsShown}\n role=\"combobox\"\n onKeyDown={handleSearchOnKeyDown}\n onBlur={handleBlur}\n onFocus={handleFocus}\n onClick={(e) => {\n e.stopPropagation();\n }}\n />\n )}\n {/* eslint-disable-next-line jsx-a11y/role-supports-aria-props */}\n <button\n // Nei dette er ikke i henhold til speccen, men VoiceOver leser den likevel og det er oppførselen vi ønsker\n aria-invalid={ariaInvalid}\n {...inputProps}\n id={buttonId}\n ref={buttonRef}\n hidden={showSearchInputField}\n type=\"button\"\n name={`${name}-btn`}\n className={clsx(\"jkl-select__button\", {\n \"jkl-select__button--active-value\":\n !!selectedValue,\n })}\n data-testid=\"jkl-select__button\"\n aria-label={`${\n selectedValueLabel || \"Velg\"\n },${label}`}\n aria-expanded={dropdownIsShown}\n aria-controls={listId}\n onBlur={handleBlur}\n onFocus={handleFocus}\n onKeyDown={handleOnKeyDown}\n onClick={toggleListVisibility}\n onMouseDown={(e) => {\n // Workaround for en Safari-bug hvor e.relatedTarget er null i onBlur\n // https://twitter.com/MilesSorce/status/1278762360669265925\n e.preventDefault();\n buttonRef.current?.focus();\n }}\n >\n {selectedValueLabel}\n </button>\n <div\n id={listId}\n ref={dropdownRef}\n // biome-ignore lint/a11y/useSemanticElements: Vi reimplementerer select\n role=\"listbox\"\n className=\"jkl-select__options-menu\"\n hidden={\n !dropdownIsShown ||\n visibleItems.every((item) => !item.visible)\n }\n aria-labelledby={labelId}\n tabIndex={-1}\n data-focus=\"controlled\" // lar oss styre markering av valg vha focus\n >\n {visibleItems.map((item, i) =>\n // Det er viktig at vi _fjerner_ elementer som ikke er synlige fra DOMen for at tastaturnavigasjon skal fungere.\n // For eksempel, hvis vi har elementene Apple, Samsung og LG i den rekkefølgen og søker etter \"l\"\n // vil Samsung ikke synes. Om vi bare setter hidden-attributtet på Samsung vil ArrowDown fra Apple ikke fungere.\n // Dette lar seg ikke gjenskape i en enhetstest med JSDOM + user-events, og Cypress lukker Select\n // ved første {downArrow} ¯\\_(ツ)_/¯. Så please test scenariet over manuelt om dette skaper trøbbel for deg.\n item.visible ? (\n <button\n key={`${listId}-${item.value}`}\n hidden={!item.visible}\n type=\"button\"\n id={`${listId}__${toLower(\n item.value,\n )}`}\n className=\"jkl-select__option\"\n data-testid=\"jkl-select__option\"\n aria-selected={\n item.value === selectedValue\n }\n // biome-ignore lint/a11y/useSemanticElements: Vi reimplementerer select\n role=\"option\"\n value={item.value}\n data-testautoid={`jkl-select__option-${i}`}\n onBlur={handleBlur}\n onFocus={handleFocus}\n onKeyDown={handleOptionOnKeyDown}\n onClick={(e) => {\n e.preventDefault();\n selectOption(item);\n }}\n onMouseOver={handleMouseOver}\n >\n {item.label}\n {item.description ? (\n <span className=\"jkl-select__option-description\">\n {item.description}\n </span>\n ) : null}\n </button>\n ) : null,\n )}\n </div>\n <ArrowVerticalAnimated\n variant=\"medium\"\n pointingDown={!dropdownIsShown}\n className=\"jkl-select__arrow\"\n />\n </div>\n )}\n />\n </>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n"],"names":["noop","Select","forwardRef","props","forwardedSelectRef","id","name","items","value","label","labelProps","onChange","onBlur","onFocus","className","helpLabel","errorLabel","invalid","searchable","inline","defaultPrompt","density","width","maxShownOptions","style","tooltip","rest","listId","useId","generateSuffix","labelId","buttonId","searchInputId","dropdownIsShown","setShown","useState","toggleListVisibility","useCallback","previousValue","isSearchable","showSearchInputField","searchValue","setSearchValue","searchFn","item","toLowerCase","includes","visibleItems","useMemo","map","getValuePair","visible","valueIsInItems","some","selectedValue","setSelectedValue","hasSelectedValue","selectedValueLabel","_a","find","selectRef","useRef","unifiedSelectRef","instance","current","usePreviousValue","useEffect","selectOption","nextValue","buttonRef","focus","previousSelectedValue","type","target","dispatchEvent","Event","bubbles","componentRootElementRef","focusInsideRef","searchFieldRef","handleFocusPlacement","isOpen","ref","listElement","focusSelected","dropdownRef","useAnimatedHeight","onFirstVisible","onTransitionEnd","useListNavigation","close","handleBlur","e","componentRootElement","contains","relatedTarget","handleFocus","handleMouseOver","preventScroll","select","searchField","button","addEventListener","ev","preventDefault","removeEventListener","handleOnKeyDown","key","stopPropagation","handleSearchOnKeyDown","shiftKey","handleOptionOnKeyDown","currentTarget","firstVisible","querySelector","handleEscape","window","jsxs","Fragment","children","tabIndex","jsx","hidden","InputGroup","clsx","React","isValidElement","cloneElement","triggerProps","_b","call","srOnly","htmlFor","render","ariaInvalid","inputProps","placeholder","toLower","role","onKeyDown","onClick","onMouseDown","every","i","onMouseOver","description","ArrowVerticalAnimated","variant","pointingDown","displayName"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePillStyles.cjs","sources":["../../../../src/components/toggle-switch/usePillStyles.ts"],"sourcesContent":["import {\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nfunction throttle<R, A extends any[]>(fn: (...args: A) => R) {\n let wait = false;\n\n return (...args: A) => {\n if (wait) return undefined;\n\n const result = fn(...args);\n\n wait = true;\n window.setTimeout(() => {\n wait = false;\n }, 200);\n\n return result;\n };\n}\n\nexport const usePillStyles = (\n ref: RefObject<HTMLLabelElement>,\n shouldTransform: boolean,\n dependencies: unknown[],\n) => {\n const [width, setWidth] = useState(0);\n const [distance, setDistance] = useState(0);\n\n const setStyles = useCallback(() => {\n if (ref.current?.parentElement) {\n setWidth(ref.current.clientWidth);\n\n const translateDistance = shouldTransform\n ? ref.current.parentElement.clientWidth -\n ref.current.clientWidth\n : 0;\n\n setDistance(translateDistance);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ref, shouldTransform, ...dependencies]);\n\n const throttledSetStyles = useMemo(() => throttle(setStyles), [setStyles]);\n\n useEffect(() => {\n setStyles();\n typeof window !== \"undefined\" &&\n window.addEventListener(\"resize\", throttledSetStyles);\n return () => {\n typeof window !== \"undefined\" &&\n window.removeEventListener(\"resize\", throttledSetStyles);\n };\n }, [setStyles, throttledSetStyles]);\n\n return {\n width,\n transform: `translateX(${distance}px)`,\n };\n};\n"],"names":["ref","shouldTransform","dependencies","width","setWidth","useState","distance","setDistance","setStyles","useCallback","_a","current","parentElement","clientWidth","translateDistance","throttledSetStyles","useMemo","fn","wait","args","result","window","setTimeout","throttle","useEffect","addEventListener","removeEventListener","transform"],"mappings":"+HAyB6B,CACzBA,EACAC,EACAC,KAEA,MAAOC,EAAOC,GAAYC,EAAAA,SAAS,IAC5BC,EAAUC,GAAeF,EAAAA,SAAS,GAEnCG,EAAYC,EAAAA,aAAY,WACtB,GAAA,OAAAC,EAAAV,EAAIW,UAAJD,EAAaE,cAAe,CACnBR,EAAAJ,EAAIW,QAAQE,aAEf,MAAAC,EAAoBb,EACpBD,EAAIW,QAAQC,cAAcC,YAC1Bb,EAAIW,QAAQE,YACZ,EAENN,EAAYO,EAAiB,IAGlC,CAACd,EAAKC,KAAoBC,IAEvBa,EAAqBC,EAAAA,SAAQ,IAvCvC,SAAsCC,GAClC,IAAIC,GAAO,EAEX,MAAO,IAAIC,KACP,GAAID,EAAa,OAEX,MAAAE,EAASH,KAAME,GAEd,OAAAD,GAAA,EACPG,OAAOC,YAAW,KACPJ,GAAA,IACR,KAEIE,EAEf,CAwB6CG,CAASf,IAAY,CAACA,IAE/DgB,OAAAA,EAAAA,WAAU,KACIhB,WACHa,OAAW,KACdA,OAAOI,iBAAiB,SAAUV,GAC/B,YACIM,OAAW,KACdA,OAAOK,oBAAoB,SAAUX,EAAkB,IAEhE,CAACP,EAAWO,IAER,CACHZ,MAAAA,EACAwB,UAAW,cAAcrB,OAC7B"}
1
+ {"version":3,"file":"usePillStyles.cjs","sources":["../../../../src/components/toggle-switch/usePillStyles.ts"],"sourcesContent":["import {\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nfunction throttle<R, A extends any[]>(fn: (...args: A) => R) {\n let wait = false;\n\n return (...args: A) => {\n if (wait) return undefined;\n\n const result = fn(...args);\n\n wait = true;\n window.setTimeout(() => {\n wait = false;\n }, 200);\n\n return result;\n };\n}\n\nexport const usePillStyles = (\n ref: RefObject<HTMLLabelElement | null>,\n shouldTransform: boolean,\n dependencies: unknown[],\n) => {\n const [width, setWidth] = useState(0);\n const [distance, setDistance] = useState(0);\n\n const setStyles = useCallback(() => {\n if (ref.current?.parentElement) {\n setWidth(ref.current.clientWidth);\n\n const translateDistance = shouldTransform\n ? ref.current.parentElement.clientWidth -\n ref.current.clientWidth\n : 0;\n\n setDistance(translateDistance);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ref, shouldTransform, ...dependencies]);\n\n const throttledSetStyles = useMemo(() => throttle(setStyles), [setStyles]);\n\n useEffect(() => {\n setStyles();\n typeof window !== \"undefined\" &&\n window.addEventListener(\"resize\", throttledSetStyles);\n return () => {\n typeof window !== \"undefined\" &&\n window.removeEventListener(\"resize\", throttledSetStyles);\n };\n }, [setStyles, throttledSetStyles]);\n\n return {\n width,\n transform: `translateX(${distance}px)`,\n };\n};\n"],"names":["ref","shouldTransform","dependencies","width","setWidth","useState","distance","setDistance","setStyles","useCallback","_a","current","parentElement","clientWidth","translateDistance","throttledSetStyles","useMemo","fn","wait","args","result","window","setTimeout","throttle","useEffect","addEventListener","removeEventListener","transform"],"mappings":"+HAyB6B,CACzBA,EACAC,EACAC,KAEA,MAAOC,EAAOC,GAAYC,EAAAA,SAAS,IAC5BC,EAAUC,GAAeF,EAAAA,SAAS,GAEnCG,EAAYC,EAAAA,aAAY,WACtB,GAAA,OAAAC,EAAAV,EAAIW,UAAJD,EAAaE,cAAe,CACnBR,EAAAJ,EAAIW,QAAQE,aAEf,MAAAC,EAAoBb,EACpBD,EAAIW,QAAQC,cAAcC,YAC1Bb,EAAIW,QAAQE,YACZ,EAENN,EAAYO,EAAiB,IAGlC,CAACd,EAAKC,KAAoBC,IAEvBa,EAAqBC,EAAAA,SAAQ,IAvCvC,SAAsCC,GAClC,IAAIC,GAAO,EAEX,MAAO,IAAIC,KACP,GAAID,EAAa,OAEX,MAAAE,EAASH,KAAME,GAEd,OAAAD,GAAA,EACPG,OAAOC,YAAW,KACPJ,GAAA,IACR,KAEIE,EAEf,CAwB6CG,CAASf,IAAY,CAACA,IAE/DgB,OAAAA,EAAAA,WAAU,KACIhB,WACHa,OAAW,KACdA,OAAOI,iBAAiB,SAAUV,GAC/B,YACIM,OAAW,KACdA,OAAOK,oBAAoB,SAAUX,EAAkB,IAEhE,CAACP,EAAWO,IAER,CACHZ,MAAAA,EACAwB,UAAW,cAAcrB,OAC7B"}
@@ -1,5 +1,5 @@
1
1
  import { RefObject } from 'react';
2
- export declare const usePillStyles: (ref: RefObject<HTMLLabelElement>, shouldTransform: boolean, dependencies: unknown[]) => {
2
+ export declare const usePillStyles: (ref: RefObject<HTMLLabelElement | null>, shouldTransform: boolean, dependencies: unknown[]) => {
3
3
  width: number;
4
4
  transform: string;
5
5
  };
@@ -12,7 +12,7 @@ export interface UseAnimatedHeightOptions<T extends HTMLElement = HTMLElement> {
12
12
  * @default "productive"
13
13
  */
14
14
  timing?: Timing;
15
- onTransitionStart?: (isOpening: boolean, ref: RefObject<T>) => void;
15
+ onTransitionStart?: (isOpening: boolean, ref: RefObject<T | null>) => void;
16
16
  /**
17
17
  * Kalles rett etter at elementet har fått display: block; i stedet for hidden;
18
18
  * Nyttig om du må flytte fokus inn i elementet og ikke vil vente til animasjonen er ferdig.
@@ -21,6 +21,6 @@ export interface UseAnimatedHeightOptions<T extends HTMLElement = HTMLElement> {
21
21
  *
22
22
  * `isOpen` er alltid `true`. Det sendes som første parameter for å ha lik funksjonssignatur som `onTransitionEnd`.
23
23
  */
24
- onFirstVisible?: (isOpen: boolean, ref: RefObject<T>) => void;
25
- onTransitionEnd?: (isOpen: boolean, ref: RefObject<T>) => void;
24
+ onFirstVisible?: (isOpen: boolean, ref: RefObject<T | null>) => void;
25
+ onTransitionEnd?: (isOpen: boolean, ref: RefObject<T | null>) => void;
26
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useAnimatedHeightBetween.cjs","sources":["../../../../src/hooks/useAnimatedHeight/useAnimatedHeightBetween.ts"],"sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from \"react\";\nimport tokens from \"../../core/tokens.js\";\nimport { useBrowserPreferences } from \"../useBrowserPreferences/useBrowserPreferences.js\";\nimport { usePreviousValue } from \"../usePreviousValue/usePreviousValue.js\";\nimport type { UseAnimatedHeightOptions } from \"./types.js\";\n\nconst defaultEasing = \"standard\";\nconst defaultTiming = \"productive\";\n\nfunction collapseElement<T extends HTMLElement>(\n elementRef: RefObject<T>,\n transition: string,\n raf1: React.MutableRefObject<number | undefined>,\n raf2: React.MutableRefObject<number | undefined>,\n) {\n const element = elementRef.current;\n\n if (!element) return;\n\n element.removeAttribute(\"style\");\n const expandedHeight = element.scrollHeight;\n\n raf1.current = requestAnimationFrame(() => {\n // Hent kollapset høyde\n element.style.removeProperty(\"transition\");\n element.dataset.expanded = \"false\";\n const collapsedHeight = element.getBoundingClientRect().height;\n element.dataset.expanded = \"true\";\n\n // Sett høyde tilbake til utvidet høyde\n element.style.setProperty(\"height\", `${expandedHeight}px`);\n element.style.setProperty(\"overflow-y\", \"hidden\");\n\n raf2.current = requestAnimationFrame(() => {\n // Sett høyde til kollapset høyde og start transition\n element.style.setProperty(\"transition\", transition);\n element.style.setProperty(\"height\", `${collapsedHeight}px`);\n element.dataset.expanded = \"false\";\n });\n });\n}\n\nfunction expandElement<T extends HTMLElement>(\n elementRef: RefObject<T>,\n transition: string,\n raf1: React.MutableRefObject<number | undefined>,\n raf2: React.MutableRefObject<number | undefined>,\n) {\n const element = elementRef.current;\n\n if (!element) return;\n\n element.removeAttribute(\"style\");\n const expandedHeight = element.scrollHeight;\n\n raf1.current = requestAnimationFrame(() => {\n // Hent utvidet høyde\n element.style.removeProperty(\"transition\");\n element.dataset.expanded = \"false\";\n const collapsedHeight = element.getBoundingClientRect().height;\n\n // Sett høyde tilbake til kollapset høyde\n element.style.setProperty(\"height\", `${collapsedHeight}px`);\n element.style.setProperty(\"overflow-y\", \"hidden\");\n\n raf2.current = requestAnimationFrame(() => {\n // Sett høyde til utvidet høyde og start transition\n element.style.setProperty(\"transition\", transition);\n element.style.setProperty(\"height\", `${expandedHeight}px`);\n element.dataset.expanded = \"true\";\n });\n });\n}\n\n/**\n * Lar deg enklere animere mellom to tilstander, gitt ved å sette `data-expanded` på et element til `true` eller `false`.\n * Du bestemmer selv hvilke stiler elementet skal ha i de to tilstandene (vha CSS/Sass), og høyden animeres dersom den endrer seg.\n * @param isExpanded indikerer om elementet skal være utvidet eller ikke\n * @param options konfigurasjon for animasjonen, og lyttere for når animasjonen starter og slutter\n * @returns En tuple med referanse til elementet og en funksjon som kan trigge animasjonen\n */\nexport function useAnimatedHeightBetween<T extends HTMLElement>(\n isExpanded: boolean,\n options?: Omit<UseAnimatedHeightOptions<T>, \"display\" | \"onFirstVisible\">,\n): [RefObject<T>, () => void] {\n const wasExpanded = usePreviousValue(isExpanded);\n const easing = options?.easing || defaultEasing;\n const timing = options?.timing || defaultTiming;\n const transition = `${tokens.motion.timing[timing]} height ${tokens.motion.easing[easing]}`;\n\n const { prefersReducedMotion } = useBrowserPreferences();\n\n const raf1 = useRef<number>();\n const raf2 = useRef<number>();\n const elementRef = useRef<T>(null);\n\n const handleTransitionEnd = useCallback(\n (event: TransitionEvent) => {\n const element = elementRef.current;\n\n // Ignore bubbling transitions from within container\n if (element && event.target === element) {\n element.removeAttribute(\"style\");\n options?.onTransitionEnd?.(isExpanded, elementRef);\n }\n },\n [options, isExpanded],\n );\n\n const runAnimation = useCallback(() => {\n const element = elementRef.current;\n\n if (!element) return;\n\n if (wasExpanded === undefined) {\n // Første render\n element.dataset.expanded = isExpanded ? \"true\" : \"false\";\n }\n\n if ((!isExpanded && !wasExpanded) || (isExpanded && wasExpanded)) {\n // Ingen endring\n return;\n }\n\n options?.onTransitionStart?.(isExpanded, elementRef);\n\n if (prefersReducedMotion) {\n element.removeAttribute(\"style\");\n element.dataset.expanded = isExpanded ? \"true\" : \"false\";\n options?.onTransitionEnd?.(isExpanded, elementRef); // make sure to call callback when animation is off\n return;\n }\n\n if (isExpanded) {\n expandElement(elementRef, transition, raf1, raf2);\n } else {\n collapseElement(elementRef, transition, raf1, raf2);\n }\n }, [wasExpanded, isExpanded, options, prefersReducedMotion, transition]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: Vi trigger med isExpanded\n useEffect(() => {\n runAnimation();\n }, [isExpanded, runAnimation]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies:\n useEffect(() => {\n const element = elementRef.current;\n if (element) {\n element.addEventListener(\"transitionend\", handleTransitionEnd);\n }\n\n return () => {\n if (element) {\n element.removeEventListener(\n \"transitionend\",\n handleTransitionEnd,\n );\n }\n };\n }, [isExpanded]);\n\n useEffect(() => {\n const r1 = raf1.current;\n const r2 = raf2.current;\n return () => {\n r1 && cancelAnimationFrame(r1);\n r2 && cancelAnimationFrame(r2);\n };\n }, []);\n\n return [elementRef, runAnimation];\n}\n"],"names":["isExpanded","options","wasExpanded","usePreviousValue","easing","timing","transition","tokens","motion","prefersReducedMotion","useBrowserPreferences","raf1","useRef","raf2","elementRef","handleTransitionEnd","useCallback","event","element","current","target","removeAttribute","_a","onTransitionEnd","call","runAnimation","dataset","expanded","onTransitionStart","_b","expandedHeight","scrollHeight","requestAnimationFrame","style","removeProperty","collapsedHeight","getBoundingClientRect","height","setProperty","expandElement","collapseElement","useEffect","addEventListener","removeEventListener","r1","r2","cancelAnimationFrame"],"mappings":"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"}
1
+ {"version":3,"file":"useAnimatedHeightBetween.cjs","sources":["../../../../src/hooks/useAnimatedHeight/useAnimatedHeightBetween.ts"],"sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from \"react\";\nimport tokens from \"../../core/tokens.js\";\nimport { useBrowserPreferences } from \"../useBrowserPreferences/useBrowserPreferences.js\";\nimport { usePreviousValue } from \"../usePreviousValue/usePreviousValue.js\";\nimport type { UseAnimatedHeightOptions } from \"./types.js\";\n\nconst defaultEasing = \"standard\";\nconst defaultTiming = \"productive\";\n\nfunction collapseElement<T extends HTMLElement>(\n elementRef: RefObject<T | null>,\n transition: string,\n raf1: React.MutableRefObject<number | undefined>,\n raf2: React.MutableRefObject<number | undefined>,\n) {\n const element = elementRef.current;\n\n if (!element) return;\n\n element.removeAttribute(\"style\");\n const expandedHeight = element.scrollHeight;\n\n raf1.current = requestAnimationFrame(() => {\n // Hent kollapset høyde\n element.style.removeProperty(\"transition\");\n element.dataset.expanded = \"false\";\n const collapsedHeight = element.getBoundingClientRect().height;\n element.dataset.expanded = \"true\";\n\n // Sett høyde tilbake til utvidet høyde\n element.style.setProperty(\"height\", `${expandedHeight}px`);\n element.style.setProperty(\"overflow-y\", \"hidden\");\n\n raf2.current = requestAnimationFrame(() => {\n // Sett høyde til kollapset høyde og start transition\n element.style.setProperty(\"transition\", transition);\n element.style.setProperty(\"height\", `${collapsedHeight}px`);\n element.dataset.expanded = \"false\";\n });\n });\n}\n\nfunction expandElement<T extends HTMLElement>(\n elementRef: RefObject<T | null>,\n transition: string,\n raf1: React.MutableRefObject<number | undefined>,\n raf2: React.MutableRefObject<number | undefined>,\n) {\n const element = elementRef.current;\n\n if (!element) return;\n\n element.removeAttribute(\"style\");\n const expandedHeight = element.scrollHeight;\n\n raf1.current = requestAnimationFrame(() => {\n // Hent utvidet høyde\n element.style.removeProperty(\"transition\");\n element.dataset.expanded = \"false\";\n const collapsedHeight = element.getBoundingClientRect().height;\n\n // Sett høyde tilbake til kollapset høyde\n element.style.setProperty(\"height\", `${collapsedHeight}px`);\n element.style.setProperty(\"overflow-y\", \"hidden\");\n\n raf2.current = requestAnimationFrame(() => {\n // Sett høyde til utvidet høyde og start transition\n element.style.setProperty(\"transition\", transition);\n element.style.setProperty(\"height\", `${expandedHeight}px`);\n element.dataset.expanded = \"true\";\n });\n });\n}\n\n/**\n * Lar deg enklere animere mellom to tilstander, gitt ved å sette `data-expanded` på et element til `true` eller `false`.\n * Du bestemmer selv hvilke stiler elementet skal ha i de to tilstandene (vha CSS/Sass), og høyden animeres dersom den endrer seg.\n * @param isExpanded indikerer om elementet skal være utvidet eller ikke\n * @param options konfigurasjon for animasjonen, og lyttere for når animasjonen starter og slutter\n * @returns En tuple med referanse til elementet og en funksjon som kan trigge animasjonen\n */\nexport function useAnimatedHeightBetween<T extends HTMLElement>(\n isExpanded: boolean,\n options?: Omit<UseAnimatedHeightOptions<T>, \"display\" | \"onFirstVisible\">,\n): [RefObject<T>, () => void] {\n const wasExpanded = usePreviousValue(isExpanded);\n const easing = options?.easing || defaultEasing;\n const timing = options?.timing || defaultTiming;\n const transition = `${tokens.motion.timing[timing]} height ${tokens.motion.easing[easing]}`;\n\n const { prefersReducedMotion } = useBrowserPreferences();\n\n const raf1 = useRef<number>();\n const raf2 = useRef<number>();\n const elementRef = useRef<T>(null);\n\n const handleTransitionEnd = useCallback(\n (event: TransitionEvent) => {\n const element = elementRef.current;\n\n // Ignore bubbling transitions from within container\n if (element && event.target === element) {\n element.removeAttribute(\"style\");\n options?.onTransitionEnd?.(isExpanded, elementRef);\n }\n },\n [options, isExpanded],\n );\n\n const runAnimation = useCallback(() => {\n const element = elementRef.current;\n\n if (!element) return;\n\n if (wasExpanded === undefined) {\n // Første render\n element.dataset.expanded = isExpanded ? \"true\" : \"false\";\n }\n\n if ((!isExpanded && !wasExpanded) || (isExpanded && wasExpanded)) {\n // Ingen endring\n return;\n }\n\n options?.onTransitionStart?.(isExpanded, elementRef);\n\n if (prefersReducedMotion) {\n element.removeAttribute(\"style\");\n element.dataset.expanded = isExpanded ? \"true\" : \"false\";\n options?.onTransitionEnd?.(isExpanded, elementRef); // make sure to call callback when animation is off\n return;\n }\n\n if (isExpanded) {\n expandElement(elementRef, transition, raf1, raf2);\n } else {\n collapseElement(elementRef, transition, raf1, raf2);\n }\n }, [wasExpanded, isExpanded, options, prefersReducedMotion, transition]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: Vi trigger med isExpanded\n useEffect(() => {\n runAnimation();\n }, [isExpanded, runAnimation]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies:\n useEffect(() => {\n const element = elementRef.current;\n if (element) {\n element.addEventListener(\"transitionend\", handleTransitionEnd);\n }\n\n return () => {\n if (element) {\n element.removeEventListener(\n \"transitionend\",\n handleTransitionEnd,\n );\n }\n };\n }, [isExpanded]);\n\n useEffect(() => {\n const r1 = raf1.current;\n const r2 = raf2.current;\n return () => {\n r1 && cancelAnimationFrame(r1);\n r2 && cancelAnimationFrame(r2);\n };\n }, []);\n\n return [elementRef, runAnimation];\n}\n"],"names":["isExpanded","options","wasExpanded","usePreviousValue","easing","timing","transition","tokens","motion","prefersReducedMotion","useBrowserPreferences","raf1","useRef","raf2","elementRef","handleTransitionEnd","useCallback","event","element","current","target","removeAttribute","_a","onTransitionEnd","call","runAnimation","dataset","expanded","onTransitionStart","_b","expandedHeight","scrollHeight","requestAnimationFrame","style","removeProperty","collapsedHeight","getBoundingClientRect","height","setProperty","expandElement","collapseElement","useEffect","addEventListener","removeEventListener","r1","r2","cancelAnimationFrame"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoAnimateHeight.cjs","sources":["../../../../src/hooks/useAnimatedHeight/useAutoAnimateHeight.ts"],"sourcesContent":["import {\n type RefObject,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport tokens from \"../../core/tokens.js\";\nimport type { Easing, Timing } from \"../../core/types.js\";\nimport { useBrowserPreferences } from \"../useBrowserPreferences/useBrowserPreferences.js\";\nimport { usePreviousValue } from \"../usePreviousValue/usePreviousValue.js\";\n\nconst defaultEasing = \"standard\";\nconst defaultTiming = \"expressive\";\n\nexport type UseAutoAnimatedHeightOptions<T extends HTMLElement> = {\n easing?: Easing;\n /**\n * Overstyr standard timing\n * @default \"expressive\"\n */\n timing?: Timing;\n onTransitionStart?: (ref: RefObject<T>) => void;\n onTransitionEnd?: (ref: RefObject<T>) => void;\n};\n\n/**\n * Gjør det enklere å animere høyden på et element når innholdet endrer seg, men kan brukes på mer generelt grunnlag.\n * Hooken tar inn en triggerverdi, og når denne endrer seg animeres høyden på elementet dersom den har endret seg.\n * @param trigger verdien som brukes til å trigge animasjonen. Dersom denne endrer seg animeres høyden på elementet.\n * @param options konfigurasjon for animasjonen, og lyttere for når animasjonen starter og slutter\n * @returns en referanse til elementet som skal animeres\n */\nexport function useAutoAnimatedHeight<T extends HTMLElement = HTMLElement>(\n trigger: any,\n options?: UseAutoAnimatedHeightOptions<T>,\n) {\n const previousTriggerValue = usePreviousValue(trigger);\n const [previousHeight, setPreviousHeight] = useState(0);\n\n const easing = options?.easing || defaultEasing;\n const timing = options?.timing || defaultTiming;\n const transition = `${tokens.motion.timing[timing]} height ${tokens.motion.easing[easing]}`;\n\n const { prefersReducedMotion } = useBrowserPreferences();\n\n const raf1 = useRef<number>();\n const raf2 = useRef<number>();\n const elementRef = useRef<T>(null);\n\n const handleTransitionEnd = useCallback(\n (event: TransitionEvent) => {\n const element = elementRef.current;\n\n // Ignore bubbling transitions from within container\n if (element && event.target === element) {\n element.removeAttribute(\"style\");\n options?.onTransitionEnd?.(elementRef);\n }\n },\n [options],\n );\n\n const animateElement = useCallback(() => {\n const element = elementRef.current;\n\n if (!element) return;\n\n element.removeAttribute(\"style\");\n const newHeight = element.scrollHeight;\n\n raf1.current = requestAnimationFrame(() => {\n // Sett høyde tilbake til forrige høyde\n element.style.removeProperty(\"transition\");\n element.style.setProperty(\"height\", `${previousHeight}px`);\n element.style.setProperty(\"overflow-y\", \"hidden\");\n\n raf2.current = requestAnimationFrame(() => {\n // Sett høyde til kollapset høyde og start transition\n element.style.setProperty(\"transition\", transition);\n element.style.setProperty(\"height\", `${newHeight}px`);\n });\n });\n\n setPreviousHeight(newHeight);\n }, [transition, previousHeight]);\n\n const runAnimation = useCallback(() => {\n const element = elementRef.current;\n\n if (!element) return;\n\n if (previousTriggerValue === undefined) {\n // Første render\n setPreviousHeight(element.scrollHeight);\n return;\n }\n\n if (trigger === previousTriggerValue) {\n // Ingen endring\n return;\n }\n\n options?.onTransitionStart?.(elementRef);\n\n if (prefersReducedMotion) {\n options?.onTransitionEnd?.(elementRef); // make sure to call callback when animation is off\n return;\n }\n\n animateElement();\n }, [\n animateElement,\n trigger,\n previousTriggerValue,\n options,\n prefersReducedMotion,\n ]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: Vi vil trige med trigger\n useEffect(() => {\n runAnimation();\n }, [trigger, runAnimation]);\n\n useEffect(() => {\n const element = elementRef.current;\n if (element) {\n element.addEventListener(\"transitionend\", handleTransitionEnd);\n }\n\n return () => {\n if (element) {\n element.removeEventListener(\n \"transitionend\",\n handleTransitionEnd,\n );\n }\n };\n }, [handleTransitionEnd]);\n\n useEffect(() => {\n const r1 = raf1.current;\n const r2 = raf2.current;\n return () => {\n r1 && cancelAnimationFrame(r1);\n r2 && cancelAnimationFrame(r2);\n };\n }, []);\n\n return elementRef;\n}\n"],"names":["trigger","options","previousTriggerValue","usePreviousValue","previousHeight","setPreviousHeight","useState","easing","timing","transition","tokens","motion","prefersReducedMotion","useBrowserPreferences","raf1","useRef","raf2","elementRef","handleTransitionEnd","useCallback","event","element","current","target","removeAttribute","_a","onTransitionEnd","call","animateElement","newHeight","scrollHeight","requestAnimationFrame","style","removeProperty","setProperty","runAnimation","onTransitionStart","_b","useEffect","addEventListener","removeEventListener","r1","r2","cancelAnimationFrame"],"mappings":"gSAiCgB,SACZA,EACAC,GAEM,MAAAC,EAAuBC,mBAAiBH,IACvCI,EAAgBC,GAAqBC,EAAAA,SAAS,GAE/CC,GAAS,MAAAN,OAAAA,EAAAA,EAASM,SA5BN,WA6BZC,GAAS,MAAAP,OAAAA,EAAAA,EAASO,SA5BN,aA6BZC,EAAa,GAAGC,EAAOC,OAAOH,OAAOA,aAAkBE,EAAOC,OAAOJ,OAAOA,MAE1EK,qBAAAA,GAAyBC,0BAE3BC,EAAOC,EAAAA,SACPC,EAAOD,EAAAA,SACPE,EAAaF,SAAU,MAEvBG,EAAsBC,EAAAA,aACvBC,UACG,MAAMC,EAAUJ,EAAWK,QAGvBD,GAAWD,EAAMG,SAAWF,IAC5BA,EAAQG,gBAAgB,SACxB,OAAAC,EAAA,MAAAxB,OAAAA,EAAAA,EAASyB,kBAATD,EAAAE,KAAA1B,EAA2BgB,GAAAA,GAGnC,CAAChB,IAGC2B,EAAiBT,EAAAA,aAAY,KAC/B,MAAME,EAAUJ,EAAWK,QAE3B,IAAKD,EAAS,OAEdA,EAAQG,gBAAgB,SACxB,MAAMK,EAAYR,EAAQS,aAErBhB,EAAAQ,QAAUS,uBAAsB,KAEzBV,EAAAW,MAAMC,eAAe,cAC7BZ,EAAQW,MAAME,YAAY,SAAU,GAAG9B,OAC/BiB,EAAAW,MAAME,YAAY,aAAc,UAEnClB,EAAAM,QAAUS,uBAAsB,KAEzBV,EAAAW,MAAME,YAAY,aAAczB,GACxCY,EAAQW,MAAME,YAAY,SAAU,GAAGL,MAAa,GACvD,IAGLxB,EAAkBwB,EAAS,GAC5B,CAACpB,EAAYL,IAEV+B,EAAehB,EAAAA,aAAY,aAC7B,MAAME,EAAUJ,EAAWK,QAE3B,GAAKD,EAEL,SAA6B,IAAzBnB,EAGA,YADAG,EAAkBgB,EAAQS,cAI9B,GAAI9B,IAAYE,EAOhB,CAAA,GAFA,OAAAuB,EAAA,MAAAxB,SAAAA,EAASmC,oBAATX,EAAAE,KAAA1B,EAA6BgB,GAEzBL,EAEA,YADA,OAAAyB,EAAA,MAAApC,OAAAA,EAAAA,EAASyB,kBAATW,EAAAV,KAAA1B,EAA2BgB,IAIhBW,GAAA,CAAA,IAChB,CACCA,EACA5B,EACAE,EACAD,EACAW,IAIJ0B,OAAAA,EAAAA,WAAU,KACOH,GAAA,GACd,CAACnC,EAASmC,IAEbG,EAAAA,WAAU,KACN,MAAMjB,EAAUJ,EAAWK,QAC3B,OAAID,GACQA,EAAAkB,iBAAiB,gBAAiBrB,GAGvC,KACCG,GACQA,EAAAmB,oBACJ,gBACAtB,EACJ,CAER,GACD,CAACA,IAEJoB,EAAAA,WAAU,KACN,MAAMG,EAAK3B,EAAKQ,QACVoB,EAAK1B,EAAKM,QAChB,MAAO,KACHmB,GAAME,qBAAqBF,GAC3BC,GAAMC,qBAAqBD,EAAE,CACjC,GACD,IAEIzB,CACX"}
1
+ {"version":3,"file":"useAutoAnimateHeight.cjs","sources":["../../../../src/hooks/useAnimatedHeight/useAutoAnimateHeight.ts"],"sourcesContent":["import {\n type RefObject,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport tokens from \"../../core/tokens.js\";\nimport type { Easing, Timing } from \"../../core/types.js\";\nimport { useBrowserPreferences } from \"../useBrowserPreferences/useBrowserPreferences.js\";\nimport { usePreviousValue } from \"../usePreviousValue/usePreviousValue.js\";\n\nconst defaultEasing = \"standard\";\nconst defaultTiming = \"expressive\";\n\nexport type UseAutoAnimatedHeightOptions<T extends HTMLElement> = {\n easing?: Easing;\n /**\n * Overstyr standard timing\n * @default \"expressive\"\n */\n timing?: Timing;\n onTransitionStart?: (ref: RefObject<T | null>) => void;\n onTransitionEnd?: (ref: RefObject<T | null>) => void;\n};\n\n/**\n * Gjør det enklere å animere høyden på et element når innholdet endrer seg, men kan brukes på mer generelt grunnlag.\n * Hooken tar inn en triggerverdi, og når denne endrer seg animeres høyden på elementet dersom den har endret seg.\n * @param trigger verdien som brukes til å trigge animasjonen. Dersom denne endrer seg animeres høyden på elementet.\n * @param options konfigurasjon for animasjonen, og lyttere for når animasjonen starter og slutter\n * @returns en referanse til elementet som skal animeres\n */\nexport function useAutoAnimatedHeight<T extends HTMLElement = HTMLElement>(\n trigger: any,\n options?: UseAutoAnimatedHeightOptions<T>,\n) {\n const previousTriggerValue = usePreviousValue(trigger);\n const [previousHeight, setPreviousHeight] = useState(0);\n\n const easing = options?.easing || defaultEasing;\n const timing = options?.timing || defaultTiming;\n const transition = `${tokens.motion.timing[timing]} height ${tokens.motion.easing[easing]}`;\n\n const { prefersReducedMotion } = useBrowserPreferences();\n\n const raf1 = useRef<number>();\n const raf2 = useRef<number>();\n const elementRef = useRef<T>(null);\n\n const handleTransitionEnd = useCallback(\n (event: TransitionEvent) => {\n const element = elementRef.current;\n\n // Ignore bubbling transitions from within container\n if (element && event.target === element) {\n element.removeAttribute(\"style\");\n options?.onTransitionEnd?.(elementRef);\n }\n },\n [options],\n );\n\n const animateElement = useCallback(() => {\n const element = elementRef.current;\n\n if (!element) return;\n\n element.removeAttribute(\"style\");\n const newHeight = element.scrollHeight;\n\n raf1.current = requestAnimationFrame(() => {\n // Sett høyde tilbake til forrige høyde\n element.style.removeProperty(\"transition\");\n element.style.setProperty(\"height\", `${previousHeight}px`);\n element.style.setProperty(\"overflow-y\", \"hidden\");\n\n raf2.current = requestAnimationFrame(() => {\n // Sett høyde til kollapset høyde og start transition\n element.style.setProperty(\"transition\", transition);\n element.style.setProperty(\"height\", `${newHeight}px`);\n });\n });\n\n setPreviousHeight(newHeight);\n }, [transition, previousHeight]);\n\n const runAnimation = useCallback(() => {\n const element = elementRef.current;\n\n if (!element) return;\n\n if (previousTriggerValue === undefined) {\n // Første render\n setPreviousHeight(element.scrollHeight);\n return;\n }\n\n if (trigger === previousTriggerValue) {\n // Ingen endring\n return;\n }\n\n options?.onTransitionStart?.(elementRef);\n\n if (prefersReducedMotion) {\n options?.onTransitionEnd?.(elementRef); // make sure to call callback when animation is off\n return;\n }\n\n animateElement();\n }, [\n animateElement,\n trigger,\n previousTriggerValue,\n options,\n prefersReducedMotion,\n ]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: Vi vil trige med trigger\n useEffect(() => {\n runAnimation();\n }, [trigger, runAnimation]);\n\n useEffect(() => {\n const element = elementRef.current;\n if (element) {\n element.addEventListener(\"transitionend\", handleTransitionEnd);\n }\n\n return () => {\n if (element) {\n element.removeEventListener(\n \"transitionend\",\n handleTransitionEnd,\n );\n }\n };\n }, [handleTransitionEnd]);\n\n useEffect(() => {\n const r1 = raf1.current;\n const r2 = raf2.current;\n return () => {\n r1 && cancelAnimationFrame(r1);\n r2 && cancelAnimationFrame(r2);\n };\n }, []);\n\n return elementRef;\n}\n"],"names":["trigger","options","previousTriggerValue","usePreviousValue","previousHeight","setPreviousHeight","useState","easing","timing","transition","tokens","motion","prefersReducedMotion","useBrowserPreferences","raf1","useRef","raf2","elementRef","handleTransitionEnd","useCallback","event","element","current","target","removeAttribute","_a","onTransitionEnd","call","animateElement","newHeight","scrollHeight","requestAnimationFrame","style","removeProperty","setProperty","runAnimation","onTransitionStart","_b","useEffect","addEventListener","removeEventListener","r1","r2","cancelAnimationFrame"],"mappings":"gSAiCgB,SACZA,EACAC,GAEM,MAAAC,EAAuBC,mBAAiBH,IACvCI,EAAgBC,GAAqBC,EAAAA,SAAS,GAE/CC,GAAS,MAAAN,OAAAA,EAAAA,EAASM,SA5BN,WA6BZC,GAAS,MAAAP,OAAAA,EAAAA,EAASO,SA5BN,aA6BZC,EAAa,GAAGC,EAAOC,OAAOH,OAAOA,aAAkBE,EAAOC,OAAOJ,OAAOA,MAE1EK,qBAAAA,GAAyBC,0BAE3BC,EAAOC,EAAAA,SACPC,EAAOD,EAAAA,SACPE,EAAaF,SAAU,MAEvBG,EAAsBC,EAAAA,aACvBC,UACG,MAAMC,EAAUJ,EAAWK,QAGvBD,GAAWD,EAAMG,SAAWF,IAC5BA,EAAQG,gBAAgB,SACxB,OAAAC,EAAA,MAAAxB,OAAAA,EAAAA,EAASyB,kBAATD,EAAAE,KAAA1B,EAA2BgB,GAAAA,GAGnC,CAAChB,IAGC2B,EAAiBT,EAAAA,aAAY,KAC/B,MAAME,EAAUJ,EAAWK,QAE3B,IAAKD,EAAS,OAEdA,EAAQG,gBAAgB,SACxB,MAAMK,EAAYR,EAAQS,aAErBhB,EAAAQ,QAAUS,uBAAsB,KAEzBV,EAAAW,MAAMC,eAAe,cAC7BZ,EAAQW,MAAME,YAAY,SAAU,GAAG9B,OAC/BiB,EAAAW,MAAME,YAAY,aAAc,UAEnClB,EAAAM,QAAUS,uBAAsB,KAEzBV,EAAAW,MAAME,YAAY,aAAczB,GACxCY,EAAQW,MAAME,YAAY,SAAU,GAAGL,MAAa,GACvD,IAGLxB,EAAkBwB,EAAS,GAC5B,CAACpB,EAAYL,IAEV+B,EAAehB,EAAAA,aAAY,aAC7B,MAAME,EAAUJ,EAAWK,QAE3B,GAAKD,EAEL,SAA6B,IAAzBnB,EAGA,YADAG,EAAkBgB,EAAQS,cAI9B,GAAI9B,IAAYE,EAOhB,CAAA,GAFA,OAAAuB,EAAA,MAAAxB,SAAAA,EAASmC,oBAATX,EAAAE,KAAA1B,EAA6BgB,GAEzBL,EAEA,YADA,OAAAyB,EAAA,MAAApC,OAAAA,EAAAA,EAASyB,kBAATW,EAAAV,KAAA1B,EAA2BgB,IAIhBW,GAAA,CAAA,IAChB,CACCA,EACA5B,EACAE,EACAD,EACAW,IAIJ0B,OAAAA,EAAAA,WAAU,KACOH,GAAA,GACd,CAACnC,EAASmC,IAEbG,EAAAA,WAAU,KACN,MAAMjB,EAAUJ,EAAWK,QAC3B,OAAID,GACQA,EAAAkB,iBAAiB,gBAAiBrB,GAGvC,KACCG,GACQA,EAAAmB,oBACJ,gBACAtB,EACJ,CAER,GACD,CAACA,IAEJoB,EAAAA,WAAU,KACN,MAAMG,EAAK3B,EAAKQ,QACVoB,EAAK1B,EAAKM,QAChB,MAAO,KACHmB,GAAME,qBAAqBF,GAC3BC,GAAMC,qBAAqBD,EAAE,CACjC,GACD,IAEIzB,CACX"}
@@ -7,8 +7,8 @@ export type UseAutoAnimatedHeightOptions<T extends HTMLElement> = {
7
7
  * @default "expressive"
8
8
  */
9
9
  timing?: Timing;
10
- onTransitionStart?: (ref: RefObject<T>) => void;
11
- onTransitionEnd?: (ref: RefObject<T>) => void;
10
+ onTransitionStart?: (ref: RefObject<T | null>) => void;
11
+ onTransitionEnd?: (ref: RefObject<T | null>) => void;
12
12
  };
13
13
  /**
14
14
  * Gjør det enklere å animere høyden på et element når innholdet endrer seg, men kan brukes på mer generelt grunnlag.
@@ -1 +1 @@
1
- {"version":3,"file":"useClickOutside.cjs","sources":["../../../../src/hooks/useClickOutside/useClickOutside.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\n/**\n * Hook for å sette opp en lytter ved klikk utenfor et element.\n * @param ref Elementet du ønsker å sjekke om en klikk er utenfor.\n * @param fn Callback-funksjon som kalles når det klikkes utenfor elementet.\n */\nexport function useClickOutside(\n ref: RefObject<HTMLElement> | null,\n fn: () => void,\n): void {\n function handleClickOutside(event: MouseEvent) {\n if (ref?.current && !ref.current.contains(event.target as Node)) {\n fn();\n }\n }\n\n useEffect(() => {\n if (ref) {\n document?.addEventListener(\"click\", handleClickOutside);\n }\n return () => {\n document?.removeEventListener(\"click\", handleClickOutside);\n };\n });\n}\n"],"names":["ref","fn","handleClickOutside","event","current","contains","target","useEffect","document","addEventListener","removeEventListener"],"mappings":"iIAOgB,SACZA,EACAC,GAEA,SAASC,EAAmBC,GACpB,MAAAH,GAAAA,EAAKI,UAAYJ,EAAII,QAAQC,SAASF,EAAMG,SACzCL,GACP,CAGJM,EAAAA,WAAU,KACFP,IACU,MAAAQ,UAAAA,SAAAC,iBAAiB,QAASP,IAEjC,KACO,MAAAM,UAAAA,SAAAE,oBAAoB,QAASR,EAAAA,IAGnD"}
1
+ {"version":3,"file":"useClickOutside.cjs","sources":["../../../../src/hooks/useClickOutside/useClickOutside.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\n/**\n * Hook for å sette opp en lytter ved klikk utenfor et element.\n * @param ref Elementet du ønsker å sjekke om en klikk er utenfor.\n * @param fn Callback-funksjon som kalles når det klikkes utenfor elementet.\n */\nexport function useClickOutside(\n ref: RefObject<HTMLElement | null> | null,\n fn: () => void,\n): void {\n function handleClickOutside(event: MouseEvent) {\n if (ref?.current && !ref.current.contains(event.target as Node)) {\n fn();\n }\n }\n\n useEffect(() => {\n if (ref) {\n document?.addEventListener(\"click\", handleClickOutside);\n }\n return () => {\n document?.removeEventListener(\"click\", handleClickOutside);\n };\n });\n}\n"],"names":["ref","fn","handleClickOutside","event","current","contains","target","useEffect","document","addEventListener","removeEventListener"],"mappings":"iIAOgB,SACZA,EACAC,GAEA,SAASC,EAAmBC,GACpB,MAAAH,GAAAA,EAAKI,UAAYJ,EAAII,QAAQC,SAASF,EAAMG,SACzCL,GACP,CAGJM,EAAAA,WAAU,KACFP,IACU,MAAAQ,UAAAA,SAAAC,iBAAiB,QAASP,IAEjC,KACO,MAAAM,UAAAA,SAAAE,oBAAoB,QAASR,EAAAA,IAGnD"}
@@ -4,4 +4,4 @@ import { RefObject } from 'react';
4
4
  * @param ref Elementet du ønsker å sjekke om en klikk er utenfor.
5
5
  * @param fn Callback-funksjon som kalles når det klikkes utenfor elementet.
6
6
  */
7
- export declare function useClickOutside(ref: RefObject<HTMLElement> | null, fn: () => void): void;
7
+ export declare function useClickOutside(ref: RefObject<HTMLElement | null> | null, fn: () => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusOutside.cjs","sources":["../../../../src/hooks/useFocusOutside/useFocusOutside.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\n/**\n * Hook for å lytte etter fokus utenfor et element. Nyttig dersom flere fokuserbare elementer samspiller i en komponent.\n * @param ref Elementet som skal observeres\n * @param fn Funksjon som kalles når fokus flyttes utenfor elementet\n */\nexport function useFocusOutside(\n ref: RefObject<HTMLElement> | null,\n fn: () => void,\n): void {\n function handleFocusOutside(event: FocusEvent) {\n if (ref?.current && !ref.current.contains(event.target as Node)) {\n fn();\n }\n }\n\n useEffect(() => {\n if (ref) {\n document?.addEventListener(\"focusin\", handleFocusOutside);\n }\n return () => {\n document?.removeEventListener(\"focusin\", handleFocusOutside);\n };\n });\n}\n"],"names":["ref","fn","handleFocusOutside","event","current","contains","target","useEffect","document","addEventListener","removeEventListener"],"mappings":"iIAOgB,SACZA,EACAC,GAEA,SAASC,EAAmBC,GACpB,MAAAH,GAAAA,EAAKI,UAAYJ,EAAII,QAAQC,SAASF,EAAMG,SACzCL,GACP,CAGJM,EAAAA,WAAU,KACFP,IACU,MAAAQ,UAAAA,SAAAC,iBAAiB,UAAWP,IAEnC,KACO,MAAAM,UAAAA,SAAAE,oBAAoB,UAAWR,EAAAA,IAGrD"}
1
+ {"version":3,"file":"useFocusOutside.cjs","sources":["../../../../src/hooks/useFocusOutside/useFocusOutside.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\n/**\n * Hook for å lytte etter fokus utenfor et element. Nyttig dersom flere fokuserbare elementer samspiller i en komponent.\n * @param ref Elementet som skal observeres\n * @param fn Funksjon som kalles når fokus flyttes utenfor elementet\n */\nexport function useFocusOutside(\n ref: RefObject<HTMLElement | null> | null,\n fn: () => void,\n): void {\n function handleFocusOutside(event: FocusEvent) {\n if (ref?.current && !ref.current.contains(event.target as Node)) {\n fn();\n }\n }\n\n useEffect(() => {\n if (ref) {\n document?.addEventListener(\"focusin\", handleFocusOutside);\n }\n return () => {\n document?.removeEventListener(\"focusin\", handleFocusOutside);\n };\n });\n}\n"],"names":["ref","fn","handleFocusOutside","event","current","contains","target","useEffect","document","addEventListener","removeEventListener"],"mappings":"iIAOgB,SACZA,EACAC,GAEA,SAASC,EAAmBC,GACpB,MAAAH,GAAAA,EAAKI,UAAYJ,EAAII,QAAQC,SAASF,EAAMG,SACzCL,GACP,CAGJM,EAAAA,WAAU,KACFP,IACU,MAAAQ,UAAAA,SAAAC,iBAAiB,UAAWP,IAEnC,KACO,MAAAM,UAAAA,SAAAE,oBAAoB,UAAWR,EAAAA,IAGrD"}
@@ -4,4 +4,4 @@ import { RefObject } from 'react';
4
4
  * @param ref Elementet som skal observeres
5
5
  * @param fn Funksjon som kalles når fokus flyttes utenfor elementet
6
6
  */
7
- export declare function useFocusOutside(ref: RefObject<HTMLElement> | null, fn: () => void): void;
7
+ export declare function useFocusOutside(ref: RefObject<HTMLElement | null> | null, fn: () => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"useKeyListener.cjs","sources":["../../../../src/hooks/useKeyListener/useKeyListener.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\n/**\n * Lar deg sette opp en tastaturlytter på et element.\n * @param ref Element som skal observeres\n * @param keys Taster som skal lyttes på\n * @param fn Funksjon som kalles når en tastetrykk observeres\n */\nexport function useKeyListener(\n ref: RefObject<HTMLElement> | null,\n keys: string[] | string | null,\n fn: (event: KeyboardEvent) => void,\n): void {\n if (typeof keys === \"string\") {\n // biome-ignore lint/style/noParameterAssign: Dette er helt greit å forstå\n keys = [keys];\n }\n function handleKeyDown(event: KeyboardEvent) {\n if (keys && !keys.includes(event.key)) {\n return;\n }\n fn(event);\n }\n\n useEffect(() => {\n const element = ref?.current;\n if (element) {\n element.addEventListener(\"keydown\", handleKeyDown);\n }\n return () => {\n if (element) {\n element.removeEventListener(\"keydown\", handleKeyDown);\n }\n };\n });\n}\n"],"names":["ref","keys","fn","handleKeyDown","event","includes","key","useEffect","element","current","addEventListener","removeEventListener"],"mappings":"gIAQgB,SACZA,EACAC,EACAC,GAMA,SAASC,EAAcC,GACfH,IAASA,EAAKI,SAASD,EAAME,MAGjCJ,EAAGE,EAAK,CARQ,iBAATH,IAEPA,EAAO,CAACA,IASZM,EAAAA,WAAU,KACN,MAAMC,EAAU,MAAAR,OAAA,EAAAA,EAAKS,QACrB,OAAID,GACQA,EAAAE,iBAAiB,UAAWP,GAEjC,KACCK,GACQA,EAAAG,oBAAoB,UAAWR,EAAa,CAE5D,GAER"}
1
+ {"version":3,"file":"useKeyListener.cjs","sources":["../../../../src/hooks/useKeyListener/useKeyListener.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\n/**\n * Lar deg sette opp en tastaturlytter på et element.\n * @param ref Element som skal observeres\n * @param keys Taster som skal lyttes på\n * @param fn Funksjon som kalles når en tastetrykk observeres\n */\nexport function useKeyListener(\n ref: RefObject<HTMLElement | null> | null,\n keys: string[] | string | null,\n fn: (event: KeyboardEvent) => void,\n): void {\n if (typeof keys === \"string\") {\n // biome-ignore lint/style/noParameterAssign: Dette er helt greit å forstå\n keys = [keys];\n }\n function handleKeyDown(event: KeyboardEvent) {\n if (keys && !keys.includes(event.key)) {\n return;\n }\n fn(event);\n }\n\n useEffect(() => {\n const element = ref?.current;\n if (element) {\n element.addEventListener(\"keydown\", handleKeyDown);\n }\n return () => {\n if (element) {\n element.removeEventListener(\"keydown\", handleKeyDown);\n }\n };\n });\n}\n"],"names":["ref","keys","fn","handleKeyDown","event","includes","key","useEffect","element","current","addEventListener","removeEventListener"],"mappings":"gIAQgB,SACZA,EACAC,EACAC,GAMA,SAASC,EAAcC,GACfH,IAASA,EAAKI,SAASD,EAAME,MAGjCJ,EAAGE,EAAK,CARQ,iBAATH,IAEPA,EAAO,CAACA,IASZM,EAAAA,WAAU,KACN,MAAMC,EAAU,MAAAR,OAAA,EAAAA,EAAKS,QACrB,OAAID,GACQA,EAAAE,iBAAiB,UAAWP,GAEjC,KACCK,GACQA,EAAAG,oBAAoB,UAAWR,EAAa,CAE5D,GAER"}
@@ -5,4 +5,4 @@ import { RefObject } from 'react';
5
5
  * @param keys Taster som skal lyttes på
6
6
  * @param fn Funksjon som kalles når en tastetrykk observeres
7
7
  */
8
- export declare function useKeyListener(ref: RefObject<HTMLElement> | null, keys: string[] | string | null, fn: (event: KeyboardEvent) => void): void;
8
+ export declare function useKeyListener(ref: RefObject<HTMLElement | null> | null, keys: string[] | string | null, fn: (event: KeyboardEvent) => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"useListNavigation.cjs","sources":["../../../../src/hooks/useListNavigation/useListNavigation.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\ntype Timer = number | undefined;\ntype KeyBuffer = { keys: string } | undefined;\ntype Direction = \"prev\" | \"next\" | \"first\" | \"last\";\ninterface MoveDetails {\n event: KeyboardEvent;\n list: HTMLElement;\n currentFocus: HTMLButtonElement;\n}\ninterface ListDetails {\n list: HTMLElement;\n search: KeyBuffer;\n searchResetTimer: Timer;\n}\ninterface SearchDetails extends ListDetails {\n key: string;\n}\ninterface EventDetails extends ListDetails {\n event: KeyboardEvent;\n}\ntype UseListNavigationProps<T> = {\n /** Ref til et element med rollen `listbox` */\n ref: RefObject<T>;\n};\n\nexport function useListNavigation<T extends HTMLElement>({\n ref,\n}: UseListNavigationProps<T>): void {\n useEffect(() => {\n let searchResetTimer: Timer;\n const search: KeyBuffer = { keys: \"\" }; // keypress buffer is an object to preserve state\n const list = ref.current;\n const handler = (event: KeyboardEvent) => {\n if (list) {\n handleListKeyNav({ list, event, search, searchResetTimer });\n }\n };\n\n if (list) {\n list.addEventListener(\"keydown\", handler);\n }\n\n return () => {\n if (list) {\n list.removeEventListener(\"keydown\", handler);\n }\n };\n }, [ref]);\n}\n\nfunction handleMoveTo(\n direction: Direction,\n { event, list, currentFocus }: MoveDetails,\n) {\n event.preventDefault();\n moveFocusTo(direction, list, currentFocus);\n}\n\nfunction handleListKeyNav({\n list,\n event,\n search,\n searchResetTimer,\n}: EventDetails) {\n const { key, target } = event;\n const currentFocus = target as HTMLButtonElement;\n\n const moveDetails = {\n event,\n list,\n currentFocus,\n };\n\n switch (key) {\n case \"ArrowUp\":\n case \"PageUp\":\n handleMoveTo(\"prev\", moveDetails);\n break;\n case \"ArrowDown\":\n case \"PageDown\":\n handleMoveTo(\"next\", moveDetails);\n break;\n case \"Home\":\n handleMoveTo(\"first\", moveDetails);\n break;\n case \"End\":\n handleMoveTo(\"last\", moveDetails);\n break;\n case \"Tab\":\n // in a standard select, tab does nothing in-menu\n event.preventDefault();\n break;\n case \"Enter\":\n case \" \":\n break;\n\n default:\n if (search !== undefined) {\n const searchResult = findItem({\n list,\n key,\n search,\n searchResetTimer,\n });\n if (searchResult) {\n searchResult.focus();\n }\n }\n break;\n }\n}\n\nfunction moveFocusTo(\n direction: Direction,\n list: HTMLElement,\n current: HTMLButtonElement,\n) {\n const thisOption = current;\n switch (direction) {\n case \"prev\": {\n const prevOption: HTMLButtonElement | null =\n thisOption &&\n (thisOption.previousElementSibling as HTMLButtonElement);\n if (prevOption) {\n prevOption.focus();\n }\n break;\n }\n case \"next\": {\n const nextOption: HTMLButtonElement | null =\n thisOption &&\n (thisOption.nextElementSibling as HTMLButtonElement);\n if (nextOption) {\n nextOption.focus();\n }\n break;\n }\n case \"first\": {\n const firstItem =\n list.querySelector<HTMLButtonElement>(`[role=\"option\"]`);\n if (firstItem) {\n firstItem.focus();\n }\n break;\n }\n case \"last\": {\n const listItems =\n list.querySelectorAll<HTMLButtonElement>(`[role=\"option\"]`);\n if (listItems.length) {\n listItems[listItems.length - 1].focus();\n }\n break;\n }\n }\n}\n\nfunction findItem({\n list,\n key,\n search,\n searchResetTimer,\n}: SearchDetails): HTMLButtonElement | null {\n const listItems = list.querySelectorAll(`[role=\"option\"]`);\n if (!listItems.length) return null;\n\n if (search) {\n search.keys = search.keys.concat(key);\n resetWhenIdle(search, searchResetTimer);\n\n for (let n = 0; n < listItems.length; n++) {\n const label = (listItems[n] as HTMLButtonElement).innerText;\n if (label && label.toLowerCase().indexOf(search.keys) === 0) {\n return listItems[n] as HTMLButtonElement;\n }\n }\n }\n\n return null;\n}\n\nfunction resetWhenIdle(search: KeyBuffer, timer: Timer) {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n timer = setTimeout(\n () => {\n // biome-ignore lint/suspicious/noAssignInExpressions: <explanation>\n search ? (search.keys = \"\") : (search = { keys: \"\" });\n timer = undefined;\n },\n 500,\n search,\n timer,\n );\n}\n"],"names":["handleMoveTo","direction","event","list","currentFocus","preventDefault","current","thisOption","prevOption","previousElementSibling","focus","nextOption","nextElementSibling","firstItem","querySelector","listItems","querySelectorAll","length","moveFocusTo","ref","useEffect","search","keys","handler","searchResetTimer","key","target","moveDetails","searchResult","concat","timer","clearTimeout","setTimeout","resetWhenIdle","n","label","innerText","toLowerCase","indexOf","findItem","handleListKeyNav","addEventListener","removeEventListener"],"mappings":"yGAmDA,SAASA,EACLC,GACEC,MAAAA,EAAOC,KAAAA,EAAMC,aAAAA,IAEfF,EAAMG,iBA0DV,SACIJ,EACAE,EACAG,GAEA,MAAMC,EAAaD,EACnB,OAAQL,GACJ,IAAK,OAAQ,CACH,MAAAO,EACFD,GACCA,EAAWE,uBACZD,GACAA,EAAWE,QAEf,KAAA,CAEJ,IAAK,OAAQ,CACH,MAAAC,EACFJ,GACCA,EAAWK,mBACZD,GACAA,EAAWD,QAEf,KAAA,CAEJ,IAAK,QAAS,CACJ,MAAAG,EACFV,EAAKW,cAAiC,mBACtCD,GACAA,EAAUH,QAEd,KAAA,CAEJ,IAAK,OAAQ,CACH,MAAAK,EACFZ,EAAKa,iBAAoC,mBACzCD,EAAUE,QACVF,EAAUA,EAAUE,OAAS,GAAGP,QAEpC,KAAA,EAGZ,CAnGgBQ,CAAAjB,EAAWE,EAAMC,EACjC,2BA/BO,UACHe,IAAAA,IAEAC,EAAAA,WAAU,KAEA,MAAAC,EAAoB,CAAEC,KAAM,IAC5BnB,EAAOgB,EAAIb,QACXiB,EAAWrB,IACTC,GAyBhB,UACIA,KAAAA,EACAD,MAAAA,EACAmB,OAAAA,EACAG,iBAAAA,IAEM,MAAEC,IAAAA,EAAKC,OAAAA,GAAWxB,EAGlByB,EAAc,CAChBzB,MAAAA,EACAC,KAAAA,EACAC,aALiBsB,GAQrB,OAAQD,GACJ,IAAK,UACL,IAAK,SACDzB,EAAa,OAAQ2B,GACrB,MACJ,IAAK,YACL,IAAK,WACD3B,EAAa,OAAQ2B,GACrB,MACJ,IAAK,OACD3B,EAAa,QAAS2B,GACtB,MACJ,IAAK,MACD3B,EAAa,OAAQ2B,GACrB,MACJ,IAAK,MAEDzB,EAAMG,iBACN,MACJ,IAAK,QACL,IAAK,IACD,MAEJ,QACI,QAAe,IAAXgB,EAAsB,CACtB,MAAMO,EA0DtB,UACIzB,KAAAA,EACAsB,IAAAA,EACAJ,OAAAA,EACAG,iBAAAA,IAEM,MAAAT,EAAYZ,EAAKa,iBAAiB,mBACpC,IAACD,EAAUE,OAAe,OAAA,KAE9B,GAAII,EAAQ,CACRA,EAAOC,KAAOD,EAAOC,KAAKO,OAAOJ,GAczC,SAAuBJ,EAAmBS,GAClCA,IACAC,aAAaD,GACLA,OAAA,GAEJA,EAAAE,YACJ,KAEIX,EAAUA,EAAOC,KAAO,GAAOD,EAAS,CAAEC,KAAM,IACxCQ,OAAA,IAEZ,IACAT,EACAS,EAER,CA5BQG,CAAcZ,EAAQG,GAEtB,IAAA,IAASU,EAAI,EAAGA,EAAInB,EAAUE,OAAQiB,IAAK,CACjC,MAAAC,EAASpB,EAAUmB,GAAyBE,UAC9C,GAAAD,GAAsD,IAA7CA,EAAME,cAAcC,QAAQjB,EAAOC,MAC5C,OAAOP,EAAUmB,EACrB,CACJ,CAGG,OAAA,IACX,CAhFqCK,CAAS,CAC1BpC,KAAAA,EACAsB,IAAAA,EACAJ,OAAAA,EACAG,iBAAAA,IAEAI,GACAA,EAAalB,OACjB,EAIhB,CA5EgB8B,CAAiB,CAAErC,KAAAA,EAAMD,MAAAA,EAAOmB,OAAAA,EAAQG,iBAL5CA,WAK8D,EAIlE,OAAIrB,GACKA,EAAAsC,iBAAiB,UAAWlB,GAG9B,KACCpB,GACKA,EAAAuC,oBAAoB,UAAWnB,EAAO,CAEnD,GACD,CAACJ,GACR"}
1
+ {"version":3,"file":"useListNavigation.cjs","sources":["../../../../src/hooks/useListNavigation/useListNavigation.ts"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\ntype Timer = number | undefined;\ntype KeyBuffer = { keys: string } | undefined;\ntype Direction = \"prev\" | \"next\" | \"first\" | \"last\";\ninterface MoveDetails {\n event: KeyboardEvent;\n list: HTMLElement;\n currentFocus: HTMLButtonElement;\n}\ninterface ListDetails {\n list: HTMLElement;\n search: KeyBuffer;\n searchResetTimer: Timer;\n}\ninterface SearchDetails extends ListDetails {\n key: string;\n}\ninterface EventDetails extends ListDetails {\n event: KeyboardEvent;\n}\ntype UseListNavigationProps<T> = {\n /** Ref til et element med rollen `listbox` */\n ref: RefObject<T | null>;\n};\n\nexport function useListNavigation<T extends HTMLElement>({\n ref,\n}: UseListNavigationProps<T>): void {\n useEffect(() => {\n let searchResetTimer: Timer;\n const search: KeyBuffer = { keys: \"\" }; // keypress buffer is an object to preserve state\n const list = ref.current;\n const handler = (event: KeyboardEvent) => {\n if (list) {\n handleListKeyNav({ list, event, search, searchResetTimer });\n }\n };\n\n if (list) {\n list.addEventListener(\"keydown\", handler);\n }\n\n return () => {\n if (list) {\n list.removeEventListener(\"keydown\", handler);\n }\n };\n }, [ref]);\n}\n\nfunction handleMoveTo(\n direction: Direction,\n { event, list, currentFocus }: MoveDetails,\n) {\n event.preventDefault();\n moveFocusTo(direction, list, currentFocus);\n}\n\nfunction handleListKeyNav({\n list,\n event,\n search,\n searchResetTimer,\n}: EventDetails) {\n const { key, target } = event;\n const currentFocus = target as HTMLButtonElement;\n\n const moveDetails = {\n event,\n list,\n currentFocus,\n };\n\n switch (key) {\n case \"ArrowUp\":\n case \"PageUp\":\n handleMoveTo(\"prev\", moveDetails);\n break;\n case \"ArrowDown\":\n case \"PageDown\":\n handleMoveTo(\"next\", moveDetails);\n break;\n case \"Home\":\n handleMoveTo(\"first\", moveDetails);\n break;\n case \"End\":\n handleMoveTo(\"last\", moveDetails);\n break;\n case \"Tab\":\n // in a standard select, tab does nothing in-menu\n event.preventDefault();\n break;\n case \"Enter\":\n case \" \":\n break;\n\n default:\n if (search !== undefined) {\n const searchResult = findItem({\n list,\n key,\n search,\n searchResetTimer,\n });\n if (searchResult) {\n searchResult.focus();\n }\n }\n break;\n }\n}\n\nfunction moveFocusTo(\n direction: Direction,\n list: HTMLElement,\n current: HTMLButtonElement,\n) {\n const thisOption = current;\n switch (direction) {\n case \"prev\": {\n const prevOption: HTMLButtonElement | null =\n thisOption &&\n (thisOption.previousElementSibling as HTMLButtonElement);\n if (prevOption) {\n prevOption.focus();\n }\n break;\n }\n case \"next\": {\n const nextOption: HTMLButtonElement | null =\n thisOption &&\n (thisOption.nextElementSibling as HTMLButtonElement);\n if (nextOption) {\n nextOption.focus();\n }\n break;\n }\n case \"first\": {\n const firstItem =\n list.querySelector<HTMLButtonElement>(`[role=\"option\"]`);\n if (firstItem) {\n firstItem.focus();\n }\n break;\n }\n case \"last\": {\n const listItems =\n list.querySelectorAll<HTMLButtonElement>(`[role=\"option\"]`);\n if (listItems.length) {\n listItems[listItems.length - 1].focus();\n }\n break;\n }\n }\n}\n\nfunction findItem({\n list,\n key,\n search,\n searchResetTimer,\n}: SearchDetails): HTMLButtonElement | null {\n const listItems = list.querySelectorAll(`[role=\"option\"]`);\n if (!listItems.length) return null;\n\n if (search) {\n search.keys = search.keys.concat(key);\n resetWhenIdle(search, searchResetTimer);\n\n for (let n = 0; n < listItems.length; n++) {\n const label = (listItems[n] as HTMLButtonElement).innerText;\n if (label && label.toLowerCase().indexOf(search.keys) === 0) {\n return listItems[n] as HTMLButtonElement;\n }\n }\n }\n\n return null;\n}\n\nfunction resetWhenIdle(search: KeyBuffer, timer: Timer) {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n timer = setTimeout(\n () => {\n // biome-ignore lint/suspicious/noAssignInExpressions: <explanation>\n search ? (search.keys = \"\") : (search = { keys: \"\" });\n timer = undefined;\n },\n 500,\n search,\n timer,\n );\n}\n"],"names":["handleMoveTo","direction","event","list","currentFocus","preventDefault","current","thisOption","prevOption","previousElementSibling","focus","nextOption","nextElementSibling","firstItem","querySelector","listItems","querySelectorAll","length","moveFocusTo","ref","useEffect","search","keys","handler","searchResetTimer","key","target","moveDetails","searchResult","concat","timer","clearTimeout","setTimeout","resetWhenIdle","n","label","innerText","toLowerCase","indexOf","findItem","handleListKeyNav","addEventListener","removeEventListener"],"mappings":"yGAmDA,SAASA,EACLC,GACEC,MAAAA,EAAOC,KAAAA,EAAMC,aAAAA,IAEfF,EAAMG,iBA0DV,SACIJ,EACAE,EACAG,GAEA,MAAMC,EAAaD,EACnB,OAAQL,GACJ,IAAK,OAAQ,CACH,MAAAO,EACFD,GACCA,EAAWE,uBACZD,GACAA,EAAWE,QAEf,KAAA,CAEJ,IAAK,OAAQ,CACH,MAAAC,EACFJ,GACCA,EAAWK,mBACZD,GACAA,EAAWD,QAEf,KAAA,CAEJ,IAAK,QAAS,CACJ,MAAAG,EACFV,EAAKW,cAAiC,mBACtCD,GACAA,EAAUH,QAEd,KAAA,CAEJ,IAAK,OAAQ,CACH,MAAAK,EACFZ,EAAKa,iBAAoC,mBACzCD,EAAUE,QACVF,EAAUA,EAAUE,OAAS,GAAGP,QAEpC,KAAA,EAGZ,CAnGgBQ,CAAAjB,EAAWE,EAAMC,EACjC,2BA/BO,UACHe,IAAAA,IAEAC,EAAAA,WAAU,KAEA,MAAAC,EAAoB,CAAEC,KAAM,IAC5BnB,EAAOgB,EAAIb,QACXiB,EAAWrB,IACTC,GAyBhB,UACIA,KAAAA,EACAD,MAAAA,EACAmB,OAAAA,EACAG,iBAAAA,IAEM,MAAEC,IAAAA,EAAKC,OAAAA,GAAWxB,EAGlByB,EAAc,CAChBzB,MAAAA,EACAC,KAAAA,EACAC,aALiBsB,GAQrB,OAAQD,GACJ,IAAK,UACL,IAAK,SACDzB,EAAa,OAAQ2B,GACrB,MACJ,IAAK,YACL,IAAK,WACD3B,EAAa,OAAQ2B,GACrB,MACJ,IAAK,OACD3B,EAAa,QAAS2B,GACtB,MACJ,IAAK,MACD3B,EAAa,OAAQ2B,GACrB,MACJ,IAAK,MAEDzB,EAAMG,iBACN,MACJ,IAAK,QACL,IAAK,IACD,MAEJ,QACI,QAAe,IAAXgB,EAAsB,CACtB,MAAMO,EA0DtB,UACIzB,KAAAA,EACAsB,IAAAA,EACAJ,OAAAA,EACAG,iBAAAA,IAEM,MAAAT,EAAYZ,EAAKa,iBAAiB,mBACpC,IAACD,EAAUE,OAAe,OAAA,KAE9B,GAAII,EAAQ,CACRA,EAAOC,KAAOD,EAAOC,KAAKO,OAAOJ,GAczC,SAAuBJ,EAAmBS,GAClCA,IACAC,aAAaD,GACLA,OAAA,GAEJA,EAAAE,YACJ,KAEIX,EAAUA,EAAOC,KAAO,GAAOD,EAAS,CAAEC,KAAM,IACxCQ,OAAA,IAEZ,IACAT,EACAS,EAER,CA5BQG,CAAcZ,EAAQG,GAEtB,IAAA,IAASU,EAAI,EAAGA,EAAInB,EAAUE,OAAQiB,IAAK,CACjC,MAAAC,EAASpB,EAAUmB,GAAyBE,UAC9C,GAAAD,GAAsD,IAA7CA,EAAME,cAAcC,QAAQjB,EAAOC,MAC5C,OAAOP,EAAUmB,EACrB,CACJ,CAGG,OAAA,IACX,CAhFqCK,CAAS,CAC1BpC,KAAAA,EACAsB,IAAAA,EACAJ,OAAAA,EACAG,iBAAAA,IAEAI,GACAA,EAAalB,OACjB,EAIhB,CA5EgB8B,CAAiB,CAAErC,KAAAA,EAAMD,MAAAA,EAAOmB,OAAAA,EAAQG,iBAL5CA,WAK8D,EAIlE,OAAIrB,GACKA,EAAAsC,iBAAiB,UAAWlB,GAG9B,KACCpB,GACKA,EAAAuC,oBAAoB,UAAWnB,EAAO,CAEnD,GACD,CAACJ,GACR"}
@@ -1,7 +1,7 @@
1
1
  import { RefObject } from 'react';
2
2
  type UseListNavigationProps<T> = {
3
3
  /** Ref til et element med rollen `listbox` */
4
- ref: RefObject<T>;
4
+ ref: RefObject<T | null>;
5
5
  };
6
6
  export declare function useListNavigation<T extends HTMLElement>({ ref, }: UseListNavigationProps<T>): void;
7
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"useMutationObserver.cjs","sources":["../../../../src/hooks/useMutationObserver/useMutationObserver.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from \"react\";\n\nconst hasWindowWithMutationObserver = () =>\n typeof window !== \"undefined\" && typeof MutationObserver !== \"undefined\";\n\n/**\n * Hook som gjør det enklere å sjekke en komponents children for endringer.\n *\n * @param targetRef Container som skal observeres. Containeren må ikke rerendres, da vil observereren bli opprettet på nytt.\n * @param onObservation Callback-funksjon som utføres når en mutasjon observeres.\n * @param config Parametere til observereren. Se https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit#properties for mer info.\n */\nexport const useMutationObserver = (\n targetRef: RefObject<HTMLElement>,\n onObservation: MutationCallback,\n config?: Partial<MutationObserverInit>,\n): void => {\n const observerRef = useRef<MutationObserver>(null);\n\n useEffect(() => {\n let observer = observerRef.current;\n\n if (hasWindowWithMutationObserver()) {\n const target = targetRef.current;\n\n if (observer) {\n observer.disconnect();\n }\n\n observer = new MutationObserver(onObservation);\n\n if (target) {\n observer.observe(target, config);\n }\n }\n return () => {\n if (hasWindowWithMutationObserver() && observer) {\n observer.disconnect();\n }\n };\n }, [targetRef, onObservation, config]);\n};\n"],"names":["hasWindowWithMutationObserver","window","MutationObserver","targetRef","onObservation","config","observerRef","useRef","useEffect","observer","current","target","disconnect","observe"],"mappings":"yGAEMA,EAAgC,WAC3BC,OAAW,YAAsBC,iBAAqB,gCAS9B,CAC/BC,EACAC,EACAC,KAEM,MAAAC,EAAcC,SAAyB,MAE7CC,EAAAA,WAAU,KACN,IAAIC,EAAWH,EAAYI,QAE3B,GAAIV,IAAiC,CACjC,MAAMW,EAASR,EAAUO,QAErBD,GACAA,EAASG,aAGFH,EAAA,IAAIP,iBAAiBE,GAE5BO,GACSF,EAAAI,QAAQF,EAAQN,EAC7B,CAEJ,MAAO,KACCL,KAAmCS,GACnCA,EAASG,YAAW,CAE5B,GACD,CAACT,EAAWC,EAAeC,GAAO"}
1
+ {"version":3,"file":"useMutationObserver.cjs","sources":["../../../../src/hooks/useMutationObserver/useMutationObserver.ts"],"sourcesContent":["import { type RefObject, useEffect, useRef } from \"react\";\n\nconst hasWindowWithMutationObserver = () =>\n typeof window !== \"undefined\" && typeof MutationObserver !== \"undefined\";\n\n/**\n * Hook som gjør det enklere å sjekke en komponents children for endringer.\n *\n * @param targetRef Container som skal observeres. Containeren må ikke rerendres, da vil observereren bli opprettet på nytt.\n * @param onObservation Callback-funksjon som utføres når en mutasjon observeres.\n * @param config Parametere til observereren. Se https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit#properties for mer info.\n */\nexport const useMutationObserver = (\n targetRef: RefObject<HTMLElement | null>,\n onObservation: MutationCallback,\n config?: Partial<MutationObserverInit>,\n): void => {\n const observerRef = useRef<MutationObserver>(null);\n\n useEffect(() => {\n let observer = observerRef.current;\n\n if (hasWindowWithMutationObserver()) {\n const target = targetRef.current;\n\n if (observer) {\n observer.disconnect();\n }\n\n observer = new MutationObserver(onObservation);\n\n if (target) {\n observer.observe(target, config);\n }\n }\n return () => {\n if (hasWindowWithMutationObserver() && observer) {\n observer.disconnect();\n }\n };\n }, [targetRef, onObservation, config]);\n};\n"],"names":["hasWindowWithMutationObserver","window","MutationObserver","targetRef","onObservation","config","observerRef","useRef","useEffect","observer","current","target","disconnect","observe"],"mappings":"yGAEMA,EAAgC,WAC3BC,OAAW,YAAsBC,iBAAqB,gCAS9B,CAC/BC,EACAC,EACAC,KAEM,MAAAC,EAAcC,SAAyB,MAE7CC,EAAAA,WAAU,KACN,IAAIC,EAAWH,EAAYI,QAE3B,GAAIV,IAAiC,CACjC,MAAMW,EAASR,EAAUO,QAErBD,GACAA,EAASG,aAGFH,EAAA,IAAIP,iBAAiBE,GAE5BO,GACSF,EAAAI,QAAQF,EAAQN,EAC7B,CAEJ,MAAO,KACCL,KAAmCS,GACnCA,EAASG,YAAW,CAE5B,GACD,CAACT,EAAWC,EAAeC,GAAO"}
@@ -6,4 +6,4 @@ import { RefObject } from 'react';
6
6
  * @param onObservation Callback-funksjon som utføres når en mutasjon observeres.
7
7
  * @param config Parametere til observereren. Se https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit#properties for mer info.
8
8
  */
9
- export declare const useMutationObserver: (targetRef: RefObject<HTMLElement>, onObservation: MutationCallback, config?: Partial<MutationObserverInit>) => void;
9
+ export declare const useMutationObserver: (targetRef: RefObject<HTMLElement | null>, onObservation: MutationCallback, config?: Partial<MutationObserverInit>) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useScrollIntoView.cjs","sources":["../../../../src/hooks/useScrollIntoView/useScrollIntoView.tsx"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\ntype ValueOf<T> = T[keyof T];\n\nexport interface ScrollIntoViewProps {\n ref: RefObject<ValueOf<HTMLElementTagNameMap>>;\n timeout?: number;\n autoScroll?: boolean;\n options?: ScrollIntoViewOptions;\n}\n\ntype ScrollFunction = () => void;\n\nexport const useScrollIntoView = ({\n ref,\n timeout = 0,\n autoScroll = true,\n options = { behavior: \"smooth\" },\n}: ScrollIntoViewProps): [ScrollFunction] => {\n const scrollIntoView: ScrollFunction = () => {\n if (ref?.current) {\n ref.current.scrollIntoView(options);\n }\n };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies:\n useEffect(() => {\n if (!autoScroll) {\n return;\n }\n const scrollFn = setTimeout(scrollIntoView, timeout);\n return () => clearTimeout(scrollFn);\n }, [ref, timeout, autoScroll]);\n\n return [scrollIntoView];\n};\n"],"names":["ref","timeout","autoScroll","options","behavior","scrollIntoView","current","useEffect","scrollFn","setTimeout","clearTimeout"],"mappings":"mIAaiC,EAC7BA,IAAAA,EACAC,QAAAA,EAAU,EACVC,WAAAA,GAAa,EACbC,QAAAA,EAAU,CAAEC,SAAU,cAEtB,MAAMC,EAAiC,KAC/B,MAAAL,GAAAA,EAAKM,SACDN,EAAAM,QAAQD,eAAeF,EAAO,EAK1CI,OAAAA,EAAAA,WAAU,KACN,IAAKL,EACD,OAEE,MAAAM,EAAWC,WAAWJ,EAAgBJ,GACrC,MAAA,IAAMS,aAAaF,EAAQ,GACnC,CAACR,EAAKC,EAASC,IAEX,CAACG,EAAc"}
1
+ {"version":3,"file":"useScrollIntoView.cjs","sources":["../../../../src/hooks/useScrollIntoView/useScrollIntoView.tsx"],"sourcesContent":["import { type RefObject, useEffect } from \"react\";\n\ntype ValueOf<T> = T[keyof T];\n\nexport interface ScrollIntoViewProps {\n ref: RefObject<ValueOf<HTMLElementTagNameMap> | null> | null;\n timeout?: number;\n autoScroll?: boolean;\n options?: ScrollIntoViewOptions;\n}\n\ntype ScrollFunction = () => void;\n\nexport const useScrollIntoView = ({\n ref,\n timeout = 0,\n autoScroll = true,\n options = { behavior: \"smooth\" },\n}: ScrollIntoViewProps): [ScrollFunction] => {\n const scrollIntoView: ScrollFunction = () => {\n if (ref?.current) {\n ref.current.scrollIntoView(options);\n }\n };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies:\n useEffect(() => {\n if (!autoScroll) {\n return;\n }\n const scrollFn = setTimeout(scrollIntoView, timeout);\n return () => clearTimeout(scrollFn);\n }, [ref, timeout, autoScroll]);\n\n return [scrollIntoView];\n};\n"],"names":["ref","timeout","autoScroll","options","behavior","scrollIntoView","current","useEffect","scrollFn","setTimeout","clearTimeout"],"mappings":"mIAaiC,EAC7BA,IAAAA,EACAC,QAAAA,EAAU,EACVC,WAAAA,GAAa,EACbC,QAAAA,EAAU,CAAEC,SAAU,cAEtB,MAAMC,EAAiC,KAC/B,MAAAL,GAAAA,EAAKM,SACDN,EAAAM,QAAQD,eAAeF,EAAO,EAK1CI,OAAAA,EAAAA,WAAU,KACN,IAAKL,EACD,OAEE,MAAAM,EAAWC,WAAWJ,EAAgBJ,GACrC,MAAA,IAAMS,aAAaF,EAAQ,GACnC,CAACR,EAAKC,EAASC,IAEX,CAACG,EAAc"}
@@ -1,7 +1,7 @@
1
1
  import { RefObject } from 'react';
2
2
  type ValueOf<T> = T[keyof T];
3
3
  export interface ScrollIntoViewProps {
4
- ref: RefObject<ValueOf<HTMLElementTagNameMap>>;
4
+ ref: RefObject<ValueOf<HTMLElementTagNameMap> | null> | null;
5
5
  timeout?: number;
6
6
  autoScroll?: boolean;
7
7
  options?: ScrollIntoViewOptions;
@@ -19,7 +19,7 @@ export type ExpanderComponent = <ElementType extends React.ElementType = "button
19
19
  export type ExpandableContext = {
20
20
  open: boolean;
21
21
  onToggle: () => void;
22
- onTransitionStart: (isOpening: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement>) => void;
23
- onTransitionEnd: (isOpen: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement>) => void;
22
+ onTransitionStart: (isOpening: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement | null>) => void;
23
+ onTransitionEnd: (isOpen: boolean, ref: RefObject<HTMLElement | HTMLDetailsElement | null>) => void;
24
24
  setExpanderHeight: (height: number) => void;
25
25
  };