@helpwave/hightide 0.1.3 → 0.1.5
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.
- package/dist/coloring/shading.cjs +2855 -24
- package/dist/coloring/shading.cjs.map +1 -1
- package/dist/coloring/shading.js +2893 -33
- package/dist/coloring/shading.js.map +1 -1
- package/dist/components/branding/HelpwaveBadge.cjs +2 -3
- package/dist/components/branding/HelpwaveBadge.cjs.map +1 -1
- package/dist/components/branding/HelpwaveBadge.js +2 -3
- package/dist/components/branding/HelpwaveBadge.js.map +1 -1
- package/dist/components/date/DatePicker.cjs +30 -9
- package/dist/components/date/DatePicker.cjs.map +1 -1
- package/dist/components/date/DatePicker.js +37 -16
- package/dist/components/date/DatePicker.js.map +1 -1
- package/dist/components/date/YearMonthPicker.cjs +30 -9
- package/dist/components/date/YearMonthPicker.cjs.map +1 -1
- package/dist/components/date/YearMonthPicker.js +36 -15
- package/dist/components/date/YearMonthPicker.js.map +1 -1
- package/dist/components/icons-and-geometry/Avatar.cjs +1 -2
- package/dist/components/icons-and-geometry/Avatar.cjs.map +1 -1
- package/dist/components/icons-and-geometry/Avatar.js +1 -2
- package/dist/components/icons-and-geometry/Avatar.js.map +1 -1
- package/dist/components/icons-and-geometry/Tag.cjs +5 -18
- package/dist/components/icons-and-geometry/Tag.cjs.map +1 -1
- package/dist/components/icons-and-geometry/Tag.d.cts +5 -3
- package/dist/components/icons-and-geometry/Tag.d.ts +5 -3
- package/dist/components/icons-and-geometry/Tag.js +5 -8
- package/dist/components/icons-and-geometry/Tag.js.map +1 -1
- package/dist/components/layout-and-navigation/BreadCrumb.cjs +2813 -1
- package/dist/components/layout-and-navigation/BreadCrumb.cjs.map +1 -1
- package/dist/components/layout-and-navigation/BreadCrumb.js +2843 -2
- package/dist/components/layout-and-navigation/BreadCrumb.js.map +1 -1
- package/dist/components/layout-and-navigation/Expandable.cjs +37 -9
- package/dist/components/layout-and-navigation/Expandable.cjs.map +1 -1
- package/dist/components/layout-and-navigation/Expandable.d.cts +20 -3
- package/dist/components/layout-and-navigation/Expandable.d.ts +20 -3
- package/dist/components/layout-and-navigation/Expandable.js +36 -9
- package/dist/components/layout-and-navigation/Expandable.js.map +1 -1
- package/dist/components/layout-and-navigation/FAQSection.cjs +34 -8
- package/dist/components/layout-and-navigation/FAQSection.cjs.map +1 -1
- package/dist/components/layout-and-navigation/FAQSection.d.cts +1 -1
- package/dist/components/layout-and-navigation/FAQSection.d.ts +1 -1
- package/dist/components/layout-and-navigation/FAQSection.js +35 -9
- package/dist/components/layout-and-navigation/FAQSection.js.map +1 -1
- package/dist/components/layout-and-navigation/StepperBar.cjs +47 -19
- package/dist/components/layout-and-navigation/StepperBar.cjs.map +1 -1
- package/dist/components/layout-and-navigation/StepperBar.d.cts +10 -7
- package/dist/components/layout-and-navigation/StepperBar.d.ts +10 -7
- package/dist/components/layout-and-navigation/StepperBar.js +45 -18
- package/dist/components/layout-and-navigation/StepperBar.js.map +1 -1
- package/dist/components/layout-and-navigation/Tile.cjs +4 -24
- package/dist/components/layout-and-navigation/Tile.cjs.map +1 -1
- package/dist/components/layout-and-navigation/Tile.d.cts +1 -5
- package/dist/components/layout-and-navigation/Tile.d.ts +1 -5
- package/dist/components/layout-and-navigation/Tile.js +3 -22
- package/dist/components/layout-and-navigation/Tile.js.map +1 -1
- package/dist/components/user-action/DateAndTimePicker.cjs +30 -9
- package/dist/components/user-action/DateAndTimePicker.cjs.map +1 -1
- package/dist/components/user-action/DateAndTimePicker.js +36 -15
- package/dist/components/user-action/DateAndTimePicker.js.map +1 -1
- package/dist/css/globals.css +0 -32
- package/dist/index.cjs +2956 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2991 -124
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/date/YearMonthPicker.tsx","../../../src/util/noop.ts","../../../src/util/array.ts","../../../src/components/layout-and-navigation/Expandable.tsx","../../../src/util/date.ts","../../../src/localization/LanguageProvider.tsx","../../../src/hooks/useLocalStorage.ts","../../../src/localization/util.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { Scrollbars } from 'react-custom-scrollbars-2'\nimport { noop } from '@/util/noop'\nimport { equalSizeGroups, range } from '@/util/array'\nimport clsx from 'clsx'\nimport { Expandable } from '@/components/layout-and-navigation/Expandable'\nimport { addDuration, monthsList, subtractDuration } from '@/util/date'\nimport { useLocale } from '@/localization/LanguageProvider'\n\nexport type YearMonthPickerProps = {\n displayedYearMonth?: Date,\n start?: Date,\n end?: Date,\n onChange?: (date: Date) => void,\n className?: string,\n maxHeight?: number,\n showValueOpen?: boolean,\n}\n\n// TODO use a dynamically loading infinite list here\nexport const YearMonthPicker = ({\n displayedYearMonth = new Date(),\n start = subtractDuration(new Date(), { years: 50 }),\n end = addDuration(new Date(), { years: 50 }),\n onChange = noop,\n className = '',\n maxHeight = 300,\n showValueOpen = true\n }: YearMonthPickerProps) => {\n const locale = useLocale()\n const ref = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n const scrollToItem = () => {\n if (ref.current) {\n ref.current.scrollIntoView({\n behavior: 'instant',\n block: 'center',\n })\n }\n }\n\n scrollToItem()\n }, [ref])\n\n if (end < start) {\n console.error(`startYear: (${start}) less than endYear: (${end})`)\n return null\n }\n\n const years = range(start.getFullYear(), end.getFullYear())\n\n return (\n <div className={clsx('col select-none', className)}>\n <Scrollbars autoHeight autoHeightMax={maxHeight} style={{ height: '100%' }}>\n <div className=\"col gap-y-1 mr-3\">\n {years.map(year => {\n const selectedYear = displayedYearMonth.getFullYear() === year\n return (\n <Expandable\n key={year}\n ref={(displayedYearMonth.getFullYear() ?? new Date().getFullYear()) === year ? ref : undefined}\n label={<span className={clsx({ 'text-primary font-bold': selectedYear })}>{year}</span>}\n initialExpansion={showValueOpen && selectedYear}\n >\n <div className=\"col gap-y-1 px-2 pb-2\">\n {equalSizeGroups([...monthsList], 3).map((monthList, index) => (\n <div key={index} className=\"row\">\n {monthList.map(month => {\n const monthIndex = monthsList.indexOf(month)\n const newDate = new Date(year, monthIndex)\n\n const selectedMonth = selectedYear && monthIndex === displayedYearMonth.getMonth()\n const firstOfMonth = new Date(year, monthIndex, 1)\n const lastOfMonth = new Date(year, monthIndex, 1)\n const isAfterStart = start === undefined || start <= addDuration(subtractDuration(lastOfMonth, { days: 1 }), { months: 1 })\n const isBeforeEnd = end === undefined || firstOfMonth <= end\n const isValid = isAfterStart && isBeforeEnd\n return (\n <button\n key={month}\n disabled={!isValid}\n className={clsx(\n 'chip hover:brightness-95 flex-1',\n {\n 'bg-gray-50 text-black': !selectedMonth && isValid,\n 'bg-primary text-on-primary': selectedMonth && isValid,\n 'bg-disabled-background text-disabled-text': !isValid\n }\n )}\n onClick={() => {\n onChange(newDate)\n }}\n >\n {new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}\n </button>\n )\n })}\n </div>\n ))}\n </div>\n </Expandable>\n )\n })}\n </div>\n </Scrollbars>\n </div>\n )\n}\n\nexport const YearMonthPickerUncontrolled = ({\n displayedYearMonth = new Date(),\n onChange = noop,\n ...props\n }: YearMonthPickerProps) => {\n const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth)\n\n useEffect(() => setYearMonth(displayedYearMonth), [displayedYearMonth])\n\n return (\n <YearMonthPicker\n displayedYearMonth={yearMonth}\n onChange={date => {\n setYearMonth(date)\n onChange(date)\n }}\n {...props}\n />\n )\n}\n","export const noop = () => undefined\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\n/**\n * @param start\n * @param end inclusive\n * @param allowEmptyRange Whether the range can be defined empty via end < start\n */\nexport const range = (start: number, end: number, allowEmptyRange: boolean = false): number[] => {\n if (end < start) {\n if (!allowEmptyRange) {\n console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set allowEmptyRange to true`)\n }\n return []\n }\n return Array.from({ length: end - start + 1 }, (_, index) => index + 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\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\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}\n","import type { PropsWithChildren, ReactNode } from 'react'\nimport { forwardRef, useState } from 'react'\nimport { ChevronDown, ChevronUp } from 'lucide-react'\nimport clsx from 'clsx'\n\ntype IconBuilder = (expanded: boolean) => ReactNode\n\nexport type ExpandableProps = PropsWithChildren<{\n label: ReactNode,\n icon?: IconBuilder,\n initialExpansion?: boolean,\n /**\n * Whether the expansion should only happen when the header is clicked or on the entire component\n */\n clickOnlyOnHeader?: boolean,\n disabled?: boolean,\n className?: string,\n headerClassName?: string,\n}>\n\nconst DefaultIcon: IconBuilder = (expanded) => expanded ?\n (<ChevronUp size={16} className=\"min-w-[16px]\"/>)\n : (<ChevronDown size={16} className=\"min-w-[16px]\"/>)\n\n/**\n * A Component for showing and hiding content\n */\nexport const Expandable = forwardRef<HTMLDivElement, ExpandableProps>(({\n children,\n label,\n icon,\n initialExpansion = false,\n clickOnlyOnHeader = true,\n disabled = false,\n className = '',\n headerClassName = ''\n }, ref) => {\n const [isExpanded, setIsExpanded] = useState(initialExpansion)\n icon ??= DefaultIcon\n\n return (\n <div\n ref={ref}\n className={clsx('col gap-y-0 bg-surface text-on-surface group rounded-lg shadow-sm', { 'cursor-pointer': !clickOnlyOnHeader && !disabled }, className)}\n onClick={() => !clickOnlyOnHeader && !disabled && setIsExpanded(!isExpanded)}\n >\n <div\n className={clsx(\n 'row py-2 px-4 rounded-lg justify-between items-center bg-surface text-on-surface select-none',\n {\n 'group-hover:brightness-95': !isExpanded,\n 'hover:brightness-95': isExpanded && !disabled,\n 'cursor-pointer': clickOnlyOnHeader && !disabled,\n },\n headerClassName\n )}\n onClick={() => clickOnlyOnHeader && !disabled && setIsExpanded(!isExpanded)}\n >\n {label}\n {icon(isExpanded)}\n </div>\n {isExpanded && (\n <div className=\"col px-4 pb-2\">\n {children}\n </div>\n )}\n </div>\n )\n})\n\nExpandable.displayName = 'Expandable'\n","import { equalSizeGroups } from './array'\n\nexport const monthsList = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'] as const\nexport type Month = typeof monthsList[number]\n\nexport const weekDayList = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] as const\nexport type WeekDay = typeof weekDayList[number]\n\nexport const formatDate = (date: Date) => {\n const year = date.getFullYear().toString().padStart(4, '0')\n const month = (date.getMonth() + 1).toString().padStart(2, '0')\n const day = (date.getDate()).toString().padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nexport const formatDateTime = (date: Date) => {\n const dateString = formatDate(date)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n return `${dateString}T${hours}:${minutes}`\n}\n\nexport const getDaysInMonth = (year: number, month: number): number => {\n const lastDayOfMonth = new Date(year, month + 1, 0)\n return lastDayOfMonth.getDate()\n}\n\nexport type Duration = {\n years?: number,\n months?: number,\n days?: number,\n hours?: number,\n minutes?: number,\n seconds?: number,\n milliseconds?: number,\n}\n\nexport const changeDuration = (date: Date, duration: Duration, isAdding?: boolean): Date => {\n const {\n years = 0,\n months = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n milliseconds = 0,\n } = duration\n\n // Check ranges\n if (years < 0) {\n console.error(`Range error years must be greater than 0: received ${years}`)\n return new Date(date)\n }\n if (months < 0 || months > 11) {\n console.error(`Range error month must be 0 <= month <= 11: received ${months}`)\n return new Date(date)\n }\n if (days < 0) {\n console.error(`Range error days must be greater than 0: received ${days}`)\n return new Date(date)\n }\n if (hours < 0 || hours > 23) {\n console.error(`Range error hours must be 0 <= hours <= 23: received ${hours}`)\n return new Date(date)\n }\n if (minutes < 0 || minutes > 59) {\n console.error(`Range error minutes must be 0 <= minutes <= 59: received ${minutes}`)\n return new Date(date)\n }\n if (seconds < 0 || seconds > 59) {\n console.error(`Range error seconds must be 0 <= seconds <= 59: received ${seconds}`)\n return new Date(date)\n }\n if (milliseconds < 0) {\n console.error(`Range error seconds must be greater than 0: received ${milliseconds}`)\n return new Date(date)\n }\n\n const multiplier = isAdding ? 1 : -1\n\n const newDate = new Date(date)\n\n newDate.setFullYear(newDate.getFullYear() + multiplier * years)\n\n newDate.setMonth(newDate.getMonth() + multiplier * months)\n\n newDate.setDate(newDate.getDate() + multiplier * days)\n\n newDate.setHours(newDate.getHours() + multiplier * hours)\n\n newDate.setMinutes(newDate.getMinutes() + multiplier * minutes)\n\n newDate.setSeconds(newDate.getSeconds() + multiplier * seconds)\n\n newDate.setMilliseconds(newDate.getMilliseconds() + multiplier * milliseconds)\n\n return newDate\n}\n\nexport const addDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, true)\n}\n\nexport const subtractDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, false)\n}\n\nexport const getBetweenDuration = (startDate: Date, endDate: Date): Duration => {\n const durationInMilliseconds = endDate.getTime() - startDate.getTime()\n\n const millisecondsInSecond = 1000\n const millisecondsInMinute = 60 * millisecondsInSecond\n const millisecondsInHour = 60 * millisecondsInMinute\n const millisecondsInDay = 24 * millisecondsInHour\n const millisecondsInMonth = 30 * millisecondsInDay // Rough estimation, can be adjusted\n\n const years = Math.floor(durationInMilliseconds / (365.25 * millisecondsInDay))\n const months = Math.floor(durationInMilliseconds / millisecondsInMonth)\n const days = Math.floor(durationInMilliseconds / millisecondsInDay)\n const hours = Math.floor((durationInMilliseconds % millisecondsInDay) / millisecondsInHour)\n const seconds = Math.floor((durationInMilliseconds % millisecondsInHour) / millisecondsInSecond)\n const milliseconds = durationInMilliseconds % millisecondsInSecond\n\n return {\n years,\n months,\n days,\n hours,\n seconds,\n milliseconds,\n }\n}\n\n/** Checks if a given date is in the range of two dates\n *\n * An undefined value for startDate or endDate means no bound for the start or end respectively\n */\nexport const isInTimeSpan = (value: Date, startDate?: Date, endDate?: Date): boolean => {\n if (startDate && endDate) {\n console.assert(startDate <= endDate)\n return startDate <= value && value <= endDate\n } else if (startDate) {\n return startDate <= value\n } else if (endDate) {\n return endDate >= value\n } else {\n return true\n }\n}\n\n/** Compare two dates on the year, month, day */\nexport const equalDate = (date1: Date, date2: Date) => {\n return date1.getFullYear() === date2.getFullYear()\n && date1.getMonth() === date2.getMonth()\n && date1.getDate() === date2.getDate()\n}\n\nexport const getWeeksForCalenderMonth = (date: Date, weekStart: WeekDay, weeks: number = 6) => {\n const month = date.getMonth()\n const year = date.getFullYear()\n\n const dayList: Date[] = []\n let currentDate = new Date(year, month, 1) // Start of month\n const weekStartIndex = weekDayList.indexOf(weekStart)\n\n // Move the current day to the week before\n while (currentDate.getDay() !== weekStartIndex) {\n currentDate = subtractDuration(currentDate, { days: 1 })\n }\n\n while (dayList.length < 7 * weeks) {\n const date = new Date(currentDate)\n date.setHours(date.getHours(), date.getMinutes()) // To make sure we are not overwriting the time\n dayList.push(date)\n currentDate = addDuration(currentDate, { days: 1 })\n }\n\n // weeks\n return equalSizeGroups(dayList, 7)\n}\n","import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'\nimport { createContext, useContext, useEffect, 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\nexport const useLanguage = () => useContext(LanguageContext)\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\ntype LanguageProviderProps = {\n initialLanguage?: Language,\n}\n\nexport const LanguageProvider = ({ initialLanguage, children }: PropsWithChildren<LanguageProviderProps>) => {\n const [language, setLanguage] = useState<Language>(initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n const [storedLanguage, setStoredLanguage] = useLocalStorage<Language>('language', initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n\n useEffect(() => {\n if (language !== initialLanguage && initialLanguage) {\n console.warn('LanguageProvider initial state changed: Prefer using languageProvider\\'s setLanguage instead')\n setLanguage(initialLanguage)\n }\n }, [initialLanguage]) // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n // TODO set locale of html tag here as well\n setStoredLanguage(language)\n }, [language, setStoredLanguage])\n\n useEffect(() => {\n if (storedLanguage !== null) {\n setLanguage(storedLanguage)\n return\n }\n\n const LanguageToTestAgainst = Object.values(LanguageUtil.languages)\n\n const matchingBrowserLanguage = window.navigator.languages\n .map(language => LanguageToTestAgainst.find((test) => language === test || language.split('-')[0] === test))\n .filter(entry => entry !== undefined)\n\n if (matchingBrowserLanguage.length === 0) return\n\n const firstMatch = matchingBrowserLanguage[0] as Language\n setLanguage(firstMatch)\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <LanguageContext.Provider value={{\n language,\n setLanguage\n }}>\n {children}\n </LanguageContext.Provider>\n )\n}","import type { Dispatch, SetStateAction } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { LocalStorageService } from '../util/storage'\n\ntype SetValue<T> = Dispatch<SetStateAction<T>>\nexport const useLocalStorage = <T>(key: string, initValue: T): [T, SetValue<T>] => {\n const get = useCallback((): T => {\n if (typeof window === 'undefined') {\n return initValue\n }\n const storageService = new LocalStorageService()\n const value = storageService.get<T>(key)\n return value || initValue\n }, [initValue, key])\n\n const [storedValue, setStoredValue] = useState<T>(get)\n\n const setValue: SetValue<T> = useCallback(value => {\n const newValue = value instanceof Function ? value(storedValue) : value\n const storageService = new LocalStorageService()\n storageService.set(key, value)\n\n setStoredValue(newValue)\n }, [storedValue, setStoredValue, key])\n\n useEffect(() => {\n setStoredValue(get())\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return [storedValue, setValue]\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}"],"mappings":"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","names":["import_react","import_clsx","clsx","import_react","import_react","import_jsx_runtime","import_jsx_runtime","clsx"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/date/YearMonthPicker.tsx","../../../src/util/noop.ts","../../../src/util/array.ts","../../../src/components/layout-and-navigation/Expandable.tsx","../../../src/util/date.ts","../../../src/localization/LanguageProvider.tsx","../../../src/hooks/useLocalStorage.ts","../../../src/localization/util.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { Scrollbars } from 'react-custom-scrollbars-2'\nimport { noop } from '@/util/noop'\nimport { equalSizeGroups, range } from '@/util/array'\nimport clsx from 'clsx'\nimport { ExpandableUncontrolled } from '@/components/layout-and-navigation/Expandable'\nimport { addDuration, monthsList, subtractDuration } from '@/util/date'\nimport { useLocale } from '@/localization/LanguageProvider'\n\nexport type YearMonthPickerProps = {\n displayedYearMonth?: Date,\n start?: Date,\n end?: Date,\n onChange?: (date: Date) => void,\n className?: string,\n maxHeight?: number,\n showValueOpen?: boolean,\n}\n\n// TODO use a dynamically loading infinite list here\nexport const YearMonthPicker = ({\n displayedYearMonth = new Date(),\n start = subtractDuration(new Date(), { years: 50 }),\n end = addDuration(new Date(), { years: 50 }),\n onChange = noop,\n className = '',\n maxHeight = 300,\n showValueOpen = true\n }: YearMonthPickerProps) => {\n const locale = useLocale()\n const ref = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n const scrollToItem = () => {\n if (ref.current) {\n ref.current.scrollIntoView({\n behavior: 'instant',\n block: 'center',\n })\n }\n }\n\n scrollToItem()\n }, [ref])\n\n if (end < start) {\n console.error(`startYear: (${start}) less than endYear: (${end})`)\n return null\n }\n\n const years = range(start.getFullYear(), end.getFullYear())\n\n return (\n <div className={clsx('col select-none', className)}>\n <Scrollbars autoHeight autoHeightMax={maxHeight} style={{ height: '100%' }}>\n <div className=\"col gap-y-1 mr-3\">\n {years.map(year => {\n const selectedYear = displayedYearMonth.getFullYear() === year\n return (\n <ExpandableUncontrolled\n key={year}\n ref={(displayedYearMonth.getFullYear() ?? new Date().getFullYear()) === year ? ref : undefined}\n label={<span className={clsx({ 'text-primary font-bold': selectedYear })}>{year}</span>}\n isExpanded={showValueOpen && selectedYear}\n >\n <div className=\"col gap-y-1 px-2 pb-2\">\n {equalSizeGroups([...monthsList], 3).map((monthList, index) => (\n <div key={index} className=\"row\">\n {monthList.map(month => {\n const monthIndex = monthsList.indexOf(month)\n const newDate = new Date(year, monthIndex)\n\n const selectedMonth = selectedYear && monthIndex === displayedYearMonth.getMonth()\n const firstOfMonth = new Date(year, monthIndex, 1)\n const lastOfMonth = new Date(year, monthIndex, 1)\n const isAfterStart = start === undefined || start <= addDuration(subtractDuration(lastOfMonth, { days: 1 }), { months: 1 })\n const isBeforeEnd = end === undefined || firstOfMonth <= end\n const isValid = isAfterStart && isBeforeEnd\n return (\n <button\n key={month}\n disabled={!isValid}\n className={clsx(\n 'chip hover:brightness-95 flex-1',\n {\n 'bg-gray-50 text-black': !selectedMonth && isValid,\n 'bg-primary text-on-primary': selectedMonth && isValid,\n 'bg-disabled-background text-disabled-text': !isValid\n }\n )}\n onClick={() => {\n onChange(newDate)\n }}\n >\n {new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}\n </button>\n )\n })}\n </div>\n ))}\n </div>\n </ExpandableUncontrolled>\n )\n })}\n </div>\n </Scrollbars>\n </div>\n )\n}\n\nexport const YearMonthPickerUncontrolled = ({\n displayedYearMonth = new Date(),\n onChange = noop,\n ...props\n }: YearMonthPickerProps) => {\n const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth)\n\n useEffect(() => setYearMonth(displayedYearMonth), [displayedYearMonth])\n\n return (\n <YearMonthPicker\n displayedYearMonth={yearMonth}\n onChange={date => {\n setYearMonth(date)\n onChange(date)\n }}\n {...props}\n />\n )\n}\n","export const noop = () => undefined\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\n/**\n * @param start\n * @param end inclusive\n * @param allowEmptyRange Whether the range can be defined empty via end < start\n */\nexport const range = (start: number, end: number, allowEmptyRange: boolean = false): number[] => {\n if (end < start) {\n if (!allowEmptyRange) {\n console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set allowEmptyRange to true`)\n }\n return []\n }\n return Array.from({ length: end - start + 1 }, (_, index) => index + 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\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\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}\n","import type { PropsWithChildren, ReactNode } from 'react'\nimport { forwardRef, useEffect, useState } from 'react'\nimport { ChevronDown, ChevronUp } from 'lucide-react'\nimport clsx from 'clsx'\nimport { noop } from '@/util/noop'\n\ntype IconBuilder = (expanded: boolean) => ReactNode\n\nexport type ExpandableProps = PropsWithChildren<{\n label: ReactNode,\n icon?: IconBuilder,\n isExpanded?: boolean,\n onChange?: (isExpanded: boolean) => void,\n /**\n * Whether the expansion should only happen when the header is clicked or on the entire component\n */\n clickOnlyOnHeader?: boolean,\n disabled?: boolean,\n className?: string,\n headerClassName?: string,\n}>\n\nconst DefaultIcon: IconBuilder = (expanded) => expanded ?\n (<ChevronUp size={16} className=\"min-w-[16px]\"/>)\n : (<ChevronDown size={16} className=\"min-w-[16px]\"/>)\n\n\n/**\n * A Component for showing and hiding content\n */\nexport const Expandable = forwardRef<HTMLDivElement, ExpandableProps>(function Expandable({\n children,\n label,\n icon,\n isExpanded = false,\n onChange = noop,\n clickOnlyOnHeader = true,\n disabled = false,\n className = '',\n headerClassName = ''\n }, ref) {\n icon ??= DefaultIcon\n\n return (\n <div\n ref={ref}\n className={clsx('col gap-y-0 bg-surface text-on-surface group rounded-lg shadow-sm', { 'cursor-pointer': !clickOnlyOnHeader && !disabled }, className)}\n onClick={() => !clickOnlyOnHeader && !disabled && onChange(!isExpanded)}\n >\n <div\n className={clsx(\n 'row py-2 px-4 rounded-lg justify-between items-center bg-surface text-on-surface select-none',\n {\n 'group-hover:brightness-95': !isExpanded,\n 'hover:brightness-95': isExpanded && !disabled,\n 'cursor-pointer': clickOnlyOnHeader && !disabled,\n },\n headerClassName\n )}\n onClick={() => clickOnlyOnHeader && !disabled && onChange(!isExpanded)}\n >\n {label}\n {icon(isExpanded)}\n </div>\n {isExpanded && (\n <div className=\"col px-4 pb-2\">\n {children}\n </div>\n )}\n </div>\n )\n})\n\nexport const ExpandableUncontrolled = forwardRef<HTMLDivElement, ExpandableProps>(function ExpandableUncontrolled({\n isExpanded,\n onChange = noop,\n ...props\n },\n ref) {\n const [usedIsExpanded, setUsedIsExpanded] = useState(isExpanded)\n\n useEffect(() => {\n setUsedIsExpanded(isExpanded)\n }, [isExpanded])\n\n return (\n <Expandable\n {...props}\n ref={ref}\n isExpanded={usedIsExpanded}\n onChange={value => {\n onChange(value)\n setUsedIsExpanded(value)\n }}\n />\n )\n})\n","import { equalSizeGroups } from './array'\n\nexport const monthsList = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'] as const\nexport type Month = typeof monthsList[number]\n\nexport const weekDayList = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] as const\nexport type WeekDay = typeof weekDayList[number]\n\nexport const formatDate = (date: Date) => {\n const year = date.getFullYear().toString().padStart(4, '0')\n const month = (date.getMonth() + 1).toString().padStart(2, '0')\n const day = (date.getDate()).toString().padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nexport const formatDateTime = (date: Date) => {\n const dateString = formatDate(date)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n return `${dateString}T${hours}:${minutes}`\n}\n\nexport const getDaysInMonth = (year: number, month: number): number => {\n const lastDayOfMonth = new Date(year, month + 1, 0)\n return lastDayOfMonth.getDate()\n}\n\nexport type Duration = {\n years?: number,\n months?: number,\n days?: number,\n hours?: number,\n minutes?: number,\n seconds?: number,\n milliseconds?: number,\n}\n\nexport const changeDuration = (date: Date, duration: Duration, isAdding?: boolean): Date => {\n const {\n years = 0,\n months = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n milliseconds = 0,\n } = duration\n\n // Check ranges\n if (years < 0) {\n console.error(`Range error years must be greater than 0: received ${years}`)\n return new Date(date)\n }\n if (months < 0 || months > 11) {\n console.error(`Range error month must be 0 <= month <= 11: received ${months}`)\n return new Date(date)\n }\n if (days < 0) {\n console.error(`Range error days must be greater than 0: received ${days}`)\n return new Date(date)\n }\n if (hours < 0 || hours > 23) {\n console.error(`Range error hours must be 0 <= hours <= 23: received ${hours}`)\n return new Date(date)\n }\n if (minutes < 0 || minutes > 59) {\n console.error(`Range error minutes must be 0 <= minutes <= 59: received ${minutes}`)\n return new Date(date)\n }\n if (seconds < 0 || seconds > 59) {\n console.error(`Range error seconds must be 0 <= seconds <= 59: received ${seconds}`)\n return new Date(date)\n }\n if (milliseconds < 0) {\n console.error(`Range error seconds must be greater than 0: received ${milliseconds}`)\n return new Date(date)\n }\n\n const multiplier = isAdding ? 1 : -1\n\n const newDate = new Date(date)\n\n newDate.setFullYear(newDate.getFullYear() + multiplier * years)\n\n newDate.setMonth(newDate.getMonth() + multiplier * months)\n\n newDate.setDate(newDate.getDate() + multiplier * days)\n\n newDate.setHours(newDate.getHours() + multiplier * hours)\n\n newDate.setMinutes(newDate.getMinutes() + multiplier * minutes)\n\n newDate.setSeconds(newDate.getSeconds() + multiplier * seconds)\n\n newDate.setMilliseconds(newDate.getMilliseconds() + multiplier * milliseconds)\n\n return newDate\n}\n\nexport const addDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, true)\n}\n\nexport const subtractDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, false)\n}\n\nexport const getBetweenDuration = (startDate: Date, endDate: Date): Duration => {\n const durationInMilliseconds = endDate.getTime() - startDate.getTime()\n\n const millisecondsInSecond = 1000\n const millisecondsInMinute = 60 * millisecondsInSecond\n const millisecondsInHour = 60 * millisecondsInMinute\n const millisecondsInDay = 24 * millisecondsInHour\n const millisecondsInMonth = 30 * millisecondsInDay // Rough estimation, can be adjusted\n\n const years = Math.floor(durationInMilliseconds / (365.25 * millisecondsInDay))\n const months = Math.floor(durationInMilliseconds / millisecondsInMonth)\n const days = Math.floor(durationInMilliseconds / millisecondsInDay)\n const hours = Math.floor((durationInMilliseconds % millisecondsInDay) / millisecondsInHour)\n const seconds = Math.floor((durationInMilliseconds % millisecondsInHour) / millisecondsInSecond)\n const milliseconds = durationInMilliseconds % millisecondsInSecond\n\n return {\n years,\n months,\n days,\n hours,\n seconds,\n milliseconds,\n }\n}\n\n/** Checks if a given date is in the range of two dates\n *\n * An undefined value for startDate or endDate means no bound for the start or end respectively\n */\nexport const isInTimeSpan = (value: Date, startDate?: Date, endDate?: Date): boolean => {\n if (startDate && endDate) {\n console.assert(startDate <= endDate)\n return startDate <= value && value <= endDate\n } else if (startDate) {\n return startDate <= value\n } else if (endDate) {\n return endDate >= value\n } else {\n return true\n }\n}\n\n/** Compare two dates on the year, month, day */\nexport const equalDate = (date1: Date, date2: Date) => {\n return date1.getFullYear() === date2.getFullYear()\n && date1.getMonth() === date2.getMonth()\n && date1.getDate() === date2.getDate()\n}\n\nexport const getWeeksForCalenderMonth = (date: Date, weekStart: WeekDay, weeks: number = 6) => {\n const month = date.getMonth()\n const year = date.getFullYear()\n\n const dayList: Date[] = []\n let currentDate = new Date(year, month, 1) // Start of month\n const weekStartIndex = weekDayList.indexOf(weekStart)\n\n // Move the current day to the week before\n while (currentDate.getDay() !== weekStartIndex) {\n currentDate = subtractDuration(currentDate, { days: 1 })\n }\n\n while (dayList.length < 7 * weeks) {\n const date = new Date(currentDate)\n date.setHours(date.getHours(), date.getMinutes()) // To make sure we are not overwriting the time\n dayList.push(date)\n currentDate = addDuration(currentDate, { days: 1 })\n }\n\n // weeks\n return equalSizeGroups(dayList, 7)\n}\n","import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'\nimport { createContext, useContext, useEffect, 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\nexport const useLanguage = () => useContext(LanguageContext)\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\ntype LanguageProviderProps = {\n initialLanguage?: Language,\n}\n\nexport const LanguageProvider = ({ initialLanguage, children }: PropsWithChildren<LanguageProviderProps>) => {\n const [language, setLanguage] = useState<Language>(initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n const [storedLanguage, setStoredLanguage] = useLocalStorage<Language>('language', initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n\n useEffect(() => {\n if (language !== initialLanguage && initialLanguage) {\n console.warn('LanguageProvider initial state changed: Prefer using languageProvider\\'s setLanguage instead')\n setLanguage(initialLanguage)\n }\n }, [initialLanguage]) // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n // TODO set locale of html tag here as well\n setStoredLanguage(language)\n }, [language, setStoredLanguage])\n\n useEffect(() => {\n if (storedLanguage !== null) {\n setLanguage(storedLanguage)\n return\n }\n\n const LanguageToTestAgainst = Object.values(LanguageUtil.languages)\n\n const matchingBrowserLanguage = window.navigator.languages\n .map(language => LanguageToTestAgainst.find((test) => language === test || language.split('-')[0] === test))\n .filter(entry => entry !== undefined)\n\n if (matchingBrowserLanguage.length === 0) return\n\n const firstMatch = matchingBrowserLanguage[0] as Language\n setLanguage(firstMatch)\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <LanguageContext.Provider value={{\n language,\n setLanguage\n }}>\n {children}\n </LanguageContext.Provider>\n )\n}","import type { Dispatch, SetStateAction } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { LocalStorageService } from '../util/storage'\n\ntype SetValue<T> = Dispatch<SetStateAction<T>>\nexport const useLocalStorage = <T>(key: string, initValue: T): [T, SetValue<T>] => {\n const get = useCallback((): T => {\n if (typeof window === 'undefined') {\n return initValue\n }\n const storageService = new LocalStorageService()\n const value = storageService.get<T>(key)\n return value || initValue\n }, [initValue, key])\n\n const [storedValue, setStoredValue] = useState<T>(get)\n\n const setValue: SetValue<T> = useCallback(value => {\n const newValue = value instanceof Function ? value(storedValue) : value\n const storageService = new LocalStorageService()\n storageService.set(key, value)\n\n setStoredValue(newValue)\n }, [storedValue, setStoredValue, key])\n\n useEffect(() => {\n setStoredValue(get())\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return [storedValue, setValue]\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}"],"mappings":"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","names":["import_react","import_clsx","Expandable","clsx","ExpandableUncontrolled","import_react","import_react","import_jsx_runtime","import_jsx_runtime","clsx"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/date/YearMonthPicker.tsx
|
|
2
|
-
import { useEffect as
|
|
2
|
+
import { useEffect as useEffect4, useRef, useState as useState4 } from "react";
|
|
3
3
|
import { Scrollbars } from "react-custom-scrollbars-2";
|
|
4
4
|
|
|
5
5
|
// src/util/noop.ts
|
|
@@ -31,29 +31,29 @@ var range = (start, end, allowEmptyRange = false) => {
|
|
|
31
31
|
import clsx2 from "clsx";
|
|
32
32
|
|
|
33
33
|
// src/components/layout-and-navigation/Expandable.tsx
|
|
34
|
-
import { forwardRef, useState } from "react";
|
|
34
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
35
35
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
36
36
|
import clsx from "clsx";
|
|
37
37
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
38
38
|
var DefaultIcon = (expanded) => expanded ? /* @__PURE__ */ jsx(ChevronUp, { size: 16, className: "min-w-[16px]" }) : /* @__PURE__ */ jsx(ChevronDown, { size: 16, className: "min-w-[16px]" });
|
|
39
|
-
var Expandable = forwardRef(({
|
|
39
|
+
var Expandable = forwardRef(function Expandable2({
|
|
40
40
|
children,
|
|
41
41
|
label,
|
|
42
42
|
icon,
|
|
43
|
-
|
|
43
|
+
isExpanded = false,
|
|
44
|
+
onChange = noop,
|
|
44
45
|
clickOnlyOnHeader = true,
|
|
45
46
|
disabled = false,
|
|
46
47
|
className = "",
|
|
47
48
|
headerClassName = ""
|
|
48
|
-
}, ref)
|
|
49
|
-
const [isExpanded, setIsExpanded] = useState(initialExpansion);
|
|
49
|
+
}, ref) {
|
|
50
50
|
icon ??= DefaultIcon;
|
|
51
51
|
return /* @__PURE__ */ jsxs(
|
|
52
52
|
"div",
|
|
53
53
|
{
|
|
54
54
|
ref,
|
|
55
55
|
className: clsx("col gap-y-0 bg-surface text-on-surface group rounded-lg shadow-sm", { "cursor-pointer": !clickOnlyOnHeader && !disabled }, className),
|
|
56
|
-
onClick: () => !clickOnlyOnHeader && !disabled &&
|
|
56
|
+
onClick: () => !clickOnlyOnHeader && !disabled && onChange(!isExpanded),
|
|
57
57
|
children: [
|
|
58
58
|
/* @__PURE__ */ jsxs(
|
|
59
59
|
"div",
|
|
@@ -67,7 +67,7 @@ var Expandable = forwardRef(({
|
|
|
67
67
|
},
|
|
68
68
|
headerClassName
|
|
69
69
|
),
|
|
70
|
-
onClick: () => clickOnlyOnHeader && !disabled &&
|
|
70
|
+
onClick: () => clickOnlyOnHeader && !disabled && onChange(!isExpanded),
|
|
71
71
|
children: [
|
|
72
72
|
label,
|
|
73
73
|
icon(isExpanded)
|
|
@@ -79,7 +79,28 @@ var Expandable = forwardRef(({
|
|
|
79
79
|
}
|
|
80
80
|
);
|
|
81
81
|
});
|
|
82
|
-
|
|
82
|
+
var ExpandableUncontrolled = forwardRef(function ExpandableUncontrolled2({
|
|
83
|
+
isExpanded,
|
|
84
|
+
onChange = noop,
|
|
85
|
+
...props
|
|
86
|
+
}, ref) {
|
|
87
|
+
const [usedIsExpanded, setUsedIsExpanded] = useState(isExpanded);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
setUsedIsExpanded(isExpanded);
|
|
90
|
+
}, [isExpanded]);
|
|
91
|
+
return /* @__PURE__ */ jsx(
|
|
92
|
+
Expandable,
|
|
93
|
+
{
|
|
94
|
+
...props,
|
|
95
|
+
ref,
|
|
96
|
+
isExpanded: usedIsExpanded,
|
|
97
|
+
onChange: (value) => {
|
|
98
|
+
onChange(value);
|
|
99
|
+
setUsedIsExpanded(value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
});
|
|
83
104
|
|
|
84
105
|
// src/util/date.ts
|
|
85
106
|
var monthsList = ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"];
|
|
@@ -140,10 +161,10 @@ var subtractDuration = (date, duration) => {
|
|
|
140
161
|
};
|
|
141
162
|
|
|
142
163
|
// src/localization/LanguageProvider.tsx
|
|
143
|
-
import { createContext, useContext, useEffect as
|
|
164
|
+
import { createContext, useContext, useEffect as useEffect3, useState as useState3 } from "react";
|
|
144
165
|
|
|
145
166
|
// src/hooks/useLocalStorage.ts
|
|
146
|
-
import { useCallback, useEffect, useState as useState2 } from "react";
|
|
167
|
+
import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
|
|
147
168
|
|
|
148
169
|
// src/localization/util.ts
|
|
149
170
|
var languages = ["en", "de"];
|
|
@@ -187,7 +208,7 @@ var YearMonthPicker = ({
|
|
|
187
208
|
}) => {
|
|
188
209
|
const locale = useLocale();
|
|
189
210
|
const ref = useRef(null);
|
|
190
|
-
|
|
211
|
+
useEffect4(() => {
|
|
191
212
|
const scrollToItem = () => {
|
|
192
213
|
if (ref.current) {
|
|
193
214
|
ref.current.scrollIntoView({
|
|
@@ -206,11 +227,11 @@ var YearMonthPicker = ({
|
|
|
206
227
|
return /* @__PURE__ */ jsx3("div", { className: clsx2("col select-none", className), children: /* @__PURE__ */ jsx3(Scrollbars, { autoHeight: true, autoHeightMax: maxHeight, style: { height: "100%" }, children: /* @__PURE__ */ jsx3("div", { className: "col gap-y-1 mr-3", children: years.map((year) => {
|
|
207
228
|
const selectedYear = displayedYearMonth.getFullYear() === year;
|
|
208
229
|
return /* @__PURE__ */ jsx3(
|
|
209
|
-
|
|
230
|
+
ExpandableUncontrolled,
|
|
210
231
|
{
|
|
211
232
|
ref: (displayedYearMonth.getFullYear() ?? (/* @__PURE__ */ new Date()).getFullYear()) === year ? ref : void 0,
|
|
212
233
|
label: /* @__PURE__ */ jsx3("span", { className: clsx2({ "text-primary font-bold": selectedYear }), children: year }),
|
|
213
|
-
|
|
234
|
+
isExpanded: showValueOpen && selectedYear,
|
|
214
235
|
children: /* @__PURE__ */ jsx3("div", { className: "col gap-y-1 px-2 pb-2", children: equalSizeGroups([...monthsList], 3).map((monthList, index) => /* @__PURE__ */ jsx3("div", { className: "row", children: monthList.map((month) => {
|
|
215
236
|
const monthIndex = monthsList.indexOf(month);
|
|
216
237
|
const newDate = new Date(year, monthIndex);
|
|
@@ -251,7 +272,7 @@ var YearMonthPickerUncontrolled = ({
|
|
|
251
272
|
...props
|
|
252
273
|
}) => {
|
|
253
274
|
const [yearMonth, setYearMonth] = useState4(displayedYearMonth);
|
|
254
|
-
|
|
275
|
+
useEffect4(() => setYearMonth(displayedYearMonth), [displayedYearMonth]);
|
|
255
276
|
return /* @__PURE__ */ jsx3(
|
|
256
277
|
YearMonthPicker,
|
|
257
278
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/date/YearMonthPicker.tsx","../../../src/util/noop.ts","../../../src/util/array.ts","../../../src/components/layout-and-navigation/Expandable.tsx","../../../src/util/date.ts","../../../src/localization/LanguageProvider.tsx","../../../src/hooks/useLocalStorage.ts","../../../src/localization/util.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { Scrollbars } from 'react-custom-scrollbars-2'\nimport { noop } from '@/util/noop'\nimport { equalSizeGroups, range } from '@/util/array'\nimport clsx from 'clsx'\nimport { Expandable } from '@/components/layout-and-navigation/Expandable'\nimport { addDuration, monthsList, subtractDuration } from '@/util/date'\nimport { useLocale } from '@/localization/LanguageProvider'\n\nexport type YearMonthPickerProps = {\n displayedYearMonth?: Date,\n start?: Date,\n end?: Date,\n onChange?: (date: Date) => void,\n className?: string,\n maxHeight?: number,\n showValueOpen?: boolean,\n}\n\n// TODO use a dynamically loading infinite list here\nexport const YearMonthPicker = ({\n displayedYearMonth = new Date(),\n start = subtractDuration(new Date(), { years: 50 }),\n end = addDuration(new Date(), { years: 50 }),\n onChange = noop,\n className = '',\n maxHeight = 300,\n showValueOpen = true\n }: YearMonthPickerProps) => {\n const locale = useLocale()\n const ref = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n const scrollToItem = () => {\n if (ref.current) {\n ref.current.scrollIntoView({\n behavior: 'instant',\n block: 'center',\n })\n }\n }\n\n scrollToItem()\n }, [ref])\n\n if (end < start) {\n console.error(`startYear: (${start}) less than endYear: (${end})`)\n return null\n }\n\n const years = range(start.getFullYear(), end.getFullYear())\n\n return (\n <div className={clsx('col select-none', className)}>\n <Scrollbars autoHeight autoHeightMax={maxHeight} style={{ height: '100%' }}>\n <div className=\"col gap-y-1 mr-3\">\n {years.map(year => {\n const selectedYear = displayedYearMonth.getFullYear() === year\n return (\n <Expandable\n key={year}\n ref={(displayedYearMonth.getFullYear() ?? new Date().getFullYear()) === year ? ref : undefined}\n label={<span className={clsx({ 'text-primary font-bold': selectedYear })}>{year}</span>}\n initialExpansion={showValueOpen && selectedYear}\n >\n <div className=\"col gap-y-1 px-2 pb-2\">\n {equalSizeGroups([...monthsList], 3).map((monthList, index) => (\n <div key={index} className=\"row\">\n {monthList.map(month => {\n const monthIndex = monthsList.indexOf(month)\n const newDate = new Date(year, monthIndex)\n\n const selectedMonth = selectedYear && monthIndex === displayedYearMonth.getMonth()\n const firstOfMonth = new Date(year, monthIndex, 1)\n const lastOfMonth = new Date(year, monthIndex, 1)\n const isAfterStart = start === undefined || start <= addDuration(subtractDuration(lastOfMonth, { days: 1 }), { months: 1 })\n const isBeforeEnd = end === undefined || firstOfMonth <= end\n const isValid = isAfterStart && isBeforeEnd\n return (\n <button\n key={month}\n disabled={!isValid}\n className={clsx(\n 'chip hover:brightness-95 flex-1',\n {\n 'bg-gray-50 text-black': !selectedMonth && isValid,\n 'bg-primary text-on-primary': selectedMonth && isValid,\n 'bg-disabled-background text-disabled-text': !isValid\n }\n )}\n onClick={() => {\n onChange(newDate)\n }}\n >\n {new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}\n </button>\n )\n })}\n </div>\n ))}\n </div>\n </Expandable>\n )\n })}\n </div>\n </Scrollbars>\n </div>\n )\n}\n\nexport const YearMonthPickerUncontrolled = ({\n displayedYearMonth = new Date(),\n onChange = noop,\n ...props\n }: YearMonthPickerProps) => {\n const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth)\n\n useEffect(() => setYearMonth(displayedYearMonth), [displayedYearMonth])\n\n return (\n <YearMonthPicker\n displayedYearMonth={yearMonth}\n onChange={date => {\n setYearMonth(date)\n onChange(date)\n }}\n {...props}\n />\n )\n}\n","export const noop = () => undefined\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\n/**\n * @param start\n * @param end inclusive\n * @param allowEmptyRange Whether the range can be defined empty via end < start\n */\nexport const range = (start: number, end: number, allowEmptyRange: boolean = false): number[] => {\n if (end < start) {\n if (!allowEmptyRange) {\n console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set allowEmptyRange to true`)\n }\n return []\n }\n return Array.from({ length: end - start + 1 }, (_, index) => index + 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\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\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}\n","import type { PropsWithChildren, ReactNode } from 'react'\nimport { forwardRef, useState } from 'react'\nimport { ChevronDown, ChevronUp } from 'lucide-react'\nimport clsx from 'clsx'\n\ntype IconBuilder = (expanded: boolean) => ReactNode\n\nexport type ExpandableProps = PropsWithChildren<{\n label: ReactNode,\n icon?: IconBuilder,\n initialExpansion?: boolean,\n /**\n * Whether the expansion should only happen when the header is clicked or on the entire component\n */\n clickOnlyOnHeader?: boolean,\n disabled?: boolean,\n className?: string,\n headerClassName?: string,\n}>\n\nconst DefaultIcon: IconBuilder = (expanded) => expanded ?\n (<ChevronUp size={16} className=\"min-w-[16px]\"/>)\n : (<ChevronDown size={16} className=\"min-w-[16px]\"/>)\n\n/**\n * A Component for showing and hiding content\n */\nexport const Expandable = forwardRef<HTMLDivElement, ExpandableProps>(({\n children,\n label,\n icon,\n initialExpansion = false,\n clickOnlyOnHeader = true,\n disabled = false,\n className = '',\n headerClassName = ''\n }, ref) => {\n const [isExpanded, setIsExpanded] = useState(initialExpansion)\n icon ??= DefaultIcon\n\n return (\n <div\n ref={ref}\n className={clsx('col gap-y-0 bg-surface text-on-surface group rounded-lg shadow-sm', { 'cursor-pointer': !clickOnlyOnHeader && !disabled }, className)}\n onClick={() => !clickOnlyOnHeader && !disabled && setIsExpanded(!isExpanded)}\n >\n <div\n className={clsx(\n 'row py-2 px-4 rounded-lg justify-between items-center bg-surface text-on-surface select-none',\n {\n 'group-hover:brightness-95': !isExpanded,\n 'hover:brightness-95': isExpanded && !disabled,\n 'cursor-pointer': clickOnlyOnHeader && !disabled,\n },\n headerClassName\n )}\n onClick={() => clickOnlyOnHeader && !disabled && setIsExpanded(!isExpanded)}\n >\n {label}\n {icon(isExpanded)}\n </div>\n {isExpanded && (\n <div className=\"col px-4 pb-2\">\n {children}\n </div>\n )}\n </div>\n )\n})\n\nExpandable.displayName = 'Expandable'\n","import { equalSizeGroups } from './array'\n\nexport const monthsList = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'] as const\nexport type Month = typeof monthsList[number]\n\nexport const weekDayList = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] as const\nexport type WeekDay = typeof weekDayList[number]\n\nexport const formatDate = (date: Date) => {\n const year = date.getFullYear().toString().padStart(4, '0')\n const month = (date.getMonth() + 1).toString().padStart(2, '0')\n const day = (date.getDate()).toString().padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nexport const formatDateTime = (date: Date) => {\n const dateString = formatDate(date)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n return `${dateString}T${hours}:${minutes}`\n}\n\nexport const getDaysInMonth = (year: number, month: number): number => {\n const lastDayOfMonth = new Date(year, month + 1, 0)\n return lastDayOfMonth.getDate()\n}\n\nexport type Duration = {\n years?: number,\n months?: number,\n days?: number,\n hours?: number,\n minutes?: number,\n seconds?: number,\n milliseconds?: number,\n}\n\nexport const changeDuration = (date: Date, duration: Duration, isAdding?: boolean): Date => {\n const {\n years = 0,\n months = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n milliseconds = 0,\n } = duration\n\n // Check ranges\n if (years < 0) {\n console.error(`Range error years must be greater than 0: received ${years}`)\n return new Date(date)\n }\n if (months < 0 || months > 11) {\n console.error(`Range error month must be 0 <= month <= 11: received ${months}`)\n return new Date(date)\n }\n if (days < 0) {\n console.error(`Range error days must be greater than 0: received ${days}`)\n return new Date(date)\n }\n if (hours < 0 || hours > 23) {\n console.error(`Range error hours must be 0 <= hours <= 23: received ${hours}`)\n return new Date(date)\n }\n if (minutes < 0 || minutes > 59) {\n console.error(`Range error minutes must be 0 <= minutes <= 59: received ${minutes}`)\n return new Date(date)\n }\n if (seconds < 0 || seconds > 59) {\n console.error(`Range error seconds must be 0 <= seconds <= 59: received ${seconds}`)\n return new Date(date)\n }\n if (milliseconds < 0) {\n console.error(`Range error seconds must be greater than 0: received ${milliseconds}`)\n return new Date(date)\n }\n\n const multiplier = isAdding ? 1 : -1\n\n const newDate = new Date(date)\n\n newDate.setFullYear(newDate.getFullYear() + multiplier * years)\n\n newDate.setMonth(newDate.getMonth() + multiplier * months)\n\n newDate.setDate(newDate.getDate() + multiplier * days)\n\n newDate.setHours(newDate.getHours() + multiplier * hours)\n\n newDate.setMinutes(newDate.getMinutes() + multiplier * minutes)\n\n newDate.setSeconds(newDate.getSeconds() + multiplier * seconds)\n\n newDate.setMilliseconds(newDate.getMilliseconds() + multiplier * milliseconds)\n\n return newDate\n}\n\nexport const addDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, true)\n}\n\nexport const subtractDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, false)\n}\n\nexport const getBetweenDuration = (startDate: Date, endDate: Date): Duration => {\n const durationInMilliseconds = endDate.getTime() - startDate.getTime()\n\n const millisecondsInSecond = 1000\n const millisecondsInMinute = 60 * millisecondsInSecond\n const millisecondsInHour = 60 * millisecondsInMinute\n const millisecondsInDay = 24 * millisecondsInHour\n const millisecondsInMonth = 30 * millisecondsInDay // Rough estimation, can be adjusted\n\n const years = Math.floor(durationInMilliseconds / (365.25 * millisecondsInDay))\n const months = Math.floor(durationInMilliseconds / millisecondsInMonth)\n const days = Math.floor(durationInMilliseconds / millisecondsInDay)\n const hours = Math.floor((durationInMilliseconds % millisecondsInDay) / millisecondsInHour)\n const seconds = Math.floor((durationInMilliseconds % millisecondsInHour) / millisecondsInSecond)\n const milliseconds = durationInMilliseconds % millisecondsInSecond\n\n return {\n years,\n months,\n days,\n hours,\n seconds,\n milliseconds,\n }\n}\n\n/** Checks if a given date is in the range of two dates\n *\n * An undefined value for startDate or endDate means no bound for the start or end respectively\n */\nexport const isInTimeSpan = (value: Date, startDate?: Date, endDate?: Date): boolean => {\n if (startDate && endDate) {\n console.assert(startDate <= endDate)\n return startDate <= value && value <= endDate\n } else if (startDate) {\n return startDate <= value\n } else if (endDate) {\n return endDate >= value\n } else {\n return true\n }\n}\n\n/** Compare two dates on the year, month, day */\nexport const equalDate = (date1: Date, date2: Date) => {\n return date1.getFullYear() === date2.getFullYear()\n && date1.getMonth() === date2.getMonth()\n && date1.getDate() === date2.getDate()\n}\n\nexport const getWeeksForCalenderMonth = (date: Date, weekStart: WeekDay, weeks: number = 6) => {\n const month = date.getMonth()\n const year = date.getFullYear()\n\n const dayList: Date[] = []\n let currentDate = new Date(year, month, 1) // Start of month\n const weekStartIndex = weekDayList.indexOf(weekStart)\n\n // Move the current day to the week before\n while (currentDate.getDay() !== weekStartIndex) {\n currentDate = subtractDuration(currentDate, { days: 1 })\n }\n\n while (dayList.length < 7 * weeks) {\n const date = new Date(currentDate)\n date.setHours(date.getHours(), date.getMinutes()) // To make sure we are not overwriting the time\n dayList.push(date)\n currentDate = addDuration(currentDate, { days: 1 })\n }\n\n // weeks\n return equalSizeGroups(dayList, 7)\n}\n","import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'\nimport { createContext, useContext, useEffect, 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\nexport const useLanguage = () => useContext(LanguageContext)\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\ntype LanguageProviderProps = {\n initialLanguage?: Language,\n}\n\nexport const LanguageProvider = ({ initialLanguage, children }: PropsWithChildren<LanguageProviderProps>) => {\n const [language, setLanguage] = useState<Language>(initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n const [storedLanguage, setStoredLanguage] = useLocalStorage<Language>('language', initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n\n useEffect(() => {\n if (language !== initialLanguage && initialLanguage) {\n console.warn('LanguageProvider initial state changed: Prefer using languageProvider\\'s setLanguage instead')\n setLanguage(initialLanguage)\n }\n }, [initialLanguage]) // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n // TODO set locale of html tag here as well\n setStoredLanguage(language)\n }, [language, setStoredLanguage])\n\n useEffect(() => {\n if (storedLanguage !== null) {\n setLanguage(storedLanguage)\n return\n }\n\n const LanguageToTestAgainst = Object.values(LanguageUtil.languages)\n\n const matchingBrowserLanguage = window.navigator.languages\n .map(language => LanguageToTestAgainst.find((test) => language === test || language.split('-')[0] === test))\n .filter(entry => entry !== undefined)\n\n if (matchingBrowserLanguage.length === 0) return\n\n const firstMatch = matchingBrowserLanguage[0] as Language\n setLanguage(firstMatch)\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <LanguageContext.Provider value={{\n language,\n setLanguage\n }}>\n {children}\n </LanguageContext.Provider>\n )\n}","import type { Dispatch, SetStateAction } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { LocalStorageService } from '../util/storage'\n\ntype SetValue<T> = Dispatch<SetStateAction<T>>\nexport const useLocalStorage = <T>(key: string, initValue: T): [T, SetValue<T>] => {\n const get = useCallback((): T => {\n if (typeof window === 'undefined') {\n return initValue\n }\n const storageService = new LocalStorageService()\n const value = storageService.get<T>(key)\n return value || initValue\n }, [initValue, key])\n\n const [storedValue, setStoredValue] = useState<T>(get)\n\n const setValue: SetValue<T> = useCallback(value => {\n const newValue = value instanceof Function ? value(storedValue) : value\n const storageService = new LocalStorageService()\n storageService.set(key, value)\n\n setStoredValue(newValue)\n }, [storedValue, setStoredValue, key])\n\n useEffect(() => {\n setStoredValue(get())\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return [storedValue, setValue]\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}"],"mappings":"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","names":["useEffect","useState","clsx","useEffect","useState","useState","jsx","jsx","useEffect","clsx","useState"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/date/YearMonthPicker.tsx","../../../src/util/noop.ts","../../../src/util/array.ts","../../../src/components/layout-and-navigation/Expandable.tsx","../../../src/util/date.ts","../../../src/localization/LanguageProvider.tsx","../../../src/hooks/useLocalStorage.ts","../../../src/localization/util.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { Scrollbars } from 'react-custom-scrollbars-2'\nimport { noop } from '@/util/noop'\nimport { equalSizeGroups, range } from '@/util/array'\nimport clsx from 'clsx'\nimport { ExpandableUncontrolled } from '@/components/layout-and-navigation/Expandable'\nimport { addDuration, monthsList, subtractDuration } from '@/util/date'\nimport { useLocale } from '@/localization/LanguageProvider'\n\nexport type YearMonthPickerProps = {\n displayedYearMonth?: Date,\n start?: Date,\n end?: Date,\n onChange?: (date: Date) => void,\n className?: string,\n maxHeight?: number,\n showValueOpen?: boolean,\n}\n\n// TODO use a dynamically loading infinite list here\nexport const YearMonthPicker = ({\n displayedYearMonth = new Date(),\n start = subtractDuration(new Date(), { years: 50 }),\n end = addDuration(new Date(), { years: 50 }),\n onChange = noop,\n className = '',\n maxHeight = 300,\n showValueOpen = true\n }: YearMonthPickerProps) => {\n const locale = useLocale()\n const ref = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n const scrollToItem = () => {\n if (ref.current) {\n ref.current.scrollIntoView({\n behavior: 'instant',\n block: 'center',\n })\n }\n }\n\n scrollToItem()\n }, [ref])\n\n if (end < start) {\n console.error(`startYear: (${start}) less than endYear: (${end})`)\n return null\n }\n\n const years = range(start.getFullYear(), end.getFullYear())\n\n return (\n <div className={clsx('col select-none', className)}>\n <Scrollbars autoHeight autoHeightMax={maxHeight} style={{ height: '100%' }}>\n <div className=\"col gap-y-1 mr-3\">\n {years.map(year => {\n const selectedYear = displayedYearMonth.getFullYear() === year\n return (\n <ExpandableUncontrolled\n key={year}\n ref={(displayedYearMonth.getFullYear() ?? new Date().getFullYear()) === year ? ref : undefined}\n label={<span className={clsx({ 'text-primary font-bold': selectedYear })}>{year}</span>}\n isExpanded={showValueOpen && selectedYear}\n >\n <div className=\"col gap-y-1 px-2 pb-2\">\n {equalSizeGroups([...monthsList], 3).map((monthList, index) => (\n <div key={index} className=\"row\">\n {monthList.map(month => {\n const monthIndex = monthsList.indexOf(month)\n const newDate = new Date(year, monthIndex)\n\n const selectedMonth = selectedYear && monthIndex === displayedYearMonth.getMonth()\n const firstOfMonth = new Date(year, monthIndex, 1)\n const lastOfMonth = new Date(year, monthIndex, 1)\n const isAfterStart = start === undefined || start <= addDuration(subtractDuration(lastOfMonth, { days: 1 }), { months: 1 })\n const isBeforeEnd = end === undefined || firstOfMonth <= end\n const isValid = isAfterStart && isBeforeEnd\n return (\n <button\n key={month}\n disabled={!isValid}\n className={clsx(\n 'chip hover:brightness-95 flex-1',\n {\n 'bg-gray-50 text-black': !selectedMonth && isValid,\n 'bg-primary text-on-primary': selectedMonth && isValid,\n 'bg-disabled-background text-disabled-text': !isValid\n }\n )}\n onClick={() => {\n onChange(newDate)\n }}\n >\n {new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}\n </button>\n )\n })}\n </div>\n ))}\n </div>\n </ExpandableUncontrolled>\n )\n })}\n </div>\n </Scrollbars>\n </div>\n )\n}\n\nexport const YearMonthPickerUncontrolled = ({\n displayedYearMonth = new Date(),\n onChange = noop,\n ...props\n }: YearMonthPickerProps) => {\n const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth)\n\n useEffect(() => setYearMonth(displayedYearMonth), [displayedYearMonth])\n\n return (\n <YearMonthPicker\n displayedYearMonth={yearMonth}\n onChange={date => {\n setYearMonth(date)\n onChange(date)\n }}\n {...props}\n />\n )\n}\n","export const noop = () => undefined\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\n/**\n * @param start\n * @param end inclusive\n * @param allowEmptyRange Whether the range can be defined empty via end < start\n */\nexport const range = (start: number, end: number, allowEmptyRange: boolean = false): number[] => {\n if (end < start) {\n if (!allowEmptyRange) {\n console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set allowEmptyRange to true`)\n }\n return []\n }\n return Array.from({ length: end - start + 1 }, (_, index) => index + 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\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\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}\n","import type { PropsWithChildren, ReactNode } from 'react'\nimport { forwardRef, useEffect, useState } from 'react'\nimport { ChevronDown, ChevronUp } from 'lucide-react'\nimport clsx from 'clsx'\nimport { noop } from '@/util/noop'\n\ntype IconBuilder = (expanded: boolean) => ReactNode\n\nexport type ExpandableProps = PropsWithChildren<{\n label: ReactNode,\n icon?: IconBuilder,\n isExpanded?: boolean,\n onChange?: (isExpanded: boolean) => void,\n /**\n * Whether the expansion should only happen when the header is clicked or on the entire component\n */\n clickOnlyOnHeader?: boolean,\n disabled?: boolean,\n className?: string,\n headerClassName?: string,\n}>\n\nconst DefaultIcon: IconBuilder = (expanded) => expanded ?\n (<ChevronUp size={16} className=\"min-w-[16px]\"/>)\n : (<ChevronDown size={16} className=\"min-w-[16px]\"/>)\n\n\n/**\n * A Component for showing and hiding content\n */\nexport const Expandable = forwardRef<HTMLDivElement, ExpandableProps>(function Expandable({\n children,\n label,\n icon,\n isExpanded = false,\n onChange = noop,\n clickOnlyOnHeader = true,\n disabled = false,\n className = '',\n headerClassName = ''\n }, ref) {\n icon ??= DefaultIcon\n\n return (\n <div\n ref={ref}\n className={clsx('col gap-y-0 bg-surface text-on-surface group rounded-lg shadow-sm', { 'cursor-pointer': !clickOnlyOnHeader && !disabled }, className)}\n onClick={() => !clickOnlyOnHeader && !disabled && onChange(!isExpanded)}\n >\n <div\n className={clsx(\n 'row py-2 px-4 rounded-lg justify-between items-center bg-surface text-on-surface select-none',\n {\n 'group-hover:brightness-95': !isExpanded,\n 'hover:brightness-95': isExpanded && !disabled,\n 'cursor-pointer': clickOnlyOnHeader && !disabled,\n },\n headerClassName\n )}\n onClick={() => clickOnlyOnHeader && !disabled && onChange(!isExpanded)}\n >\n {label}\n {icon(isExpanded)}\n </div>\n {isExpanded && (\n <div className=\"col px-4 pb-2\">\n {children}\n </div>\n )}\n </div>\n )\n})\n\nexport const ExpandableUncontrolled = forwardRef<HTMLDivElement, ExpandableProps>(function ExpandableUncontrolled({\n isExpanded,\n onChange = noop,\n ...props\n },\n ref) {\n const [usedIsExpanded, setUsedIsExpanded] = useState(isExpanded)\n\n useEffect(() => {\n setUsedIsExpanded(isExpanded)\n }, [isExpanded])\n\n return (\n <Expandable\n {...props}\n ref={ref}\n isExpanded={usedIsExpanded}\n onChange={value => {\n onChange(value)\n setUsedIsExpanded(value)\n }}\n />\n )\n})\n","import { equalSizeGroups } from './array'\n\nexport const monthsList = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'] as const\nexport type Month = typeof monthsList[number]\n\nexport const weekDayList = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] as const\nexport type WeekDay = typeof weekDayList[number]\n\nexport const formatDate = (date: Date) => {\n const year = date.getFullYear().toString().padStart(4, '0')\n const month = (date.getMonth() + 1).toString().padStart(2, '0')\n const day = (date.getDate()).toString().padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nexport const formatDateTime = (date: Date) => {\n const dateString = formatDate(date)\n const hours = date.getHours().toString().padStart(2, '0')\n const minutes = date.getMinutes().toString().padStart(2, '0')\n return `${dateString}T${hours}:${minutes}`\n}\n\nexport const getDaysInMonth = (year: number, month: number): number => {\n const lastDayOfMonth = new Date(year, month + 1, 0)\n return lastDayOfMonth.getDate()\n}\n\nexport type Duration = {\n years?: number,\n months?: number,\n days?: number,\n hours?: number,\n minutes?: number,\n seconds?: number,\n milliseconds?: number,\n}\n\nexport const changeDuration = (date: Date, duration: Duration, isAdding?: boolean): Date => {\n const {\n years = 0,\n months = 0,\n days = 0,\n hours = 0,\n minutes = 0,\n seconds = 0,\n milliseconds = 0,\n } = duration\n\n // Check ranges\n if (years < 0) {\n console.error(`Range error years must be greater than 0: received ${years}`)\n return new Date(date)\n }\n if (months < 0 || months > 11) {\n console.error(`Range error month must be 0 <= month <= 11: received ${months}`)\n return new Date(date)\n }\n if (days < 0) {\n console.error(`Range error days must be greater than 0: received ${days}`)\n return new Date(date)\n }\n if (hours < 0 || hours > 23) {\n console.error(`Range error hours must be 0 <= hours <= 23: received ${hours}`)\n return new Date(date)\n }\n if (minutes < 0 || minutes > 59) {\n console.error(`Range error minutes must be 0 <= minutes <= 59: received ${minutes}`)\n return new Date(date)\n }\n if (seconds < 0 || seconds > 59) {\n console.error(`Range error seconds must be 0 <= seconds <= 59: received ${seconds}`)\n return new Date(date)\n }\n if (milliseconds < 0) {\n console.error(`Range error seconds must be greater than 0: received ${milliseconds}`)\n return new Date(date)\n }\n\n const multiplier = isAdding ? 1 : -1\n\n const newDate = new Date(date)\n\n newDate.setFullYear(newDate.getFullYear() + multiplier * years)\n\n newDate.setMonth(newDate.getMonth() + multiplier * months)\n\n newDate.setDate(newDate.getDate() + multiplier * days)\n\n newDate.setHours(newDate.getHours() + multiplier * hours)\n\n newDate.setMinutes(newDate.getMinutes() + multiplier * minutes)\n\n newDate.setSeconds(newDate.getSeconds() + multiplier * seconds)\n\n newDate.setMilliseconds(newDate.getMilliseconds() + multiplier * milliseconds)\n\n return newDate\n}\n\nexport const addDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, true)\n}\n\nexport const subtractDuration = (date: Date, duration: Duration): Date => {\n return changeDuration(date, duration, false)\n}\n\nexport const getBetweenDuration = (startDate: Date, endDate: Date): Duration => {\n const durationInMilliseconds = endDate.getTime() - startDate.getTime()\n\n const millisecondsInSecond = 1000\n const millisecondsInMinute = 60 * millisecondsInSecond\n const millisecondsInHour = 60 * millisecondsInMinute\n const millisecondsInDay = 24 * millisecondsInHour\n const millisecondsInMonth = 30 * millisecondsInDay // Rough estimation, can be adjusted\n\n const years = Math.floor(durationInMilliseconds / (365.25 * millisecondsInDay))\n const months = Math.floor(durationInMilliseconds / millisecondsInMonth)\n const days = Math.floor(durationInMilliseconds / millisecondsInDay)\n const hours = Math.floor((durationInMilliseconds % millisecondsInDay) / millisecondsInHour)\n const seconds = Math.floor((durationInMilliseconds % millisecondsInHour) / millisecondsInSecond)\n const milliseconds = durationInMilliseconds % millisecondsInSecond\n\n return {\n years,\n months,\n days,\n hours,\n seconds,\n milliseconds,\n }\n}\n\n/** Checks if a given date is in the range of two dates\n *\n * An undefined value for startDate or endDate means no bound for the start or end respectively\n */\nexport const isInTimeSpan = (value: Date, startDate?: Date, endDate?: Date): boolean => {\n if (startDate && endDate) {\n console.assert(startDate <= endDate)\n return startDate <= value && value <= endDate\n } else if (startDate) {\n return startDate <= value\n } else if (endDate) {\n return endDate >= value\n } else {\n return true\n }\n}\n\n/** Compare two dates on the year, month, day */\nexport const equalDate = (date1: Date, date2: Date) => {\n return date1.getFullYear() === date2.getFullYear()\n && date1.getMonth() === date2.getMonth()\n && date1.getDate() === date2.getDate()\n}\n\nexport const getWeeksForCalenderMonth = (date: Date, weekStart: WeekDay, weeks: number = 6) => {\n const month = date.getMonth()\n const year = date.getFullYear()\n\n const dayList: Date[] = []\n let currentDate = new Date(year, month, 1) // Start of month\n const weekStartIndex = weekDayList.indexOf(weekStart)\n\n // Move the current day to the week before\n while (currentDate.getDay() !== weekStartIndex) {\n currentDate = subtractDuration(currentDate, { days: 1 })\n }\n\n while (dayList.length < 7 * weeks) {\n const date = new Date(currentDate)\n date.setHours(date.getHours(), date.getMinutes()) // To make sure we are not overwriting the time\n dayList.push(date)\n currentDate = addDuration(currentDate, { days: 1 })\n }\n\n // weeks\n return equalSizeGroups(dayList, 7)\n}\n","import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'\nimport { createContext, useContext, useEffect, 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\nexport const useLanguage = () => useContext(LanguageContext)\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\ntype LanguageProviderProps = {\n initialLanguage?: Language,\n}\n\nexport const LanguageProvider = ({ initialLanguage, children }: PropsWithChildren<LanguageProviderProps>) => {\n const [language, setLanguage] = useState<Language>(initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n const [storedLanguage, setStoredLanguage] = useLocalStorage<Language>('language', initialLanguage ?? LanguageUtil.DEFAULT_LANGUAGE)\n\n useEffect(() => {\n if (language !== initialLanguage && initialLanguage) {\n console.warn('LanguageProvider initial state changed: Prefer using languageProvider\\'s setLanguage instead')\n setLanguage(initialLanguage)\n }\n }, [initialLanguage]) // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => {\n // TODO set locale of html tag here as well\n setStoredLanguage(language)\n }, [language, setStoredLanguage])\n\n useEffect(() => {\n if (storedLanguage !== null) {\n setLanguage(storedLanguage)\n return\n }\n\n const LanguageToTestAgainst = Object.values(LanguageUtil.languages)\n\n const matchingBrowserLanguage = window.navigator.languages\n .map(language => LanguageToTestAgainst.find((test) => language === test || language.split('-')[0] === test))\n .filter(entry => entry !== undefined)\n\n if (matchingBrowserLanguage.length === 0) return\n\n const firstMatch = matchingBrowserLanguage[0] as Language\n setLanguage(firstMatch)\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <LanguageContext.Provider value={{\n language,\n setLanguage\n }}>\n {children}\n </LanguageContext.Provider>\n )\n}","import type { Dispatch, SetStateAction } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\nimport { LocalStorageService } from '../util/storage'\n\ntype SetValue<T> = Dispatch<SetStateAction<T>>\nexport const useLocalStorage = <T>(key: string, initValue: T): [T, SetValue<T>] => {\n const get = useCallback((): T => {\n if (typeof window === 'undefined') {\n return initValue\n }\n const storageService = new LocalStorageService()\n const value = storageService.get<T>(key)\n return value || initValue\n }, [initValue, key])\n\n const [storedValue, setStoredValue] = useState<T>(get)\n\n const setValue: SetValue<T> = useCallback(value => {\n const newValue = value instanceof Function ? value(storedValue) : value\n const storageService = new LocalStorageService()\n storageService.set(key, value)\n\n setStoredValue(newValue)\n }, [storedValue, setStoredValue, key])\n\n useEffect(() => {\n setStoredValue(get())\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n return [storedValue, setValue]\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}"],"mappings":"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","names":["useEffect","useState","clsx","Expandable","ExpandableUncontrolled","useEffect","useState","useEffect","useState","jsx","jsx","useEffect","clsx","useState"]}
|
|
@@ -37,7 +37,6 @@ __export(Avatar_exports, {
|
|
|
37
37
|
default: () => Avatar_default
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(Avatar_exports);
|
|
40
|
-
var import_image = __toESM(require("next/image"), 1);
|
|
41
40
|
var import_clsx = __toESM(require("clsx"), 1);
|
|
42
41
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
43
42
|
var avtarSizeList = ["tiny", "small", "medium", "large"];
|
|
@@ -66,7 +65,7 @@ var Avatar = ({ avatarUrl, alt, size = "medium", className = "" }) => {
|
|
|
66
65
|
return (
|
|
67
66
|
// TODO transparent or white background later
|
|
68
67
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_clsx.default)(`rounded-full bg-primary`, className), style, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
69
|
-
|
|
68
|
+
"img",
|
|
70
69
|
{
|
|
71
70
|
className: "rounded-full border border-gray-200",
|
|
72
71
|
style,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/icons-and-geometry/Avatar.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../../src/components/icons-and-geometry/Avatar.tsx"],"sourcesContent":["import clsx from 'clsx'\n\nexport const avtarSizeList = ['tiny', 'small', 'medium', 'large'] as const\nexport type AvatarSize = typeof avtarSizeList[number]\nexport const avatarSizeMapping: Record<AvatarSize, number> = {\n tiny: 24,\n small: 32,\n medium: 48,\n large: 64\n}\n\nexport type AvatarProps = {\n avatarUrl: string,\n alt: string,\n size?: AvatarSize,\n className?: string,\n}\n\n/**\n * A component for showing a profile picture\n */\nexport const Avatar = ({ avatarUrl, alt, size = 'medium', className = '' }: AvatarProps) => {\n // TODO remove later\n avatarUrl = 'https://cdn.helpwave.de/boringavatar.svg'\n\n const avtarSize = {\n tiny: 24,\n small: 32,\n medium: 48,\n large: 64,\n }[size]\n\n const style = {\n width: avtarSize + 'px',\n height: avtarSize + 'px',\n maxWidth: avtarSize + 'px',\n maxHeight: avtarSize + 'px',\n minWidth: avtarSize + 'px',\n minHeight: avtarSize + 'px',\n }\n\n return (\n // TODO transparent or white background later\n <div className={clsx(`rounded-full bg-primary`, className)} style={style}>\n <img\n className=\"rounded-full border border-gray-200\"\n style={style}\n src={avatarUrl}\n alt={alt}\n width={avtarSize}\n height={avtarSize}\n />\n </div>\n )\n}\n\nexport type AvatarGroupProps = {\n avatars: Omit<AvatarProps, 'size'>[],\n maxShownProfiles?: number,\n size?: AvatarSize,\n}\n\n/**\n * A component for showing a group of Avatar's\n */\nexport const AvatarGroup = ({\n avatars,\n maxShownProfiles = 5,\n size = 'tiny'\n }: AvatarGroupProps) => {\n const displayedProfiles = avatars.length < maxShownProfiles ? avatars : avatars.slice(0, maxShownProfiles)\n const diameter = avatarSizeMapping[size]\n const stackingOverlap = 0.5 // given as a percentage\n const notDisplayedProfiles = avatars.length - maxShownProfiles\n const avatarGroupWidth = diameter * (stackingOverlap * (displayedProfiles.length - 1) + 1)\n return (\n <div className=\"row relative\" style={{ height: diameter + 'px' }}>\n <div style={{ width: avatarGroupWidth + 'px' }}>\n {displayedProfiles.map((avatar, index) => (\n <div\n key={index}\n className=\"absolute\"\n style={{ left: (index * diameter * stackingOverlap) + 'px', zIndex: maxShownProfiles - index }}\n >\n <Avatar avatarUrl={avatar.avatarUrl} alt={avatar.alt} size={size}/>\n </div>\n ))}\n </div>\n {\n notDisplayedProfiles > 0 && (\n <div\n className=\"truncate row items-center\"\n style={{ fontSize: (diameter / 2) + 'px', marginLeft: (1 + diameter / 16) + 'px' }}\n >\n <span>+ {notDisplayedProfiles}</span>\n </div>\n )\n }\n </div>\n )\n}\n\nexport default { Avatar, AvatarGroup }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiB;AA4CX;AA1CC,IAAM,gBAAgB,CAAC,QAAQ,SAAS,UAAU,OAAO;AAEzD,IAAM,oBAAgD;AAAA,EAC3D,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAYO,IAAM,SAAS,CAAC,EAAE,WAAW,KAAK,OAAO,UAAU,YAAY,GAAG,MAAmB;AAE1F,cAAY;AAEZ,QAAM,YAAY;AAAA,IAChB,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,EAAE,IAAI;AAEN,QAAM,QAAQ;AAAA,IACZ,OAAO,YAAY;AAAA,IACnB,QAAQ,YAAY;AAAA,IACpB,UAAU,YAAY;AAAA,IACtB,WAAW,YAAY;AAAA,IACvB,UAAU,YAAY;AAAA,IACtB,WAAW,YAAY;AAAA,EACzB;AAEA;AAAA;AAAA,IAEE,4CAAC,SAAI,eAAW,YAAAA,SAAK,2BAA2B,SAAS,GAAG,OAC1D;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,IACV,GACF;AAAA;AAEJ;AAWO,IAAM,cAAc,CAAC;AAAA,EACE;AAAA,EACA,mBAAmB;AAAA,EACnB,OAAO;AACT,MAAwB;AAClD,QAAM,oBAAoB,QAAQ,SAAS,mBAAmB,UAAU,QAAQ,MAAM,GAAG,gBAAgB;AACzG,QAAM,WAAW,kBAAkB,IAAI;AACvC,QAAM,kBAAkB;AACxB,QAAM,uBAAuB,QAAQ,SAAS;AAC9C,QAAM,mBAAmB,YAAY,mBAAmB,kBAAkB,SAAS,KAAK;AACxF,SACE,6CAAC,SAAI,WAAU,gBAAe,OAAO,EAAE,QAAQ,WAAW,KAAK,GAC7D;AAAA,gDAAC,SAAI,OAAO,EAAE,OAAO,mBAAmB,KAAK,GAC1C,4BAAkB,IAAI,CAAC,QAAQ,UAC9B;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QACV,OAAO,EAAE,MAAO,QAAQ,WAAW,kBAAmB,MAAM,QAAQ,mBAAmB,MAAM;AAAA,QAE7F,sDAAC,UAAO,WAAW,OAAO,WAAW,KAAK,OAAO,KAAK,MAAW;AAAA;AAAA,MAJ5D;AAAA,IAKP,CACD,GACH;AAAA,IAEE,uBAAuB,KACrB;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,UAAW,WAAW,IAAK,MAAM,YAAa,IAAI,WAAW,KAAM,KAAK;AAAA,QAEjF,uDAAC,UAAK;AAAA;AAAA,UAAG;AAAA,WAAqB;AAAA;AAAA,IAChC;AAAA,KAGN;AAEJ;AAEA,IAAO,iBAAQ,EAAE,QAAQ,YAAY;","names":["clsx"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/components/icons-and-geometry/Avatar.tsx
|
|
2
|
-
import Image from "next/image";
|
|
3
2
|
import clsx from "clsx";
|
|
4
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
4
|
var avtarSizeList = ["tiny", "small", "medium", "large"];
|
|
@@ -28,7 +27,7 @@ var Avatar = ({ avatarUrl, alt, size = "medium", className = "" }) => {
|
|
|
28
27
|
return (
|
|
29
28
|
// TODO transparent or white background later
|
|
30
29
|
/* @__PURE__ */ jsx("div", { className: clsx(`rounded-full bg-primary`, className), style, children: /* @__PURE__ */ jsx(
|
|
31
|
-
|
|
30
|
+
"img",
|
|
32
31
|
{
|
|
33
32
|
className: "rounded-full border border-gray-200",
|
|
34
33
|
style,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/icons-and-geometry/Avatar.tsx"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../../src/components/icons-and-geometry/Avatar.tsx"],"sourcesContent":["import clsx from 'clsx'\n\nexport const avtarSizeList = ['tiny', 'small', 'medium', 'large'] as const\nexport type AvatarSize = typeof avtarSizeList[number]\nexport const avatarSizeMapping: Record<AvatarSize, number> = {\n tiny: 24,\n small: 32,\n medium: 48,\n large: 64\n}\n\nexport type AvatarProps = {\n avatarUrl: string,\n alt: string,\n size?: AvatarSize,\n className?: string,\n}\n\n/**\n * A component for showing a profile picture\n */\nexport const Avatar = ({ avatarUrl, alt, size = 'medium', className = '' }: AvatarProps) => {\n // TODO remove later\n avatarUrl = 'https://cdn.helpwave.de/boringavatar.svg'\n\n const avtarSize = {\n tiny: 24,\n small: 32,\n medium: 48,\n large: 64,\n }[size]\n\n const style = {\n width: avtarSize + 'px',\n height: avtarSize + 'px',\n maxWidth: avtarSize + 'px',\n maxHeight: avtarSize + 'px',\n minWidth: avtarSize + 'px',\n minHeight: avtarSize + 'px',\n }\n\n return (\n // TODO transparent or white background later\n <div className={clsx(`rounded-full bg-primary`, className)} style={style}>\n <img\n className=\"rounded-full border border-gray-200\"\n style={style}\n src={avatarUrl}\n alt={alt}\n width={avtarSize}\n height={avtarSize}\n />\n </div>\n )\n}\n\nexport type AvatarGroupProps = {\n avatars: Omit<AvatarProps, 'size'>[],\n maxShownProfiles?: number,\n size?: AvatarSize,\n}\n\n/**\n * A component for showing a group of Avatar's\n */\nexport const AvatarGroup = ({\n avatars,\n maxShownProfiles = 5,\n size = 'tiny'\n }: AvatarGroupProps) => {\n const displayedProfiles = avatars.length < maxShownProfiles ? avatars : avatars.slice(0, maxShownProfiles)\n const diameter = avatarSizeMapping[size]\n const stackingOverlap = 0.5 // given as a percentage\n const notDisplayedProfiles = avatars.length - maxShownProfiles\n const avatarGroupWidth = diameter * (stackingOverlap * (displayedProfiles.length - 1) + 1)\n return (\n <div className=\"row relative\" style={{ height: diameter + 'px' }}>\n <div style={{ width: avatarGroupWidth + 'px' }}>\n {displayedProfiles.map((avatar, index) => (\n <div\n key={index}\n className=\"absolute\"\n style={{ left: (index * diameter * stackingOverlap) + 'px', zIndex: maxShownProfiles - index }}\n >\n <Avatar avatarUrl={avatar.avatarUrl} alt={avatar.alt} size={size}/>\n </div>\n ))}\n </div>\n {\n notDisplayedProfiles > 0 && (\n <div\n className=\"truncate row items-center\"\n style={{ fontSize: (diameter / 2) + 'px', marginLeft: (1 + diameter / 16) + 'px' }}\n >\n <span>+ {notDisplayedProfiles}</span>\n </div>\n )\n }\n </div>\n )\n}\n\nexport default { Avatar, AvatarGroup }\n"],"mappings":";AAAA,OAAO,UAAU;AA4CX,cAkDM,YAlDN;AA1CC,IAAM,gBAAgB,CAAC,QAAQ,SAAS,UAAU,OAAO;AAEzD,IAAM,oBAAgD;AAAA,EAC3D,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAYO,IAAM,SAAS,CAAC,EAAE,WAAW,KAAK,OAAO,UAAU,YAAY,GAAG,MAAmB;AAE1F,cAAY;AAEZ,QAAM,YAAY;AAAA,IAChB,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,EACT,EAAE,IAAI;AAEN,QAAM,QAAQ;AAAA,IACZ,OAAO,YAAY;AAAA,IACnB,QAAQ,YAAY;AAAA,IACpB,UAAU,YAAY;AAAA,IACtB,WAAW,YAAY;AAAA,IACvB,UAAU,YAAY;AAAA,IACtB,WAAW,YAAY;AAAA,EACzB;AAEA;AAAA;AAAA,IAEE,oBAAC,SAAI,WAAW,KAAK,2BAA2B,SAAS,GAAG,OAC1D;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV;AAAA,QACA,KAAK;AAAA,QACL;AAAA,QACA,OAAO;AAAA,QACP,QAAQ;AAAA;AAAA,IACV,GACF;AAAA;AAEJ;AAWO,IAAM,cAAc,CAAC;AAAA,EACE;AAAA,EACA,mBAAmB;AAAA,EACnB,OAAO;AACT,MAAwB;AAClD,QAAM,oBAAoB,QAAQ,SAAS,mBAAmB,UAAU,QAAQ,MAAM,GAAG,gBAAgB;AACzG,QAAM,WAAW,kBAAkB,IAAI;AACvC,QAAM,kBAAkB;AACxB,QAAM,uBAAuB,QAAQ,SAAS;AAC9C,QAAM,mBAAmB,YAAY,mBAAmB,kBAAkB,SAAS,KAAK;AACxF,SACE,qBAAC,SAAI,WAAU,gBAAe,OAAO,EAAE,QAAQ,WAAW,KAAK,GAC7D;AAAA,wBAAC,SAAI,OAAO,EAAE,OAAO,mBAAmB,KAAK,GAC1C,4BAAkB,IAAI,CAAC,QAAQ,UAC9B;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QACV,OAAO,EAAE,MAAO,QAAQ,WAAW,kBAAmB,MAAM,QAAQ,mBAAmB,MAAM;AAAA,QAE7F,8BAAC,UAAO,WAAW,OAAO,WAAW,KAAK,OAAO,KAAK,MAAW;AAAA;AAAA,MAJ5D;AAAA,IAKP,CACD,GACH;AAAA,IAEE,uBAAuB,KACrB;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,UAAW,WAAW,IAAK,MAAM,YAAa,IAAI,WAAW,KAAM,KAAK;AAAA,QAEjF,+BAAC,UAAK;AAAA;AAAA,UAAG;AAAA,WAAqB;AAAA;AAAA,IAChC;AAAA,KAGN;AAEJ;AAEA,IAAO,iBAAQ,EAAE,QAAQ,YAAY;","names":[]}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/components/icons-and-geometry/Tag.tsx
|
|
@@ -33,20 +23,17 @@ __export(Tag_exports, {
|
|
|
33
23
|
TagIcon: () => TagIcon
|
|
34
24
|
});
|
|
35
25
|
module.exports = __toCommonJS(Tag_exports);
|
|
36
|
-
var import_image = __toESM(require("next/image"), 1);
|
|
37
26
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
38
27
|
var TagIcon = ({
|
|
39
28
|
className,
|
|
40
|
-
|
|
41
|
-
height = 16,
|
|
42
|
-
...props
|
|
29
|
+
size = 16
|
|
43
30
|
}) => {
|
|
44
31
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
45
|
-
|
|
32
|
+
"img",
|
|
46
33
|
{
|
|
47
|
-
|
|
48
|
-
width,
|
|
49
|
-
height,
|
|
34
|
+
style: { width: `${size}px`, height: `${size}px`, minWidth: `${size}px`, minHeight: `${size}px` },
|
|
35
|
+
width: size,
|
|
36
|
+
height: size,
|
|
50
37
|
alt: "",
|
|
51
38
|
src: "https://cdn.helpwave.de/icons/label.png",
|
|
52
39
|
className
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/icons-and-geometry/Tag.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/components/icons-and-geometry/Tag.tsx"],"sourcesContent":["export type TagProps = {\n size?: number,\n className?: string,\n}\n\n/**\n * Tag icon from flaticon\n *\n * https://www.flaticon.com/free-icon/price-tag_721550?term=label&page=1&position=8&origin=tag&related_id=721550\n *\n * When using it make attribution\n */\nexport const TagIcon = ({\n className,\n size = 16,\n }: TagProps) => {\n return (\n <img\n style={{ width: `${size}px`, height: `${size}px`, minWidth: `${size}px`, minHeight: `${size}px` }}\n width={size}\n height={size}\n alt=\"\"\n src=\"https://cdn.helpwave.de/icons/label.png\"\n className={className}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBI;AALG,IAAM,UAAU,CAAC;AAAA,EACE;AAAA,EACA,OAAO;AACT,MAAgB;AACtC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,OAAO,GAAG,IAAI,MAAM,QAAQ,GAAG,IAAI,MAAM,UAAU,GAAG,IAAI,MAAM,WAAW,GAAG,IAAI,KAAK;AAAA,MAChG,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,KAAI;AAAA,MACJ,KAAI;AAAA,MACJ;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ImageProps } from 'next/image';
|
|
3
2
|
|
|
4
|
-
type TagProps =
|
|
3
|
+
type TagProps = {
|
|
4
|
+
size?: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
5
7
|
/**
|
|
6
8
|
* Tag icon from flaticon
|
|
7
9
|
*
|
|
@@ -9,6 +11,6 @@ type TagProps = Omit<ImageProps, 'src' | 'alt'>;
|
|
|
9
11
|
*
|
|
10
12
|
* When using it make attribution
|
|
11
13
|
*/
|
|
12
|
-
declare const TagIcon: ({ className,
|
|
14
|
+
declare const TagIcon: ({ className, size, }: TagProps) => react_jsx_runtime.JSX.Element;
|
|
13
15
|
|
|
14
16
|
export { TagIcon, type TagProps };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ImageProps } from 'next/image';
|
|
3
2
|
|
|
4
|
-
type TagProps =
|
|
3
|
+
type TagProps = {
|
|
4
|
+
size?: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
5
7
|
/**
|
|
6
8
|
* Tag icon from flaticon
|
|
7
9
|
*
|
|
@@ -9,6 +11,6 @@ type TagProps = Omit<ImageProps, 'src' | 'alt'>;
|
|
|
9
11
|
*
|
|
10
12
|
* When using it make attribution
|
|
11
13
|
*/
|
|
12
|
-
declare const TagIcon: ({ className,
|
|
14
|
+
declare const TagIcon: ({ className, size, }: TagProps) => react_jsx_runtime.JSX.Element;
|
|
13
15
|
|
|
14
16
|
export { TagIcon, type TagProps };
|