@helpwave/hightide 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/layout/Carousel.tsx","../../../src/utils/array.ts","../../../src/components/user-action/Button.tsx","../../../src/localization/LanguageProvider.tsx","../../../src/hooks/useLocalStorage.ts","../../../src/localization/util.ts","../../../src/localization/useTranslation.ts"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState\n} from 'react'\nimport clsx from 'clsx'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { createLoopingListWithIndex, range } from '@/src/utils/array'\nimport { IconButton } from '../user-action/Button'\nimport type { Translation } from '@/src/localization/useTranslation'\nimport { useTranslation } from '@/src/localization/useTranslation'\n\n//\n// CarouselContext\n//\ntype CarouselContextType = {\n id: string,\n currentIndex: number,\n slideCount: number,\n isLooping: boolean,\n}\n\nconst CarouselContext = createContext<CarouselContextType | null>(null)\n\nconst useCarouselContext = () => {\n const context = useContext(CarouselContext)\n if (!context) {\n console.error('useCarouselContext must be used within CarouselContext')\n }\n return context\n}\n\n//\n// CarouselTab\n//\ntype CarouselTabTranslationType = {\n showSlide: string,\n slideNavigation: string,\n}\n\nconst defaultCarouselTabTranslationType: Translation<CarouselTabTranslationType> = {\n en: {\n showSlide: `Show Slide {{index}}`,\n slideNavigation: 'Slide navigation'\n },\n de: {\n showSlide: 'Zeige Slide {{index}}',\n slideNavigation: 'Slide Navigation',\n }\n}\n\ntype CarouselTabsProps = {\n onChange: (index: number) => void,\n}\n\nexport default function CarouselTabs({\n onChange,\n }: CarouselTabsProps) {\n const translation = useTranslation<CarouselTabTranslationType>([\n defaultCarouselTabTranslationType,\n ])\n const { id, slideCount, currentIndex, isLooping } = useCarouselContext()\n\n const tabRefs = useRef<(HTMLButtonElement | null)[]>([])\n\n const handleKeyDown = (event: React.KeyboardEvent, index: number) => {\n let newIndex = index\n if (event.key === 'ArrowRight') {\n newIndex = isLooping ? (index + 1) % slideCount : Math.max(index + 1, slideCount - 1)\n } else if (event.key === 'ArrowLeft') {\n newIndex = isLooping ? (index - 1 + slideCount) % slideCount : Math.max(index - 1, 0)\n } else {\n return\n }\n event.preventDefault()\n onChange(newIndex)\n tabRefs.current[newIndex]?.focus()\n }\n\n return (\n <div\n className=\"flex-row-1 items-center justify-center w-full my-2\"\n role=\"tablist\"\n aria-label={translation('slideNavigation')}\n id={`${id}-tablist`}\n >\n {range(slideCount).map((index) => {\n const isSelected = currentIndex === index\n return (\n <button\n id={`${id}-tab-${index}`}\n key={index}\n ref={(el) => (tabRefs.current[index] = el)}\n\n onClick={() => onChange(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n\n className={clsx(\n 'w-8 min-w-8 h-3 min-h-3 first:rounded-l-md last:rounded-r-md',\n {\n 'bg-carousel-dot-disabled hover:bg-carousel-dot-active': currentIndex !== index,\n 'bg-carousel-dot-active hover:brightness-90': currentIndex === index,\n }\n )}\n\n role=\"tab\"\n tabIndex={isSelected ? 0 : -1}\n aria-label={translation('showSlide', { replacements: { index: (index + 1).toString() } })}\n aria-selected={isSelected}\n aria-controls={`slide-${index}`}\n aria-disabled={isSelected}\n />\n )\n })}\n </div>\n )\n}\n\n//\n// CarouselSlide\n//\ntype CarouselSlideTranslationType = {\n slide: string,\n slideOf: string,\n}\n\nconst defaultCarouselSlideTranslationType: Translation<CarouselSlideTranslationType> = {\n en: {\n slide: 'Slide',\n slideOf: `Slide {{index}} of {{length}} slides`,\n },\n de: {\n slide: 'Slide',\n slideOf: `Slide {{index}} von {{length}} slides`,\n }\n}\n\nexport interface CarouselSlideProps extends HTMLAttributes<HTMLDivElement> {\n index: number,\n}\n\nexport const CarouselSlide = forwardRef<HTMLDivElement, CarouselSlideProps>(\n function CarouselSlide({\n index,\n ...props\n }, ref) {\n const translation = useTranslation<CarouselSlideTranslationType>([defaultCarouselSlideTranslationType])\n const { id, currentIndex, slideCount } = useCarouselContext()\n\n const isSelected = currentIndex === index\n\n return (\n <div\n {...props}\n ref={ref}\n id={`${id}-slide-${index}`}\n\n className={clsx('focus-style-none group/slide', props.className)}\n\n tabIndex={isSelected ? 0 : undefined}\n role=\"group\"\n aria-roledescription={translation('slide')}\n aria-label={translation('slideOf', {\n replacements: {\n index: (index + 1).toString(),\n length: (slideCount).toString(),\n },\n })}\n aria-hidden={isSelected ? undefined : true}\n />\n )\n }\n)\n\n//\n// Carousel\n//\ntype DragState = {\n dragStartX: number,\n dragOffsetX: number,\n}\n\ntype CarouselTranslationType = {\n slide: string,\n carousel: string,\n slideOf: string,\n chooseSlide: string,\n}\n\nconst defaultCarouselTranslationType: Translation<CarouselTranslationType> = {\n en: {\n slide: 'Slide',\n carousel: 'Carousel',\n slideOf: `Slide {{index}} of {{length}} slides`,\n chooseSlide: 'Choose slide to display'\n },\n de: {\n slide: 'Slide',\n carousel: 'Karussell',\n slideOf: `Slide {{index}} von {{length}} slides`,\n chooseSlide: 'Wähle die angezeigte Slide aus'\n }\n}\n\ntype ItemType = {\n item: ReactNode,\n index: number,\n}\n\nexport type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {\n children: ReactNode[],\n animationTime?: number,\n isLooping?: boolean,\n isAutoPlaying?: boolean,\n autoLoopingTimeOut?: number,\n autoLoopAnimationTime?: number,\n hintNext?: boolean,\n arrows?: boolean,\n dots?: boolean,\n /**\n * Percentage that is allowed to be scrolled further\n */\n overScrollThreshold?: number,\n blurColor?: string,\n heightClassName?: string,\n widthClassName?: string,\n slideContainerProps?: HTMLAttributes<HTMLDivElement>,\n onSlideChanged?: (index: number) => void,\n}\n\nexport const Carousel = ({\n children,\n animationTime = 200,\n isLooping = false,\n isAutoPlaying = false,\n autoLoopingTimeOut = 5000,\n autoLoopAnimationTime = 1000,\n hintNext = false,\n arrows = false,\n dots = true,\n blurColor = 'from-background',\n heightClassName = 'h-96',\n widthClassName = 'w-[70%] desktop:w-1/2',\n slideContainerProps,\n onSlideChanged,\n ...props\n }: CarouselProps) => {\n const translation = useTranslation([defaultCarouselTranslationType])\n const slideRefs = useRef<HTMLDivElement[]>([])\n const [currentIndex, setCurrentIndex] = useState<number>(0)\n const [hasFocus, setHasFocus] = useState(false)\n const [dragState, setDragState] = useState<DragState>()\n const isPaused = hasFocus\n const carouselContainerRef = useRef<HTMLDivElement>(null)\n const [disableClick, setDisableClick] = useState(false)\n\n const timeOut = useRef<NodeJS.Timeout | undefined>(undefined)\n\n const length = useMemo(() => children.length, [children])\n const paddingItemCount = 3 // The number of items to append left and right of the list to allow for clean transition when looping\n\n const generatedId = 'carousel' + useId()\n const id = props.id ?? generatedId\n\n // Validation\n if (isAutoPlaying && !isLooping) {\n console.error('When isAutoLooping is true, isLooping should also be true')\n isLooping = true\n }\n autoLoopingTimeOut = Math.max(0, autoLoopingTimeOut) // time between transitions\n animationTime = Math.max(100, animationTime) // in ms, must be > 0\n autoLoopAnimationTime = Math.max(200, autoLoopAnimationTime)\n\n useEffect(() => {\n const carousel = carouselContainerRef.current\n\n if (carousel) {\n function onFocus() {\n setHasFocus(true)\n }\n\n function onBlur() {\n setHasFocus(false)\n }\n\n\n carousel?.addEventListener('focusin', onFocus)\n carousel?.addEventListener('focusout', onBlur)\n return () => {\n carousel?.removeEventListener('focusin', onFocus)\n carousel?.removeEventListener('focusin', onFocus)\n }\n }\n }, [])\n\n const getStyleOffset = (index: number) => {\n const baseOffset = -50 + (index - currentIndex) * 100\n return `${baseOffset}%`\n }\n\n const canGoLeft = () => {\n return isLooping || currentIndex !== 0\n }\n\n const canGoRight = useCallback(() => {\n return isLooping || currentIndex !== length - 1\n }, [currentIndex, isLooping, length])\n\n const left = () => {\n if (canGoLeft()) {\n setCurrentIndex(currentIndex - 1)\n }\n }\n\n const right = useCallback(() => {\n if (canGoRight()) {\n setCurrentIndex((currentIndex + length + 1) % length)\n }\n }, [canGoRight, currentIndex, length])\n\n useEffect(() => {\n if (!timeOut.current && !isPaused) {\n if (autoLoopingTimeOut > 0) {\n timeOut.current = setTimeout(() => {\n right()\n timeOut.current = undefined\n }, autoLoopingTimeOut)\n } else {\n right()\n }\n }\n if ((isPaused || !!dragState) && timeOut.current) {\n clearTimeout(timeOut.current)\n timeOut.current = undefined\n }\n }, [right, isPaused, autoLoopingTimeOut, dragState])\n\n\n let items: ItemType[] = children.map((item, index) => ({\n index,\n item\n }))\n let before: ItemType[] = []\n let after: ItemType[] = []\n if (isLooping) {\n before = createLoopingListWithIndex(children, length - 1, paddingItemCount, false).reverse().map(([index, item]) => ({\n index,\n item\n }))\n after = createLoopingListWithIndex(children, 0, paddingItemCount).map(([index, item]) => ({\n index,\n item\n }))\n\n items = [...before, ...items, ...after]\n }\n\n const handlePointerDown = (e: React.PointerEvent) => {\n setDragState({\n dragOffsetX: 0,\n dragStartX: e.clientX,\n })\n }\n\n const handlePointerMove = (e: React.PointerEvent) => {\n if (!dragState) return\n setDragState(prevState => ({ dragStartX: prevState.dragStartX, dragOffsetX: e.clientX - prevState.dragStartX }))\n }\n\n const handlePointerUp = () => {\n if (!dragState) return\n if (dragState.dragOffsetX > 50) {\n left()\n } else if (dragState.dragOffsetX < -50) {\n right()\n }\n setDragState(undefined)\n }\n\n useEffect(() => {\n setDisableClick(!dragState)\n }, [dragState])\n\n useEffect(() => {\n onSlideChanged?.(currentIndex)\n }, [currentIndex]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <CarouselContext.Provider value={{ id, currentIndex, slideCount: length, isLooping }}>\n <div\n ref={carouselContainerRef}\n {...props}\n className={clsx('flex-col-2 items-center w-full', props.className)}\n\n id={id}\n role=\"region\"\n aria-roledescription={translation('slide')}\n >\n <div\n {...slideContainerProps}\n className={clsx(`relative w-full overflow-hidden`, heightClassName, slideContainerProps?.className)}\n >\n {hintNext ? (\n <div\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerLeave={handlePointerUp}\n className={clsx(`flex-row-2 relative h-full`, heightClassName)}\n >\n <div className=\"flex-row-2 relative h-full w-full px-2 overflow-hidden\">\n {items.map(({\n item,\n index\n }, listIndex) => {\n const isInItems = before.length <= listIndex && listIndex < items.length - after.length\n\n return (\n <CarouselSlide\n ref={isInItems ? slideRefs[index] : undefined}\n key={listIndex}\n index={index}\n className={clsx(\n `absolute left-[50%] h-full overflow-hidden transition-transform ease-in-out`,\n widthClassName\n )}\n onClick={() => !disableClick && setCurrentIndex(index)}\n style={{\n translate: `calc(${getStyleOffset(listIndex - (isLooping ? paddingItemCount : 0))} + ${dragState ? dragState.dragOffsetX : 0}px)`,\n transitionDuration: dragState ? '0ms' : ((isAutoPlaying && !isPaused ? autoLoopAnimationTime : animationTime) + 'ms'),\n }}\n >\n {item}\n </CarouselSlide>\n )\n })}\n </div>\n <div\n className={clsx(`hidden desktop:block pointer-events-none absolute left-0 h-full w-[20%] bg-gradient-to-r to-transparent`, blurColor)}\n />\n <div\n className={clsx(`hidden desktop:block pointer-events-none absolute right-0 h-full w-[20%] bg-gradient-to-l to-transparent`, blurColor)}\n />\n </div>\n ) : (\n <div\n ref={slideRefs[currentIndex]}\n className={clsx('px-16 h-full')}\n\n tabIndex={0}\n role=\"group\"\n aria-roledescription={translation('slide')}\n aria-label={translation('slideOf', {\n replacements: {\n index: (currentIndex + 1).toString(),\n length: items.length.toString()\n }\n })}\n >\n {children[currentIndex]}\n </div>\n )}\n {arrows && (\n <>\n <IconButton\n color=\"neutral\"\n className={clsx('absolute z-10 left-2 top-1/2 -translate-y-1/2 shadow-md', { hidden: !canGoLeft() })}\n disabled={!canGoLeft()}\n onClick={() => left()}\n >\n <ChevronLeft size={24}/>\n </IconButton>\n <IconButton\n color=\"neutral\"\n className={clsx('absolute z-10 right-2 top-1/2 -translate-y-1/2 shadow-md', { hidden: !canGoRight() })}\n disabled={!canGoRight()}\n onClick={() => right()}\n >\n <ChevronRight size={24}/>\n </IconButton>\n </>\n )}\n </div>\n {dots && (<CarouselTabs onChange={setCurrentIndex}/>)}\n </div>\n </CarouselContext.Provider>\n )\n}\n","export const equalSizeGroups = <T>(array: T[], groupSize: number): T[][] => {\n if (groupSize <= 0) {\n console.warn(`group size should be greater than 0: groupSize = ${groupSize}`)\n return [[...array]]\n }\n\n const groups = []\n for (let i = 0; i < array.length; i += groupSize) {\n groups.push(array.slice(i, Math.min(i + groupSize, array.length)))\n }\n return groups\n}\n\nexport type RangeOptions = {\n /** Whether the range can be defined empty via end < start without a warning */\n allowEmptyRange: boolean,\n stepSize: number,\n exclusiveStart: boolean,\n exclusiveEnd: boolean,\n}\n\nconst defaultRangeOptions: RangeOptions = {\n allowEmptyRange: false,\n stepSize: 1,\n exclusiveStart: false,\n exclusiveEnd: true,\n}\n\n/**\n * @param endOrRange The end value or a range [start, end], end is exclusive\n * @param options the options for defining the range\n */\nexport const range = (endOrRange: number | [number, number], options?: Partial<RangeOptions>): number[] => {\n const { allowEmptyRange, stepSize, exclusiveStart, exclusiveEnd } = { ...defaultRangeOptions, ...options }\n let start = 0\n let end: number\n if (typeof endOrRange === 'number') {\n end = endOrRange\n } else {\n start = endOrRange[0]\n end = endOrRange[1]\n }\n if (!exclusiveEnd) {\n end -= 1\n }\n if (exclusiveStart) {\n start += 1\n }\n\n if (end - 1 < start) {\n if (!allowEmptyRange) {\n console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set options.allowEmptyRange to true`)\n }\n return []\n }\n return Array.from({ length: end - start }, (_, index) => index * stepSize + start)\n}\n\n/** Finds the closest match\n * @param list The list of all possible matches\n * @param firstCloser Return whether item1 is closer than item2\n */\nexport const closestMatch = <T>(list: T[], firstCloser: (item1: T, item2: T) => boolean) => {\n return list.reduce((item1, item2) => {\n return firstCloser(item1, item2) ? item1 : item2\n })\n}\n\n/**\n * returns the item in middle of a list and its neighbours before and after\n * e.g. [1,2,3,4,5,6] for item = 1 would return [5,6,1,2,3]\n */\nexport const getNeighbours = <T>(list: T[], item: T, neighbourDistance: number = 2) => {\n const index = list.indexOf(item)\n const totalItems = neighbourDistance * 2 + 1\n if (list.length < totalItems) {\n console.warn('List is to short')\n return list\n }\n\n if (index === -1) {\n console.error('item not found in list')\n return list.splice(0, totalItems)\n }\n\n let start = index - neighbourDistance\n if (start < 0) {\n start += list.length\n }\n const end = (index + neighbourDistance + 1) % list.length\n\n const result: T[] = []\n let ignoreOnce = list.length === totalItems\n for (let i = start; i !== end || ignoreOnce; i = (i + 1) % list.length) {\n result.push(list[i]!)\n if (end === i && ignoreOnce) {\n ignoreOnce = false\n }\n }\n return result\n}\n\nexport const createLoopingListWithIndex = <T>(list: T[], startIndex: number = 0, length: number = 0, forwards: boolean = true) => {\n if (length < 0) {\n console.warn(`createLoopingList: length must be >= 0, given ${length}`)\n } else if (length === 0) {\n length = list.length\n }\n\n const returnList: [number, T][] = []\n\n if (forwards) {\n for (let i = startIndex; returnList.length < length; i = (i + 1) % list.length) {\n returnList.push([i, list[i]!])\n }\n } else {\n for (let i = startIndex; returnList.length < length; i = i === 0 ? i = list.length - 1 : i - 1) {\n returnList.push([i, list[i]!])\n }\n }\n\n return returnList\n}\n\nexport const createLoopingList = <T>(list: T[], startIndex: number = 0, length: number = 0, forwards: boolean = true) => {\n return createLoopingListWithIndex(list, startIndex, length, forwards).map(([_, item]) => item)\n}\n\n/**\n * @param list The list to be changed\n * @param move The shifting applied to the array (can be negative)\n */\nconst moveItems = <T>(list: T[], move: number = 0) => {\n const result = []\n let start = move\n if (start < 0) {\n start = list.length - move\n }\n start = start % list.length\n for (let i = 0; i < list.length; i++) {\n result[i] = list[(i + start) % list.length]\n }\n return result\n}\n\nexport const ArrayUtil = {\n unique: <T>(list: T[]): T[] => {\n const seen = new Set<T>()\n return list.filter((item) => {\n if (seen.has(item)) {\n return false\n }\n seen.add(item)\n return true\n })\n },\n difference: <T>(list: T[], removeList: T[]): T[] => {\n const remove = new Set<T>(removeList)\n return list.filter((item) => !remove.has(item))\n },\n moveItems,\n}\n","import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { forwardRef } from 'react'\nimport clsx from 'clsx'\n\n\nexport const ButtonColorUtil = {\n solid: ['primary', 'secondary', 'tertiary', 'positive', 'warning', 'negative', 'neutral'] as const,\n text: ['primary', 'negative', 'neutral'] as const,\n outline: ['primary'] as const,\n}\n\nexport const IconButtonUtil = {\n icon: [...ButtonColorUtil.solid, 'transparent'] as const,\n}\n\n\n/**\n * The allowed colors for the SolidButton and IconButton\n */\nexport type SolidButtonColor = typeof ButtonColorUtil.solid[number]\n/**\n * The allowed colors for the OutlineButton\n */\nexport type OutlineButtonColor = typeof ButtonColorUtil.outline[number]\n/**\n * The allowed colors for the TextButton\n */\nexport type TextButtonColor = typeof ButtonColorUtil.text[number]\n/**\n * The allowed colors for the IconButton\n */\nexport type IconButtonColor = typeof IconButtonUtil.icon[number]\n\n\n/**\n * The different sizes for a button\n */\ntype ButtonSizes = 'small' | 'medium' | 'large' | 'none'\n\ntype IconButtonSize = 'tiny' | 'small' | 'medium' | 'large' | 'none'\n\n/**\n * The shard properties between all button types\n */\nexport type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n /**\n * @default 'medium'\n */\n size?: ButtonSizes,\n}\n\nconst paddingMapping: Record<ButtonSizes, string> = {\n none: '',\n small: 'btn-sm',\n medium: 'btn-md',\n large: 'btn-lg'\n}\n\nconst iconPaddingMapping: Record<IconButtonSize, string> = {\n none: '',\n tiny: 'icon-btn-xs',\n small: 'icon-btn-sm',\n medium: 'icon-btn-md',\n large: 'icon-btn-lg'\n}\n\nexport const ButtonUtil = {\n paddingMapping,\n iconPaddingMapping\n}\n\ntype ButtonWithIconsProps = ButtonProps & {\n startIcon?: ReactNode,\n endIcon?: ReactNode,\n}\n\nexport type SolidButtonProps = ButtonWithIconsProps & {\n color?: SolidButtonColor,\n}\n\nexport type OutlineButtonProps = ButtonWithIconsProps & {\n color?: OutlineButtonColor,\n}\n\nexport type TextButtonProps = ButtonWithIconsProps & {\n color?: TextButtonColor,\n coloredHoverBackground?: boolean,\n}\n\n/**\n * The shard properties between all button types\n */\nexport type IconButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n /**\n * @default 'medium'\n */\n size?: IconButtonSize,\n color?: IconButtonColor,\n}\n\n/**\n * A button with a solid background and different sizes\n */\nexport const SolidButton = forwardRef<HTMLButtonElement, SolidButtonProps>(function SolidButton({\n children,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }, ref) {\n const colorClasses = {\n primary: 'not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text',\n secondary: 'not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text',\n tertiary: 'not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text',\n positive: 'not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text',\n warning: 'not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text',\n negative: 'not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text',\n neutral: 'not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'not-group-disabled:text-button-solid-primary-icon',\n secondary: 'not-group-disabled:text-button-solid-secondary-icon',\n tertiary: 'not-group-disabled:text-button-solid-tertiary-icon',\n positive: 'not-group-disabled:text-button-solid-positive-icon',\n warning: 'not-group-disabled:text-button-solid-warning-icon',\n negative: 'not-group-disabled:text-button-solid-negative-icon',\n neutral: 'not-group-disabled:text-button-solid-neutral-icon',\n }[color]\n\n return (\n <button\n ref={ref}\n onClick={onClick}\n className={clsx(\n 'group font-semibold',\n colorClasses,\n 'not-disabled:hover:brightness-90',\n 'disabled:text-disabled disabled:bg-disabled-background',\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n})\n\n/**\n * A button with an outline border and different sizes\n */\nexport const OutlineButton = ({\n children,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: OutlineButtonProps) => {\n const colorClasses = {\n primary: 'not-disabled:border-button-outline-primary-text not-disabled:text-button-outline-primary-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'not-group-disabled:text-button-outline-primary-icon',\n }[color]\n return (\n <button\n onClick={onClick}\n className={clsx(\n 'group font-semibold bg-transparent border-2 ',\n 'not-disabled:hover:brightness-80',\n colorClasses,\n 'disabled:text-disabled disabled:border-disabled-outline',\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n/**\n * A text that is a button that can have different sizes\n */\nexport const TextButton = ({\n children,\n color = 'neutral',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n coloredHoverBackground = true,\n className,\n ...restProps\n }: TextButtonProps) => {\n const colorClasses = {\n primary: 'not-disabled:bg-transparent not-disabled:text-button-text-primary-text not-disabled:focus-visible:outline-button-text-primary-text',\n negative: 'not-disabled:bg-transparent not-disabled:text-button-text-negative-text not-disabled:focus-visible:outline-button-text-negative-text',\n neutral: 'not-disabled:bg-transparent not-disabled:text-button-text-neutral-text not-disabled:focus-visible:outline-button-text-neutral-text',\n }[color]\n\n const backgroundColor = {\n primary: 'not-disabled:hover:bg-button-text-primary-text/20 not-disabled:focus-visible:bg-button-text-primary-text/20',\n negative: 'not-disabled:hover:bg-button-text-negative-text/20 not-disabled:focus-visible:bg-button-text-negative-text/20',\n neutral: 'not-disabled:hover:bg-button-text-neutral-text/20 not-disabled:focus-visible:bg-button-text-neutral-text/20',\n }[color]\n\n const iconColorClasses = {\n primary: 'not-group-disabled:text-button-text-primary-icon',\n negative: 'not-group-disabled:text-button-text-negative-icon',\n neutral: 'not-group-disabled:text-button-text-neutral-icon',\n }[color]\n\n return (\n <button\n onClick={onClick}\n className={clsx(\n 'group font-semibold',\n 'disabled:text-disabled',\n colorClasses,\n {\n [backgroundColor]: coloredHoverBackground,\n 'not-disabled:hover:bg-button-text-hover-background': !coloredHoverBackground,\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n\n/**\n * A button for icons with a solid background and different sizes\n */\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton({\n children,\n color = 'primary',\n size = 'medium',\n className,\n ...restProps\n }, ref)\n{\n const colorClasses = {\n primary: 'not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text',\n secondary: 'not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text',\n tertiary: 'not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text',\n positive: 'not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text',\n warning: 'not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text',\n negative: 'not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text',\n neutral: 'not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text',\n transparent: 'not-disabled:bg-transparent',\n }[color]\n\n return (\n <button\n ref={ref}\n className={clsx(\n colorClasses,\n 'not-disabled:hover:brightness-90',\n 'disabled:text-disabled',\n {\n 'disabled:bg-disabled-background': color !== 'transparent',\n 'disabled:opacity-70': color === 'transparent',\n 'not-disabled:hover:bg-button-text-hover-background': color === 'transparent',\n },\n ButtonUtil.iconPaddingMapping[size],\n className\n )}\n {...restProps}\n >\n {children}\n </button>\n )\n})","import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'\nimport { createContext, useContext, useEffect, useMemo, useState } from 'react'\nimport { useLocalStorage } from '../hooks/useLocalStorage'\nimport type { Language } from './util'\nimport { LanguageUtil } from './util'\n\nexport type LanguageContextValue = {\n language: Language,\n setLanguage: Dispatch<SetStateAction<Language>>,\n}\n\nexport const LanguageContext = createContext<LanguageContextValue>({\n language: LanguageUtil.DEFAULT_LANGUAGE,\n setLanguage: (v) => v\n})\n\ntype LanguageWithSystem = Language | 'system'\n\ntype LanguageProviderProps = {\n language?: LanguageWithSystem,\n}\n\nexport const LanguageProvider = ({ children, language }: PropsWithChildren<LanguageProviderProps>) => {\n const {\n value: storedLanguage,\n setValue: setStoredLanguage,\n deleteValue: deleteStoredLanguage\n } = useLocalStorage<LanguageWithSystem>('language', 'system')\n const [languagePreference, setLanguagePreference] = useState<LanguageWithSystem>('system')\n\n const resolvedLanguage = useMemo(() => {\n if (language && language !== 'system') {\n return language\n }\n if (storedLanguage && storedLanguage !== 'system') {\n return storedLanguage\n }\n if (languagePreference !== 'system') {\n return languagePreference\n }\n return LanguageUtil.DEFAULT_LANGUAGE\n }, [language, languagePreference, storedLanguage])\n\n useEffect(() => {\n if(!language) return\n if (language === 'system') {\n deleteStoredLanguage()\n } else {\n setStoredLanguage(language)\n }\n }, [language]) // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n const LanguageToTestAgainst = Object.values(LanguageUtil.languages)\n\n const detectLanguage = () => {\n const matchingBrowserLanguage = window.navigator.languages\n .map(language =>\n LanguageToTestAgainst.find(\n (test) => language === test || language.split('-')[0] === test\n ))\n .filter((entry): entry is Language => entry !== undefined)\n\n if (matchingBrowserLanguage.length === 0) return\n\n const firstMatch = matchingBrowserLanguage[0]\n setLanguagePreference(firstMatch)\n }\n detectLanguage()\n\n window.addEventListener('languagechange', detectLanguage)\n return () => {\n window.removeEventListener('languagechange', detectLanguage)\n }\n }, [])\n\n return (\n <LanguageContext.Provider value={{\n language: resolvedLanguage,\n setLanguage: (newLanguage) => {\n if (language !== 'system') {\n console.warn('LanguageProvider: Attempting to change the ' +\n \"language while setting a fixed language won't have any effect. \" +\n 'Change the language provided to the LanguageProvider instead.')\n }\n setStoredLanguage(newLanguage)\n }\n }}>\n {children}\n </LanguageContext.Provider>\n )\n}\n\nexport const useLanguage = () => {\n const context = useContext(LanguageContext)\n if (!context) {\n throw new Error('useLanguage must be used within LanguageContext. Try adding a LanguageProvider around your app.')\n }\n return context\n}\n\nexport const useLocale = (overWriteLanguage?: Language) => {\n const { language } = useLanguage()\n const mapping: Record<Language, string> = {\n en: 'en-US',\n de: 'de-DE'\n }\n return mapping[overWriteLanguage ?? language]\n}\n","'use client'\n\nimport type { Dispatch, SetStateAction } from 'react'\nimport { useCallback, useState } from 'react'\nimport { LocalStorageService } from '@/src/utils/storage'\nimport { resolveSetState } from '@/src/utils/resolveSetState'\n\ntype SetValue<T> = Dispatch<SetStateAction<T>>\n\ntype UseLocalStorageResult<T> = {\n value: T,\n setValue: SetValue<T>,\n deleteValue: () => void,\n}\n\n/**\n * @param key Key under which to save the data\n * @param backupValue Used if the storage is unavailable or no value is present\n *\n * The backup value will never be saved to the storage unless you explicitly\n */\nexport const useLocalStorage = <T>(key: string, backupValue: T): UseLocalStorageResult<T> => {\n const get = useCallback((): T => {\n if (typeof window === 'undefined') {\n return backupValue\n }\n const storageService = new LocalStorageService()\n try {\n const value = storageService.get<T>(key)\n return value || backupValue\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_) {\n console.warn('useLocalStorage: Error while reading the stored value. Make sure your typing is correct.')\n storageService.delete(key)\n console.info(`useLocalStorage: deleted erroneous value for key: ${key}`)\n return backupValue\n }\n }, [backupValue, key])\n\n const [storedValue, setStoredValue] = useState<T>(get)\n\n const setValue: SetValue<T> = useCallback(action => {\n const newValue = resolveSetState(action, storedValue)\n const storageService = new LocalStorageService()\n storageService.set(key, newValue)\n\n setStoredValue(newValue)\n }, [storedValue, setStoredValue, key])\n\n const deleteValue = () => {\n const storageService = new LocalStorageService()\n storageService.delete(key)\n setStoredValue(backupValue)\n }\n\n return { value: storedValue, setValue, deleteValue }\n}","/**\n * The supported languages\n */\nconst languages = ['en', 'de'] as const\n\n/**\n * The supported languages\n */\nexport type Language = typeof languages[number]\n\n/**\n * The supported languages' names in their respective language\n */\nconst languagesLocalNames: Record<Language, string> = {\n en: 'English',\n de: 'Deutsch',\n}\n\n/**\n * The default language\n */\nconst DEFAULT_LANGUAGE: Language = 'en'\n\n/**\n * A constant definition for holding data regarding languages\n */\nexport const LanguageUtil = {\n languages,\n DEFAULT_LANGUAGE,\n languagesLocalNames,\n}","import { useLanguage } from './LanguageProvider'\nimport type { Language } from './util'\n\n/**\n * A type describing the pluralization of a word\n */\nexport type TranslationPlural = {\n zero?: string,\n one?: string,\n two?: string,\n few?: string,\n many?: string,\n other: string,\n}\n\n/**\n * The type describing all values of a translation\n */\nexport type TranslationType = Record<string, string | TranslationPlural>\n\n/**\n * The type of translations\n */\nexport type Translation<T extends TranslationType> = Record<Language, T>\n\ntype OverwriteTranslationType<T extends TranslationType> = {\n language?: Language,\n translation?: Translation<Partial<T>>,\n}\n\n/**\n * Adds the `language` prop to the component props.\n *\n * @param Translation the type of the translation object\n *\n * @param Props the type of the component props, defaults to `Record<string, never>`,\n * if you don't expect any other props other than `language` and get an\n * error when using your component (because it uses `forwardRef` etc.)\n * you can try out `Record<string, unknown>`, this might resolve your\n * problem as `SomeType & never` is still `never` but `SomeType & unknown`\n * is `SomeType` which means that adding back props (like `ref` etc.)\n * works properly\n */\nexport type PropsForTranslation<\n Translation extends TranslationType,\n Props = unknown\n> = Props & {\n overwriteTranslation?: OverwriteTranslationType<Translation>,\n}\n\ntype StringKeys<T> = Extract<keyof T, string>;\n\ntype TranslationFunctionOptions = {\n replacements?: Record<string, string>,\n count?: number,\n}\ntype TranslationFunction<T extends TranslationType> = (key: StringKeys<T>, options?: TranslationFunctionOptions) => string\n\nexport const TranslationPluralCount = {\n zero: 0,\n one: 1,\n two: 2,\n few: 3,\n many: 11,\n other: -1,\n}\n\n\nexport const useTranslation = <T extends TranslationType>(\n translations: Translation<Partial<TranslationType>>[],\n overwriteTranslation: OverwriteTranslationType<T> = {}\n): TranslationFunction<T> => {\n const { language: languageProp, translation: overwrite } = overwriteTranslation\n const { language: inferredLanguage } = useLanguage()\n const usedLanguage = languageProp ?? inferredLanguage\n const usedTranslations = [...translations]\n if (overwrite) {\n usedTranslations.push(overwrite)\n }\n\n return (key: StringKeys<T>, options?: TranslationFunctionOptions): string => {\n const { count, replacements } = { ...{ count: 0, replacements: {} }, ...options }\n\n try {\n for (let i = translations.length - 1; i >= 0; i--) {\n const translation = translations[i]\n const localizedTranslation = translation[usedLanguage]\n if (!localizedTranslation) {\n continue\n }\n const value = localizedTranslation[key]\n if(!value) {\n continue\n }\n\n let forProcessing: string\n if (typeof value !== 'string') {\n if (count === TranslationPluralCount.zero && value?.zero) {\n forProcessing = value.zero\n } else if (count === TranslationPluralCount.one && value?.one) {\n forProcessing = value.one\n } else if (count === TranslationPluralCount.two && value?.two) {\n forProcessing = value.two\n } else if (TranslationPluralCount.few <= count && count < TranslationPluralCount.many && value?.few) {\n forProcessing = value.few\n } else if (count > TranslationPluralCount.many && value?.many) {\n forProcessing = value.many\n } else {\n forProcessing = value.other\n }\n } else {\n forProcessing = value\n }\n forProcessing = forProcessing.replace(/\\{\\{(\\w+)}}/g, (_, placeholder) => {\n return replacements[placeholder] ?? `{{key:${placeholder}}}` // fallback if key is missing\n })\n return forProcessing\n }\n } catch (e) {\n console.error(e)\n }\n return `{{${usedLanguage}:${key}}}`\n }\n}"],"mappings":"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","names":["createContext","forwardRef","useCallback","useContext","useEffect","useMemo","useState","clsx","SolidButton","IconButton","useState","jsx","jsx","jsxs","createContext","useContext","clsx","forwardRef","CarouselSlide","useState","useMemo","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../../../src/components/layout/Carousel.tsx","../../../src/utils/array.ts","../../../src/components/user-action/Button.tsx","../../../src/localization/LanguageProvider.tsx","../../../src/hooks/useLocalStorage.ts","../../../src/localization/util.ts","../../../src/localization/useTranslation.ts"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from 'react'\nimport React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState\n} from 'react'\nimport clsx from 'clsx'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\nimport { createLoopingListWithIndex, range } from '@/src/utils/array'\nimport { IconButton } from '../user-action/Button'\nimport type { Translation } from '@/src/localization/useTranslation'\nimport { useTranslation } from '@/src/localization/useTranslation'\n\n//\n// CarouselContext\n//\ntype CarouselContextType = {\n id: string,\n currentIndex: number,\n slideCount: number,\n isLooping: boolean,\n}\n\nconst CarouselContext = createContext<CarouselContextType | null>(null)\n\nconst useCarouselContext = () => {\n const context = useContext(CarouselContext)\n if (!context) {\n console.error('useCarouselContext must be used within CarouselContext')\n }\n return context\n}\n\n//\n// CarouselTab\n//\ntype CarouselTabTranslationType = {\n showSlide: string,\n slideNavigation: string,\n}\n\nconst defaultCarouselTabTranslationType: Translation<CarouselTabTranslationType> = {\n en: {\n showSlide: `Show Slide {{index}}`,\n slideNavigation: 'Slide navigation'\n },\n de: {\n showSlide: 'Zeige Slide {{index}}',\n slideNavigation: 'Slide Navigation',\n }\n}\n\ntype CarouselTabsProps = {\n onChange: (index: number) => void,\n}\n\nexport default function CarouselTabs({\n onChange,\n }: CarouselTabsProps) {\n const translation = useTranslation<CarouselTabTranslationType>([\n defaultCarouselTabTranslationType,\n ])\n const { id, slideCount, currentIndex, isLooping } = useCarouselContext()\n\n const tabRefs = useRef<(HTMLButtonElement | null)[]>([])\n\n const handleKeyDown = (event: React.KeyboardEvent, index: number) => {\n let newIndex = index\n if (event.key === 'ArrowRight') {\n newIndex = isLooping ? (index + 1) % slideCount : Math.max(index + 1, slideCount - 1)\n } else if (event.key === 'ArrowLeft') {\n newIndex = isLooping ? (index - 1 + slideCount) % slideCount : Math.max(index - 1, 0)\n } else {\n return\n }\n event.preventDefault()\n onChange(newIndex)\n tabRefs.current[newIndex]?.focus()\n }\n\n return (\n <div\n className=\"flex-row-1 items-center justify-center w-full my-2\"\n role=\"tablist\"\n aria-label={translation('slideNavigation')}\n id={`${id}-tablist`}\n >\n {range(slideCount).map((index) => {\n const isSelected = currentIndex === index\n return (\n <button\n id={`${id}-tab-${index}`}\n key={index}\n ref={(el) => (tabRefs.current[index] = el)}\n\n onClick={() => onChange(index)}\n onKeyDown={(e) => handleKeyDown(e, index)}\n\n className={clsx(\n 'w-8 min-w-8 h-3 min-h-3 first:rounded-l-md last:rounded-r-md',\n {\n 'bg-carousel-dot-disabled hover:bg-carousel-dot-active': currentIndex !== index,\n 'bg-carousel-dot-active hover:brightness-90': currentIndex === index,\n }\n )}\n\n role=\"tab\"\n tabIndex={isSelected ? 0 : -1}\n aria-label={translation('showSlide', { replacements: { index: (index + 1).toString() } })}\n aria-selected={isSelected}\n aria-controls={`slide-${index}`}\n aria-disabled={isSelected}\n />\n )\n })}\n </div>\n )\n}\n\n//\n// CarouselSlide\n//\ntype CarouselSlideTranslationType = {\n slide: string,\n slideOf: string,\n}\n\nconst defaultCarouselSlideTranslationType: Translation<CarouselSlideTranslationType> = {\n en: {\n slide: 'Slide',\n slideOf: `Slide {{index}} of {{length}} slides`,\n },\n de: {\n slide: 'Slide',\n slideOf: `Slide {{index}} von {{length}} slides`,\n }\n}\n\nexport interface CarouselSlideProps extends HTMLAttributes<HTMLDivElement> {\n isSelected: boolean,\n index: number,\n}\n\nexport const CarouselSlide = forwardRef<HTMLDivElement, CarouselSlideProps>(\n function CarouselSlide({\n index,\n isSelected,\n ...props\n }, ref) {\n const translation = useTranslation<CarouselSlideTranslationType>([defaultCarouselSlideTranslationType])\n const { id, slideCount } = useCarouselContext()\n\n return (\n <div\n {...props}\n ref={ref}\n id={`${id}-slide-${index}`}\n\n className={clsx('focus-style-none group/slide', props.className)}\n\n tabIndex={isSelected ? 0 : undefined}\n role=\"group\"\n aria-roledescription={translation('slide')}\n aria-label={translation('slideOf', {\n replacements: {\n index: (index + 1).toString(),\n length: (slideCount).toString(),\n },\n })}\n aria-hidden={isSelected ? undefined : true}\n />\n )\n }\n)\n\n//\n// Carousel\n//\ntype DragState = {\n dragStartX: number,\n dragOffsetX: number,\n}\n\ntype CarouselTranslationType = {\n slide: string,\n carousel: string,\n slideOf: string,\n chooseSlide: string,\n}\n\nconst defaultCarouselTranslationType: Translation<CarouselTranslationType> = {\n en: {\n slide: 'Slide',\n carousel: 'Carousel',\n slideOf: `Slide {{index}} of {{length}} slides`,\n chooseSlide: 'Choose slide to display'\n },\n de: {\n slide: 'Slide',\n carousel: 'Karussell',\n slideOf: `Slide {{index}} von {{length}} slides`,\n chooseSlide: 'Wähle die angezeigte Slide aus'\n }\n}\n\ntype ItemType = {\n item: ReactNode,\n index: number,\n}\n\nexport type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {\n children: ReactNode[],\n animationTime?: number,\n isLooping?: boolean,\n isAutoPlaying?: boolean,\n autoLoopingTimeOut?: number,\n autoLoopAnimationTime?: number,\n hintNext?: boolean,\n arrows?: boolean,\n dots?: boolean,\n /**\n * Percentage that is allowed to be scrolled further\n */\n overScrollThreshold?: number,\n blurColor?: string,\n heightClassName?: string,\n slideClassName?: string,\n slideContainerProps?: HTMLAttributes<HTMLDivElement>,\n onSlideChanged?: (index: number) => void,\n}\n\nexport const Carousel = ({\n children,\n animationTime = 200,\n isLooping = false,\n isAutoPlaying = false,\n autoLoopingTimeOut = 5000,\n autoLoopAnimationTime = 1000,\n hintNext = false,\n arrows = false,\n dots = true,\n blurColor = 'from-background',\n heightClassName = 'h-96',\n slideClassName = 'w-[70%] desktop:w-1/2',\n slideContainerProps,\n onSlideChanged,\n ...props\n }: CarouselProps) => {\n const translation = useTranslation([defaultCarouselTranslationType])\n const slideRefs = useRef<HTMLDivElement[]>([])\n const [currentIndex, setCurrentIndex] = useState<number>(0)\n const [hasFocus, setHasFocus] = useState(false)\n const [dragState, setDragState] = useState<DragState>()\n const isPaused = hasFocus\n const carouselContainerRef = useRef<HTMLDivElement>(null)\n const [disableClick, setDisableClick] = useState(false)\n\n const timeOut = useRef<NodeJS.Timeout | undefined>(undefined)\n\n const length = useMemo(() => children.length, [children])\n const paddingItemCount = 3 // The number of items to append left and right of the list to allow for clean transition when looping\n\n const generatedId = 'carousel' + useId()\n const id = props.id ?? generatedId\n\n // Validation\n if (isAutoPlaying && !isLooping) {\n console.error('When isAutoLooping is true, isLooping should also be true')\n isLooping = true\n }\n autoLoopingTimeOut = Math.max(0, autoLoopingTimeOut) // time between transitions\n animationTime = Math.max(100, animationTime) // in ms, must be > 0\n autoLoopAnimationTime = Math.max(200, autoLoopAnimationTime)\n\n useEffect(() => {\n const carousel = carouselContainerRef.current\n\n if (carousel) {\n function onFocus() {\n setHasFocus(true)\n }\n\n function onBlur() {\n setHasFocus(false)\n }\n\n\n carousel?.addEventListener('focusin', onFocus)\n carousel?.addEventListener('focusout', onBlur)\n return () => {\n carousel?.removeEventListener('focusin', onFocus)\n carousel?.removeEventListener('focusin', onFocus)\n }\n }\n }, [])\n\n const getStyleOffset = (index: number) => {\n const baseOffset = -50 + (index - currentIndex) * 100\n return `${baseOffset}%`\n }\n\n const canGoLeft = () => {\n return isLooping || currentIndex !== 0\n }\n\n const canGoRight = useCallback(() => {\n return isLooping || currentIndex !== length - 1\n }, [currentIndex, isLooping, length])\n\n const left = () => {\n if (canGoLeft()) {\n setCurrentIndex(currentIndex - 1)\n }\n }\n\n const right = useCallback(() => {\n if (canGoRight()) {\n setCurrentIndex((currentIndex + length + 1) % length)\n }\n }, [canGoRight, currentIndex, length])\n\n useEffect(() => {\n if (!timeOut.current && !isPaused) {\n if (autoLoopingTimeOut > 0) {\n timeOut.current = setTimeout(() => {\n right()\n timeOut.current = undefined\n }, autoLoopingTimeOut)\n } else {\n right()\n }\n }\n if ((isPaused || !!dragState) && timeOut.current) {\n clearTimeout(timeOut.current)\n timeOut.current = undefined\n }\n }, [right, isPaused, autoLoopingTimeOut, dragState])\n\n\n let items: ItemType[] = children.map((item, index) => ({\n index,\n item\n }))\n let before: ItemType[] = []\n let after: ItemType[] = []\n if (isLooping) {\n before = createLoopingListWithIndex(children, length - 1, paddingItemCount, false).reverse().map(([index, item]) => ({\n index,\n item\n }))\n after = createLoopingListWithIndex(children, 0, paddingItemCount).map(([index, item]) => ({\n index,\n item\n }))\n\n items = [...before, ...items, ...after]\n }\n\n const handlePointerDown = (e: React.PointerEvent) => {\n setDragState({\n dragOffsetX: 0,\n dragStartX: e.clientX,\n })\n }\n\n const handlePointerMove = (e: React.PointerEvent) => {\n if (!dragState) return\n setDragState(prevState => ({ dragStartX: prevState.dragStartX, dragOffsetX: e.clientX - prevState.dragStartX }))\n }\n\n const handlePointerUp = () => {\n if (!dragState) return\n if (dragState.dragOffsetX > 50) {\n left()\n } else if (dragState.dragOffsetX < -50) {\n right()\n }\n setDragState(undefined)\n }\n\n useEffect(() => {\n setDisableClick(!dragState)\n }, [dragState])\n\n useEffect(() => {\n onSlideChanged?.(currentIndex)\n }, [currentIndex]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <CarouselContext.Provider value={{ id, currentIndex, slideCount: length, isLooping }}>\n <div\n ref={carouselContainerRef}\n {...props}\n className={clsx('flex-col-2 items-center w-full', props.className)}\n\n id={id}\n role=\"region\"\n aria-roledescription={translation('slide')}\n >\n <div\n {...slideContainerProps}\n className={clsx(`relative w-full overflow-hidden`, heightClassName, slideContainerProps?.className)}\n >\n {hintNext ? (\n <div\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerLeave={handlePointerUp}\n className={clsx(`flex-row-2 relative h-full`, heightClassName)}\n >\n <div className=\"flex-row-2 relative h-full w-full px-2 overflow-hidden\">\n {items.map(({\n item,\n index\n }, listIndex) => {\n const isInItems = before.length <= listIndex && listIndex < items.length - after.length\n\n return (\n <CarouselSlide\n ref={isInItems ? slideRefs[index] : undefined}\n key={listIndex}\n index={index}\n isSelected={isInItems && currentIndex === index}\n className={clsx(\n `absolute left-[50%] h-full overflow-hidden transition-transform ease-in-out`,\n slideClassName\n )}\n onClick={() => !disableClick && setCurrentIndex(index)}\n style={{\n translate: `calc(${getStyleOffset(listIndex - (isLooping ? paddingItemCount : 0))} + ${dragState ? dragState.dragOffsetX : 0}px)`,\n transitionDuration: dragState ? '0ms' : ((isAutoPlaying && !isPaused ? autoLoopAnimationTime : animationTime) + 'ms'),\n }}\n >\n {item}\n </CarouselSlide>\n )\n })}\n </div>\n <div\n className={clsx(`hidden desktop:block pointer-events-none absolute left-0 h-full w-[20%] bg-gradient-to-r to-transparent`, blurColor)}\n />\n <div\n className={clsx(`hidden desktop:block pointer-events-none absolute right-0 h-full w-[20%] bg-gradient-to-l to-transparent`, blurColor)}\n />\n </div>\n ) : (\n <div\n ref={slideRefs[currentIndex]}\n className={clsx('px-16 h-full')}\n\n tabIndex={0}\n role=\"group\"\n aria-roledescription={translation('slide')}\n aria-label={translation('slideOf', {\n replacements: {\n index: (currentIndex + 1).toString(),\n length: items.length.toString()\n }\n })}\n >\n {children[currentIndex]}\n </div>\n )}\n {arrows && (\n <>\n <IconButton\n color=\"neutral\"\n className={clsx('absolute z-10 left-2 top-1/2 -translate-y-1/2 shadow-md', { hidden: !canGoLeft() })}\n disabled={!canGoLeft()}\n onClick={() => left()}\n >\n <ChevronLeft size={24}/>\n </IconButton>\n <IconButton\n color=\"neutral\"\n className={clsx('absolute z-10 right-2 top-1/2 -translate-y-1/2 shadow-md', { hidden: !canGoRight() })}\n disabled={!canGoRight()}\n onClick={() => right()}\n >\n <ChevronRight size={24}/>\n </IconButton>\n </>\n )}\n </div>\n {dots && (<CarouselTabs onChange={setCurrentIndex}/>)}\n </div>\n </CarouselContext.Provider>\n )\n}\n","export const equalSizeGroups = <T>(array: T[], groupSize: number): T[][] => {\n if (groupSize <= 0) {\n console.warn(`group size should be greater than 0: groupSize = ${groupSize}`)\n return [[...array]]\n }\n\n const groups = []\n for (let i = 0; i < array.length; i += groupSize) {\n groups.push(array.slice(i, Math.min(i + groupSize, array.length)))\n }\n return groups\n}\n\nexport type RangeOptions = {\n /** Whether the range can be defined empty via end < start without a warning */\n allowEmptyRange: boolean,\n stepSize: number,\n exclusiveStart: boolean,\n exclusiveEnd: boolean,\n}\n\nconst defaultRangeOptions: RangeOptions = {\n allowEmptyRange: false,\n stepSize: 1,\n exclusiveStart: false,\n exclusiveEnd: true,\n}\n\n/**\n * @param endOrRange The end value or a range [start, end], end is exclusive\n * @param options the options for defining the range\n */\nexport const range = (endOrRange: number | [number, number], options?: Partial<RangeOptions>): number[] => {\n const { allowEmptyRange, stepSize, exclusiveStart, exclusiveEnd } = { ...defaultRangeOptions, ...options }\n let start = 0\n let end: number\n if (typeof endOrRange === 'number') {\n end = endOrRange\n } else {\n start = endOrRange[0]\n end = endOrRange[1]\n }\n if (!exclusiveEnd) {\n end -= 1\n }\n if (exclusiveStart) {\n start += 1\n }\n\n if (end - 1 < start) {\n if (!allowEmptyRange) {\n console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set options.allowEmptyRange to true`)\n }\n return []\n }\n return Array.from({ length: end - start }, (_, index) => index * stepSize + start)\n}\n\n/** Finds the closest match\n * @param list The list of all possible matches\n * @param firstCloser Return whether item1 is closer than item2\n */\nexport const closestMatch = <T>(list: T[], firstCloser: (item1: T, item2: T) => boolean) => {\n return list.reduce((item1, item2) => {\n return firstCloser(item1, item2) ? item1 : item2\n })\n}\n\n/**\n * returns the item in middle of a list and its neighbours before and after\n * e.g. [1,2,3,4,5,6] for item = 1 would return [5,6,1,2,3]\n */\nexport const getNeighbours = <T>(list: T[], item: T, neighbourDistance: number = 2) => {\n const index = list.indexOf(item)\n const totalItems = neighbourDistance * 2 + 1\n if (list.length < totalItems) {\n console.warn('List is to short')\n return list\n }\n\n if (index === -1) {\n console.error('item not found in list')\n return list.splice(0, totalItems)\n }\n\n let start = index - neighbourDistance\n if (start < 0) {\n start += list.length\n }\n const end = (index + neighbourDistance + 1) % list.length\n\n const result: T[] = []\n let ignoreOnce = list.length === totalItems\n for (let i = start; i !== end || ignoreOnce; i = (i + 1) % list.length) {\n result.push(list[i]!)\n if (end === i && ignoreOnce) {\n ignoreOnce = false\n }\n }\n return result\n}\n\nexport const createLoopingListWithIndex = <T>(list: T[], startIndex: number = 0, length: number = 0, forwards: boolean = true) => {\n if (length < 0) {\n console.warn(`createLoopingList: length must be >= 0, given ${length}`)\n } else if (length === 0) {\n length = list.length\n }\n\n const returnList: [number, T][] = []\n\n if (forwards) {\n for (let i = startIndex; returnList.length < length; i = (i + 1) % list.length) {\n returnList.push([i, list[i]!])\n }\n } else {\n for (let i = startIndex; returnList.length < length; i = i === 0 ? i = list.length - 1 : i - 1) {\n returnList.push([i, list[i]!])\n }\n }\n\n return returnList\n}\n\nexport const createLoopingList = <T>(list: T[], startIndex: number = 0, length: number = 0, forwards: boolean = true) => {\n return createLoopingListWithIndex(list, startIndex, length, forwards).map(([_, item]) => item)\n}\n\n/**\n * @param list The list to be changed\n * @param move The shifting applied to the array (can be negative)\n */\nconst moveItems = <T>(list: T[], move: number = 0) => {\n const result = []\n let start = move\n if (start < 0) {\n start = list.length - move\n }\n start = start % list.length\n for (let i = 0; i < list.length; i++) {\n result[i] = list[(i + start) % list.length]\n }\n return result\n}\n\nexport const ArrayUtil = {\n unique: <T>(list: T[]): T[] => {\n const seen = new Set<T>()\n return list.filter((item) => {\n if (seen.has(item)) {\n return false\n }\n seen.add(item)\n return true\n })\n },\n difference: <T>(list: T[], removeList: T[]): T[] => {\n const remove = new Set<T>(removeList)\n return list.filter((item) => !remove.has(item))\n },\n moveItems,\n}\n","import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { forwardRef } from 'react'\nimport clsx from 'clsx'\n\n\nexport const ButtonColorUtil = {\n solid: ['primary', 'secondary', 'tertiary', 'positive', 'warning', 'negative', 'neutral'] as const,\n text: ['primary', 'negative', 'neutral'] as const,\n outline: ['primary'] as const,\n}\n\nexport const IconButtonUtil = {\n icon: [...ButtonColorUtil.solid, 'transparent'] as const,\n}\n\n\n/**\n * The allowed colors for the SolidButton and IconButton\n */\nexport type SolidButtonColor = typeof ButtonColorUtil.solid[number]\n/**\n * The allowed colors for the OutlineButton\n */\nexport type OutlineButtonColor = typeof ButtonColorUtil.outline[number]\n/**\n * The allowed colors for the TextButton\n */\nexport type TextButtonColor = typeof ButtonColorUtil.text[number]\n/**\n * The allowed colors for the IconButton\n */\nexport type IconButtonColor = typeof IconButtonUtil.icon[number]\n\n\n/**\n * The different sizes for a button\n */\ntype ButtonSizes = 'small' | 'medium' | 'large' | 'none'\n\ntype IconButtonSize = 'tiny' | 'small' | 'medium' | 'large' | 'none'\n\n/**\n * The shard properties between all button types\n */\nexport type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n /**\n * @default 'medium'\n */\n size?: ButtonSizes,\n}\n\nconst paddingMapping: Record<ButtonSizes, string> = {\n none: '',\n small: 'btn-sm',\n medium: 'btn-md',\n large: 'btn-lg'\n}\n\nconst iconPaddingMapping: Record<IconButtonSize, string> = {\n none: '',\n tiny: 'icon-btn-xs',\n small: 'icon-btn-sm',\n medium: 'icon-btn-md',\n large: 'icon-btn-lg'\n}\n\nexport const ButtonUtil = {\n paddingMapping,\n iconPaddingMapping\n}\n\ntype ButtonWithIconsProps = ButtonProps & {\n startIcon?: ReactNode,\n endIcon?: ReactNode,\n}\n\nexport type SolidButtonProps = ButtonWithIconsProps & {\n color?: SolidButtonColor,\n}\n\nexport type OutlineButtonProps = ButtonWithIconsProps & {\n color?: OutlineButtonColor,\n}\n\nexport type TextButtonProps = ButtonWithIconsProps & {\n color?: TextButtonColor,\n coloredHoverBackground?: boolean,\n}\n\n/**\n * The shard properties between all button types\n */\nexport type IconButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n /**\n * @default 'medium'\n */\n size?: IconButtonSize,\n color?: IconButtonColor,\n}\n\n/**\n * A button with a solid background and different sizes\n */\nexport const SolidButton = forwardRef<HTMLButtonElement, SolidButtonProps>(function SolidButton({\n children,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }, ref) {\n const colorClasses = {\n primary: 'not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text',\n secondary: 'not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text',\n tertiary: 'not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text',\n positive: 'not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text',\n warning: 'not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text',\n negative: 'not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text',\n neutral: 'not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'not-group-disabled:text-button-solid-primary-icon',\n secondary: 'not-group-disabled:text-button-solid-secondary-icon',\n tertiary: 'not-group-disabled:text-button-solid-tertiary-icon',\n positive: 'not-group-disabled:text-button-solid-positive-icon',\n warning: 'not-group-disabled:text-button-solid-warning-icon',\n negative: 'not-group-disabled:text-button-solid-negative-icon',\n neutral: 'not-group-disabled:text-button-solid-neutral-icon',\n }[color]\n\n return (\n <button\n ref={ref}\n onClick={onClick}\n className={clsx(\n 'group font-semibold',\n colorClasses,\n 'not-disabled:hover:brightness-90',\n 'disabled:text-disabled disabled:bg-disabled-background',\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n})\n\n/**\n * A button with an outline border and different sizes\n */\nexport const OutlineButton = ({\n children,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: OutlineButtonProps) => {\n const colorClasses = {\n primary: 'not-disabled:border-button-outline-primary-text not-disabled:text-button-outline-primary-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'not-group-disabled:text-button-outline-primary-icon',\n }[color]\n return (\n <button\n onClick={onClick}\n className={clsx(\n 'group font-semibold bg-transparent border-2 ',\n 'not-disabled:hover:brightness-80',\n colorClasses,\n 'disabled:text-disabled disabled:border-disabled-outline',\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n/**\n * A text that is a button that can have different sizes\n */\nexport const TextButton = ({\n children,\n color = 'neutral',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n coloredHoverBackground = true,\n className,\n ...restProps\n }: TextButtonProps) => {\n const colorClasses = {\n primary: 'not-disabled:bg-transparent not-disabled:text-button-text-primary-text not-disabled:focus-visible:outline-button-text-primary-text',\n negative: 'not-disabled:bg-transparent not-disabled:text-button-text-negative-text not-disabled:focus-visible:outline-button-text-negative-text',\n neutral: 'not-disabled:bg-transparent not-disabled:text-button-text-neutral-text not-disabled:focus-visible:outline-button-text-neutral-text',\n }[color]\n\n const backgroundColor = {\n primary: 'not-disabled:hover:bg-button-text-primary-text/20 not-disabled:focus-visible:bg-button-text-primary-text/20',\n negative: 'not-disabled:hover:bg-button-text-negative-text/20 not-disabled:focus-visible:bg-button-text-negative-text/20',\n neutral: 'not-disabled:hover:bg-button-text-neutral-text/20 not-disabled:focus-visible:bg-button-text-neutral-text/20',\n }[color]\n\n const iconColorClasses = {\n primary: 'not-group-disabled:text-button-text-primary-icon',\n negative: 'not-group-disabled:text-button-text-negative-icon',\n neutral: 'not-group-disabled:text-button-text-neutral-icon',\n }[color]\n\n return (\n <button\n onClick={onClick}\n className={clsx(\n 'group font-semibold',\n 'disabled:text-disabled',\n colorClasses,\n {\n [backgroundColor]: coloredHoverBackground,\n 'not-disabled:hover:bg-button-text-hover-background': !coloredHoverBackground,\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx(\n iconColorClasses,\n 'group-disabled:text-disabled-icon'\n )}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n\n/**\n * A button for icons with a solid background and different sizes\n */\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton({\n children,\n color = 'primary',\n size = 'medium',\n className,\n ...restProps\n }, ref)\n{\n const colorClasses = {\n primary: 'not-disabled:bg-button-solid-primary-background not-disabled:text-button-solid-primary-text',\n secondary: 'not-disabled:bg-button-solid-secondary-background not-disabled:text-button-solid-secondary-text',\n tertiary: 'not-disabled:bg-button-solid-tertiary-background not-disabled:text-button-solid-tertiary-text',\n positive: 'not-disabled:bg-button-solid-positive-background not-disabled:text-button-solid-positive-text',\n warning: 'not-disabled:bg-button-solid-warning-background not-disabled:text-button-solid-warning-text',\n negative: 'not-disabled:bg-button-solid-negative-background not-disabled:text-button-solid-negative-text',\n neutral: 'not-disabled:bg-button-solid-neutral-background not-disabled:text-button-solid-neutral-text',\n transparent: 'not-disabled:bg-transparent',\n }[color]\n\n return (\n <button\n ref={ref}\n className={clsx(\n colorClasses,\n 'not-disabled:hover:brightness-90',\n 'disabled:text-disabled',\n {\n 'disabled:bg-disabled-background': color !== 'transparent',\n 'disabled:opacity-70': color === 'transparent',\n 'not-disabled:hover:bg-button-text-hover-background': color === 'transparent',\n },\n ButtonUtil.iconPaddingMapping[size],\n className\n )}\n {...restProps}\n >\n {children}\n </button>\n )\n})","import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'\nimport { createContext, useContext, useEffect, useMemo, useState } from 'react'\nimport { useLocalStorage } from '../hooks/useLocalStorage'\nimport type { Language } from './util'\nimport { LanguageUtil } from './util'\n\nexport type LanguageContextValue = {\n language: Language,\n setLanguage: Dispatch<SetStateAction<Language>>,\n}\n\nexport const LanguageContext = createContext<LanguageContextValue>({\n language: LanguageUtil.DEFAULT_LANGUAGE,\n setLanguage: (v) => v\n})\n\ntype LanguageWithSystem = Language | 'system'\n\ntype LanguageProviderProps = {\n language?: LanguageWithSystem,\n}\n\nexport const LanguageProvider = ({ children, language }: PropsWithChildren<LanguageProviderProps>) => {\n const {\n value: storedLanguage,\n setValue: setStoredLanguage,\n deleteValue: deleteStoredLanguage\n } = useLocalStorage<LanguageWithSystem>('language', 'system')\n const [languagePreference, setLanguagePreference] = useState<LanguageWithSystem>('system')\n\n const resolvedLanguage = useMemo(() => {\n if (language && language !== 'system') {\n return language\n }\n if (storedLanguage && storedLanguage !== 'system') {\n return storedLanguage\n }\n if (languagePreference !== 'system') {\n return languagePreference\n }\n return LanguageUtil.DEFAULT_LANGUAGE\n }, [language, languagePreference, storedLanguage])\n\n useEffect(() => {\n if(!language) return\n if (language === 'system') {\n deleteStoredLanguage()\n } else {\n setStoredLanguage(language)\n }\n }, [language]) // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n const LanguageToTestAgainst = Object.values(LanguageUtil.languages)\n\n const detectLanguage = () => {\n const matchingBrowserLanguage = window.navigator.languages\n .map(language =>\n LanguageToTestAgainst.find(\n (test) => language === test || language.split('-')[0] === test\n ))\n .filter((entry): entry is Language => entry !== undefined)\n\n if (matchingBrowserLanguage.length === 0) return\n\n const firstMatch = matchingBrowserLanguage[0]\n setLanguagePreference(firstMatch)\n }\n detectLanguage()\n\n window.addEventListener('languagechange', detectLanguage)\n return () => {\n window.removeEventListener('languagechange', detectLanguage)\n }\n }, [])\n\n return (\n <LanguageContext.Provider value={{\n language: resolvedLanguage,\n setLanguage: (newLanguage) => {\n if (language !== 'system') {\n console.warn('LanguageProvider: Attempting to change the ' +\n \"language while setting a fixed language won't have any effect. \" +\n 'Change the language provided to the LanguageProvider instead.')\n }\n setStoredLanguage(newLanguage)\n }\n }}>\n {children}\n </LanguageContext.Provider>\n )\n}\n\nexport const useLanguage = () => {\n const context = useContext(LanguageContext)\n if (!context) {\n throw new Error('useLanguage must be used within LanguageContext. Try adding a LanguageProvider around your app.')\n }\n return context\n}\n\nexport const useLocale = (overWriteLanguage?: Language) => {\n const { language } = useLanguage()\n const mapping: Record<Language, string> = {\n en: 'en-US',\n de: 'de-DE'\n }\n return mapping[overWriteLanguage ?? language]\n}\n","'use client'\n\nimport type { Dispatch, SetStateAction } from 'react'\nimport { useCallback, useState } from 'react'\nimport { LocalStorageService } from '@/src/utils/storage'\nimport { resolveSetState } from '@/src/utils/resolveSetState'\n\ntype SetValue<T> = Dispatch<SetStateAction<T>>\n\ntype UseLocalStorageResult<T> = {\n value: T,\n setValue: SetValue<T>,\n deleteValue: () => void,\n}\n\n/**\n * @param key Key under which to save the data\n * @param backupValue Used if the storage is unavailable or no value is present\n *\n * The backup value will never be saved to the storage unless you explicitly\n */\nexport const useLocalStorage = <T>(key: string, backupValue: T): UseLocalStorageResult<T> => {\n const get = useCallback((): T => {\n if (typeof window === 'undefined') {\n return backupValue\n }\n const storageService = new LocalStorageService()\n try {\n const value = storageService.get<T>(key)\n return value || backupValue\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (_) {\n console.warn('useLocalStorage: Error while reading the stored value. Make sure your typing is correct.')\n storageService.delete(key)\n console.info(`useLocalStorage: deleted erroneous value for key: ${key}`)\n return backupValue\n }\n }, [backupValue, key])\n\n const [storedValue, setStoredValue] = useState<T>(get)\n\n const setValue: SetValue<T> = useCallback(action => {\n const newValue = resolveSetState(action, storedValue)\n const storageService = new LocalStorageService()\n storageService.set(key, newValue)\n\n setStoredValue(newValue)\n }, [storedValue, setStoredValue, key])\n\n const deleteValue = () => {\n const storageService = new LocalStorageService()\n storageService.delete(key)\n setStoredValue(backupValue)\n }\n\n return { value: storedValue, setValue, deleteValue }\n}","/**\n * The supported languages\n */\nconst languages = ['en', 'de'] as const\n\n/**\n * The supported languages\n */\nexport type Language = typeof languages[number]\n\n/**\n * The supported languages' names in their respective language\n */\nconst languagesLocalNames: Record<Language, string> = {\n en: 'English',\n de: 'Deutsch',\n}\n\n/**\n * The default language\n */\nconst DEFAULT_LANGUAGE: Language = 'en'\n\n/**\n * A constant definition for holding data regarding languages\n */\nexport const LanguageUtil = {\n languages,\n DEFAULT_LANGUAGE,\n languagesLocalNames,\n}","import { useLanguage } from './LanguageProvider'\nimport type { Language } from './util'\n\n/**\n * A type describing the pluralization of a word\n */\nexport type TranslationPlural = {\n zero?: string,\n one?: string,\n two?: string,\n few?: string,\n many?: string,\n other: string,\n}\n\n/**\n * The type describing all values of a translation\n */\nexport type TranslationType = Record<string, string | TranslationPlural>\n\n/**\n * The type of translations\n */\nexport type Translation<T extends TranslationType> = Record<Language, T>\n\ntype OverwriteTranslationType<T extends TranslationType> = {\n language?: Language,\n translation?: Translation<Partial<T>>,\n}\n\n/**\n * Adds the `language` prop to the component props.\n *\n * @param Translation the type of the translation object\n *\n * @param Props the type of the component props, defaults to `Record<string, never>`,\n * if you don't expect any other props other than `language` and get an\n * error when using your component (because it uses `forwardRef` etc.)\n * you can try out `Record<string, unknown>`, this might resolve your\n * problem as `SomeType & never` is still `never` but `SomeType & unknown`\n * is `SomeType` which means that adding back props (like `ref` etc.)\n * works properly\n */\nexport type PropsForTranslation<\n Translation extends TranslationType,\n Props = unknown\n> = Props & {\n overwriteTranslation?: OverwriteTranslationType<Translation>,\n}\n\ntype StringKeys<T> = Extract<keyof T, string>;\n\ntype TranslationFunctionOptions = {\n replacements?: Record<string, string>,\n count?: number,\n}\ntype TranslationFunction<T extends TranslationType> = (key: StringKeys<T>, options?: TranslationFunctionOptions) => string\n\nexport const TranslationPluralCount = {\n zero: 0,\n one: 1,\n two: 2,\n few: 3,\n many: 11,\n other: -1,\n}\n\n\nexport const useTranslation = <T extends TranslationType>(\n translations: Translation<Partial<TranslationType>>[],\n overwriteTranslation: OverwriteTranslationType<T> = {}\n): TranslationFunction<T> => {\n const { language: languageProp, translation: overwrite } = overwriteTranslation\n const { language: inferredLanguage } = useLanguage()\n const usedLanguage = languageProp ?? inferredLanguage\n const usedTranslations = [...translations]\n if (overwrite) {\n usedTranslations.push(overwrite)\n }\n\n return (key: StringKeys<T>, options?: TranslationFunctionOptions): string => {\n const { count, replacements } = { ...{ count: 0, replacements: {} }, ...options }\n\n try {\n for (let i = translations.length - 1; i >= 0; i--) {\n const translation = translations[i]\n const localizedTranslation = translation[usedLanguage]\n if (!localizedTranslation) {\n continue\n }\n const value = localizedTranslation[key]\n if(!value) {\n continue\n }\n\n let forProcessing: string\n if (typeof value !== 'string') {\n if (count === TranslationPluralCount.zero && value?.zero) {\n forProcessing = value.zero\n } else if (count === TranslationPluralCount.one && value?.one) {\n forProcessing = value.one\n } else if (count === TranslationPluralCount.two && value?.two) {\n forProcessing = value.two\n } else if (TranslationPluralCount.few <= count && count < TranslationPluralCount.many && value?.few) {\n forProcessing = value.few\n } else if (count > TranslationPluralCount.many && value?.many) {\n forProcessing = value.many\n } else {\n forProcessing = value.other\n }\n } else {\n forProcessing = value\n }\n forProcessing = forProcessing.replace(/\\{\\{(\\w+)}}/g, (_, placeholder) => {\n return replacements[placeholder] ?? `{{key:${placeholder}}}` // fallback if key is missing\n })\n return forProcessing\n }\n } catch (e) {\n console.error(e)\n }\n return `{{${usedLanguage}:${key}}}`\n }\n}"],"mappings":"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","names":["createContext","forwardRef","useCallback","useContext","useEffect","useMemo","useState","clsx","SolidButton","IconButton","useState","jsx","jsx","jsxs","createContext","useContext","clsx","forwardRef","CarouselSlide","useState","useMemo","useEffect","useCallback"]}
@@ -433,11 +433,11 @@ var defaultCarouselSlideTranslationType = {
433
433
  var CarouselSlide = (0, import_react4.forwardRef)(
434
434
  function CarouselSlide2({
435
435
  index,
436
+ isSelected,
436
437
  ...props
437
438
  }, ref) {
438
439
  const translation = useTranslation([defaultCarouselSlideTranslationType]);
439
- const { id, currentIndex, slideCount } = useCarouselContext();
440
- const isSelected = currentIndex === index;
440
+ const { id, slideCount } = useCarouselContext();
441
441
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
442
442
  "div",
443
443
  {
@@ -485,7 +485,7 @@ var Carousel = ({
485
485
  dots = true,
486
486
  blurColor = "from-background",
487
487
  heightClassName = "h-96",
488
- widthClassName = "w-[70%] desktop:w-1/2",
488
+ slideClassName = "w-[70%] desktop:w-1/2",
489
489
  slideContainerProps,
490
490
  onSlideChanged,
491
491
  ...props
@@ -639,9 +639,10 @@ var Carousel = ({
639
639
  {
640
640
  ref: isInItems ? slideRefs[index] : void 0,
641
641
  index,
642
+ isSelected: isInItems && currentIndex === index,
642
643
  className: (0, import_clsx2.default)(
643
644
  `absolute left-[50%] h-full overflow-hidden transition-transform ease-in-out`,
644
- widthClassName
645
+ slideClassName
645
646
  ),
646
647
  onClick: () => !disableClick && setCurrentIndex(index),
647
648
  style: {