UNPKG

@helpwave/hightide

Version:

helpwave's component and theming library

1,468 lines (1,435 loc) 48.4 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/components/user-action/MultiSelect.tsx var MultiSelect_exports = {}; __export(MultiSelect_exports, { MultiSelect: () => MultiSelect, MultiSelectUncontrolled: () => MultiSelectUncontrolled }); module.exports = __toCommonJS(MultiSelect_exports); var import_react14 = require("react"); var import_react15 = require("react"); // src/localization/LanguageProvider.tsx var import_react2 = require("react"); // src/hooks/useLocalStorage.ts var import_react = require("react"); // src/localization/util.ts var languages = ["en", "de"]; var languagesLocalNames = { en: "English", de: "Deutsch" }; var DEFAULT_LANGUAGE = "en"; var LanguageUtil = { languages, DEFAULT_LANGUAGE, languagesLocalNames }; // src/localization/LanguageProvider.tsx var import_jsx_runtime = require("react/jsx-runtime"); var LanguageContext = (0, import_react2.createContext)({ language: LanguageUtil.DEFAULT_LANGUAGE, setLanguage: (v) => v }); var useLanguage = () => (0, import_react2.useContext)(LanguageContext); // src/localization/useTranslation.ts var TranslationPluralCount = { zero: 0, one: 1, two: 2, few: 3, many: 11, other: -1 }; var useTranslation = (translations, overwriteTranslation = {}) => { const { language: languageProp, translation: overwrite } = overwriteTranslation; const { language: inferredLanguage } = useLanguage(); const usedLanguage = languageProp ?? inferredLanguage; const usedTranslations = [...translations]; if (overwrite) { usedTranslations.push(overwrite); } return (key, options) => { const { count, replacements } = { ...{ count: 0, replacements: {} }, ...options }; try { for (let i = translations.length - 1; i >= 0; i--) { const translation = translations[i]; const localizedTranslation = translation[usedLanguage]; if (!localizedTranslation) { continue; } const value = localizedTranslation[key]; if (!value) { continue; } let forProcessing; if (typeof value !== "string") { if (count === TranslationPluralCount.zero && value?.zero) { forProcessing = value.zero; } else if (count === TranslationPluralCount.one && value?.one) { forProcessing = value.one; } else if (count === TranslationPluralCount.two && value?.two) { forProcessing = value.two; } else if (TranslationPluralCount.few <= count && count < TranslationPluralCount.many && value?.few) { forProcessing = value.few; } else if (count > TranslationPluralCount.many && value?.many) { forProcessing = value.many; } else { forProcessing = value.other; } } else { forProcessing = value; } forProcessing = forProcessing.replace(/\{\{(\w+)}}/g, (_, placeholder) => { return replacements[placeholder] ?? `{{key:${placeholder}}}`; }); return forProcessing; } } catch (e) { console.error(e); } return `{{${usedLanguage}:${key}}}`; }; }; // src/components/user-action/MultiSelect.tsx var import_clsx10 = __toESM(require("clsx")); // src/components/user-action/Label.tsx var import_clsx = __toESM(require("clsx")); var import_jsx_runtime2 = require("react/jsx-runtime"); var styleMapping = { labelSmall: "textstyle-label-sm", labelMedium: "textstyle-label-md", labelBig: "textstyle-label-lg" }; var Label = ({ children, name, labelType = "labelSmall", className, ...props }) => { return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { ...props, className: (0, import_clsx.default)(styleMapping[labelType], className), children: children ? children : name }); }; // src/components/user-action/Button.tsx var import_react3 = require("react"); var import_clsx2 = __toESM(require("clsx")); var import_jsx_runtime3 = require("react/jsx-runtime"); var ButtonColorUtil = { solid: ["primary", "secondary", "tertiary", "positive", "warning", "negative", "neutral"], text: ["primary", "negative", "neutral"], outline: ["primary"] }; var IconButtonUtil = { icon: [...ButtonColorUtil.solid, "transparent"] }; var paddingMapping = { small: "btn-sm", medium: "btn-md", large: "btn-lg" }; var iconPaddingMapping = { tiny: "icon-btn-xs", small: "icon-btn-sm", medium: "icon-btn-md", large: "icon-btn-lg" }; var ButtonUtil = { paddingMapping, iconPaddingMapping }; var SolidButton = (0, import_react3.forwardRef)(function SolidButton2({ children, color = "primary", size = "medium", startIcon, endIcon, onClick, className, ...restProps }, ref) { const colorClasses = { primary: "not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text", secondary: "not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text", tertiary: "not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text", positive: "not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text", warning: "not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text", negative: "not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text", neutral: "not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text" }[color]; const iconColorClasses = { primary: "not-group-disabled:text-button-solid-primary-icon", secondary: "not-group-disabled:text-button-solid-secondary-icon", tertiary: "not-group-disabled:text-button-solid-tertiary-icon", positive: "not-group-disabled:text-button-solid-positive-icon", warning: "not-group-disabled:text-button-solid-warning-icon", negative: "not-group-disabled:text-button-solid-negative-icon", neutral: "not-group-disabled:text-button-solid-neutral-icon" }[color]; return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( "button", { ref, onClick, className: (0, import_clsx2.default)( "group font-semibold", colorClasses, "not-disabled:hover:brightness-90", "disabled:text-disabled-text disabled:bg-disabled-background", ButtonUtil.paddingMapping[size], className ), ...restProps, children: [ startIcon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: (0, import_clsx2.default)( iconColorClasses, "group-disabled:text-disabled-icon" ), children: startIcon } ), children, endIcon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: (0, import_clsx2.default)( iconColorClasses, "group-disabled:text-disabled-icon" ), children: endIcon } ) ] } ); }); var IconButton = ({ children, color = "primary", size = "medium", className, ...restProps }) => { const colorClasses = { primary: "not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text", secondary: "not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text", tertiary: "not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text", positive: "not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text", warning: "not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text", negative: "not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text", neutral: "not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text", transparent: "not-disabled:bg-transparent" }[color]; return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { className: (0, import_clsx2.default)( colorClasses, "not-disabled:hover:brightness-90", "disabled:text-disabled-text", { "disabled:bg-disabled-background": color !== "transparent", "disabled:opacity-70": color === "transparent", "not-disabled:hover:bg-button-text-hover-background": color === "transparent" }, ButtonUtil.iconPaddingMapping[size], className ), ...restProps, children } ); }; // src/components/layout-and-navigation/Chip.tsx var import_clsx3 = __toESM(require("clsx")); var import_jsx_runtime4 = require("react/jsx-runtime"); var Chip = ({ children, trailingIcon, color = "default", variant = "normal", className = "", ...restProps }) => { const colorMapping = { default: "text-tag-default-text bg-tag-default-background", dark: "text-tag-dark-text bg-tag-dark-background", red: "text-tag-red-text bg-tag-red-background", yellow: "text-tag-yellow-text bg-tag-yellow-background", green: "text-tag-green-text bg-tag-green-background", blue: "text-tag-blue-text bg-tag-blue-background", pink: "text-tag-pink-text bg-tag-pink-background" }[color]; const colorMappingIcon = { default: "text-tag-default-icon", dark: "text-tag-dark-icon", red: "text-tag-red-icon", yellow: "text-tag-yellow-icon", green: "text-tag-green-icon", blue: "text-tag-blue-icon", pink: "text-tag-pink-icon" }[color]; return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( "div", { ...restProps, className: (0, import_clsx3.default)( `row w-fit px-2 py-1 font-semibold`, colorMapping, { "rounded-md": variant === "normal", "rounded-full": variant === "fullyRounded" }, className ), children: [ children, trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: colorMappingIcon, children: trailingIcon }) ] } ); }; var ChipList = ({ list, className = "" }) => { return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: (0, import_clsx3.default)("flex flex-wrap gap-x-2 gap-y-2", className), children: list.map((value, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( Chip, { ...value, color: value.color ?? "default", variant: value.variant ?? "normal", children: value.children }, index )) }); }; // src/localization/defaults/form.ts var formTranslation = { en: { add: "Add", all: "All", apply: "Apply", back: "Back", cancel: "Cancel", change: "Change", clear: "Clear", click: "Click", clickToCopy: "Click to Copy", close: "Close", confirm: "Confirm", copy: "Copy", copied: "Copied", create: "Create", decline: "Decline", delete: "Delete", discard: "Discard", discardChanges: "Discard Changes", done: "Done", edit: "Edit", enterText: "Enter text here", error: "Error", exit: "Exit", fieldRequiredError: "This field is required.", invalidEmailError: "Please enter a valid email address.", less: "Less", loading: "Loading", maxLengthError: "Maximum length exceeded.", minLengthError: "Minimum length not met.", more: "More", next: "Next", no: "No", none: "None", of: "of", optional: "Optional", pleaseWait: "Please wait...", previous: "Previous", remove: "Remove", required: "Required", reset: "Reset", save: "Save", saved: "Saved", search: "Search", select: "Select", selectOption: "Select an option", show: "Show", showMore: "Show more", showLess: "Show less", submit: "Submit", success: "Success", update: "Update", unsavedChanges: "Unsaved Changes", unsavedChangesSaveQuestion: "Do you want to save your changes?", yes: "Yes" }, de: { add: "Hinzuf\xFCgen", all: "Alle", apply: "Anwenden", back: "Zur\xFCck", cancel: "Abbrechen", change: "\xC4ndern", clear: "L\xF6schen", click: "Klicken", clickToCopy: "Zum kopieren klicken", close: "Schlie\xDFen", confirm: "Best\xE4tigen", copy: "Kopieren", copied: "Kopiert", create: "Erstellen", decline: "Ablehnen", delete: "L\xF6schen", discard: "Verwerfen", discardChanges: "\xC4nderungen Verwerfen", done: "Fertig", edit: "Bearbeiten", enterText: "Text hier eingeben", error: "Fehler", exit: "Beenden", fieldRequiredError: "Dieses Feld ist erforderlich.", invalidEmailError: "Bitte geben Sie eine g\xFCltige E-Mail-Adresse ein.", less: "Weniger", loading: "L\xE4dt", maxLengthError: "Maximale L\xE4nge \xFCberschritten.", minLengthError: "Mindestl\xE4nge nicht erreicht.", more: "Mehr", next: "Weiter", no: "Nein", none: "Nichts", of: "von", optional: "Optional", pleaseWait: "Bitte warten...", previous: "Vorherige", remove: "Entfernen", required: "Erforderlich", reset: "Zur\xFCcksetzen", save: "Speichern", saved: "Gespeichert", search: "Suche", select: "Select", selectOption: "Option ausw\xE4hlen", show: "Anzeigen", showMore: "Mehr anzeigen", showLess: "Weniger anzeigen", submit: "Abschicken", success: "Erfolg", update: "Update", unsavedChanges: "Ungespeicherte \xC4nderungen", unsavedChangesSaveQuestion: "M\xF6chtest du die \xC4nderungen speichern?", yes: "Ja" } }; // src/components/user-action/Menu.tsx var import_react6 = require("react"); var import_clsx4 = __toESM(require("clsx")); // src/hooks/useOutsideClick.ts var import_react4 = require("react"); var useOutsideClick = (refs, handler) => { (0, import_react4.useEffect)(() => { const listener = (event) => { if (event.target === null) return; if (refs.some((ref) => !ref.current || ref.current.contains(event.target))) { return; } handler(); }; document.addEventListener("mousedown", listener); document.addEventListener("touchstart", listener); return () => { document.removeEventListener("mousedown", listener); document.removeEventListener("touchstart", listener); }; }, [refs, handler]); }; // src/hooks/useHoverState.ts var import_react5 = require("react"); var defaultUseHoverStateProps = { closingDelay: 200, isDisabled: false }; var useHoverState = (props = void 0) => { const { closingDelay, isDisabled } = { ...defaultUseHoverStateProps, ...props }; const [isHovered, setIsHovered] = (0, import_react5.useState)(false); const [timer, setTimer] = (0, import_react5.useState)(); const onMouseEnter = () => { if (isDisabled) { return; } clearTimeout(timer); setIsHovered(true); }; const onMouseLeave = () => { if (isDisabled) { return; } setTimer(setTimeout(() => { setIsHovered(false); }, closingDelay)); }; (0, import_react5.useEffect)(() => { if (timer) { return () => { clearTimeout(timer); }; } }); (0, import_react5.useEffect)(() => { if (timer) { clearTimeout(timer); } }, [isDisabled]); return { isHovered, setIsHovered, handlers: { onMouseEnter, onMouseLeave } }; }; // src/util/PropsWithFunctionChildren.ts var resolve = (children, bag) => { if (typeof children === "function") { return children(bag); } return children ?? void 0; }; var BagFunctionUtil = { resolve }; // src/hooks/usePopoverPosition.ts var defaultPopoverPositionOptions = { edgePadding: 16, outerGap: 4, horizontalAlignment: "leftInside", verticalAlignment: "bottomOutside", disabled: false }; var usePopoverPosition = (trigger, options) => { const { edgePadding, outerGap, verticalAlignment, horizontalAlignment, disabled } = { ...defaultPopoverPositionOptions, ...options }; if (disabled || !trigger) { return {}; } const left = { leftOutside: trigger.left - outerGap, leftInside: trigger.left, rightOutside: trigger.right + outerGap, rightInside: trigger.right, center: trigger.left + trigger.width / 2 }[horizontalAlignment]; const top = { topOutside: trigger.top - outerGap, topInside: trigger.top, bottomOutside: trigger.bottom + outerGap, bottomInside: trigger.bottom, center: trigger.top + trigger.height / 2 }[verticalAlignment]; const translateX = { leftOutside: "-100%", leftInside: void 0, rightOutside: void 0, rightInside: "-100%", center: "-50%" }[horizontalAlignment]; const translateY = { topOutside: "-100%", topInside: void 0, bottomOutside: void 0, bottomInside: "-100%", center: "-50%" }[verticalAlignment]; return { left: Math.max(left, edgePadding), top: Math.max(top, edgePadding), translate: [translateX ?? "0", translateY ?? "0"].join(" ") }; }; // src/components/user-action/Menu.tsx var import_react_dom = require("react-dom"); var import_jsx_runtime5 = require("react/jsx-runtime"); function getScrollableParents(element) { const scrollables = []; let parent = element.parentElement; while (parent) { scrollables.push(parent); parent = parent.parentElement; } return scrollables; } var Menu = ({ trigger, children, alignmentHorizontal = "leftInside", alignmentVertical = "bottomOutside", showOnHover = false, disabled = false, menuClassName = "" }) => { const { isHovered: isOpen, setIsHovered: setIsOpen } = useHoverState({ isDisabled: !showOnHover || disabled }); const triggerRef = (0, import_react6.useRef)(null); const menuRef = (0, import_react6.useRef)(null); useOutsideClick([triggerRef, menuRef], () => setIsOpen(false)); const [isHidden, setIsHidden] = (0, import_react6.useState)(true); const bag = { isOpen, close: () => setIsOpen(false), toggleOpen: () => setIsOpen((prevState) => !prevState), disabled }; const menuPosition = usePopoverPosition( triggerRef.current?.getBoundingClientRect(), { verticalAlignment: alignmentVertical, horizontalAlignment: alignmentHorizontal, disabled } ); (0, import_react6.useEffect)(() => { if (!isOpen) return; const triggerEl = triggerRef.current; if (!triggerEl) return; const scrollableParents = getScrollableParents(triggerEl); const close = () => setIsOpen(false); scrollableParents.forEach((parent) => { parent.addEventListener("scroll", close); }); window.addEventListener("resize", close); return () => { scrollableParents.forEach((parent) => { parent.removeEventListener("scroll", close); }); window.removeEventListener("resize", close); }; }, [isOpen, setIsOpen]); (0, import_react6.useEffect)(() => { if (isOpen) { setIsHidden(false); } }, [isOpen]); return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [ trigger(bag, triggerRef), (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)( "div", { ref: menuRef, onClick: (e) => e.stopPropagation(), className: (0, import_clsx4.default)( "absolute rounded-md bg-menu-background text-menu-text shadow-around-lg shadow-strong z-[300]", { "animate-pop-in": isOpen, "animate-pop-out": !isOpen, "hidden": isHidden }, menuClassName ), onAnimationEnd: () => { if (!isOpen) { setIsHidden(true); } }, style: { ...menuPosition }, children: BagFunctionUtil.resolve(children, bag) } ), document.body) ] }); }; // src/components/layout-and-navigation/Expandable.tsx var import_react7 = require("react"); var import_lucide_react = require("lucide-react"); var import_clsx5 = __toESM(require("clsx")); // src/util/noop.ts var noop = () => void 0; // src/components/layout-and-navigation/Expandable.tsx var import_jsx_runtime6 = require("react/jsx-runtime"); var ExpansionIcon = ({ isExpanded, className }) => { return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( import_lucide_react.ChevronDown, { className: (0, import_clsx5.default)( "min-w-6 w-6 min-h-6 h-6 transition-transform duration-200 ease-in-out", { "rotate-180": isExpanded }, className ) } ); }; var Expandable = (0, import_react7.forwardRef)(function Expandable2({ children, label, icon, isExpanded = false, onChange = noop, clickOnlyOnHeader = true, disabled = false, className, headerClassName, contentClassName, contentExpandedClassName }, ref) { const defaultIcon = (0, import_react7.useCallback)((expanded) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpansionIcon, { isExpanded: expanded }), []); icon ??= defaultIcon; return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)( "div", { ref, className: (0, import_clsx5.default)("flex-col-0 bg-surface text-on-surface group rounded-lg shadow-sm", { "cursor-pointer": !clickOnlyOnHeader && !disabled }, className), onClick: () => !clickOnlyOnHeader && !disabled && onChange(!isExpanded), children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)( "div", { className: (0, import_clsx5.default)( "flex-row-2 py-2 px-4 rounded-lg justify-between items-center bg-surface text-on-surface select-none", { "group-hover:brightness-97": !isExpanded, "hover:brightness-97": isExpanded && !disabled, "cursor-pointer": clickOnlyOnHeader && !disabled }, headerClassName ), onClick: () => clickOnlyOnHeader && !disabled && onChange(!isExpanded), children: [ label, icon(isExpanded) ] } ), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "div", { className: (0, import_clsx5.default)( "flex-col-2 px-4 transition-all duration-300 ease-in-out", { [(0, import_clsx5.default)("max-h-96 opacity-100 pb-2 overflow-y-auto", contentExpandedClassName)]: isExpanded, "max-h-0 opacity-0 overflow-hidden": !isExpanded }, contentClassName ), children } ) ] } ); }); var ExpandableUncontrolled = (0, import_react7.forwardRef)(function ExpandableUncontrolled2({ isExpanded, onChange = noop, ...props }, ref) { const [usedIsExpanded, setUsedIsExpanded] = (0, import_react7.useState)(isExpanded); (0, import_react7.useEffect)(() => { setUsedIsExpanded(isExpanded); }, [isExpanded]); return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( Expandable, { ...props, ref, isExpanded: usedIsExpanded, onChange: (value) => { onChange(value); setUsedIsExpanded(value); } } ); }); // src/components/user-action/Input.tsx var import_react11 = require("react"); var import_clsx6 = __toESM(require("clsx")); // src/hooks/useDelay.ts var import_react8 = require("react"); var defaultOptions = { delay: 3e3, disabled: false }; function useDelay(options) { const [timer, setTimer] = (0, import_react8.useState)(void 0); const { delay, disabled } = { ...defaultOptions, ...options }; const clearTimer = () => { clearTimeout(timer); setTimer(void 0); }; const restartTimer = (onDelayFinish) => { if (disabled) { return; } clearTimeout(timer); setTimer(setTimeout(() => { onDelayFinish(); setTimer(void 0); }, delay)); }; (0, import_react8.useEffect)(() => { return () => { clearTimeout(timer); }; }, [timer]); (0, import_react8.useEffect)(() => { if (disabled) { clearTimeout(timer); setTimer(void 0); } }, [disabled, timer]); return { restartTimer, clearTimer, hasActiveTimer: !!timer }; } // src/hooks/useFocusManagement.ts var import_react9 = require("react"); function useFocusManagement() { const getFocusableElements = (0, import_react9.useCallback)(() => { return Array.from( document.querySelectorAll( 'input, button, select, textarea, a[href], [tabindex]:not([tabindex="-1"])' ) ).filter( (el) => el instanceof HTMLElement && !el.hasAttribute("disabled") && !el.hasAttribute("hidden") && el.tabIndex !== -1 ); }, []); const getNextFocusElement = (0, import_react9.useCallback)(() => { const elements = getFocusableElements(); if (elements.length === 0) { return void 0; } let nextElement = elements[0]; if (document.activeElement instanceof HTMLElement) { const currentIndex = elements.indexOf(document.activeElement); nextElement = elements[(currentIndex + 1) % elements.length]; } return nextElement; }, [getFocusableElements]); const focusNext = (0, import_react9.useCallback)(() => { const nextElement = getNextFocusElement(); nextElement?.focus(); }, [getNextFocusElement]); const getPreviousFocusElement = (0, import_react9.useCallback)(() => { const elements = getFocusableElements(); if (elements.length === 0) { return void 0; } let previousElement = elements[0]; if (document.activeElement instanceof HTMLElement) { const currentIndex = elements.indexOf(document.activeElement); if (currentIndex === 0) { previousElement = elements[elements.length - 1]; } else { previousElement = elements[currentIndex - 1]; } } return previousElement; }, [getFocusableElements]); const focusPrevious = (0, import_react9.useCallback)(() => { const previousElement = getPreviousFocusElement(); if (previousElement) previousElement.focus(); }, [getPreviousFocusElement]); return { getFocusableElements, getNextFocusElement, getPreviousFocusElement, focusNext, focusPrevious }; } // src/hooks/useFocusOnceVisible.ts var import_react10 = __toESM(require("react")); var useFocusOnceVisible = (ref, disable = false) => { const [hasUsedFocus, setHasUsedFocus] = import_react10.default.useState(false); (0, import_react10.useEffect)(() => { if (disable || hasUsedFocus) { return; } const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting && !hasUsedFocus) { ref.current?.focus(); setHasUsedFocus(hasUsedFocus); } }, { threshold: 0.1 }); if (ref.current) { observer.observe(ref.current); } return () => observer.disconnect(); }, [disable, hasUsedFocus, ref]); }; // src/components/user-action/Input.tsx var import_jsx_runtime7 = require("react/jsx-runtime"); var getInputClassName = ({ disabled = false, hasError = false }) => { return (0, import_clsx6.default)( "px-2 py-1.5 rounded-md border-2", { "bg-input-background text-input-text hover:border-primary focus:border-primary": !disabled && !hasError, "bg-on-negative text-negative border-negative-border hover:border-negative-border-hover": !disabled && hasError, "bg-disabled-background text-disabled-text border-disabled-border": disabled } ); }; var defaultEditCompleteOptions = { onBlur: true, afterDelay: true, delay: 2500 }; var Input = (0, import_react11.forwardRef)(function Input2({ id, type = "text", value, label, onChange = noop, onChangeText = noop, onEditCompleted, className = "", allowEnterComplete = true, expanded = true, autoFocus = false, onBlur, editCompleteOptions, containerClassName, disabled, ...restProps }, forwardedRef) { const { onBlur: allowEditCompleteOnBlur, afterDelay, delay } = { ...defaultEditCompleteOptions, ...editCompleteOptions }; const { restartTimer, clearTimer } = useDelay({ delay, disabled: !afterDelay }); const innerRef = (0, import_react11.useRef)(null); const { focusNext } = useFocusManagement(); useFocusOnceVisible(innerRef, !autoFocus); (0, import_react11.useImperativeHandle)(forwardedRef, () => innerRef.current); const handleKeyDown = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); innerRef.current?.blur(); focusNext(); } }; return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: (0, import_clsx6.default)({ "w-full": expanded }, containerClassName), children: [ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Label, { ...label, htmlFor: id, className: (0, import_clsx6.default)("mb-1", label.className) }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "input", { ...restProps, ref: innerRef, value, id, type, disabled, className: (0, import_clsx6.default)(getInputClassName({ disabled }), className), onKeyDown: allowEnterComplete ? handleKeyDown : void 0, onBlur: (event) => { onBlur?.(event); if (onEditCompleted && allowEditCompleteOnBlur) { onEditCompleted(event.target.value); clearTimer(); } }, onChange: (e) => { const value2 = e.target.value; if (onEditCompleted) { restartTimer(() => { if (innerRef.current) { innerRef.current.blur(); if (!allowEditCompleteOnBlur) { onEditCompleted(value2); } } else { onEditCompleted(value2); } }); } onChange(e); onChangeText(value2); } } ) ] }); }); var FormInput = (0, import_react11.forwardRef)(function FormInput2({ id, labelText, errorText, className, labelClassName, errorClassName, containerClassName, required, disabled, ...restProps }, ref) { const input = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "input", { ...restProps, ref, id, disabled, className: (0, import_clsx6.default)( getInputClassName({ disabled, hasError: !!errorText }), className ) } ); return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: (0, import_clsx6.default)("flex flex-col gap-y-1", containerClassName), children: [ labelText && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { htmlFor: id, className: (0, import_clsx6.default)("textstyle-label-md", labelClassName), children: [ labelText, required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-primary font-bold", children: "*" }) ] }), input, errorText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: id, className: (0, import_clsx6.default)("text-negative", errorClassName), children: errorText }) ] }); }); // src/components/user-action/SearchBar.tsx var import_lucide_react2 = require("lucide-react"); var import_clsx7 = require("clsx"); var import_jsx_runtime8 = require("react/jsx-runtime"); var SearchBar = ({ placeholder, onSearch, disableOnSearch, containerClassName, ...inputProps }) => { const translation = useTranslation([formTranslation]); return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: (0, import_clsx7.clsx)("flex-row-2 justify-between items-center", containerClassName), children: [ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)( Input, { ...inputProps, placeholder: placeholder ?? translation("search") } ), onSearch && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(IconButton, { color: "neutral", disabled: disableOnSearch, onClick: onSearch, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react2.Search, { className: "w-full h-full" }) }) ] }); }; // src/hooks/useSearch.ts var import_react12 = require("react"); // src/util/simpleSearch.ts var MultiSubjectSearchWithMapping = (search, objects, mapping) => { return objects.filter((object) => { const mappedSearchKeywords = mapping(object)?.map((value) => value.toLowerCase().trim()); if (!mappedSearchKeywords) { return true; } return search.every((searchValue) => !!mappedSearchKeywords.find((value) => !!value && value.includes(searchValue.toLowerCase().trim()))); }); }; // src/hooks/useSearch.ts var useSearch = ({ list, initialSearch, searchMapping, additionalSearchTags, isSearchInstant = true, sortingFunction, filter, disabled = false }) => { const [search, setSearch] = (0, import_react12.useState)(initialSearch ?? ""); const [result, setResult] = (0, import_react12.useState)(list); const searchTags = (0, import_react12.useMemo)(() => additionalSearchTags ?? [], [additionalSearchTags]); const updateSearch = (0, import_react12.useCallback)((newSearch) => { const usedSearch = newSearch ?? search; if (newSearch) { setSearch(search); } setResult(MultiSubjectSearchWithMapping([usedSearch, ...searchTags], list, searchMapping)); }, [searchTags, list, search, searchMapping]); (0, import_react12.useEffect)(() => { if (isSearchInstant) { setResult(MultiSubjectSearchWithMapping([search, ...searchTags], list, searchMapping)); } }, [searchTags, isSearchInstant, list, search, searchMapping, additionalSearchTags]); const filteredResult = (0, import_react12.useMemo)(() => { if (!filter) { return result; } return result.filter(filter); }, [result, filter]); const sortedAndFilteredResult = (0, import_react12.useMemo)(() => { if (!sortingFunction) { return filteredResult; } return filteredResult.sort(sortingFunction); }, [filteredResult, sortingFunction]); const usedResult = (0, import_react12.useMemo)(() => { if (!disabled) { return sortedAndFilteredResult; } return list; }, [disabled, list, sortedAndFilteredResult]); return { result: usedResult, hasResult: usedResult.length > 0, allItems: list, updateSearch, search, setSearch }; }; // src/components/user-action/Checkbox.tsx var import_react13 = require("react"); var CheckboxPrimitive = __toESM(require("@radix-ui/react-checkbox")); var import_lucide_react3 = require("lucide-react"); var import_clsx8 = __toESM(require("clsx")); var import_jsx_runtime9 = require("react/jsx-runtime"); var checkboxSizeMapping = { small: "size-5", medium: "size-6", large: "size-8" }; var checkboxIconSizeMapping = { small: "size-4", medium: "size-5", large: "size-7" }; var Checkbox = ({ id, label, checked, disabled, onChange, onChangeTristate, size = "medium", className = "", containerClassName }) => { const usedSizeClass = checkboxSizeMapping[size]; const innerIconSize = checkboxIconSizeMapping[size]; const propagateChange = (checked2) => { if (onChangeTristate) { onChangeTristate(checked2); } if (onChange) { onChange(checked2 === "indeterminate" ? false : checked2); } }; const changeValue = () => { if (disabled) { return; } const newValue = checked === "indeterminate" ? false : !checked; propagateChange(newValue); }; return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)( "div", { className: (0, import_clsx8.default)("group flex-row-2 items-center", { "cursor-pointer": !disabled, "cursor-not-allowed": disabled }, containerClassName), onClick: changeValue, children: [ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)( CheckboxPrimitive.Root, { onCheckedChange: propagateChange, checked, disabled, id, className: (0, import_clsx8.default)(usedSizeClass, `items-center border-2 rounded outline-none `, { "text-disabled-text border-disabled-outline bg-disabled-background cursor-not-allowed": disabled, "focus:border-primary group-hover:border-primary ": !disabled, "bg-input-background": !disabled && !checked, "bg-primary/30 border-primary text-primary": !disabled && checked === true || checked === "indeterminate" }, className), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(CheckboxPrimitive.Indicator, { children: [ checked === true && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.Check, { className: innerIconSize }), checked === "indeterminate" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.Minus, { className: innerIconSize }) ] }) } ), label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)( Label, { ...label, className: (0, import_clsx8.default)( label.className, { "cursor-pointer": !disabled, "cursor-not-allowed": disabled } ), htmlFor: id } ) ] } ); }; // src/components/user-action/MultiSelect.tsx var import_lucide_react5 = require("lucide-react"); // src/components/layout-and-navigation/Tile.tsx var import_clsx9 = __toESM(require("clsx")); var import_lucide_react4 = require("lucide-react"); var import_jsx_runtime10 = require("react/jsx-runtime"); var Tile = ({ title, titleClassName, description, descriptionClassName, onClick, isSelected = false, disabled = false, prefix, suffix, normalClassName = "hover:bg-primary/40 cursor-pointer", selectedClassName = "bg-primary/20", disabledClassName = "text-disabled-text bg-disabled-background cursor-not-allowed", className }) => { return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)( "div", { className: (0, import_clsx9.default)( "flex-row-2 w-full items-center", { [normalClassName]: onClick && !disabled, [selectedClassName]: isSelected && !disabled, [disabledClassName]: disabled }, className ), onClick: disabled ? void 0 : onClick, children: [ prefix, /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex-col-0 w-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: (0, import_clsx9.default)(titleClassName ?? "textstyle-title-normal"), children: title }), !!description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: (0, import_clsx9.default)(descriptionClassName ?? "textstyle-description"), children: description }) ] }), suffix ?? (isSelected ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.Check, { size: 24 }) : void 0) ] } ); }; var ListTile = ({ ...props }) => { return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)( Tile, { ...props, titleClassName: props.titleClassName ?? "font-semibold", className: (0, import_clsx9.default)("px-2 py-1 rounded-md", props.className), disabledClassName: props.disabledClassName ?? "text-disabled-text cursor-not-allowed" } ); }; // src/components/user-action/MultiSelect.tsx var import_jsx_runtime11 = require("react/jsx-runtime"); var defaultMultiSelectTranslation = { en: { selected: `{{amount}} selected` }, de: { selected: `{{amount}} ausgew\xE4hlt` } }; var MultiSelect = ({ overwriteTranslation, label, options, onChange, hintText, selectedDisplayOverwrite, searchOptions, additionalItems, useChipDisplay = false, className, triggerClassName, hintTextClassName, ...menuProps }) => { const translation = useTranslation([formTranslation, defaultMultiSelectTranslation], overwriteTranslation); const { result, search, setSearch } = useSearch({ list: options, searchMapping: (0, import_react14.useCallback)((item) => item.searchTags, []), ...searchOptions }); const selectedItems = options.filter((value) => value.selected); const isShowingHint = !selectedDisplayOverwrite && selectedItems.length === 0; return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: (0, import_clsx10.default)(className), children: [ label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( Label, { ...label, htmlFor: label.name, className: (0, import_clsx10.default)(" mb-1", label.className), labelType: label.labelType ?? "labelSmall" } ), /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( Menu, { ...menuProps, trigger: ({ toggleOpen, isOpen, disabled }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( "button", { ref, className: (0, import_clsx10.default)( "group btn-md justify-between w-full border-2 h-auto", "not-disabled:bg-input-background not-disabled:text-input-text not-disabled:hover:border-primary", "disabled:bg-disabled-background disabled:text-disabled-text disabled:border-disabled-border", { "min-h-14": useChipDisplay }, triggerClassName ), onClick: toggleOpen, disabled, children: useChipDisplay ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: isShowingHint ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( "div", { className: (0, import_clsx10.default)( "icon-btn-sm ", { "bg-button-solid-neutral-background text-button-solid-neutral-text hover:brightness-90 group-hover:brightness-90": !disabled, "bg-disabled-background text-disabled-text": disabled } ), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react5.Plus, {}) } ) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChipList, { list: selectedItems.map((value) => ({ children: value.label })) }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [ !isShowingHint && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "font-semibold", children: selectedDisplayOverwrite ?? translation("selected", { replacements: { amount: selectedItems.length.toString() } }) }), isShowingHint && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: (0, import_clsx10.default)("textstyle-description", hintTextClassName), children: hintText ?? translation("select") }), /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ExpansionIcon, { isExpanded: isOpen }) ] }) } ), menuClassName: (0, import_clsx10.default)("flex-col-2 p-2 max-h-96 overflow-hidden", menuProps.menuClassName), children: (bag) => { const { close } = bag; return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [ !searchOptions?.disabled && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( SearchBar, { value: search, onChangeText: setSearch, autoFocus: true } ), /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex-col-2 overflow-y-auto", children: [ result.map((option, index) => { const update = () => { onChange(options.map((value) => value.value === option.value ? { ...option, selected: !value.selected } : value)); }; return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( ListTile, { prefix: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( Checkbox, { checked: option.selected, onChange: update, size: "small", disabled: option.disabled } ), title: option.label, onClick: update, disabled: option.disabled }, index ); }), additionalItems && additionalItems({ ...bag, search }) ] }), /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex-row-2 justify-between", children: [ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex-row-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( SolidButton, { color: "neutral", size: "small", onClick: () => { onChange(options.map((option) => ({ ...option, selected: !option.disabled }))); }, disabled: options.every((value) => value.selected || value.disabled), children: translation("all") } ), /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( SolidButton, { color: "neutral", size: "small", onClick: () => { onChange(options.map((option) => ({ ...option, selected: false }))); }, children: translation("none") } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SolidButton, { size: "small", onClick: close, children: "Done" }) ] }) ] }); } } ) ] }); }; var MultiSelectUncontrolled = ({ options, onChange, ...props }) => { const [usedOptions, setUsedOptions] = (0, import_react15.useState)(options); (0, import_react15.useEffect)(() => { setUsedOptions(options); }, [options]); return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)( MultiSelect, { ...props, options: usedOptions, onChange: (options2) => { setUsedOptions(options2); onChange(options2); } } ); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { MultiSelect, MultiSelectUncontrolled }); //# sourceMappingURL=MultiSelect.js.map