@helpwave/hightide 0.1.7 → 0.1.9
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/components/branding/HelpwaveBadge.js +30 -8
- package/dist/components/branding/HelpwaveBadge.js.map +1 -1
- package/dist/components/branding/HelpwaveBadge.mjs +30 -8
- package/dist/components/branding/HelpwaveBadge.mjs.map +1 -1
- package/dist/components/date/DatePicker.d.mts +3 -4
- package/dist/components/date/DatePicker.d.ts +3 -4
- package/dist/components/date/DatePicker.js +135 -33
- package/dist/components/date/DatePicker.js.map +1 -1
- package/dist/components/date/DatePicker.mjs +135 -33
- package/dist/components/date/DatePicker.mjs.map +1 -1
- package/dist/components/date/DayPicker.d.mts +1 -1
- package/dist/components/date/DayPicker.d.ts +1 -1
- package/dist/components/date/DayPicker.js +23 -11
- package/dist/components/date/DayPicker.js.map +1 -1
- package/dist/components/date/DayPicker.mjs +23 -11
- package/dist/components/date/DayPicker.mjs.map +1 -1
- package/dist/components/date/TimeDisplay.d.mts +5 -19
- package/dist/components/date/TimeDisplay.d.ts +5 -19
- package/dist/components/date/TimeDisplay.js +121 -37
- package/dist/components/date/TimeDisplay.js.map +1 -1
- package/dist/components/date/TimeDisplay.mjs +121 -37
- package/dist/components/date/TimeDisplay.mjs.map +1 -1
- package/dist/components/date/YearMonthPicker.js +106 -22
- package/dist/components/date/YearMonthPicker.js.map +1 -1
- package/dist/components/date/YearMonthPicker.mjs +106 -22
- package/dist/components/date/YearMonthPicker.mjs.map +1 -1
- package/dist/components/dialogs/ConfirmDialog.d.mts +2 -5
- package/dist/components/dialogs/ConfirmDialog.d.ts +2 -5
- package/dist/components/dialogs/ConfirmDialog.js +157 -25
- package/dist/components/dialogs/ConfirmDialog.js.map +1 -1
- package/dist/components/dialogs/ConfirmDialog.mjs +157 -25
- package/dist/components/dialogs/ConfirmDialog.mjs.map +1 -1
- package/dist/components/icons-and-geometry/Avatar.js +2 -2
- package/dist/components/icons-and-geometry/Avatar.js.map +1 -1
- package/dist/components/icons-and-geometry/Avatar.mjs +2 -2
- package/dist/components/icons-and-geometry/Avatar.mjs.map +1 -1
- package/dist/components/layout-and-navigation/Carousel.d.mts +1 -1
- package/dist/components/layout-and-navigation/Carousel.d.ts +1 -1
- package/dist/components/layout-and-navigation/Carousel.js +86 -28
- package/dist/components/layout-and-navigation/Carousel.js.map +1 -1
- package/dist/components/layout-and-navigation/Carousel.mjs +86 -28
- package/dist/components/layout-and-navigation/Carousel.mjs.map +1 -1
- package/dist/components/layout-and-navigation/Expandable.d.mts +3 -0
- package/dist/components/layout-and-navigation/Expandable.d.ts +3 -0
- package/dist/components/layout-and-navigation/Expandable.js +4 -3
- package/dist/components/layout-and-navigation/Expandable.js.map +1 -1
- package/dist/components/layout-and-navigation/Expandable.mjs +4 -3
- package/dist/components/layout-and-navigation/Expandable.mjs.map +1 -1
- package/dist/components/layout-and-navigation/FAQSection.js +4 -3
- package/dist/components/layout-and-navigation/FAQSection.js.map +1 -1
- package/dist/components/layout-and-navigation/FAQSection.mjs +4 -3
- package/dist/components/layout-and-navigation/FAQSection.mjs.map +1 -1
- package/dist/components/layout-and-navigation/Overlay.d.mts +2 -3
- package/dist/components/layout-and-navigation/Overlay.d.ts +2 -3
- package/dist/components/layout-and-navigation/Overlay.js +152 -12
- package/dist/components/layout-and-navigation/Overlay.js.map +1 -1
- package/dist/components/layout-and-navigation/Overlay.mjs +152 -12
- package/dist/components/layout-and-navigation/Overlay.mjs.map +1 -1
- package/dist/components/layout-and-navigation/Pagination.d.mts +2 -3
- package/dist/components/layout-and-navigation/Pagination.d.ts +2 -3
- package/dist/components/layout-and-navigation/Pagination.js +145 -13
- package/dist/components/layout-and-navigation/Pagination.js.map +1 -1
- package/dist/components/layout-and-navigation/Pagination.mjs +145 -13
- package/dist/components/layout-and-navigation/Pagination.mjs.map +1 -1
- package/dist/components/layout-and-navigation/SearchableList.d.mts +10 -8
- package/dist/components/layout-and-navigation/SearchableList.d.ts +10 -8
- package/dist/components/layout-and-navigation/SearchableList.js +261 -30
- package/dist/components/layout-and-navigation/SearchableList.js.map +1 -1
- package/dist/components/layout-and-navigation/SearchableList.mjs +261 -30
- package/dist/components/layout-and-navigation/SearchableList.mjs.map +1 -1
- package/dist/components/layout-and-navigation/StepperBar.d.mts +2 -5
- package/dist/components/layout-and-navigation/StepperBar.d.ts +2 -5
- package/dist/components/layout-and-navigation/StepperBar.js +149 -19
- package/dist/components/layout-and-navigation/StepperBar.js.map +1 -1
- package/dist/components/layout-and-navigation/StepperBar.mjs +149 -19
- package/dist/components/layout-and-navigation/StepperBar.mjs.map +1 -1
- package/dist/components/layout-and-navigation/Table.js +146 -14
- package/dist/components/layout-and-navigation/Table.js.map +1 -1
- package/dist/components/layout-and-navigation/Table.mjs +146 -14
- package/dist/components/layout-and-navigation/Table.mjs.map +1 -1
- package/dist/components/layout-and-navigation/TextImage.d.mts +3 -4
- package/dist/components/layout-and-navigation/TextImage.d.ts +3 -4
- package/dist/components/layout-and-navigation/TextImage.js +153 -19
- package/dist/components/layout-and-navigation/TextImage.js.map +1 -1
- package/dist/components/layout-and-navigation/TextImage.mjs +153 -19
- package/dist/components/layout-and-navigation/TextImage.mjs.map +1 -1
- package/dist/components/layout-and-navigation/Tile.d.mts +10 -15
- package/dist/components/layout-and-navigation/Tile.d.ts +10 -15
- package/dist/components/layout-and-navigation/Tile.js +30 -8
- package/dist/components/layout-and-navigation/Tile.js.map +1 -1
- package/dist/components/layout-and-navigation/Tile.mjs +30 -8
- package/dist/components/layout-and-navigation/Tile.mjs.map +1 -1
- package/dist/components/loading-states/ErrorComponent.js +48 -8
- package/dist/components/loading-states/ErrorComponent.js.map +1 -1
- package/dist/components/loading-states/ErrorComponent.mjs +48 -8
- package/dist/components/loading-states/ErrorComponent.mjs.map +1 -1
- package/dist/components/loading-states/LoadingAndErrorComponent.d.mts +1 -0
- package/dist/components/loading-states/LoadingAndErrorComponent.d.ts +1 -0
- package/dist/components/loading-states/LoadingAndErrorComponent.js +147 -15
- package/dist/components/loading-states/LoadingAndErrorComponent.js.map +1 -1
- package/dist/components/loading-states/LoadingAndErrorComponent.mjs +147 -15
- package/dist/components/loading-states/LoadingAndErrorComponent.mjs.map +1 -1
- package/dist/components/loading-states/LoadingAnimation.d.mts +2 -3
- package/dist/components/loading-states/LoadingAnimation.d.ts +2 -3
- package/dist/components/loading-states/LoadingAnimation.js +145 -13
- package/dist/components/loading-states/LoadingAnimation.js.map +1 -1
- package/dist/components/loading-states/LoadingAnimation.mjs +145 -13
- package/dist/components/loading-states/LoadingAnimation.mjs.map +1 -1
- package/dist/components/loading-states/ProgressIndicator.js +2 -2
- package/dist/components/loading-states/ProgressIndicator.js.map +1 -1
- package/dist/components/loading-states/ProgressIndicator.mjs +2 -2
- package/dist/components/loading-states/ProgressIndicator.mjs.map +1 -1
- package/dist/components/modals/ConfirmModal.d.mts +2 -5
- package/dist/components/modals/ConfirmModal.d.ts +2 -5
- package/dist/components/modals/ConfirmModal.js +158 -28
- package/dist/components/modals/ConfirmModal.js.map +1 -1
- package/dist/components/modals/ConfirmModal.mjs +158 -28
- package/dist/components/modals/ConfirmModal.mjs.map +1 -1
- package/dist/components/modals/DiscardChangesModal.d.mts +2 -7
- package/dist/components/modals/DiscardChangesModal.d.ts +2 -7
- package/dist/components/modals/DiscardChangesModal.js +162 -48
- package/dist/components/modals/DiscardChangesModal.js.map +1 -1
- package/dist/components/modals/DiscardChangesModal.mjs +162 -48
- package/dist/components/modals/DiscardChangesModal.mjs.map +1 -1
- package/dist/components/modals/InputModal.d.mts +1 -0
- package/dist/components/modals/InputModal.d.ts +1 -0
- package/dist/components/modals/InputModal.js +158 -28
- package/dist/components/modals/InputModal.js.map +1 -1
- package/dist/components/modals/InputModal.mjs +158 -28
- package/dist/components/modals/InputModal.mjs.map +1 -1
- package/dist/components/modals/LanguageModal.d.mts +3 -2
- package/dist/components/modals/LanguageModal.d.ts +3 -2
- package/dist/components/modals/LanguageModal.js +538 -166
- package/dist/components/modals/LanguageModal.js.map +1 -1
- package/dist/components/modals/LanguageModal.mjs +537 -165
- package/dist/components/modals/LanguageModal.mjs.map +1 -1
- package/dist/components/modals/ThemeModal.d.mts +5 -5
- package/dist/components/modals/ThemeModal.d.ts +5 -5
- package/dist/components/modals/ThemeModal.js +547 -176
- package/dist/components/modals/ThemeModal.js.map +1 -1
- package/dist/components/modals/ThemeModal.mjs +544 -173
- package/dist/components/modals/ThemeModal.mjs.map +1 -1
- package/dist/components/properties/CheckboxProperty.d.mts +3 -5
- package/dist/components/properties/CheckboxProperty.d.ts +3 -5
- package/dist/components/properties/CheckboxProperty.js +148 -26
- package/dist/components/properties/CheckboxProperty.js.map +1 -1
- package/dist/components/properties/CheckboxProperty.mjs +148 -26
- package/dist/components/properties/CheckboxProperty.mjs.map +1 -1
- package/dist/components/properties/DateProperty.d.mts +1 -0
- package/dist/components/properties/DateProperty.d.ts +1 -0
- package/dist/components/properties/DateProperty.js +146 -14
- package/dist/components/properties/DateProperty.js.map +1 -1
- package/dist/components/properties/DateProperty.mjs +146 -14
- package/dist/components/properties/DateProperty.mjs.map +1 -1
- package/dist/components/properties/MultiSelectProperty.d.mts +5 -5
- package/dist/components/properties/MultiSelectProperty.d.ts +5 -5
- package/dist/components/properties/MultiSelectProperty.js +626 -456
- package/dist/components/properties/MultiSelectProperty.js.map +1 -1
- package/dist/components/properties/MultiSelectProperty.mjs +622 -452
- package/dist/components/properties/MultiSelectProperty.mjs.map +1 -1
- package/dist/components/properties/NumberProperty.d.mts +1 -0
- package/dist/components/properties/NumberProperty.d.ts +1 -0
- package/dist/components/properties/NumberProperty.js +148 -16
- package/dist/components/properties/NumberProperty.js.map +1 -1
- package/dist/components/properties/NumberProperty.mjs +148 -16
- package/dist/components/properties/NumberProperty.mjs.map +1 -1
- package/dist/components/properties/PropertyBase.d.mts +2 -3
- package/dist/components/properties/PropertyBase.d.ts +2 -3
- package/dist/components/properties/PropertyBase.js +146 -14
- package/dist/components/properties/PropertyBase.js.map +1 -1
- package/dist/components/properties/PropertyBase.mjs +146 -14
- package/dist/components/properties/PropertyBase.mjs.map +1 -1
- package/dist/components/properties/SelectProperty.d.mts +5 -6
- package/dist/components/properties/SelectProperty.d.ts +5 -6
- package/dist/components/properties/SelectProperty.js +542 -279
- package/dist/components/properties/SelectProperty.js.map +1 -1
- package/dist/components/properties/SelectProperty.mjs +542 -279
- package/dist/components/properties/SelectProperty.mjs.map +1 -1
- package/dist/components/properties/TextProperty.d.mts +2 -1
- package/dist/components/properties/TextProperty.d.ts +2 -1
- package/dist/components/properties/TextProperty.js +150 -18
- package/dist/components/properties/TextProperty.js.map +1 -1
- package/dist/components/properties/TextProperty.mjs +150 -18
- package/dist/components/properties/TextProperty.mjs.map +1 -1
- package/dist/components/user-action/DateAndTimePicker.d.mts +4 -20
- package/dist/components/user-action/DateAndTimePicker.d.ts +4 -20
- package/dist/components/user-action/DateAndTimePicker.js +233 -71
- package/dist/components/user-action/DateAndTimePicker.js.map +1 -1
- package/dist/components/user-action/DateAndTimePicker.mjs +233 -71
- package/dist/components/user-action/DateAndTimePicker.mjs.map +1 -1
- package/dist/components/user-action/Menu.d.mts +14 -8
- package/dist/components/user-action/Menu.d.ts +14 -8
- package/dist/components/user-action/Menu.js +32 -11
- package/dist/components/user-action/Menu.js.map +1 -1
- package/dist/components/user-action/Menu.mjs +32 -11
- package/dist/components/user-action/Menu.mjs.map +1 -1
- package/dist/components/user-action/MultiSelect.d.mts +13 -23
- package/dist/components/user-action/MultiSelect.d.ts +13 -23
- package/dist/components/user-action/MultiSelect.js +632 -325
- package/dist/components/user-action/MultiSelect.js.map +1 -1
- package/dist/components/user-action/MultiSelect.mjs +629 -323
- package/dist/components/user-action/MultiSelect.mjs.map +1 -1
- package/dist/components/user-action/Select.d.mts +5 -18
- package/dist/components/user-action/Select.d.ts +5 -18
- package/dist/components/user-action/Select.js +447 -113
- package/dist/components/user-action/Select.js.map +1 -1
- package/dist/components/user-action/Select.mjs +442 -107
- package/dist/components/user-action/Select.mjs.map +1 -1
- package/dist/components/user-action/Tooltip.js +2 -2
- package/dist/components/user-action/Tooltip.js.map +1 -1
- package/dist/components/user-action/Tooltip.mjs +2 -2
- package/dist/components/user-action/Tooltip.mjs.map +1 -1
- package/dist/css/globals.css +224 -207
- package/dist/css/uncompiled/globals.css +138 -74
- package/dist/hooks/useSearch.d.mts +17 -0
- package/dist/hooks/useSearch.d.ts +17 -0
- package/dist/hooks/useSearch.js +66 -0
- package/dist/hooks/useSearch.js.map +1 -0
- package/dist/hooks/useSearch.mjs +42 -0
- package/dist/hooks/useSearch.mjs.map +1 -0
- package/dist/index.d.mts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.js +882 -758
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +836 -717
- package/dist/index.mjs.map +1 -1
- package/dist/localization/defaults/form.d.mts +54 -0
- package/dist/localization/defaults/form.d.ts +54 -0
- package/dist/localization/defaults/form.js +127 -0
- package/dist/localization/defaults/form.js.map +1 -0
- package/dist/localization/defaults/form.mjs +103 -0
- package/dist/localization/defaults/form.mjs.map +1 -0
- package/dist/localization/defaults/time.d.mts +39 -0
- package/dist/localization/defaults/time.d.ts +39 -0
- package/dist/localization/defaults/time.js +101 -0
- package/dist/localization/defaults/time.js.map +1 -0
- package/dist/localization/defaults/time.mjs +76 -0
- package/dist/localization/defaults/time.mjs.map +1 -0
- package/dist/localization/useTranslation.d.mts +30 -6
- package/dist/localization/useTranslation.d.ts +30 -6
- package/dist/localization/useTranslation.js +46 -6
- package/dist/localization/useTranslation.js.map +1 -1
- package/dist/localization/useTranslation.mjs +46 -6
- package/dist/localization/useTranslation.mjs.map +1 -1
- package/dist/theming/useTheme.d.mts +4 -2
- package/dist/theming/useTheme.d.ts +4 -2
- package/dist/theming/useTheme.js +10 -2
- package/dist/theming/useTheme.js.map +1 -1
- package/dist/theming/useTheme.mjs +10 -2
- package/dist/theming/useTheme.mjs.map +1 -1
- package/dist/util/PropsWithFunctionChildren.d.mts +14 -0
- package/dist/util/PropsWithFunctionChildren.d.ts +14 -0
- package/dist/util/PropsWithFunctionChildren.js +38 -0
- package/dist/util/PropsWithFunctionChildren.js.map +1 -0
- package/dist/util/PropsWithFunctionChildren.mjs +14 -0
- package/dist/util/PropsWithFunctionChildren.mjs.map +1 -0
- package/dist/util/simpleSearch.d.mts +2 -2
- package/dist/util/simpleSearch.d.ts +2 -2
- package/dist/util/simpleSearch.js +4 -1
- package/dist/util/simpleSearch.js.map +1 -1
- package/dist/util/simpleSearch.mjs +4 -1
- package/dist/util/simpleSearch.mjs.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 { ExpandableUncontrolled } from '../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 {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 </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
|
+
{"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","../../../src/components/user-action/Button.tsx"],"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 '../layout-and-navigation/Expandable'\nimport { addDuration, monthsList, subtractDuration } from '../../util/date'\nimport { useLocale } from '../../localization/LanguageProvider'\nimport { SolidButton } from '../user-action/Button'\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 contentClassName=\"gap-y-1\"\n >\n {equalSizeGroups([...monthsList], 3).map((monthList, index) => (\n <div key={index} className=\"row gap-x-1\">\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 <SolidButton\n key={month}\n disabled={!isValid}\n color={selectedMonth && isValid ? 'primary' : 'neutral'}\n className=\"flex-1\"\n size=\"small\"\n onClick={() => {\n onChange(newDate)\n }}\n >\n {new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}\n </SolidButton>\n )\n })}\n </div>\n ))}\n </ExpandableUncontrolled>\n )\n })}\n </div>\n </Scrollbars>\n </div>\n )\n}\n\nexport const YearMonthPickerUncontrolled = ({\n displayedYearMonth,\n onChange = noop,\n ...props\n }: YearMonthPickerProps) => {\n const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth ?? new Date())\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 contentClassName?: 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 contentClassName,\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={clsx('col px-4 pb-2', contentClassName)}>\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}","import type { ButtonHTMLAttributes, PropsWithChildren, ReactNode } from 'react'\nimport clsx from 'clsx'\n\n\nexport const ButtonColorUtil = {\n solid: ['primary', 'secondary', 'tertiary', 'positive', 'warning', 'negative', 'neutral'] as const,\n text: ['primary', 'negative', 'neutral'] as const,\n outline: ['primary'] as const,\n}\n\n\n/**\n * The allowed colors for the SolidButton and IconButton\n */\nexport type SolidButtonColor = typeof ButtonColorUtil.solid[number]\n/**\n * The allowed colors for the OutlineButton\n */\nexport type OutlineButtonColor = typeof ButtonColorUtil.outline[number]\n/**\n * The allowed colors for the TextButton\n */\nexport type TextButtonColor = typeof ButtonColorUtil.text[number]\n\n/**\n * The different sizes for a button\n */\ntype ButtonSizes = 'small' | 'medium' | 'large'\n\n/**\n * The shard properties between all button types\n */\nexport type ButtonProps = PropsWithChildren<{\n /**\n * @default 'medium'\n */\n size?: ButtonSizes,\n}> & ButtonHTMLAttributes<Element>\n\nconst paddingMapping: Record<ButtonSizes, string> = {\n small: 'btn-sm',\n medium: 'btn-md',\n large: 'btn-lg'\n}\n\nconst iconPaddingMapping: Record<ButtonSizes, string> = {\n small: 'icon-btn-sm',\n medium: 'icon-btn-md',\n large: 'icon-btn-lg'\n}\n\nexport const ButtonUtil = {\n paddingMapping,\n iconPaddingMapping\n}\n\ntype ButtonWithIconsProps = ButtonProps & {\n startIcon?: ReactNode,\n endIcon?: ReactNode,\n}\n\nexport type SolidButtonProps = ButtonWithIconsProps & {\n color?: SolidButtonColor,\n}\n\nexport type OutlineButtonProps = ButtonWithIconsProps & {\n color?: OutlineButtonColor,\n}\n\nexport type TextButtonProps = ButtonWithIconsProps & {\n color?: TextButtonColor,\n}\n\nexport type IconButtonProps = ButtonProps & {\n color?: SolidButtonColor,\n}\n\n/**\n * A button with a solid background and different sizes\n */\nconst SolidButton = ({\n children,\n disabled = false,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: SolidButtonProps) => {\n const colorClasses = {\n primary: 'bg-button-solid-primary-background text-button-solid-primary-text',\n secondary: 'bg-button-solid-secondary-background text-button-solid-secondary-text',\n tertiary: 'bg-button-solid-tertiary-background text-button-solid-tertiary-text',\n positive: 'bg-button-solid-positive-background text-button-solid-positive-text',\n warning: 'bg-button-solid-warning-background text-button-solid-warning-text',\n negative: 'bg-button-solid-negative-background text-button-solid-negative-text',\n neutral: 'bg-button-solid-neutral-background text-button-solid-neutral-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'text-button-solid-primary-icon',\n secondary: 'text-button-solid-secondary-icon',\n tertiary: 'text-button-solid-tertiary-icon',\n positive: 'text-button-solid-positive-icon',\n warning: 'text-button-solid-warning-icon',\n negative: 'text-button-solid-negative-icon',\n neutral: 'text-button-solid-neutral-icon',\n }[color]\n\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text bg-disabled-background cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:brightness-90')]: !disabled\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n/**\n * A button with an outline border and different sizes\n */\nconst OutlineButton = ({\n children,\n disabled = false,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: OutlineButtonProps) => {\n const colorClasses = {\n primary: 'bg-transparent border-2 border-button-outline-primary-text text-button-outline-primary-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'text-button-outline-primary-icon',\n }[color]\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text border-disabled-outline cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:brightness-80')]: !disabled,\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n/**\n * A text that is a button that can have different sizes\n */\nconst TextButton = ({\n children,\n disabled = false,\n color = 'neutral',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: TextButtonProps) => {\n const colorClasses = {\n primary: 'bg-transparent text-button-text-primary-text',\n negative: 'bg-transparent text-button-text-negative-text',\n neutral: 'bg-transparent text-button-text-neutral-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'text-button-text-primary-icon',\n negative: 'text-button-text-negative-icon',\n neutral: 'text-button-text-neutral-icon',\n }[color]\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:bg-button-text-hover-background rounded-full')]: !disabled,\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n\n/**\n * A button for icons with a solid background and different sizes\n */\nconst IconButton = ({\n children,\n disabled = false,\n color = 'primary',\n size = 'medium',\n onClick,\n className,\n ...restProps\n }: IconButtonProps) => {\n const colorClasses = {\n primary: 'bg-button-solid-primary-background text-button-solid-primary-text',\n secondary: 'bg-button-solid-secondary-background text-button-solid-secondary-text',\n tertiary: 'bg-button-solid-tertiary-background text-button-solid-tertiary-text',\n positive: 'bg-button-solid-positive-background text-button-solid-positive-text',\n warning: 'bg-button-solid-warning-background text-button-solid-warning-text',\n negative: 'bg-button-solid-negative-background text-button-solid-negative-text',\n neutral: 'bg-button-solid-neutral-background text-button-solid-neutral-text',\n }[color]\n\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text bg-disabled-background cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:brightness-90')]: !disabled\n },\n ButtonUtil.iconPaddingMapping[size],\n className\n )}\n {...restProps}\n >\n {children}\n </button>\n )\n}\n\nexport { SolidButton, OutlineButton, TextButton, IconButton }\n"],"mappings":"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","names":["import_react","import_clsx","Expandable","clsx","ExpandableUncontrolled","import_react","import_react","import_jsx_runtime","import_clsx","import_jsx_runtime","clsx","import_jsx_runtime","clsx"]}
|
|
@@ -28,7 +28,7 @@ var range = (start, end, allowEmptyRange = false) => {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// src/components/date/YearMonthPicker.tsx
|
|
31
|
-
import
|
|
31
|
+
import clsx3 from "clsx";
|
|
32
32
|
|
|
33
33
|
// src/components/layout-and-navigation/Expandable.tsx
|
|
34
34
|
import { forwardRef, useEffect, useState } from "react";
|
|
@@ -44,8 +44,9 @@ var Expandable = forwardRef(function Expandable2({
|
|
|
44
44
|
onChange = noop,
|
|
45
45
|
clickOnlyOnHeader = true,
|
|
46
46
|
disabled = false,
|
|
47
|
-
className
|
|
48
|
-
headerClassName
|
|
47
|
+
className,
|
|
48
|
+
headerClassName,
|
|
49
|
+
contentClassName
|
|
49
50
|
}, ref) {
|
|
50
51
|
icon ??= DefaultIcon;
|
|
51
52
|
return /* @__PURE__ */ jsxs(
|
|
@@ -74,7 +75,7 @@ var Expandable = forwardRef(function Expandable2({
|
|
|
74
75
|
]
|
|
75
76
|
}
|
|
76
77
|
),
|
|
77
|
-
isExpanded && /* @__PURE__ */ jsx("div", { className: "col px-4 pb-2", children })
|
|
78
|
+
isExpanded && /* @__PURE__ */ jsx("div", { className: clsx("col px-4 pb-2", contentClassName), children })
|
|
78
79
|
]
|
|
79
80
|
}
|
|
80
81
|
);
|
|
@@ -195,8 +196,95 @@ var useLocale = (overWriteLanguage) => {
|
|
|
195
196
|
return mapping[overWriteLanguage ?? language];
|
|
196
197
|
};
|
|
197
198
|
|
|
199
|
+
// src/components/user-action/Button.tsx
|
|
200
|
+
import clsx2 from "clsx";
|
|
201
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
202
|
+
var paddingMapping = {
|
|
203
|
+
small: "btn-sm",
|
|
204
|
+
medium: "btn-md",
|
|
205
|
+
large: "btn-lg"
|
|
206
|
+
};
|
|
207
|
+
var iconPaddingMapping = {
|
|
208
|
+
small: "icon-btn-sm",
|
|
209
|
+
medium: "icon-btn-md",
|
|
210
|
+
large: "icon-btn-lg"
|
|
211
|
+
};
|
|
212
|
+
var ButtonUtil = {
|
|
213
|
+
paddingMapping,
|
|
214
|
+
iconPaddingMapping
|
|
215
|
+
};
|
|
216
|
+
var SolidButton = ({
|
|
217
|
+
children,
|
|
218
|
+
disabled = false,
|
|
219
|
+
color = "primary",
|
|
220
|
+
size = "medium",
|
|
221
|
+
startIcon,
|
|
222
|
+
endIcon,
|
|
223
|
+
onClick,
|
|
224
|
+
className,
|
|
225
|
+
...restProps
|
|
226
|
+
}) => {
|
|
227
|
+
const colorClasses = {
|
|
228
|
+
primary: "bg-button-solid-primary-background text-button-solid-primary-text",
|
|
229
|
+
secondary: "bg-button-solid-secondary-background text-button-solid-secondary-text",
|
|
230
|
+
tertiary: "bg-button-solid-tertiary-background text-button-solid-tertiary-text",
|
|
231
|
+
positive: "bg-button-solid-positive-background text-button-solid-positive-text",
|
|
232
|
+
warning: "bg-button-solid-warning-background text-button-solid-warning-text",
|
|
233
|
+
negative: "bg-button-solid-negative-background text-button-solid-negative-text",
|
|
234
|
+
neutral: "bg-button-solid-neutral-background text-button-solid-neutral-text"
|
|
235
|
+
}[color];
|
|
236
|
+
const iconColorClasses = {
|
|
237
|
+
primary: "text-button-solid-primary-icon",
|
|
238
|
+
secondary: "text-button-solid-secondary-icon",
|
|
239
|
+
tertiary: "text-button-solid-tertiary-icon",
|
|
240
|
+
positive: "text-button-solid-positive-icon",
|
|
241
|
+
warning: "text-button-solid-warning-icon",
|
|
242
|
+
negative: "text-button-solid-negative-icon",
|
|
243
|
+
neutral: "text-button-solid-neutral-icon"
|
|
244
|
+
}[color];
|
|
245
|
+
return /* @__PURE__ */ jsxs2(
|
|
246
|
+
"button",
|
|
247
|
+
{
|
|
248
|
+
onClick: disabled ? void 0 : onClick,
|
|
249
|
+
disabled: disabled || onClick === void 0,
|
|
250
|
+
className: clsx2(
|
|
251
|
+
{
|
|
252
|
+
"text-disabled-text bg-disabled-background cursor-not-allowed": disabled,
|
|
253
|
+
[clsx2(colorClasses, "hover:brightness-90")]: !disabled
|
|
254
|
+
},
|
|
255
|
+
ButtonUtil.paddingMapping[size],
|
|
256
|
+
className
|
|
257
|
+
),
|
|
258
|
+
...restProps,
|
|
259
|
+
children: [
|
|
260
|
+
startIcon && /* @__PURE__ */ jsx3(
|
|
261
|
+
"span",
|
|
262
|
+
{
|
|
263
|
+
className: clsx2({
|
|
264
|
+
[iconColorClasses]: !disabled,
|
|
265
|
+
[`text-disabled-icon`]: disabled
|
|
266
|
+
}),
|
|
267
|
+
children: startIcon
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
children,
|
|
271
|
+
endIcon && /* @__PURE__ */ jsx3(
|
|
272
|
+
"span",
|
|
273
|
+
{
|
|
274
|
+
className: clsx2({
|
|
275
|
+
[iconColorClasses]: !disabled,
|
|
276
|
+
[`text-disabled-icon`]: disabled
|
|
277
|
+
}),
|
|
278
|
+
children: endIcon
|
|
279
|
+
}
|
|
280
|
+
)
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
|
|
198
286
|
// src/components/date/YearMonthPicker.tsx
|
|
199
|
-
import { jsx as
|
|
287
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
200
288
|
var YearMonthPicker = ({
|
|
201
289
|
displayedYearMonth = /* @__PURE__ */ new Date(),
|
|
202
290
|
start = subtractDuration(/* @__PURE__ */ new Date(), { years: 50 }),
|
|
@@ -224,15 +312,16 @@ var YearMonthPicker = ({
|
|
|
224
312
|
return null;
|
|
225
313
|
}
|
|
226
314
|
const years = range(start.getFullYear(), end.getFullYear());
|
|
227
|
-
return /* @__PURE__ */
|
|
315
|
+
return /* @__PURE__ */ jsx4("div", { className: clsx3("col select-none", className), children: /* @__PURE__ */ jsx4(Scrollbars, { autoHeight: true, autoHeightMax: maxHeight, style: { height: "100%" }, children: /* @__PURE__ */ jsx4("div", { className: "col gap-y-1 mr-3", children: years.map((year) => {
|
|
228
316
|
const selectedYear = displayedYearMonth.getFullYear() === year;
|
|
229
|
-
return /* @__PURE__ */
|
|
317
|
+
return /* @__PURE__ */ jsx4(
|
|
230
318
|
ExpandableUncontrolled,
|
|
231
319
|
{
|
|
232
320
|
ref: (displayedYearMonth.getFullYear() ?? (/* @__PURE__ */ new Date()).getFullYear()) === year ? ref : void 0,
|
|
233
|
-
label: /* @__PURE__ */
|
|
321
|
+
label: /* @__PURE__ */ jsx4("span", { className: clsx3({ "text-primary font-bold": selectedYear }), children: year }),
|
|
234
322
|
isExpanded: showValueOpen && selectedYear,
|
|
235
|
-
|
|
323
|
+
contentClassName: "gap-y-1",
|
|
324
|
+
children: equalSizeGroups([...monthsList], 3).map((monthList, index) => /* @__PURE__ */ jsx4("div", { className: "row gap-x-1", children: monthList.map((month) => {
|
|
236
325
|
const monthIndex = monthsList.indexOf(month);
|
|
237
326
|
const newDate = new Date(year, monthIndex);
|
|
238
327
|
const selectedMonth = selectedYear && monthIndex === displayedYearMonth.getMonth();
|
|
@@ -241,18 +330,13 @@ var YearMonthPicker = ({
|
|
|
241
330
|
const isAfterStart = start === void 0 || start <= addDuration(subtractDuration(lastOfMonth, { days: 1 }), { months: 1 });
|
|
242
331
|
const isBeforeEnd = end === void 0 || firstOfMonth <= end;
|
|
243
332
|
const isValid = isAfterStart && isBeforeEnd;
|
|
244
|
-
return /* @__PURE__ */
|
|
245
|
-
|
|
333
|
+
return /* @__PURE__ */ jsx4(
|
|
334
|
+
SolidButton,
|
|
246
335
|
{
|
|
247
336
|
disabled: !isValid,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
"bg-gray-50 text-black": !selectedMonth && isValid,
|
|
252
|
-
"bg-primary text-on-primary": selectedMonth && isValid,
|
|
253
|
-
"bg-disabled-background text-disabled-text": !isValid
|
|
254
|
-
}
|
|
255
|
-
),
|
|
337
|
+
color: selectedMonth && isValid ? "primary" : "neutral",
|
|
338
|
+
className: "flex-1",
|
|
339
|
+
size: "small",
|
|
256
340
|
onClick: () => {
|
|
257
341
|
onChange(newDate);
|
|
258
342
|
},
|
|
@@ -267,13 +351,13 @@ var YearMonthPicker = ({
|
|
|
267
351
|
}) }) }) });
|
|
268
352
|
};
|
|
269
353
|
var YearMonthPickerUncontrolled = ({
|
|
270
|
-
displayedYearMonth
|
|
354
|
+
displayedYearMonth,
|
|
271
355
|
onChange = noop,
|
|
272
356
|
...props
|
|
273
357
|
}) => {
|
|
274
|
-
const [yearMonth, setYearMonth] = useState4(displayedYearMonth);
|
|
358
|
+
const [yearMonth, setYearMonth] = useState4(displayedYearMonth ?? /* @__PURE__ */ new Date());
|
|
275
359
|
useEffect4(() => setYearMonth(displayedYearMonth), [displayedYearMonth]);
|
|
276
|
-
return /* @__PURE__ */
|
|
360
|
+
return /* @__PURE__ */ jsx4(
|
|
277
361
|
YearMonthPicker,
|
|
278
362
|
{
|
|
279
363
|
displayedYearMonth: yearMonth,
|
|
@@ -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 { ExpandableUncontrolled } from '../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 {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 </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"]}
|
|
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","../../../src/components/user-action/Button.tsx"],"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 '../layout-and-navigation/Expandable'\nimport { addDuration, monthsList, subtractDuration } from '../../util/date'\nimport { useLocale } from '../../localization/LanguageProvider'\nimport { SolidButton } from '../user-action/Button'\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 contentClassName=\"gap-y-1\"\n >\n {equalSizeGroups([...monthsList], 3).map((monthList, index) => (\n <div key={index} className=\"row gap-x-1\">\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 <SolidButton\n key={month}\n disabled={!isValid}\n color={selectedMonth && isValid ? 'primary' : 'neutral'}\n className=\"flex-1\"\n size=\"small\"\n onClick={() => {\n onChange(newDate)\n }}\n >\n {new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}\n </SolidButton>\n )\n })}\n </div>\n ))}\n </ExpandableUncontrolled>\n )\n })}\n </div>\n </Scrollbars>\n </div>\n )\n}\n\nexport const YearMonthPickerUncontrolled = ({\n displayedYearMonth,\n onChange = noop,\n ...props\n }: YearMonthPickerProps) => {\n const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth ?? new Date())\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 contentClassName?: 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 contentClassName,\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={clsx('col px-4 pb-2', contentClassName)}>\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}","import type { ButtonHTMLAttributes, PropsWithChildren, ReactNode } from 'react'\nimport clsx from 'clsx'\n\n\nexport const ButtonColorUtil = {\n solid: ['primary', 'secondary', 'tertiary', 'positive', 'warning', 'negative', 'neutral'] as const,\n text: ['primary', 'negative', 'neutral'] as const,\n outline: ['primary'] as const,\n}\n\n\n/**\n * The allowed colors for the SolidButton and IconButton\n */\nexport type SolidButtonColor = typeof ButtonColorUtil.solid[number]\n/**\n * The allowed colors for the OutlineButton\n */\nexport type OutlineButtonColor = typeof ButtonColorUtil.outline[number]\n/**\n * The allowed colors for the TextButton\n */\nexport type TextButtonColor = typeof ButtonColorUtil.text[number]\n\n/**\n * The different sizes for a button\n */\ntype ButtonSizes = 'small' | 'medium' | 'large'\n\n/**\n * The shard properties between all button types\n */\nexport type ButtonProps = PropsWithChildren<{\n /**\n * @default 'medium'\n */\n size?: ButtonSizes,\n}> & ButtonHTMLAttributes<Element>\n\nconst paddingMapping: Record<ButtonSizes, string> = {\n small: 'btn-sm',\n medium: 'btn-md',\n large: 'btn-lg'\n}\n\nconst iconPaddingMapping: Record<ButtonSizes, string> = {\n small: 'icon-btn-sm',\n medium: 'icon-btn-md',\n large: 'icon-btn-lg'\n}\n\nexport const ButtonUtil = {\n paddingMapping,\n iconPaddingMapping\n}\n\ntype ButtonWithIconsProps = ButtonProps & {\n startIcon?: ReactNode,\n endIcon?: ReactNode,\n}\n\nexport type SolidButtonProps = ButtonWithIconsProps & {\n color?: SolidButtonColor,\n}\n\nexport type OutlineButtonProps = ButtonWithIconsProps & {\n color?: OutlineButtonColor,\n}\n\nexport type TextButtonProps = ButtonWithIconsProps & {\n color?: TextButtonColor,\n}\n\nexport type IconButtonProps = ButtonProps & {\n color?: SolidButtonColor,\n}\n\n/**\n * A button with a solid background and different sizes\n */\nconst SolidButton = ({\n children,\n disabled = false,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: SolidButtonProps) => {\n const colorClasses = {\n primary: 'bg-button-solid-primary-background text-button-solid-primary-text',\n secondary: 'bg-button-solid-secondary-background text-button-solid-secondary-text',\n tertiary: 'bg-button-solid-tertiary-background text-button-solid-tertiary-text',\n positive: 'bg-button-solid-positive-background text-button-solid-positive-text',\n warning: 'bg-button-solid-warning-background text-button-solid-warning-text',\n negative: 'bg-button-solid-negative-background text-button-solid-negative-text',\n neutral: 'bg-button-solid-neutral-background text-button-solid-neutral-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'text-button-solid-primary-icon',\n secondary: 'text-button-solid-secondary-icon',\n tertiary: 'text-button-solid-tertiary-icon',\n positive: 'text-button-solid-positive-icon',\n warning: 'text-button-solid-warning-icon',\n negative: 'text-button-solid-negative-icon',\n neutral: 'text-button-solid-neutral-icon',\n }[color]\n\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text bg-disabled-background cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:brightness-90')]: !disabled\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n/**\n * A button with an outline border and different sizes\n */\nconst OutlineButton = ({\n children,\n disabled = false,\n color = 'primary',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: OutlineButtonProps) => {\n const colorClasses = {\n primary: 'bg-transparent border-2 border-button-outline-primary-text text-button-outline-primary-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'text-button-outline-primary-icon',\n }[color]\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text border-disabled-outline cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:brightness-80')]: !disabled,\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n/**\n * A text that is a button that can have different sizes\n */\nconst TextButton = ({\n children,\n disabled = false,\n color = 'neutral',\n size = 'medium',\n startIcon,\n endIcon,\n onClick,\n className,\n ...restProps\n }: TextButtonProps) => {\n const colorClasses = {\n primary: 'bg-transparent text-button-text-primary-text',\n negative: 'bg-transparent text-button-text-negative-text',\n neutral: 'bg-transparent text-button-text-neutral-text',\n }[color]\n\n const iconColorClasses = {\n primary: 'text-button-text-primary-icon',\n negative: 'text-button-text-negative-icon',\n neutral: 'text-button-text-neutral-icon',\n }[color]\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:bg-button-text-hover-background rounded-full')]: !disabled,\n },\n ButtonUtil.paddingMapping[size],\n className\n )}\n {...restProps}\n >\n {startIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {startIcon}\n </span>\n )}\n {children}\n {endIcon && (\n <span\n className={clsx({\n [iconColorClasses]: !disabled,\n [`text-disabled-icon`]: disabled\n })}\n >\n {endIcon}\n </span>\n )}\n </button>\n )\n}\n\n\n/**\n * A button for icons with a solid background and different sizes\n */\nconst IconButton = ({\n children,\n disabled = false,\n color = 'primary',\n size = 'medium',\n onClick,\n className,\n ...restProps\n }: IconButtonProps) => {\n const colorClasses = {\n primary: 'bg-button-solid-primary-background text-button-solid-primary-text',\n secondary: 'bg-button-solid-secondary-background text-button-solid-secondary-text',\n tertiary: 'bg-button-solid-tertiary-background text-button-solid-tertiary-text',\n positive: 'bg-button-solid-positive-background text-button-solid-positive-text',\n warning: 'bg-button-solid-warning-background text-button-solid-warning-text',\n negative: 'bg-button-solid-negative-background text-button-solid-negative-text',\n neutral: 'bg-button-solid-neutral-background text-button-solid-neutral-text',\n }[color]\n\n return (\n <button\n onClick={disabled ? undefined : onClick}\n disabled={disabled || onClick === undefined}\n className={clsx(\n {\n 'text-disabled-text bg-disabled-background cursor-not-allowed': disabled,\n [clsx(colorClasses, 'hover:brightness-90')]: !disabled\n },\n ButtonUtil.iconPaddingMapping[size],\n className\n )}\n {...restProps}\n >\n {children}\n </button>\n )\n}\n\nexport { SolidButton, OutlineButton, TextButton, IconButton }\n"],"mappings":"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","names":["useEffect","useState","clsx","Expandable","ExpandableUncontrolled","useEffect","useState","useEffect","useState","jsx","clsx","jsx","jsxs","jsxs","clsx","jsx","jsx","useEffect","clsx","useState"]}
|
|
@@ -3,13 +3,10 @@ import { PropsWithChildren } from 'react';
|
|
|
3
3
|
import { SolidButtonColor } from '../user-action/Button.mjs';
|
|
4
4
|
import { PropsForTranslation } from '../../localization/useTranslation.mjs';
|
|
5
5
|
import { DialogProps } from '../layout-and-navigation/Overlay.mjs';
|
|
6
|
+
import { FormTranslationType } from '../../localization/defaults/form.mjs';
|
|
6
7
|
import '../../localization/util.mjs';
|
|
7
8
|
|
|
8
|
-
type ConfirmDialogTranslation =
|
|
9
|
-
confirm: string;
|
|
10
|
-
cancel: string;
|
|
11
|
-
decline: string;
|
|
12
|
-
};
|
|
9
|
+
type ConfirmDialogTranslation = FormTranslationType;
|
|
13
10
|
type ConfirmDialogType = 'positive' | 'negative' | 'neutral' | 'primary';
|
|
14
11
|
type ButtonOverwriteType = {
|
|
15
12
|
text?: string;
|
|
@@ -3,13 +3,10 @@ import { PropsWithChildren } from 'react';
|
|
|
3
3
|
import { SolidButtonColor } from '../user-action/Button.js';
|
|
4
4
|
import { PropsForTranslation } from '../../localization/useTranslation.js';
|
|
5
5
|
import { DialogProps } from '../layout-and-navigation/Overlay.js';
|
|
6
|
+
import { FormTranslationType } from '../../localization/defaults/form.js';
|
|
6
7
|
import '../../localization/util.js';
|
|
7
8
|
|
|
8
|
-
type ConfirmDialogTranslation =
|
|
9
|
-
confirm: string;
|
|
10
|
-
cancel: string;
|
|
11
|
-
decline: string;
|
|
12
|
-
};
|
|
9
|
+
type ConfirmDialogTranslation = FormTranslationType;
|
|
13
10
|
type ConfirmDialogType = 'positive' | 'negative' | 'neutral' | 'primary';
|
|
14
11
|
type ButtonOverwriteType = {
|
|
15
12
|
text?: string;
|