UNPKG

@helpwave/hightide

Version:

helpwave's component and theming library

1,539 lines (1,503 loc) 181 kB
// src/coloring/shading.ts import tinycolor from "tinycolor2"; // src/coloring/types.ts var shadingColorValues = [0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1e3]; // src/coloring/shading.ts var generateShadingColors = (partialShading) => { const shading = { 0: "#FFFFFF", 1e3: "#000000" }; let index = 1; while (index < shadingColorValues.length - 1) { const previous = shadingColorValues[index - 1]; const current = shadingColorValues[index]; if (partialShading[current] !== void 0) { shading[current] = partialShading[current]; index++; continue; } let j = index + 1; while (j < shadingColorValues.length) { if (partialShading[shadingColorValues[j]] !== void 0) { break; } j++; } if (j === shadingColorValues.length) { j = shadingColorValues.length - 1; } const nextFound = shadingColorValues[j]; const interval = nextFound - previous; for (let k = index; k < j; k++) { const current2 = shadingColorValues[k]; const previousValue = partialShading[previous] ?? shading[previous]; const nextValue = partialShading[nextFound] ?? shading[nextFound]; shading[current2] = tinycolor.mix(tinycolor(previousValue), tinycolor(nextValue), (current2 - previous) / interval * 100).toHexString(); } index = j; } return shading; }; // src/components/Avatar.tsx import Image from "next/image"; import clsx from "clsx"; import { jsx } from "react/jsx-runtime"; var avtarSizeList = ["tiny", "small", "medium", "large"]; var avatarSizeMapping = { tiny: 24, small: 32, medium: 48, large: 64 }; var Avatar = ({ avatarUrl, alt, size = "medium", className = "" }) => { avatarUrl = "https://cdn.helpwave.de/boringavatar.svg"; const avtarSize = { tiny: 24, small: 32, medium: 48, large: 64 }[size]; const style = { width: avtarSize + "px", height: avtarSize + "px", maxWidth: avtarSize + "px", maxHeight: avtarSize + "px", minWidth: avtarSize + "px", minHeight: avtarSize + "px" }; return ( // TODO transparent or white background later /* @__PURE__ */ jsx("div", { className: clsx(`rounded-full bg-primary`, className), style, children: /* @__PURE__ */ jsx( Image, { className: "rounded-full border border-gray-200", style, src: avatarUrl, alt, width: avtarSize, height: avtarSize } ) }) ); }; // src/components/AvatarGroup.tsx import { jsx as jsx2, jsxs } from "react/jsx-runtime"; var AvatarGroup = ({ avatars, maxShownProfiles = 5, size = "tiny" }) => { const displayedProfiles = avatars.length < maxShownProfiles ? avatars : avatars.slice(0, maxShownProfiles); const diameter = avatarSizeMapping[size]; const stackingOverlap = 0.5; const notDisplayedProfiles = avatars.length - maxShownProfiles; const avatarGroupWidth = diameter * (stackingOverlap * (displayedProfiles.length - 1) + 1); return /* @__PURE__ */ jsxs("div", { className: "row relative", style: { height: diameter + "px" }, children: [ /* @__PURE__ */ jsx2("div", { style: { width: avatarGroupWidth + "px" }, children: displayedProfiles.map((avatar, index) => /* @__PURE__ */ jsx2( "div", { className: "absolute", style: { left: index * diameter * stackingOverlap + "px", zIndex: maxShownProfiles - index }, children: /* @__PURE__ */ jsx2(Avatar, { avatarUrl: avatar.avatarUrl, alt: avatar.alt, size }) }, index )) }), notDisplayedProfiles > 0 && /* @__PURE__ */ jsx2( "div", { className: "truncate row items-center", style: { fontSize: diameter / 2 + "px", marginLeft: 1 + diameter / 16 + "px" }, children: /* @__PURE__ */ jsxs("span", { children: [ "+ ", notDisplayedProfiles ] }) } ) ] }); }; // src/components/BreadCrumb.tsx import Link from "next/link"; import clsx2 from "clsx"; import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime"; var BreadCrumb = ({ crumbs, linkClassName, containerClassName }) => { const color = "text-description"; return /* @__PURE__ */ jsx3("div", { className: clsx2("row", containerClassName), children: crumbs.map((crumb, index) => /* @__PURE__ */ jsxs2("div", { children: [ /* @__PURE__ */ jsx3(Link, { href: crumb.link, className: clsx2(linkClassName, { [`${color} hover:brightness-60`]: index !== crumbs.length - 1 }), children: crumb.display }), index !== crumbs.length - 1 && /* @__PURE__ */ jsx3("span", { className: clsx2(`px-1`, color), children: "/" }) ] }, index)) }); }; // src/components/Button.tsx import clsx3 from "clsx"; import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime"; var ButtonSizePaddings = { small: "btn-sm", medium: "btn-md", large: "btn-lg" }; var SolidButton = ({ children, disabled = false, color = "primary", size = "medium", startIcon, endIcon, onClick, className, ...restProps }) => { const colorClasses = { primary: "bg-button-solid-primary-background text-button-solid-primary-text", secondary: "bg-button-solid-secondary-background text-button-solid-secondary-text", tertiary: "bg-button-solid-tertiary-background text-button-solid-tertiary-text", positive: "bg-button-solid-positive-background text-button-solid-positive-text", warning: "bg-button-solid-warning-background text-button-solid-warning-text", negative: "bg-button-solid-negative-background text-button-solid-negative-text" }[color]; const iconColorClasses = { primary: "text-button-solid-primary-icon", secondary: "text-button-solid-secondary-icon", tertiary: "text-button-solid-tertiary-icon", positive: "text-button-solid-positive-icon", warning: "text-button-solid-warning-icon", negative: "text-button-solid-negative-icon" }[color]; return /* @__PURE__ */ jsxs3( "button", { onClick: disabled ? void 0 : onClick, disabled: disabled || onClick === void 0, className: clsx3( className, { "text-disabled-text bg-disabled-background": disabled, [clsx3(colorClasses, "hover:brightness-90")]: !disabled }, ButtonSizePaddings[size] ), ...restProps, children: [ startIcon && /* @__PURE__ */ jsx4( "span", { className: clsx3({ [iconColorClasses]: !disabled, [`text-disabled-icon`]: disabled }), children: startIcon } ), children, endIcon && /* @__PURE__ */ jsx4( "span", { className: clsx3({ [iconColorClasses]: !disabled, [`text-disabled-icon`]: disabled }), children: endIcon } ) ] } ); }; var OutlineButton = ({ children, disabled = false, color = "primary", size = "medium", startIcon, endIcon, onClick, className, ...restProps }) => { const colorClasses = { primary: "bg-transparent border-2 border-button-outline-primary-text text-button-outline-primary-text" }[color]; const iconColorClasses = { primary: "text-button-outline-primary-icon" }[color]; return /* @__PURE__ */ jsxs3( "button", { onClick: disabled ? void 0 : onClick, disabled: disabled || onClick === void 0, className: clsx3( className, { "text-disabled-text border-disabled-outline)": disabled, [clsx3(colorClasses, "hover:brightness-80")]: !disabled }, ButtonSizePaddings[size] ), ...restProps, children: [ startIcon && /* @__PURE__ */ jsx4( "span", { className: clsx3({ [iconColorClasses]: !disabled, [`text-disabled-icon`]: disabled }), children: startIcon } ), children, endIcon && /* @__PURE__ */ jsx4( "span", { className: clsx3({ [iconColorClasses]: !disabled, [`text-disabled-icon`]: disabled }), children: endIcon } ) ] } ); }; var TextButton = ({ children, disabled = false, color = "neutral", size = "medium", startIcon, endIcon, onClick, className, ...restProps }) => { const colorClasses = { negative: "bg-transparent text-button-text-negative-text", neutral: "bg-transparent text-button-text-neutral-text" }[color]; const iconColorClasses = { negative: "text-button-text-negative-icon", neutral: "text-button-text-neutral-icon" }[color]; return /* @__PURE__ */ jsxs3( "button", { onClick: disabled ? void 0 : onClick, disabled: disabled || onClick === void 0, className: clsx3( className, { "text-disabled-text": disabled, [clsx3(colorClasses, "hover:bg-button-text-hover-background rounded-full")]: !disabled }, ButtonSizePaddings[size] ), ...restProps, children: [ startIcon && /* @__PURE__ */ jsx4( "span", { className: clsx3({ [iconColorClasses]: !disabled, [`text-disabled-icon`]: disabled }), children: startIcon } ), children, endIcon && /* @__PURE__ */ jsx4( "span", { className: clsx3({ [iconColorClasses]: !disabled, [`text-disabled-icon`]: disabled }), children: endIcon } ) ] } ); }; // src/components/ChipList.tsx import clsx4 from "clsx"; import { jsx as jsx5, jsxs as jsxs4 } from "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__ */ jsxs4( "div", { ...restProps, className: clsx4( `row w-fit px-2 py-1`, colorMapping, { "rounded-md": variant === "normal", "rounded-full": variant === "fullyRounded" }, className ), children: [ children, trailingIcon && /* @__PURE__ */ jsx5("span", { className: colorMappingIcon, children: trailingIcon }) ] } ); }; var ChipList = ({ list, className = "" }) => { return /* @__PURE__ */ jsx5("div", { className: clsx4("flex flex-wrap gap-x-4 gap-y-2", className), children: list.map((value, index) => /* @__PURE__ */ jsx5( Chip, { ...value, color: value.color ?? "dark", variant: value.variant ?? "normal", children: value.children }, index )) }); }; // src/components/Circle.tsx import clsx5 from "clsx"; import { jsx as jsx6 } from "react/jsx-runtime"; var Circle = ({ radius = 20, className = "bg-primary", style, ...restProps }) => { const size = radius * 2; return /* @__PURE__ */ jsx6( "div", { className: clsx5(`rounded-full`, className), style: { width: `${size}px`, height: `${size}px`, ...style }, ...restProps } ); }; // src/components/ErrorComponent.tsx import { AlertOctagon } from "lucide-react"; // src/hooks/useLanguage.tsx import { createContext, useContext, useEffect as useEffect2, useState as useState2 } from "react"; // src/hooks/useLocalStorage.tsx import { useCallback, useEffect, useState } from "react"; // src/util/storage.ts var StorageService = class { // this seems to be a bug in eslint as 'paramter-properties' is a special syntax of typescript constructor(storage) { this.storage = storage; } get(key) { const value = this.storage.getItem(key); if (value === null) { return null; } return JSON.parse(value); } set(key, value) { this.storage.setItem(key, JSON.stringify(value)); } delete(key) { this.storage.removeItem(key); } deleteAll() { this.storage.clear(); } }; var LocalStorageService = class extends StorageService { constructor() { super(window.localStorage); } }; var SessionStorageService = class extends StorageService { constructor() { super(window.sessionStorage); } }; // src/hooks/useLocalStorage.tsx var useLocalStorage = (key, initValue) => { const get = useCallback(() => { if (typeof window === "undefined") { return initValue; } const storageService = new LocalStorageService(); const value = storageService.get(key); return value || initValue; }, [initValue, key]); const [storedValue, setStoredValue] = useState(get); const setValue = useCallback((value) => { const newValue = value instanceof Function ? value(storedValue) : value; const storageService = new LocalStorageService(); storageService.set(key, value); setStoredValue(newValue); }, [storedValue, setStoredValue, key]); useEffect(() => { setStoredValue(get()); }, []); return [storedValue, setValue]; }; // src/hooks/useLanguage.tsx import { jsx as jsx7 } from "react/jsx-runtime"; var languages = ["en", "de"]; var languagesLocalNames = { en: "English", de: "Deutsch" }; var DEFAULT_LANGUAGE = "en"; var LanguageContext = createContext({ language: DEFAULT_LANGUAGE, setLanguage: (v) => v }); var useLanguage = () => useContext(LanguageContext); var useLocale = (overWriteLanguage) => { const { language } = useLanguage(); const mapping = { en: "en-US", de: "de-DE" }; return mapping[overWriteLanguage ?? language]; }; var ProvideLanguage = ({ initialLanguage, children }) => { const [language, setLanguage] = useState2(initialLanguage ?? DEFAULT_LANGUAGE); const [storedLanguage, setStoredLanguage] = useLocalStorage("language", initialLanguage ?? DEFAULT_LANGUAGE); useEffect2(() => { if (language !== initialLanguage && initialLanguage) { console.warn("LanguageProvider initial state changed: Prefer using useLanguages's setLanguage instead"); setLanguage(initialLanguage); } }, [initialLanguage]); useEffect2(() => { setStoredLanguage(language); }, [language, setStoredLanguage]); useEffect2(() => { if (storedLanguage !== null) { setLanguage(storedLanguage); return; } const languagesToTestAgainst = Object.values(languages); const matchingBrowserLanguages = window.navigator.languages.map((language2) => languagesToTestAgainst.find((test) => language2 === test || language2.split("-")[0] === test)).filter((entry) => entry !== void 0); if (matchingBrowserLanguages.length === 0) return; const firstMatch = matchingBrowserLanguages[0]; setLanguage(firstMatch); }, []); return /* @__PURE__ */ jsx7(LanguageContext.Provider, { value: { language, setLanguage }, children }); }; // src/hooks/useTranslation.ts var useTranslation = (defaults, translationOverwrite = {}) => { const { language: languageProp, translation: overwrite } = translationOverwrite; const { language: inferredLanguage } = useLanguage(); const usedLanguage = languageProp ?? inferredLanguage; let defaultValues = defaults[usedLanguage]; if (overwrite && overwrite[usedLanguage]) { defaultValues = { ...defaultValues, ...overwrite[usedLanguage] }; } return defaultValues; }; // src/components/ErrorComponent.tsx import clsx6 from "clsx"; import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime"; var defaultErrorComponentTranslation = { en: { errorOccurred: "An error occurred" }, de: { errorOccurred: "Ein Fehler ist aufgetreten" } }; var ErrorComponent = ({ overwriteTranslation, errorText, classname }) => { const translation = useTranslation(defaultErrorComponentTranslation, overwriteTranslation); return /* @__PURE__ */ jsxs5("div", { className: clsx6("col items-center justify-center gap-y-4 w-full h-24", classname), children: [ /* @__PURE__ */ jsx8(AlertOctagon, { size: 64, className: "text-warning" }), errorText ?? `${translation.errorOccurred} :(` ] }); }; // src/components/Expandable.tsx import { forwardRef, useState as useState3 } from "react"; import { ChevronDown, ChevronUp } from "lucide-react"; import clsx7 from "clsx"; import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime"; var DefaultIcon = (expanded) => expanded ? /* @__PURE__ */ jsx9(ChevronUp, { size: 16, className: "min-w-[16px]" }) : /* @__PURE__ */ jsx9(ChevronDown, { size: 16, className: "min-w-[16px]" }); var Expandable = forwardRef(({ children, label, icon, initialExpansion = false, clickOnlyOnHeader = true, className = "", headerClassName = "" }, ref) => { const [isExpanded, setIsExpanded] = useState3(initialExpansion); icon ??= DefaultIcon; return /* @__PURE__ */ jsxs6( "div", { ref, className: clsx7("col bg-surface text-on-surface group rounded-lg shadow-sm", { "cursor-pointer": !clickOnlyOnHeader }, className), onClick: () => !clickOnlyOnHeader && setIsExpanded(!isExpanded), children: [ /* @__PURE__ */ jsxs6( "button", { className: clsx7("btn-md rounded-lg justify-between items-center bg-surface text-on-surface", { "group-hover:brightness-95": !isExpanded }, headerClassName), onClick: () => clickOnlyOnHeader && setIsExpanded(!isExpanded), children: [ label, icon(isExpanded) ] } ), isExpanded && /* @__PURE__ */ jsx9("div", { className: "col", children }) ] } ); }); Expandable.displayName = "Expandable"; // src/components/HelpwaveBadge.tsx import clsx10 from "clsx"; // src/components/icons/Helpwave.tsx import { clsx as clsx8 } from "clsx"; import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime"; var Helpwave = ({ color = "currentColor", animate = "none", size = 64, ...props }) => { const isLoadingAnimation = animate === "loading"; let svgAnimationKey = ""; if (animate === "pulse") { svgAnimationKey = "animate-pulse"; } else if (animate === "bounce") { svgAnimationKey = "animate-bounce"; } if (size < 0) { console.error("size cannot be less than 0"); size = 64; } return /* @__PURE__ */ jsx10( "svg", { width: size, height: size, viewBox: "0 0 888 888", fill: "none", strokeLinecap: "round", strokeWidth: 48, ...props, children: /* @__PURE__ */ jsxs7("g", { className: clsx8(svgAnimationKey), children: [ /* @__PURE__ */ jsx10("path", { className: clsx8({ "animate-wave-big-left-up": isLoadingAnimation }), d: "M144 543.235C144 423.259 232.164 326 340.92 326", stroke: color, strokeDasharray: "1000" }), /* @__PURE__ */ jsx10("path", { className: clsx8({ "animate-wave-big-right-down": isLoadingAnimation }), d: "M537.84 544.104C429.084 544.104 340.92 446.844 340.92 326.869", stroke: color, strokeDasharray: "1000" }), /* @__PURE__ */ jsx10("path", { className: clsx8({ "animate-wave-small-left-up": isLoadingAnimation }), d: "M462.223 518.035C462.223 432.133 525.348 362.495 603.217 362.495", stroke: color, strokeDasharray: "1000" }), /* @__PURE__ */ jsx10("path", { className: clsx8({ "animate-wave-small-right-down": isLoadingAnimation }), d: "M745.001 519.773C666.696 519.773 603.218 450.136 603.218 364.233", stroke: color, strokeDasharray: "1000" }) ] }) } ); }; // src/components/layout/Tile.tsx import Image2 from "next/image"; import clsx9 from "clsx"; import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime"; var Tile = ({ title, description, prefix, suffix, className }) => { return /* @__PURE__ */ jsxs8("div", { className: clsx9("row gap-x-4 w-full items-center", className), children: [ prefix, /* @__PURE__ */ jsxs8("div", { className: "col w-full", children: [ /* @__PURE__ */ jsx11("span", { className: clsx9(title.className), children: title.value }), !!description && /* @__PURE__ */ jsx11("span", { className: clsx9(description.className ?? "textstyle-description"), children: description.value }) ] }), suffix ] }); }; var TileWithImage = ({ url, imageLocation = "prefix", imageSize = { width: 24, height: 24 }, imageClassName = "", ...tileProps }) => { const image = /* @__PURE__ */ jsx11(Image2, { src: url, alt: "", ...imageSize, className: clsx9(imageClassName) }); return /* @__PURE__ */ jsx11( Tile, { ...tileProps, prefix: imageLocation === "prefix" ? image : void 0, suffix: imageLocation === "suffix" ? image : void 0 } ); }; // src/components/HelpwaveBadge.tsx import { jsx as jsx12 } from "react/jsx-runtime"; var HelpwaveBadge = ({ size = "small", title = "helpwave", className = "" }) => { const iconSize = size === "small" ? 24 : 64; return /* @__PURE__ */ jsx12( Tile, { prefix: /* @__PURE__ */ jsx12(Helpwave, { size: iconSize }), title: { value: title, className: size === "small" ? "textstyle-title-lg text-base" : "textstyle-title-xl" }, className: clsx10( { "px-2 py-1 rounded-md": size === "small", "px-4 py-1 rounded-md": size === "large" }, className ) } ); }; // src/components/HideableContentSection.tsx import { useState as useState4 } from "react"; import { ChevronDown as ChevronDown2, ChevronUp as ChevronUp2 } from "lucide-react"; import clsx11 from "clsx"; import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime"; var HideableContentSection = ({ initiallyOpen = true, disabled, children, header }) => { const [open, setOpen] = useState4(initiallyOpen); return /* @__PURE__ */ jsxs9("div", { className: "col", children: [ /* @__PURE__ */ jsxs9( "div", { className: clsx11("row justify-between items-center", { "cursor-pointer": !disabled }), onClick: () => { if (!disabled) { setOpen(!open); } }, children: [ /* @__PURE__ */ jsx13("div", { children: header }), !disabled && (open ? /* @__PURE__ */ jsx13(ChevronUp2, {}) : /* @__PURE__ */ jsx13(ChevronDown2, {})) ] } ), open && /* @__PURE__ */ jsx13("div", { children }) ] }); }; // src/components/InputGroup.tsx import { useEffect as useEffect3, useState as useState5 } from "react"; import { ChevronDown as ChevronDown3, ChevronUp as ChevronUp3 } from "lucide-react"; import clsx12 from "clsx"; // src/util/noop.ts var noop = () => void 0; // src/components/InputGroup.tsx import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime"; var InputGroup = ({ children, title, expanded = true, isExpandable = true, disabled = false, onChange = noop, className = "" }) => { const [isExpanded, setIsExpanded] = useState5(expanded); useEffect3(() => { setIsExpanded(expanded); }, [expanded]); return /* @__PURE__ */ jsxs10("div", { className: clsx12("col gap-y-4 p-4 bg-white rounded-xl", className), children: [ /* @__PURE__ */ jsxs10( "div", { className: clsx12( "row justify-between items-center", { "cursor-pointer": isExpandable && !disabled, "cursor-not-allowed": disabled }, { "text-primary": !disabled, "text-primary/40": disabled } ), onClick: () => { if (!isExpandable) { return; } const updatedIsExpanded = !isExpanded; onChange(updatedIsExpanded); setIsExpanded(updatedIsExpanded); }, children: [ /* @__PURE__ */ jsx14("span", { className: "textstyle-title-md", children: title }), /* @__PURE__ */ jsx14("div", { className: clsx12("rounded-full text-white w-6 h-6", { "bg-primary": isExpandable && !disabled || expanded, "bg-primary/40": disabled }), children: isExpanded ? /* @__PURE__ */ jsx14(ChevronUp3, { className: "-translate-y-[1px]", size: 24 }) : /* @__PURE__ */ jsx14(ChevronDown3, { className: "translate-y-[1px]", size: 24 }) }) ] } ), isExpanded && /* @__PURE__ */ jsx14("div", { className: "col gap-y-2 h-full", children }) ] }); }; // src/components/LoadingAndErrorComponent.tsx import { useState as useState6 } from "react"; // src/components/LoadingAnimation.tsx import clsx13 from "clsx"; import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime"; var defaultLoadingAnimationTranslation = { en: { loading: "Loading data" }, de: { loading: "Lade Daten" } }; var LoadingAnimation = ({ overwriteTranslation, loadingText, classname }) => { const translation = useTranslation(defaultLoadingAnimationTranslation, overwriteTranslation); return /* @__PURE__ */ jsxs11("div", { className: clsx13("col items-center justify-center w-full h-24", classname), children: [ /* @__PURE__ */ jsx15(Helpwave, { animate: "loading" }), loadingText ?? `${translation.loading}...` ] }); }; // src/components/LoadingAndErrorComponent.tsx import { jsx as jsx16 } from "react/jsx-runtime"; var LoadingAndErrorComponent = ({ children, isLoading = false, hasError = false, errorProps, loadingProps, minimumLoadingDuration }) => { const [isInMinimumLoading, setIsInMinimumLoading] = useState6(false); const [hasUsedMinimumLoading, setHasUsedMinimumLoading] = useState6(false); if (minimumLoadingDuration && !isInMinimumLoading && !hasUsedMinimumLoading) { setIsInMinimumLoading(true); setTimeout(() => { setIsInMinimumLoading(false); setHasUsedMinimumLoading(true); }, minimumLoadingDuration); } if (isLoading || minimumLoadingDuration && isInMinimumLoading) { return /* @__PURE__ */ jsx16(LoadingAnimation, { ...loadingProps }); } if (hasError) { return /* @__PURE__ */ jsx16(ErrorComponent, { ...errorProps }); } return children; }; // src/components/LoadingButton.tsx import clsx14 from "clsx"; import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime"; var LoadingButton = ({ isLoading = false, size = "medium", onClick, ...rest }) => { const paddingClass = ButtonSizePaddings[size]; return /* @__PURE__ */ jsxs12("div", { className: "inline-block relative", children: [ isLoading && /* @__PURE__ */ jsx17("div", { className: clsx14("absolute inset-0 row items-center justify-center bg-white/40", paddingClass), children: /* @__PURE__ */ jsx17(Helpwave, { animate: "loading", className: "text-white" }) }), /* @__PURE__ */ jsx17(SolidButton, { ...rest, disabled: rest.disabled, onClick: isLoading ? noop : onClick }) ] }); }; // src/components/MarkdownInterpreter.tsx import { Fragment, jsx as jsx18 } from "react/jsx-runtime"; var astNodeInserterType = ["helpwave", "newline"]; var ASTNodeInterpreter = ({ node, isRoot = false, className = "" }) => { switch (node.type) { case "newline": return /* @__PURE__ */ jsx18("br", {}); case "text": return isRoot ? /* @__PURE__ */ jsx18("span", { className, children: node.text }) : node.text; case "helpwave": return /* @__PURE__ */ jsx18("span", { className: "font-bold font-space no-underline", children: "helpwave" }); case "none": return isRoot ? /* @__PURE__ */ jsx18("span", { className, children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }) : /* @__PURE__ */ jsx18(Fragment, { children: node.children.map((value, index) => /* @__PURE__ */ jsx18(ASTNodeInterpreter, { node: value }, index)) }); case "bold": return /* @__PURE__ */ jsx18("b", { children: node.children.map((value, index) => /* @__PURE__ */ jsx18(ASTNodeInterpreter, { node: value }, index)) }); case "italic": return /* @__PURE__ */ jsx18("i", { children: node.children.map((value, index) => /* @__PURE__ */ jsx18(ASTNodeInterpreter, { node: value }, index)) }); case "underline": return /* @__PURE__ */ jsx18("u", { children: node.children.map((value, index) => /* @__PURE__ */ jsx18(ASTNodeInterpreter, { node: value }, index)) }); case "font-space": return /* @__PURE__ */ jsx18("span", { className: "font-space", children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }); case "primary": return /* @__PURE__ */ jsx18("span", { className: "text-primary", children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }); case "secondary": return /* @__PURE__ */ jsx18("span", { className: "text-secondary", children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }); case "warn": return /* @__PURE__ */ jsx18("span", { className: "text-warning", children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }); case "positive": return /* @__PURE__ */ jsx18("span", { className: "text-positive", children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }); case "negative": return /* @__PURE__ */ jsx18("span", { className: "text-negative", children: node.children.map((value, index) => /* @__PURE__ */ jsx18( ASTNodeInterpreter, { node: value }, index )) }); default: return null; } }; var modifierIdentifierMapping = [ { id: "i", name: "italic" }, { id: "b", name: "bold" }, { id: "u", name: "underline" }, { id: "space", name: "font-space" }, { id: "primary", name: "primary" }, { id: "secondary", name: "secondary" }, { id: "warn", name: "warn" }, { id: "positive", name: "positive" }, { id: "negative", name: "negative" } ]; var inserterIdentifierMapping = [ { id: "helpwave", name: "helpwave" }, { id: "newline", name: "newline" } ]; var parseMarkdown = (text, commandStart = "\\", open = "{", close = "}") => { let start = text.indexOf(commandStart); const children = []; while (text !== "") { if (start === -1) { children.push({ type: "text", text }); break; } children.push(parseMarkdown(text.substring(0, start))); text = text.substring(start); if (text.length <= 1) { children.push({ type: "text", text }); text = ""; continue; } const simpleReplace = [commandStart, open, close]; if (simpleReplace.some((value) => text[1] === value)) { children.push({ type: "text", text: simpleReplace.find((value) => text[1] === value) }); text = text.substring(2); start = text.indexOf(commandStart); continue; } const inserter = inserterIdentifierMapping.find((value) => text.substring(1).startsWith(value.id)); if (inserter) { children.push({ type: inserter.name }); text = text.substring(inserter.id.length + 1); start = text.indexOf(commandStart); continue; } const modifier = modifierIdentifierMapping.find((value) => text.substring(1).startsWith(value.id)); if (modifier) { if (text[modifier.id.length + 1] !== open) { children.push({ type: "text", text: text.substring(0, modifier.id.length + 1) }); text = text.substring(modifier.id.length + 2); start = text.indexOf(commandStart); continue; } let closing = -1; let index = modifier.id.length + 2; let counter = 1; let escaping = false; while (index < text.length) { if (text[index] === open && !escaping) { counter++; } if (text[index] === close && !escaping) { counter--; if (counter === 0) { closing = index; break; } } escaping = text[index] === commandStart; index++; } if (closing !== -1) { children.push({ type: modifier.name, children: [parseMarkdown(text.substring(modifier.id.length + 2, closing))] }); text = text.substring(closing + 1); start = text.indexOf(commandStart); continue; } } children.push({ type: "text", text: text[0] }); text = text.substring(1); start = text.indexOf(commandStart); } return { type: "none", children }; }; var optimizeTree = (node) => { if (node.type === "text") { return !node.text ? void 0 : node; } if (astNodeInserterType.some((value) => value === node.type)) { return node; } const currentNode = node; if (currentNode.children.length === 0) { return void 0; } let children = []; for (let i = 0; i < currentNode.children.length; i++) { const child = optimizeTree(currentNode.children[i]); if (!child) { continue; } if (child.type === "none") { children.push(...child.children); } else { children.push(child); } } currentNode.children = children; children = []; for (let i = 0; i < currentNode.children.length; i++) { const child = currentNode.children[i]; if (child) { if (child.type === "text" && children[children.length - 1]?.type === "text") { children[children.length - 1].text += child.text; } else { children.push(child); } } } currentNode.children = children; return currentNode; }; var MarkdownInterpreter = ({ text, className }) => { const tree = parseMarkdown(text); const optimizedTree = optimizeTree(tree); return /* @__PURE__ */ jsx18(ASTNodeInterpreter, { node: optimizedTree, isRoot: true, className }); }; // src/components/Pagination.tsx import { ChevronLast, ChevronLeft, ChevronFirst, ChevronRight } from "lucide-react"; import clsx15 from "clsx"; import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime"; var defaultPaginationTranslations = { en: { of: "of" }, de: { of: "von" } }; var Pagination = ({ overwriteTranslation, page, numberOfPages, onPageChanged }) => { const translation = useTranslation(defaultPaginationTranslations, overwriteTranslation); const changePage = (page2) => { onPageChanged(page2); }; const noPages = numberOfPages === 0; const onFirstPage = page === 0 && !noPages; const onLastPage = page === numberOfPages - 1; return /* @__PURE__ */ jsxs13("div", { className: clsx15("row", { "opacity-30": noPages }), children: [ /* @__PURE__ */ jsx19("button", { onClick: () => changePage(0), disabled: onFirstPage, children: /* @__PURE__ */ jsx19(ChevronFirst, { className: clsx15({ "opacity-30": onFirstPage }) }) }), /* @__PURE__ */ jsx19("button", { onClick: () => changePage(page - 1), disabled: onFirstPage, children: /* @__PURE__ */ jsx19(ChevronLeft, { className: clsx15({ "opacity-30": onFirstPage }) }) }), /* @__PURE__ */ jsxs13("div", { className: "min-w-[80px] justify-center mx-2", children: [ /* @__PURE__ */ jsx19("span", { className: "select-none text-right flex-1", children: noPages ? 0 : page + 1 }), /* @__PURE__ */ jsx19("span", { className: "select-none mx-2", children: translation.of }), /* @__PURE__ */ jsx19("span", { className: "select-none text-left flex-1", children: numberOfPages }) ] }), /* @__PURE__ */ jsx19("button", { onClick: () => changePage(page + 1), disabled: onLastPage || noPages, children: /* @__PURE__ */ jsx19(ChevronRight, { className: clsx15({ "opacity-30": onLastPage }) }) }), /* @__PURE__ */ jsx19("button", { onClick: () => changePage(numberOfPages - 1), disabled: onLastPage || noPages, children: /* @__PURE__ */ jsx19(ChevronLast, { className: clsx15({ "opacity-30": onLastPage }) }) }) ] }); }; // src/components/Profile.tsx import clsx16 from "clsx"; import Image3 from "next/image"; import Link2 from "next/link"; import { Github, Globe, Linkedin, Mail } from "lucide-react"; import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime"; var SocialIcon = ({ type, url, size = 24 }) => { let icon; switch (type) { case "mail": icon = /* @__PURE__ */ jsx20(Mail, { size }); break; case "linkedin": icon = /* @__PURE__ */ jsx20(Linkedin, { size }); break; case "github": icon = /* @__PURE__ */ jsx20(Github, { size }); break; case "website": icon = /* @__PURE__ */ jsx20(Globe, { size }); break; case "medium": icon = /* @__PURE__ */ jsx20(Globe, { size }); break; default: icon = /* @__PURE__ */ jsx20(Helpwave, { size: 24 }); } return /* @__PURE__ */ jsx20(Link2, { href: url, target: "_blank", children: /* @__PURE__ */ jsx20(Chip, { color: "dark", className: "!p-2", children: icon }) }); }; var Profile = ({ name, imageUrl, badge, prefix, suffix, roleBadge, role, tags, info, socials, className, imageClassName, ...divProps }) => { return /* @__PURE__ */ jsxs14( "div", { ...divProps, className: clsx16(`col items-center text-center rounded-3xl p-3 pb-4 bg-white text-gray-900 w-min shadow-around-lg`, className), children: [ /* @__PURE__ */ jsxs14("div", { className: "relative mb-6", children: [ /* @__PURE__ */ jsx20("div", { className: clsx16("relative rounded-xl row items-center justify-center overflow-hidden", imageClassName), children: /* @__PURE__ */ jsx20(Image3, { src: imageUrl, alt: "", className: clsx16("z-[2] object-cover", imageClassName), width: 0, height: 0, style: { width: "auto", height: "auto" } }) }), /* @__PURE__ */ jsx20("div", { className: "absolute top-[6px] left-[6px] z-[3]", children: badge }), roleBadge && /* @__PURE__ */ jsx20("div", { className: "absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-2/3 z-[4] rounded-md", children: /* @__PURE__ */ jsx20(Chip, { color: "dark", className: "font-bold px-3", children: roleBadge }) }) ] }), prefix && /* @__PURE__ */ jsx20("span", { className: "font-semibold", children: prefix }), /* @__PURE__ */ jsx20("h2", { id: name, className: "textstyle-title-md", children: name }), suffix && /* @__PURE__ */ jsx20("span", { className: "text-sm mb-1", children: suffix }), role && /* @__PURE__ */ jsx20("span", { className: "font-space font-bold text-sm", children: role }), tags && /* @__PURE__ */ jsx20("div", { className: "flex flex-wrap mx-4 mt-2 gap-x-2 justify-center", children: tags.map((tag, index) => /* @__PURE__ */ jsx20("span", { className: "textstyle-description text-sm", children: `#${tag}` }, index)) }), info && /* @__PURE__ */ jsx20("span", { className: "mt-2 text-sm", children: info }), socials && /* @__PURE__ */ jsx20("div", { className: "flex flex-wrap grow items-end justify-center gap-x-4 gap-y-2 mt-4", children: socials.map((socialIconProps, index) => /* @__PURE__ */ jsx20(SocialIcon, { ...socialIconProps, size: socialIconProps.size ?? 20 }, index)) }) ] } ); }; // src/components/ProgressIndicator.tsx import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime"; var sizeMapping = { small: 16, medium: 24, big: 48 }; var ProgressIndicator = ({ progress, strokeWidth = 5, size = "medium", direction = "counterclockwise", rotation = 0 }) => { const currentSize = sizeMapping[size]; const center = currentSize / 2; const radius = center - strokeWidth / 2; const arcLength = 2 * Math.PI * radius; const arcOffset = arcLength * progress; if (direction === "clockwise") { rotation += 360 * progress; } return /* @__PURE__ */ jsxs15( "svg", { style: { height: `${currentSize}px`, width: `${currentSize}px`, transform: `rotate(${rotation}deg)` }, children: [ /* @__PURE__ */ jsx21( "circle", { cx: center, cy: center, r: radius, fill: "transparent", strokeWidth, className: "stroke-primary" } ), /* @__PURE__ */ jsx21( "circle", { cx: center, cy: center, r: radius, fill: "transparent", strokeWidth, strokeDasharray: arcLength, strokeDashoffset: arcOffset, className: "stroke-gray-300" } ) ] } ); }; // src/components/Ring.tsx import { useCallback as useCallback2, useEffect as useEffect4, useState as useState7 } from "react"; import clsx17 from "clsx"; import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime"; var Ring = ({ innerSize = 20, width = 7, className = "outline-primary" }) => { return /* @__PURE__ */ jsx22( "div", { className: clsx17(`bg-transparent rounded-full outline`, className), style: { width: `${innerSize}px`, height: `${innerSize}px`, outlineWidth: `${width}px` } } ); }; var AnimatedRing = ({ innerSize, width, className, fillAnimationDuration = 3, repeating = false, onAnimationFinished = noop, style }) => { const [currentWidth, setCurrentWidth] = useState7(0); const milliseconds = 1e3 * fillAnimationDuration; const animate = useCallback2((timestamp, startTime) => { const progress = Math.min((timestamp - startTime) / milliseconds, 1); const newWidth = Math.min(width * progress, width); setCurrentWidth(newWidth); if (progress < 1) { requestAnimationFrame((newTimestamp) => animate(newTimestamp, startTime)); } else { onAnimationFinished(); if (repeating) { setCurrentWidth(0); requestAnimationFrame((newTimestamp) => animate(newTimestamp, newTimestamp)); } } }, [milliseconds, onAnimationFinished, repeating, width]); useEffect4(() => { if (currentWidth < width) { requestAnimationFrame((timestamp) => animate(timestamp, timestamp)); } }, []); return /* @__PURE__ */ jsx22( "div", { className: "row items-center justify-center", style: { width: `${innerSize + 2 * width}px`, height: `${innerSize + 2 * width}px`, ...style }, children: /* @__PURE__ */ jsx22( Ring, { innerSize, width: currentWidth, className } ) } ); }; var RingWave = ({ startInnerSize = 20, endInnerSize = 30, width, className, fillAnimationDuration = 3, repeating = false, onAnimationFinished = noop, style }) => { const [currentInnerSize, setCurrentInnerSize] = useState7(startInnerSize); const distance = endInnerSize - startInnerSize; const milliseconds = 1e3 * fillAnimationDuration; const animate = useCallback2((timestamp, startTime) => { const progress = Math.min((timestamp - startTime) / milliseconds, 1); const newInnerSize = Math.min( startInnerSize + distance * progress, endInnerSize ); setCurrentInnerSize(newInnerSize); if (progress < 1) { requestAnimationFrame((newTimestamp) => animate(newTimestamp, startTime)); } else { onAnimationFinished(); if (repeating) { setCurrentInnerSize(startInnerSize); requestAnimationFrame((newTimestamp) => animate(newTimestamp, newTimestamp)); } } }, [distance, endInnerSize, milliseconds, onAnimationFinished, repeating, startInnerSize]); useEffect4(() => { if (currentInnerSize < endInnerSize) { requestAnimationFrame((timestamp) => animate(timestamp, timestamp)); } }, []); return /* @__PURE__ */ jsx22( "div", { className: "row items-center justify-center", style: { width: `${endInnerSize + 2 * width}px`, height: `${endInnerSize + 2 * width}px`, ...style }, children: /* @__PURE__ */ jsx22( Ring, { innerSize: currentInnerSize, width, className } ) } ); }; var RadialRings = ({ circle1ClassName = "bg-primary/90 outline-primary/90", circle2ClassName = "bg-primary/60 outline-primary/60", circle3ClassName = "bg-primary/40 outline-primary/40", waveWidth = 10, waveBaseColor = "outline-white/20", sizeCircle1 = 100, sizeCircle2 = 200, sizeCircle3 = 300 }) => { const [currentRing, setCurrentRing] = useState7(0); const size = sizeCircle3; return /* @__PURE__ */ jsxs16( "div", { className: "relative", style: { width: `${sizeCircle3}px`, height: `${sizeCircle3}px` }, children: [ /* @__PURE__ */ jsx22( Circle, { radius: sizeCircle1 / 2, className: clsx17(circle1ClassName, `absolute z-[10] -translate-y-1/2 -translate-x-1/2`), style: { left: `${size / 2}px`, top: `${size / 2}px` } } ), currentRing === 0 ? /* @__PURE__ */ jsx22( AnimatedRing, { innerSize: sizeCircle1, width: (sizeCircle2 - sizeCircle1) / 2, onAnimationFinished: () => currentRing === 0 ? setCurrentRing(1) : null, repeating: true, className: clsx17( circle2ClassName, { "opacity-5": currentRing !== 0 } ), style: { left: `${size / 2}px`, top: `${size / 2}px`, position: "absolute", translate: `-50% -50%`, zIndex: 9 } } ) : null, currentRing === 2 ? /* @__PURE__ */ jsx22( RingWave, { startInnerSize: sizeCircle1 - waveWidth, endInnerSize: sizeCircle2, width: waveWidth, repeating: true, className: clsx17(waveBaseColor, `opacity-5`), style: { left: `${size / 2}px`, top: `${size / 2}px`, position: "absolute", translate: `-50% -50%`, zIndex: 9 } } ) : null, /* @__PURE__ */ jsx22( Circle, { radius: sizeCircle2 / 2, className: clsx17( circle2ClassName, { "opacity-20": currentRing < 1 }, `absolute z-[8] -translate-y-1/2 -translate-x-1/2` ), style: { left: `${size / 2}px`, top: `${size / 2}px` } } ), currentRing === 1 ? /* @__PURE__ */ jsx22( AnimatedRing, { innerSize: sizeCircle2 - 1, width: (sizeCircle3 - sizeCircle2) / 2, onAnimationFinished: () => currentRing === 1 ? setCurrentRing(2) : null, repeating: true, className: clsx17(circle3ClassName), style: { left: `${size / 2}px`, top: `${size / 2}px`, position: "absolute", translate: `-50% -50%`, zIndex: 7 } } ) : null, currentRing === 2 ? /* @__PURE__ */ jsx22( RingWave, { startInnerSize: sizeCircle2, endInnerSize: sizeCircle3 - waveWidth, width: waveWidth, repeating: true, className: clsx17(waveBaseColor, `opacity-5`), style: { left: `${size / 2}px`, top: `${size / 2}px`, position: "absolute", translate: `-50% -50%`, zIndex: 7 } } ) : null, /* @__PURE__ */ jsx22( Circle, { radius: sizeCircle3 / 2, className: clsx17( circle3ClassName, { "opacity-20": currentRing < 2 }, `absolute z-[6] -translate-y-1/2 -translate-x-1/2` ), style: { left: `${size / 2}px`, top: `${size / 2}px` } } ) ] } ); }; // src/components/SearchableList.tsx import { useEffect as useEffect7, useMemo, useState as useState10 } from "react"; import { Search } from "lucide-react"; import clsx19 from "clsx"; // src/util/simpleSearch.ts var MultiSubjectSearchWithMapping = (search, objects, mapping) => { return objects.filter((object) => { const mappedSearchKeywords = mapping(object).map((value) => value ? value.toLowerCase().trim() : void 0); return search.every((searchValue) => !!mappedSearchKeywords.find((value) => !!value && value.includes(searchValue.toLowerCase().trim()))); }); }; var MultiSearchWithMapping = (search, objects, mapping) => { return objects.filter((object) => { const mappedSearchKeywords = mapping(object).map((value) => value.toLowerCase().trim()); return !!mappedSearchKeywords.fi