@helpwave/hightide
Version:
helpwave's component and theming library
1,539 lines (1,503 loc) • 181 kB
JavaScript
// 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