@helpwave/hightide 0.0.1
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/.storybook/main.ts +24 -0
- package/.storybook/preview.tsx +67 -0
- package/LICENSE +373 -0
- package/README.md +8 -0
- package/coloring/shading.ts +46 -0
- package/coloring/types.ts +13 -0
- package/components/Avatar.tsx +58 -0
- package/components/AvatarGroup.tsx +48 -0
- package/components/BreadCrumb.tsx +35 -0
- package/components/Button.tsx +236 -0
- package/components/ChipList.tsx +89 -0
- package/components/Circle.tsx +27 -0
- package/components/ErrorComponent.tsx +40 -0
- package/components/Expandable.tsx +61 -0
- package/components/HelpwaveBadge.tsx +35 -0
- package/components/HideableContentSection.tsx +43 -0
- package/components/InputGroup.tsx +72 -0
- package/components/LoadingAndErrorComponent.tsx +47 -0
- package/components/LoadingAnimation.tsx +40 -0
- package/components/LoadingButton.tsx +27 -0
- package/components/MarkdownInterpreter.tsx +278 -0
- package/components/Pagination.tsx +65 -0
- package/components/Profile.tsx +124 -0
- package/components/ProgressIndicator.tsx +58 -0
- package/components/Ring.tsx +286 -0
- package/components/SearchableList.tsx +69 -0
- package/components/SortButton.tsx +33 -0
- package/components/Span.tsx +0 -0
- package/components/StepperBar.tsx +124 -0
- package/components/Table.tsx +330 -0
- package/components/TechRadar.tsx +247 -0
- package/components/TextImage.tsx +86 -0
- package/components/TimeDisplay.tsx +121 -0
- package/components/Tooltip.tsx +92 -0
- package/components/VerticalDivider.tsx +51 -0
- package/components/date/DatePicker.tsx +164 -0
- package/components/date/DayPicker.tsx +95 -0
- package/components/date/TimePicker.tsx +167 -0
- package/components/date/YearMonthPicker.tsx +130 -0
- package/components/examples/InputGroupExample.tsx +58 -0
- package/components/examples/MultiSelectExample.tsx +57 -0
- package/components/examples/SearchableSelectExample.tsx +34 -0
- package/components/examples/SelectExample.tsx +28 -0
- package/components/examples/StackingModals.tsx +54 -0
- package/components/examples/TableExample.tsx +159 -0
- package/components/examples/TextareaExample.tsx +23 -0
- package/components/examples/TileExample.tsx +25 -0
- package/components/examples/Title.tsx +0 -0
- package/components/examples/date/DateTimePickerExample.tsx +53 -0
- package/components/examples/properties/CheckboxPropertyExample.tsx +29 -0
- package/components/examples/properties/DatePropertyExample.tsx +44 -0
- package/components/examples/properties/MultiSelectPropertyExample.tsx +39 -0
- package/components/examples/properties/NumberPropertyExample.tsx +28 -0
- package/components/examples/properties/SelectPropertyExample.tsx +39 -0
- package/components/examples/properties/TextPropertyExample.tsx +30 -0
- package/components/icons/Helpwave.tsx +51 -0
- package/components/icons/Tag.tsx +29 -0
- package/components/layout/Carousel.tsx +396 -0
- package/components/layout/DividerInserter.tsx +37 -0
- package/components/layout/FAQSection.tsx +57 -0
- package/components/layout/Tile.tsx +67 -0
- package/components/modals/ConfirmDialog.tsx +105 -0
- package/components/modals/DiscardChangesDialog.tsx +71 -0
- package/components/modals/InputModal.tsx +26 -0
- package/components/modals/LanguageModal.tsx +76 -0
- package/components/modals/Modal.tsx +149 -0
- package/components/modals/ModalRegister.tsx +45 -0
- package/components/properties/CheckboxProperty.tsx +62 -0
- package/components/properties/DateProperty.tsx +58 -0
- package/components/properties/MultiSelectProperty.tsx +82 -0
- package/components/properties/NumberProperty.tsx +86 -0
- package/components/properties/PropertyBase.tsx +84 -0
- package/components/properties/SelectProperty.tsx +67 -0
- package/components/properties/TextProperty.tsx +81 -0
- package/components/user-input/Checkbox.tsx +139 -0
- package/components/user-input/DateAndTimePicker.tsx +156 -0
- package/components/user-input/Input.tsx +192 -0
- package/components/user-input/Label.tsx +32 -0
- package/components/user-input/Menu.tsx +75 -0
- package/components/user-input/MultiSelect.tsx +158 -0
- package/components/user-input/ScrollPicker.tsx +240 -0
- package/components/user-input/SearchableSelect.tsx +36 -0
- package/components/user-input/Select.tsx +132 -0
- package/components/user-input/Textarea.tsx +86 -0
- package/components/user-input/ToggleableInput.tsx +115 -0
- package/eslint.config.js +3 -0
- package/globals.css +488 -0
- package/hooks/useHoverState.ts +88 -0
- package/hooks/useLanguage.tsx +78 -0
- package/hooks/useLocalStorage.tsx +33 -0
- package/hooks/useOutsideClick.ts +25 -0
- package/hooks/useSaveDelay.ts +46 -0
- package/hooks/useTheme.tsx +57 -0
- package/hooks/useTranslation.ts +43 -0
- package/index.ts +0 -0
- package/package.json +71 -0
- package/postcss.config.mjs +7 -0
- package/stories/README.md +23 -0
- package/stories/coloring/shading.stories.tsx +54 -0
- package/stories/geometry/Circle.stories.tsx +16 -0
- package/stories/geometry/rings/AnimatedRing.stories.tsx +18 -0
- package/stories/geometry/rings/RadialRings.stories.tsx +19 -0
- package/stories/geometry/rings/Ring.stories.tsx +17 -0
- package/stories/geometry/rings/RingWave.stories.tsx +20 -0
- package/stories/layout/FAQSection.stories.tsx +49 -0
- package/stories/layout/InputGroup.stories.tsx +19 -0
- package/stories/layout/Table.stories.tsx +19 -0
- package/stories/layout/TextImage.stories.tsx +24 -0
- package/stories/layout/chip/Chip.stories.tsx +19 -0
- package/stories/layout/chip/ChipList.stories.tsx +27 -0
- package/stories/layout/tile/Tile.stories.ts +20 -0
- package/stories/layout/tile/TileWithImage.stories.tsx +27 -0
- package/stories/other/BreadCrumbs.stories.tsx +21 -0
- package/stories/other/HelpwaveBadge.stories.tsx +18 -0
- package/stories/other/HelpwaveSpinner.stories.tsx +19 -0
- package/stories/other/MarkdownInterpreter.stories.tsx +18 -0
- package/stories/other/Profile.stories.tsx +52 -0
- package/stories/other/SearchableList.stories.tsx +21 -0
- package/stories/other/StackingModals.stories.tsx +16 -0
- package/stories/other/TechRadar.stories.tsx +14 -0
- package/stories/other/Translation.stories.tsx +56 -0
- package/stories/other/VerticalDivider.stories.tsx +20 -0
- package/stories/other/avatar/Avatar.stories.tsx +19 -0
- package/stories/other/avatar/AvatarGroup.stories.tsx +26 -0
- package/stories/other/tooltip/Tooltip.stories.tsx +30 -0
- package/stories/other/tooltip/TooltipStack.stories.tsx +39 -0
- package/stories/user-action/button/LoadingButton.stories.tsx +21 -0
- package/stories/user-action/button/OutlineButton.stories.tsx +22 -0
- package/stories/user-action/button/SolidButton.stories.tsx +22 -0
- package/stories/user-action/button/TextButton.stories.tsx +22 -0
- package/stories/user-action/input/Checkbox.stories.tsx +20 -0
- package/stories/user-action/input/Label.stories.tsx +18 -0
- package/stories/user-action/input/ScrollPicker.stories.tsx +20 -0
- package/stories/user-action/input/Textarea.stories.tsx +22 -0
- package/stories/user-action/input/date/DatePicker.stories.tsx +23 -0
- package/stories/user-action/input/date/DateTimePicker.stories.tsx +26 -0
- package/stories/user-action/input/date/DayPicker.stories.tsx +20 -0
- package/stories/user-action/input/date/TimePicker.stories.tsx +20 -0
- package/stories/user-action/input/date/YearMonthPicker.stories.tsx +21 -0
- package/stories/user-action/input/select/MultiSelect.stories.tsx +39 -0
- package/stories/user-action/input/select/SearchableSelect.stories.tsx +32 -0
- package/stories/user-action/input/select/Select.stories.tsx +30 -0
- package/stories/user-action/properties/CheckboxProperty.stories.tsx +20 -0
- package/stories/user-action/properties/DateProperty.stories.tsx +21 -0
- package/stories/user-action/properties/MultiSelectProperty.stories.tsx +33 -0
- package/stories/user-action/properties/NumberProperty.stories.tsx +21 -0
- package/stories/user-action/properties/PropertyBase.stories.tsx +28 -0
- package/stories/user-action/properties/SingleSelectProperty.stories.tsx +35 -0
- package/stories/user-action/properties/TextProperty.stories.tsx +20 -0
- package/tsconfig.json +20 -0
- package/util/array.ts +115 -0
- package/util/builder.ts +9 -0
- package/util/date.ts +180 -0
- package/util/easeFunctions.ts +37 -0
- package/util/emailValidation.ts +3 -0
- package/util/loopingArray.ts +94 -0
- package/util/math.ts +3 -0
- package/util/news.ts +43 -0
- package/util/noop.ts +1 -0
- package/util/simpleSearch.ts +65 -0
- package/util/storage.ts +37 -0
- package/util/types.ts +4 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
|
+
import { Scrollbars } from 'react-custom-scrollbars-2'
|
|
3
|
+
import { noop } from '../../util/noop'
|
|
4
|
+
import { equalSizeGroups, range } from '../../util/array'
|
|
5
|
+
import clsx from 'clsx'
|
|
6
|
+
import { Expandable } from '../Expandable'
|
|
7
|
+
import { addDuration, monthsList, subtractDuration } from '../../util/date'
|
|
8
|
+
import { useLocale } from '../../hooks/useLanguage'
|
|
9
|
+
|
|
10
|
+
export type YearMonthPickerProps = {
|
|
11
|
+
displayedYearMonth?: Date,
|
|
12
|
+
start?: Date,
|
|
13
|
+
end?: Date,
|
|
14
|
+
onChange?: (date: Date) => void,
|
|
15
|
+
className?: string,
|
|
16
|
+
maxHeight?: number,
|
|
17
|
+
showValueOpen?: boolean,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// TODO use a dynamically loading infinite list here
|
|
21
|
+
export const YearMonthPicker = ({
|
|
22
|
+
displayedYearMonth = new Date(),
|
|
23
|
+
start = subtractDuration(new Date(), { years: 50 }),
|
|
24
|
+
end = addDuration(new Date(), { years: 50 }),
|
|
25
|
+
onChange = noop,
|
|
26
|
+
className = '',
|
|
27
|
+
maxHeight = 300,
|
|
28
|
+
showValueOpen = true
|
|
29
|
+
}: YearMonthPickerProps) => {
|
|
30
|
+
const locale = useLocale()
|
|
31
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const scrollToItem = () => {
|
|
35
|
+
if (ref.current) {
|
|
36
|
+
ref.current.scrollIntoView({
|
|
37
|
+
behavior: 'instant',
|
|
38
|
+
block: 'center',
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
scrollToItem()
|
|
44
|
+
}, [ref])
|
|
45
|
+
|
|
46
|
+
if (end < start) {
|
|
47
|
+
console.error(`startYear: (${start}) less than endYear: (${end})`)
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const years = range(start.getFullYear(), end.getFullYear())
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className={clsx('col select-none', className)}>
|
|
55
|
+
<Scrollbars autoHeight autoHeightMax={maxHeight} style={{ height: '100%' }}>
|
|
56
|
+
<div className="col gap-y-1 mr-3">
|
|
57
|
+
{years.map(year => {
|
|
58
|
+
const selectedYear = displayedYearMonth.getFullYear() === year
|
|
59
|
+
return (
|
|
60
|
+
<Expandable
|
|
61
|
+
key={year}
|
|
62
|
+
ref={(displayedYearMonth.getFullYear() ?? new Date().getFullYear()) === year ? ref : undefined}
|
|
63
|
+
label={<span className={clsx({ 'text-primary font-bold': selectedYear })}>{year}</span>}
|
|
64
|
+
initialExpansion={showValueOpen && selectedYear}
|
|
65
|
+
>
|
|
66
|
+
<div className="col gap-y-1 px-2 pb-2">
|
|
67
|
+
{equalSizeGroups([...monthsList], 3).map((monthList, index) => (
|
|
68
|
+
<div key={index} className="row">
|
|
69
|
+
{monthList.map(month => {
|
|
70
|
+
const monthIndex = monthsList.indexOf(month)
|
|
71
|
+
const newDate = new Date(year, monthIndex)
|
|
72
|
+
|
|
73
|
+
const selectedMonth = selectedYear && monthIndex === displayedYearMonth.getMonth()
|
|
74
|
+
const firstOfMonth = new Date(year, monthIndex, 1)
|
|
75
|
+
const lastOfMonth = new Date(year, monthIndex, 1)
|
|
76
|
+
const isAfterStart = start === undefined || start <= addDuration(subtractDuration(lastOfMonth, { days: 1 }), { months: 1 })
|
|
77
|
+
const isBeforeEnd = end === undefined || firstOfMonth <= end
|
|
78
|
+
const isValid = isAfterStart && isBeforeEnd
|
|
79
|
+
return (
|
|
80
|
+
<button
|
|
81
|
+
key={month}
|
|
82
|
+
disabled={!isValid}
|
|
83
|
+
className={clsx(
|
|
84
|
+
'chip hover:brightness-95 flex-1',
|
|
85
|
+
{
|
|
86
|
+
'bg-gray-50 text-black': !selectedMonth && isValid,
|
|
87
|
+
'bg-primary text-on-primary': selectedMonth && isValid,
|
|
88
|
+
'bg-disabled-background text-disabled-text': !isValid
|
|
89
|
+
}
|
|
90
|
+
)}
|
|
91
|
+
onClick={() => {
|
|
92
|
+
onChange(newDate)
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{new Intl.DateTimeFormat(locale, { month: 'short' }).format(newDate)}
|
|
96
|
+
</button>
|
|
97
|
+
)
|
|
98
|
+
})}
|
|
99
|
+
</div>
|
|
100
|
+
))}
|
|
101
|
+
</div>
|
|
102
|
+
</Expandable>
|
|
103
|
+
)
|
|
104
|
+
})}
|
|
105
|
+
</div>
|
|
106
|
+
</Scrollbars>
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const ControlledYearMonthPicker = ({
|
|
112
|
+
displayedYearMonth = new Date(),
|
|
113
|
+
onChange = noop,
|
|
114
|
+
...props
|
|
115
|
+
}: YearMonthPickerProps) => {
|
|
116
|
+
const [yearMonth, setYearMonth] = useState<Date>(displayedYearMonth)
|
|
117
|
+
|
|
118
|
+
useEffect(() => setYearMonth(displayedYearMonth), [displayedYearMonth])
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<YearMonthPicker
|
|
122
|
+
displayedYearMonth={yearMonth}
|
|
123
|
+
onChange={date => {
|
|
124
|
+
setYearMonth(date)
|
|
125
|
+
onChange(date)
|
|
126
|
+
}}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { InputGroupProps } from '../InputGroup'
|
|
3
|
+
import { InputGroup } from '../InputGroup'
|
|
4
|
+
import { Select } from '../user-input/Select'
|
|
5
|
+
import { Input } from '../user-input/Input'
|
|
6
|
+
import { Textarea } from '../user-input/Textarea'
|
|
7
|
+
|
|
8
|
+
export type InputGroupExampleProps = Omit<InputGroupProps, 'inputs' | 'onChange'>
|
|
9
|
+
|
|
10
|
+
type InputType = {
|
|
11
|
+
subject?: string,
|
|
12
|
+
propertyName: string,
|
|
13
|
+
description: string,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Example for an Input Group
|
|
18
|
+
*/
|
|
19
|
+
export const InputGroupExample = ({
|
|
20
|
+
...props
|
|
21
|
+
}: InputGroupExampleProps) => {
|
|
22
|
+
const [state, setState] = useState<InputType>({
|
|
23
|
+
propertyName: '',
|
|
24
|
+
description: ''
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<InputGroup
|
|
29
|
+
{...props}
|
|
30
|
+
onChange={console.log}
|
|
31
|
+
>
|
|
32
|
+
<Select
|
|
33
|
+
key="item1"
|
|
34
|
+
label={{ name: 'Subject Type', labelType: 'labelSmall' }}
|
|
35
|
+
value={state.subject}
|
|
36
|
+
options={[
|
|
37
|
+
{ value: 'organization', label: 'Organization' },
|
|
38
|
+
{ value: 'ward', label: 'Ward' },
|
|
39
|
+
{ value: 'bed', label: 'Bed' },
|
|
40
|
+
{ value: 'patient', label: 'Patient' },
|
|
41
|
+
]}
|
|
42
|
+
onChange={subject => setState({ ...state, subject })}
|
|
43
|
+
/>
|
|
44
|
+
<Input
|
|
45
|
+
key="item2"
|
|
46
|
+
label={{ name: 'Property Name' }}
|
|
47
|
+
value={state.propertyName}
|
|
48
|
+
onChange={propertyName => setState({ ...state, propertyName })}
|
|
49
|
+
/>
|
|
50
|
+
<Textarea
|
|
51
|
+
key="item3"
|
|
52
|
+
label={{ name: 'Description' }}
|
|
53
|
+
value={state.description}
|
|
54
|
+
onChange={description => setState({ ...state, description })}
|
|
55
|
+
/>
|
|
56
|
+
</InputGroup>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { MultiSelectProps } from '../user-input/MultiSelect'
|
|
3
|
+
import { MultiSelect } from '../user-input/MultiSelect'
|
|
4
|
+
import { ChipList } from '../ChipList'
|
|
5
|
+
|
|
6
|
+
type MultiSelectExampleProps = Omit<MultiSelectProps<string>, 'search' | 'selectedDisplay'> & {
|
|
7
|
+
enableSearch: boolean,
|
|
8
|
+
useChipDisplay: boolean,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const MultiSelectExample = ({
|
|
12
|
+
options,
|
|
13
|
+
hintText,
|
|
14
|
+
enableSearch,
|
|
15
|
+
onChange,
|
|
16
|
+
useChipDisplay = false,
|
|
17
|
+
...props
|
|
18
|
+
}: MultiSelectExampleProps) => {
|
|
19
|
+
const [usedOptions, setUsedOptions] = useState(options)
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
setUsedOptions(options)
|
|
23
|
+
}, [options])
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
setUsedOptions(options)
|
|
27
|
+
}, [options])
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setUsedOptions(options.map(value => ({ ...value, selected: false })))
|
|
31
|
+
}, [hintText, options])
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<MultiSelect
|
|
35
|
+
options={usedOptions}
|
|
36
|
+
onChange={value => {
|
|
37
|
+
onChange(value)
|
|
38
|
+
setUsedOptions(value)
|
|
39
|
+
}}
|
|
40
|
+
hintText={hintText}
|
|
41
|
+
search={enableSearch ? { initialSearch: '', searchMapping: value => [value.label] } : undefined}
|
|
42
|
+
selectedDisplay={useChipDisplay ?
|
|
43
|
+
({ items }) => {
|
|
44
|
+
const selected = items.filter(value => value.selected)
|
|
45
|
+
if (selected.length === 0) {
|
|
46
|
+
return (<span>Select</span>)
|
|
47
|
+
}
|
|
48
|
+
return (
|
|
49
|
+
<ChipList
|
|
50
|
+
list={selected.map(value => ({ children: value.label }))}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
} : undefined}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { SearchableSelectProps } from '../user-input/SearchableSelect'
|
|
3
|
+
import { SearchableSelect } from '../user-input/SearchableSelect'
|
|
4
|
+
|
|
5
|
+
export type SearchableSelectExampleProps = Omit<SearchableSelectProps<string>, 'searchMapping'|'additionalItems'>
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Example for a Searchable select
|
|
9
|
+
*/
|
|
10
|
+
export const SearchableSelectExample = ({
|
|
11
|
+
value,
|
|
12
|
+
options,
|
|
13
|
+
onChange,
|
|
14
|
+
...props
|
|
15
|
+
}: SearchableSelectExampleProps) => {
|
|
16
|
+
const [selected, setSelected] = useState<string | undefined>(value)
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
setSelected(options.find(value1 => value1.value === value)?.value)
|
|
20
|
+
}, [options, value])
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<SearchableSelect
|
|
24
|
+
{...props}
|
|
25
|
+
value={selected}
|
|
26
|
+
options={options}
|
|
27
|
+
onChange={value => {
|
|
28
|
+
setSelected(value)
|
|
29
|
+
onChange(value)
|
|
30
|
+
}}
|
|
31
|
+
searchMapping={value1 => [value1.value]}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { Select, type SelectProps } from '../user-input/Select'
|
|
3
|
+
|
|
4
|
+
type SelectExampleProps<T> = Omit<SelectProps<T>, 'onChange' | 'additionalItems' | 'selectedDisplayOverwrite'>
|
|
5
|
+
|
|
6
|
+
export const SelectExample = <T, >({ options, value, hintText, ...props }: SelectExampleProps<T>) => {
|
|
7
|
+
const [selected, setSelected] = useState(value)
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (options.find(options => options.value === value)) {
|
|
11
|
+
setSelected(value)
|
|
12
|
+
}
|
|
13
|
+
}, [options, value])
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
setSelected(undefined)
|
|
17
|
+
}, [hintText])
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Select
|
|
21
|
+
value={selected}
|
|
22
|
+
options={options}
|
|
23
|
+
onChange={value => setSelected(value)}
|
|
24
|
+
hintText={hintText}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { ModalRegister } from '../modals/ModalRegister'
|
|
3
|
+
import { ConfirmDialog } from '../modals/ConfirmDialog'
|
|
4
|
+
import { SolidButton } from '../Button'
|
|
5
|
+
import { modalRootName } from '../modals/Modal'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* An Example Component for Stacking Modals
|
|
9
|
+
*/
|
|
10
|
+
export const StackingModals = () => {
|
|
11
|
+
const [modal1, setModal1] = useState(false)
|
|
12
|
+
const [modal2, setModal2] = useState(false)
|
|
13
|
+
const [modal3, setModal3] = useState(false)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<ModalRegister>
|
|
17
|
+
<ConfirmDialog
|
|
18
|
+
id="1"
|
|
19
|
+
isOpen={modal1}
|
|
20
|
+
onConfirm={() => setModal1(false)}
|
|
21
|
+
onBackgroundClick={() => setModal1(false)}
|
|
22
|
+
onCloseClick={() => setModal1(false)}
|
|
23
|
+
modalClassName="!bg-yellow-200 min-h-[300px]"
|
|
24
|
+
>
|
|
25
|
+
{'I\'m Modal 1'}
|
|
26
|
+
<SolidButton onClick={() => setModal2(true)}>Open Modal 2</SolidButton>
|
|
27
|
+
</ConfirmDialog>
|
|
28
|
+
<ConfirmDialog
|
|
29
|
+
id="2"
|
|
30
|
+
isOpen={modal2}
|
|
31
|
+
onConfirm={() => setModal2(false)}
|
|
32
|
+
onBackgroundClick={() => setModal2(false)}
|
|
33
|
+
onCloseClick={() => setModal2(false)}
|
|
34
|
+
modalClassName="!bg-green-200 min-w-[300px]"
|
|
35
|
+
>
|
|
36
|
+
{'The next layer of Modals!'}
|
|
37
|
+
{'This is Modal 2'}
|
|
38
|
+
<SolidButton onClick={() => setModal3(true)}>Open Modal 3</SolidButton>
|
|
39
|
+
</ConfirmDialog>
|
|
40
|
+
<ConfirmDialog
|
|
41
|
+
id="3"
|
|
42
|
+
isOpen={modal3}
|
|
43
|
+
onConfirm={() => setModal3(false)}
|
|
44
|
+
onBackgroundClick={() => setModal3(false)}
|
|
45
|
+
onCloseClick={() => setModal3(false)}
|
|
46
|
+
>
|
|
47
|
+
This is Modal 3!
|
|
48
|
+
</ConfirmDialog>
|
|
49
|
+
<div className="row items-center justify-center min-h-[400px]" id={modalRootName}>
|
|
50
|
+
<SolidButton onClick={() => setModal1(true)}>Open Modal 1</SolidButton>
|
|
51
|
+
</div>
|
|
52
|
+
</ModalRegister>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { TableProps, TableSortingFunctionType, TableSortingType, TableState } from '../Table'
|
|
3
|
+
import {
|
|
4
|
+
addElementToTable,
|
|
5
|
+
defaultTableStatePagination,
|
|
6
|
+
defaultTableStateSelection,
|
|
7
|
+
removeFromTableSelection,
|
|
8
|
+
Table
|
|
9
|
+
} from '../Table'
|
|
10
|
+
import { Input } from '../user-input/Input'
|
|
11
|
+
import { SolidButton, TextButton } from '../Button'
|
|
12
|
+
import { SortButton } from '../SortButton'
|
|
13
|
+
|
|
14
|
+
export type DataType = {
|
|
15
|
+
id: string,
|
|
16
|
+
name: string,
|
|
17
|
+
age: number,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const exampleData: DataType[] = [
|
|
21
|
+
{ id: 'data1', name: 'Name 1', age: 23 },
|
|
22
|
+
{ id: 'data2', name: 'Name 2', age: 21 },
|
|
23
|
+
{ id: 'data3', name: 'Name 3', age: 32 },
|
|
24
|
+
{ id: 'data4', name: 'Name 4', age: 42 },
|
|
25
|
+
{ id: 'data5', name: 'Name 5', age: 17 },
|
|
26
|
+
{ id: 'data6', name: 'Name 6', age: 26 },
|
|
27
|
+
{ id: 'data7', name: 'Name 7', age: 19 },
|
|
28
|
+
{ id: 'data8', name: 'Name 8', age: 31 }
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
type SortingKeys = 'id'|'name'|'age'
|
|
32
|
+
|
|
33
|
+
const TableExample = ({ data: initialData }: Pick<TableProps<DataType>, 'data'>) => {
|
|
34
|
+
const [data, setData] = useState<DataType[]>(initialData)
|
|
35
|
+
const [tableState, setTableState] = useState<TableState>({
|
|
36
|
+
pagination: defaultTableStatePagination,
|
|
37
|
+
selection: defaultTableStateSelection
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
setData(initialData)
|
|
42
|
+
}, [initialData])
|
|
43
|
+
|
|
44
|
+
const [sorting, setSorting] = useState<[SortingKeys, TableSortingType]>()
|
|
45
|
+
const [sortingKey, ascending] = sorting ?? ['', 'ascending']
|
|
46
|
+
const idMapping = (data: DataType) => data.id
|
|
47
|
+
|
|
48
|
+
const sortingFunctions: Record<SortingKeys, Record<TableSortingType, TableSortingFunctionType<DataType>>> = {
|
|
49
|
+
id: {
|
|
50
|
+
ascending: (t1, t2) => t1.id.localeCompare(t2.id),
|
|
51
|
+
descending: (t1, t2) => t1.id.localeCompare(t2.id) * -1,
|
|
52
|
+
},
|
|
53
|
+
name: {
|
|
54
|
+
ascending: (t1, t2) => t1.name.localeCompare(t2.name),
|
|
55
|
+
descending: (t1, t2) => t1.name.localeCompare(t2.name) * -1,
|
|
56
|
+
},
|
|
57
|
+
age: {
|
|
58
|
+
ascending: (t1, t2) => t1.age - t2.age,
|
|
59
|
+
descending: (t1, t2) => (t1.age - t2.age) * -1,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="col gap-y-12 items-center">
|
|
65
|
+
<Table
|
|
66
|
+
stateManagement={[tableState, (newTableState) => {
|
|
67
|
+
setTableState(newTableState)
|
|
68
|
+
setData(data)
|
|
69
|
+
}]}
|
|
70
|
+
data={data}
|
|
71
|
+
identifierMapping={idMapping}
|
|
72
|
+
rowMappingToCells={dataObject => [
|
|
73
|
+
<span key="id" className="textstyle-title-md w-[100px] text-ellipsis overflow-hidden block">{dataObject.id}</span>,
|
|
74
|
+
<Input key="name" value={dataObject.name} onChange={text => {
|
|
75
|
+
setData(data.map(value => value.id === dataObject.id ? { ...dataObject, name: text } : value))
|
|
76
|
+
setSorting(undefined)
|
|
77
|
+
}} />,
|
|
78
|
+
<Input key="age" type="number" value={dataObject.age.toString()} onChange={text => {
|
|
79
|
+
setData(data.map(value => value.id === dataObject.id ? { ...dataObject, age: parseInt(text) } : value))
|
|
80
|
+
setSorting(undefined)
|
|
81
|
+
}} />,
|
|
82
|
+
<TextButton
|
|
83
|
+
key="delete"
|
|
84
|
+
color="negative"
|
|
85
|
+
onClick={() => {
|
|
86
|
+
const newData = data.filter(value => value.id !== dataObject.id)
|
|
87
|
+
setData(newData)
|
|
88
|
+
setTableState(removeFromTableSelection(tableState, [dataObject], data.length, idMapping))
|
|
89
|
+
}}
|
|
90
|
+
>Delete</TextButton>
|
|
91
|
+
]}
|
|
92
|
+
header={[
|
|
93
|
+
<SortButton
|
|
94
|
+
key="headerId"
|
|
95
|
+
ascending={sortingKey === 'id' ? ascending : undefined}
|
|
96
|
+
onClick={newTableSorting => {
|
|
97
|
+
setSorting(['id', newTableSorting])
|
|
98
|
+
setData(data.sort(sortingFunctions.id[newTableSorting]))
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<span className="textstyle-table-header">Id</span>
|
|
102
|
+
</SortButton>,
|
|
103
|
+
<SortButton
|
|
104
|
+
key="name"
|
|
105
|
+
ascending={sortingKey === 'name' ? ascending : undefined}
|
|
106
|
+
onClick={newTableSorting => {
|
|
107
|
+
setSorting(['name', newTableSorting])
|
|
108
|
+
setData(data.sort(sortingFunctions.name[newTableSorting]))
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<span className="textstyle-table-header">Name</span>
|
|
112
|
+
</SortButton>,
|
|
113
|
+
<SortButton
|
|
114
|
+
key="name"
|
|
115
|
+
ascending={sortingKey === 'age' ? ascending : undefined}
|
|
116
|
+
onClick={newTableSorting => {
|
|
117
|
+
setSorting(['age', newTableSorting])
|
|
118
|
+
setData(data.sort(sortingFunctions.age[newTableSorting]))
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<span key="age" className="textstyle-table-header">age</span>
|
|
122
|
+
</SortButton>,
|
|
123
|
+
<></>
|
|
124
|
+
]}
|
|
125
|
+
/>
|
|
126
|
+
<div className="row gap-x-2">
|
|
127
|
+
<SolidButton
|
|
128
|
+
className="w-auto"
|
|
129
|
+
onClick={() => {
|
|
130
|
+
const newData = {
|
|
131
|
+
id: Math.random().toString(),
|
|
132
|
+
name: 'Name ' + data.length,
|
|
133
|
+
age: Math.ceil(Math.random() * 100)
|
|
134
|
+
}
|
|
135
|
+
const withNewData = [...data, newData]
|
|
136
|
+
const sorted = sortingKey ? withNewData.sort(sortingFunctions[sortingKey][ascending]) : withNewData
|
|
137
|
+
setData(sorted)
|
|
138
|
+
setTableState(addElementToTable(tableState, sorted, newData, idMapping))
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
{'Add Data'}
|
|
142
|
+
</SolidButton>
|
|
143
|
+
<TextButton
|
|
144
|
+
className="w-auto"
|
|
145
|
+
onClick={() => {
|
|
146
|
+
const selectedData = data.filter((d) => tableState.selection?.currentSelection.includes(idMapping(d)))
|
|
147
|
+
const unselectedData = data.filter((d) => !tableState.selection?.currentSelection.includes(idMapping(d)))
|
|
148
|
+
setData(unselectedData)
|
|
149
|
+
setTableState(removeFromTableSelection(tableState, selectedData, data.length, idMapping))
|
|
150
|
+
}}
|
|
151
|
+
>
|
|
152
|
+
{'Remove all selected'}
|
|
153
|
+
</TextButton>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default TableExample
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { TextareaProps } from '../user-input/Textarea'
|
|
3
|
+
import { Textarea } from '../user-input/Textarea'
|
|
4
|
+
|
|
5
|
+
export type TextareaExampleProps = Omit<TextareaProps, 'onChange'|'onEditCompleted'>
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Example for the Textarea component
|
|
9
|
+
*/
|
|
10
|
+
export const TextareaExample = ({
|
|
11
|
+
value,
|
|
12
|
+
...props
|
|
13
|
+
}: TextareaExampleProps) => {
|
|
14
|
+
const [text, setText] = useState<string>(value as string)
|
|
15
|
+
return (
|
|
16
|
+
<Textarea
|
|
17
|
+
{...props}
|
|
18
|
+
value={text}
|
|
19
|
+
onChange={setText}
|
|
20
|
+
onEditCompleted={setText}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Info, X } from 'lucide-react'
|
|
2
|
+
import type { TileProps } from '../layout/Tile'
|
|
3
|
+
import { Tile } from '../layout/Tile'
|
|
4
|
+
|
|
5
|
+
export type TileExampleProps = Omit<TileProps, 'prefix' | 'suffix'> & {
|
|
6
|
+
prefix: boolean,
|
|
7
|
+
suffix: boolean,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* An Example for using the tile
|
|
12
|
+
*/
|
|
13
|
+
export const TileExample = ({
|
|
14
|
+
prefix,
|
|
15
|
+
suffix,
|
|
16
|
+
...restProps
|
|
17
|
+
}: TileExampleProps) => {
|
|
18
|
+
return (
|
|
19
|
+
<Tile
|
|
20
|
+
{...restProps}
|
|
21
|
+
prefix={prefix ? <Info size={20}/> : undefined}
|
|
22
|
+
suffix={suffix ? <X size={20}/> : undefined}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { DateTimePickerProps } from '../../user-input/DateAndTimePicker'
|
|
3
|
+
import { DateTimePicker } from '../../user-input/DateAndTimePicker'
|
|
4
|
+
import { noop } from '../../../util/noop'
|
|
5
|
+
import type { DatePickerProps } from '../../date/DatePicker'
|
|
6
|
+
import type { TimePickerProps } from '../../date/TimePicker'
|
|
7
|
+
import type { YearMonthPickerProps } from '../../date/YearMonthPicker'
|
|
8
|
+
import type { DayPickerProps } from '../../date/DayPicker'
|
|
9
|
+
|
|
10
|
+
export type DateTimePickerExampleProps = Omit<DateTimePickerProps, 'datePickerProps' | 'timePickerProps'> &
|
|
11
|
+
Pick<DatePickerProps, 'initialDisplay'> & Pick<TimePickerProps, 'is24HourFormat' | 'minuteIncrement'> &
|
|
12
|
+
Pick<YearMonthPickerProps, 'showValueOpen'> & Pick<DayPickerProps, 'markToday' | 'weekStart'>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Example for the DateTimePicker
|
|
16
|
+
*/
|
|
17
|
+
export const DateTimePickerExample = ({
|
|
18
|
+
value,
|
|
19
|
+
onChange = noop,
|
|
20
|
+
onRemove = noop,
|
|
21
|
+
onFinish = noop,
|
|
22
|
+
initialDisplay,
|
|
23
|
+
is24HourFormat,
|
|
24
|
+
minuteIncrement,
|
|
25
|
+
showValueOpen,
|
|
26
|
+
markToday,
|
|
27
|
+
weekStart,
|
|
28
|
+
...props
|
|
29
|
+
}: DateTimePickerExampleProps) => {
|
|
30
|
+
const [time, setTime] = useState(value)
|
|
31
|
+
|
|
32
|
+
useEffect(() => setTime(value), [value])
|
|
33
|
+
return (
|
|
34
|
+
<DateTimePicker
|
|
35
|
+
{...props}
|
|
36
|
+
value={time}
|
|
37
|
+
onChange={date => {
|
|
38
|
+
onChange(date)
|
|
39
|
+
setTime(date)
|
|
40
|
+
}}
|
|
41
|
+
onRemove={() => {
|
|
42
|
+
onRemove()
|
|
43
|
+
setTime(new Date())
|
|
44
|
+
}}
|
|
45
|
+
onFinish={date => {
|
|
46
|
+
onFinish(date)
|
|
47
|
+
setTime(date)
|
|
48
|
+
}}
|
|
49
|
+
timePickerProps={{ is24HourFormat, minuteIncrement }}
|
|
50
|
+
datePickerProps={{ initialDisplay, dayPickerProps: { markToday, weekStart }, yearMonthPickerProps: { showValueOpen } }}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import type { CheckboxPropertyProps } from '../../properties/CheckboxProperty'
|
|
3
|
+
import { CheckboxProperty } from '../../properties/CheckboxProperty'
|
|
4
|
+
|
|
5
|
+
export type CheckboxPropertyExampleProps = Omit<CheckboxPropertyProps, 'onChange' | 'onRemove'> & {
|
|
6
|
+
readOnly: boolean,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Example for using the CheckboxProperty
|
|
11
|
+
*/
|
|
12
|
+
export const CheckboxPropertyExample = ({
|
|
13
|
+
value = false,
|
|
14
|
+
...restProps
|
|
15
|
+
}: CheckboxPropertyExampleProps) => {
|
|
16
|
+
const [usedValue, setUsedValue] = useState<boolean>(value)
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
setUsedValue(value)
|
|
20
|
+
}, [value])
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<CheckboxProperty
|
|
24
|
+
{...restProps}
|
|
25
|
+
onChange={setUsedValue}
|
|
26
|
+
value={usedValue}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|