@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.
Files changed (162) hide show
  1. package/.storybook/main.ts +24 -0
  2. package/.storybook/preview.tsx +67 -0
  3. package/LICENSE +373 -0
  4. package/README.md +8 -0
  5. package/coloring/shading.ts +46 -0
  6. package/coloring/types.ts +13 -0
  7. package/components/Avatar.tsx +58 -0
  8. package/components/AvatarGroup.tsx +48 -0
  9. package/components/BreadCrumb.tsx +35 -0
  10. package/components/Button.tsx +236 -0
  11. package/components/ChipList.tsx +89 -0
  12. package/components/Circle.tsx +27 -0
  13. package/components/ErrorComponent.tsx +40 -0
  14. package/components/Expandable.tsx +61 -0
  15. package/components/HelpwaveBadge.tsx +35 -0
  16. package/components/HideableContentSection.tsx +43 -0
  17. package/components/InputGroup.tsx +72 -0
  18. package/components/LoadingAndErrorComponent.tsx +47 -0
  19. package/components/LoadingAnimation.tsx +40 -0
  20. package/components/LoadingButton.tsx +27 -0
  21. package/components/MarkdownInterpreter.tsx +278 -0
  22. package/components/Pagination.tsx +65 -0
  23. package/components/Profile.tsx +124 -0
  24. package/components/ProgressIndicator.tsx +58 -0
  25. package/components/Ring.tsx +286 -0
  26. package/components/SearchableList.tsx +69 -0
  27. package/components/SortButton.tsx +33 -0
  28. package/components/Span.tsx +0 -0
  29. package/components/StepperBar.tsx +124 -0
  30. package/components/Table.tsx +330 -0
  31. package/components/TechRadar.tsx +247 -0
  32. package/components/TextImage.tsx +86 -0
  33. package/components/TimeDisplay.tsx +121 -0
  34. package/components/Tooltip.tsx +92 -0
  35. package/components/VerticalDivider.tsx +51 -0
  36. package/components/date/DatePicker.tsx +164 -0
  37. package/components/date/DayPicker.tsx +95 -0
  38. package/components/date/TimePicker.tsx +167 -0
  39. package/components/date/YearMonthPicker.tsx +130 -0
  40. package/components/examples/InputGroupExample.tsx +58 -0
  41. package/components/examples/MultiSelectExample.tsx +57 -0
  42. package/components/examples/SearchableSelectExample.tsx +34 -0
  43. package/components/examples/SelectExample.tsx +28 -0
  44. package/components/examples/StackingModals.tsx +54 -0
  45. package/components/examples/TableExample.tsx +159 -0
  46. package/components/examples/TextareaExample.tsx +23 -0
  47. package/components/examples/TileExample.tsx +25 -0
  48. package/components/examples/Title.tsx +0 -0
  49. package/components/examples/date/DateTimePickerExample.tsx +53 -0
  50. package/components/examples/properties/CheckboxPropertyExample.tsx +29 -0
  51. package/components/examples/properties/DatePropertyExample.tsx +44 -0
  52. package/components/examples/properties/MultiSelectPropertyExample.tsx +39 -0
  53. package/components/examples/properties/NumberPropertyExample.tsx +28 -0
  54. package/components/examples/properties/SelectPropertyExample.tsx +39 -0
  55. package/components/examples/properties/TextPropertyExample.tsx +30 -0
  56. package/components/icons/Helpwave.tsx +51 -0
  57. package/components/icons/Tag.tsx +29 -0
  58. package/components/layout/Carousel.tsx +396 -0
  59. package/components/layout/DividerInserter.tsx +37 -0
  60. package/components/layout/FAQSection.tsx +57 -0
  61. package/components/layout/Tile.tsx +67 -0
  62. package/components/modals/ConfirmDialog.tsx +105 -0
  63. package/components/modals/DiscardChangesDialog.tsx +71 -0
  64. package/components/modals/InputModal.tsx +26 -0
  65. package/components/modals/LanguageModal.tsx +76 -0
  66. package/components/modals/Modal.tsx +149 -0
  67. package/components/modals/ModalRegister.tsx +45 -0
  68. package/components/properties/CheckboxProperty.tsx +62 -0
  69. package/components/properties/DateProperty.tsx +58 -0
  70. package/components/properties/MultiSelectProperty.tsx +82 -0
  71. package/components/properties/NumberProperty.tsx +86 -0
  72. package/components/properties/PropertyBase.tsx +84 -0
  73. package/components/properties/SelectProperty.tsx +67 -0
  74. package/components/properties/TextProperty.tsx +81 -0
  75. package/components/user-input/Checkbox.tsx +139 -0
  76. package/components/user-input/DateAndTimePicker.tsx +156 -0
  77. package/components/user-input/Input.tsx +192 -0
  78. package/components/user-input/Label.tsx +32 -0
  79. package/components/user-input/Menu.tsx +75 -0
  80. package/components/user-input/MultiSelect.tsx +158 -0
  81. package/components/user-input/ScrollPicker.tsx +240 -0
  82. package/components/user-input/SearchableSelect.tsx +36 -0
  83. package/components/user-input/Select.tsx +132 -0
  84. package/components/user-input/Textarea.tsx +86 -0
  85. package/components/user-input/ToggleableInput.tsx +115 -0
  86. package/eslint.config.js +3 -0
  87. package/globals.css +488 -0
  88. package/hooks/useHoverState.ts +88 -0
  89. package/hooks/useLanguage.tsx +78 -0
  90. package/hooks/useLocalStorage.tsx +33 -0
  91. package/hooks/useOutsideClick.ts +25 -0
  92. package/hooks/useSaveDelay.ts +46 -0
  93. package/hooks/useTheme.tsx +57 -0
  94. package/hooks/useTranslation.ts +43 -0
  95. package/index.ts +0 -0
  96. package/package.json +71 -0
  97. package/postcss.config.mjs +7 -0
  98. package/stories/README.md +23 -0
  99. package/stories/coloring/shading.stories.tsx +54 -0
  100. package/stories/geometry/Circle.stories.tsx +16 -0
  101. package/stories/geometry/rings/AnimatedRing.stories.tsx +18 -0
  102. package/stories/geometry/rings/RadialRings.stories.tsx +19 -0
  103. package/stories/geometry/rings/Ring.stories.tsx +17 -0
  104. package/stories/geometry/rings/RingWave.stories.tsx +20 -0
  105. package/stories/layout/FAQSection.stories.tsx +49 -0
  106. package/stories/layout/InputGroup.stories.tsx +19 -0
  107. package/stories/layout/Table.stories.tsx +19 -0
  108. package/stories/layout/TextImage.stories.tsx +24 -0
  109. package/stories/layout/chip/Chip.stories.tsx +19 -0
  110. package/stories/layout/chip/ChipList.stories.tsx +27 -0
  111. package/stories/layout/tile/Tile.stories.ts +20 -0
  112. package/stories/layout/tile/TileWithImage.stories.tsx +27 -0
  113. package/stories/other/BreadCrumbs.stories.tsx +21 -0
  114. package/stories/other/HelpwaveBadge.stories.tsx +18 -0
  115. package/stories/other/HelpwaveSpinner.stories.tsx +19 -0
  116. package/stories/other/MarkdownInterpreter.stories.tsx +18 -0
  117. package/stories/other/Profile.stories.tsx +52 -0
  118. package/stories/other/SearchableList.stories.tsx +21 -0
  119. package/stories/other/StackingModals.stories.tsx +16 -0
  120. package/stories/other/TechRadar.stories.tsx +14 -0
  121. package/stories/other/Translation.stories.tsx +56 -0
  122. package/stories/other/VerticalDivider.stories.tsx +20 -0
  123. package/stories/other/avatar/Avatar.stories.tsx +19 -0
  124. package/stories/other/avatar/AvatarGroup.stories.tsx +26 -0
  125. package/stories/other/tooltip/Tooltip.stories.tsx +30 -0
  126. package/stories/other/tooltip/TooltipStack.stories.tsx +39 -0
  127. package/stories/user-action/button/LoadingButton.stories.tsx +21 -0
  128. package/stories/user-action/button/OutlineButton.stories.tsx +22 -0
  129. package/stories/user-action/button/SolidButton.stories.tsx +22 -0
  130. package/stories/user-action/button/TextButton.stories.tsx +22 -0
  131. package/stories/user-action/input/Checkbox.stories.tsx +20 -0
  132. package/stories/user-action/input/Label.stories.tsx +18 -0
  133. package/stories/user-action/input/ScrollPicker.stories.tsx +20 -0
  134. package/stories/user-action/input/Textarea.stories.tsx +22 -0
  135. package/stories/user-action/input/date/DatePicker.stories.tsx +23 -0
  136. package/stories/user-action/input/date/DateTimePicker.stories.tsx +26 -0
  137. package/stories/user-action/input/date/DayPicker.stories.tsx +20 -0
  138. package/stories/user-action/input/date/TimePicker.stories.tsx +20 -0
  139. package/stories/user-action/input/date/YearMonthPicker.stories.tsx +21 -0
  140. package/stories/user-action/input/select/MultiSelect.stories.tsx +39 -0
  141. package/stories/user-action/input/select/SearchableSelect.stories.tsx +32 -0
  142. package/stories/user-action/input/select/Select.stories.tsx +30 -0
  143. package/stories/user-action/properties/CheckboxProperty.stories.tsx +20 -0
  144. package/stories/user-action/properties/DateProperty.stories.tsx +21 -0
  145. package/stories/user-action/properties/MultiSelectProperty.stories.tsx +33 -0
  146. package/stories/user-action/properties/NumberProperty.stories.tsx +21 -0
  147. package/stories/user-action/properties/PropertyBase.stories.tsx +28 -0
  148. package/stories/user-action/properties/SingleSelectProperty.stories.tsx +35 -0
  149. package/stories/user-action/properties/TextProperty.stories.tsx +20 -0
  150. package/tsconfig.json +20 -0
  151. package/util/array.ts +115 -0
  152. package/util/builder.ts +9 -0
  153. package/util/date.ts +180 -0
  154. package/util/easeFunctions.ts +37 -0
  155. package/util/emailValidation.ts +3 -0
  156. package/util/loopingArray.ts +94 -0
  157. package/util/math.ts +3 -0
  158. package/util/news.ts +43 -0
  159. package/util/noop.ts +1 -0
  160. package/util/simpleSearch.ts +65 -0
  161. package/util/storage.ts +37 -0
  162. 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
+ }