@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
package/globals.css ADDED
@@ -0,0 +1,488 @@
1
+ @import 'tailwindcss';
2
+
3
+ /* variants */
4
+ @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
5
+ @custom-variant light (&:where([data-theme=light], [data-theme=light] *));
6
+
7
+ /* utilities */
8
+ @utility col {
9
+ @apply flex flex-col gap-y-2;
10
+ }
11
+
12
+ @utility row {
13
+ @apply flex flex-row gap-x-2;
14
+ }
15
+
16
+ @layer utilities {
17
+ .animation-delay-0 {
18
+ animation-delay: 0ms;
19
+ }
20
+
21
+ .animation-delay-100 {
22
+ animation-delay: 100ms;
23
+ }
24
+
25
+ .animation-delay-200 {
26
+ animation-delay: 200ms;
27
+ }
28
+
29
+ .animation-delay-300 {
30
+ animation-delay: 300ms;
31
+ }
32
+
33
+ .animation-delay-400 {
34
+ animation-delay: 400ms;
35
+ }
36
+
37
+ .animation-delay-500 {
38
+ animation-delay: 500ms;
39
+ }
40
+
41
+ .animation-delay-1000 {
42
+ animation-delay: 1000ms;
43
+ }
44
+
45
+ @keyframes fade-in {
46
+ 0% {
47
+ opacity: 0;
48
+ }
49
+ 100% {
50
+ opacity: 1;
51
+ }
52
+ }
53
+
54
+ @keyframes fade-out {
55
+ 0% {
56
+ opacity: 1;
57
+ }
58
+ 100% {
59
+ opacity: 0;
60
+ }
61
+ }
62
+
63
+ @keyframes bigLeftUp {
64
+ 0%, 25% {
65
+ stroke-dashoffset: 1000;
66
+ }
67
+ 50%, 75%, 100% {
68
+ stroke-dashoffset: 0;
69
+ }
70
+ }
71
+
72
+ @keyframes bigRightDown {
73
+ 0%, 25%, 50% {
74
+ stroke-dashoffset: 0;
75
+ }
76
+ 75%, 100% {
77
+ stroke-dashoffset: -1000;
78
+ }
79
+ }
80
+
81
+ @keyframes smallLeftUp {
82
+ 0%, 25%, 50% {
83
+ stroke-dashoffset: 1000;
84
+ }
85
+ 75%, 100% {
86
+ stroke-dashoffset: 0;
87
+ }
88
+ }
89
+
90
+ @keyframes smallRightDown {
91
+ 0%, 25% {
92
+ stroke-dashoffset: 0;
93
+ }
94
+ 50%, 75%, 100% {
95
+ stroke-dashoffset: -1000;
96
+ }
97
+ }
98
+
99
+ .animate-fade {
100
+ animation: fade-out 3s ease-in-out;
101
+ }
102
+
103
+ .animate-wave-big-left-up {
104
+ animation: bigLeftUp 1.7s ease-in infinite normal;
105
+ }
106
+
107
+ .animate-wave-big-right-down {
108
+ animation: bigRightDown 1.7s ease-in infinite reverse;
109
+ }
110
+
111
+ .animate-wave-small-left-up {
112
+ animation: smallLeftUp 1.7s ease-in infinite normal;
113
+ }
114
+
115
+ .animate-wave-small-right-down {
116
+ animation: smallRightDown 1.7s ease-in infinite reverse;
117
+ }
118
+
119
+ .animate-tooltip-fade-in {
120
+ animation: fade-in 0.2s ease-in-out forwards;
121
+ }
122
+
123
+ .animate-tooltip-fade-out {
124
+ animation: fade-in 0.2s ease-in-out forwards;
125
+ }
126
+
127
+ .shadow-around {
128
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.02),
129
+ 0 -2px 2px 0 rgba(0, 0, 0, 0.02),
130
+ 2px 0 2px 0 rgba(0, 0, 0, 0.02),
131
+ -2px 0 2px 0 rgba(0, 0, 0, 0.02);
132
+ }
133
+
134
+ .shadow-around-md {
135
+ box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.02),
136
+ 0 -5px 5px 0 rgba(0, 0, 0, 0.02),
137
+ 5px 0 5px 0 rgba(0, 0, 0, 0.02),
138
+ -5px 0 5px 0 rgba(0, 0, 0, 0.02);
139
+ }
140
+
141
+ .shadow-around-lg {
142
+ box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.02),
143
+ 0 -10px 10px 0 rgba(0, 0, 0, 0.02),
144
+ 10px 0 10px 0 rgba(0, 0, 0, 0.02),
145
+ -10px 0 10px 0 rgba(0, 0, 0, 0.02);
146
+ }
147
+
148
+ .shadow-around-xl {
149
+ box-shadow: 0 20px 20px 0 rgba(0, 0, 0, 0.02),
150
+ 0 -20px 20px 0 rgba(0, 0, 0, 0.02),
151
+ 20px 0 20px 0 rgba(0, 0, 0, 0.02),
152
+ -20px 0 20px 0 rgba(0, 0, 0, 0.02);
153
+ }
154
+ }
155
+
156
+ /* The theme variables */
157
+ @theme {
158
+ /* Colors for with default values of the light theme */
159
+ --color-white: #FFFFFF;
160
+ --color-black: #000000;
161
+ --color-gray-25: #F8F8F8;
162
+ --color-gray-50: #F2F2F2;
163
+ --color-gray-100: #E6E6E6;
164
+ --color-gray-200: #CCCCCC;
165
+ --color-gray-300: #B3B3B3;
166
+ --color-gray-400: #999999;
167
+ --color-gray-500: #888888;
168
+ --color-gray-600: #666666;
169
+ --color-gray-700: #4D4D4D;
170
+ --color-gray-800: #333333;
171
+ --color-gray-900: #1A1A1A;
172
+ --color-gray-950: #0D0D0D;
173
+
174
+ --color-green-100: #D1EFD8;
175
+ --color-green-500: #69CB81;
176
+ --color-green-900: #2C5536;
177
+
178
+ --color-orange-100: #FBECD9;
179
+ --color-orange-200: #F7D8B3;
180
+ --color-orange-500: #EA9E40;
181
+
182
+ --color-purple-50: #EFE6FD;
183
+ --color-purple-100: #CEB0FA;
184
+ --color-purple-400: #8470c5;
185
+ --color-purple-500: #694BB4;
186
+
187
+ --color-blue-50: #F6FAFF;
188
+ --color-blue-100: #D6E3F9;
189
+ --color-blue-200: #99B9EF;
190
+ --color-blue-500: #3272DF;
191
+ --color-blue-600: #285BB2;
192
+ --color-blue-800: #1A4080;
193
+
194
+ --color-red-50: #FFF9F9;
195
+ --color-red-100: #FBE0E2;
196
+ --color-red-200: #F7C2C5;
197
+ --color-red-300: #F4A3A7;
198
+ --color-red-400: #E3798A;
199
+ --color-red-500: #DC576D;
200
+ --color-red-900: #5C252E;
201
+
202
+ --color-primary: var(--color-purple-500);
203
+ --color-on-primary: #FFFFFF;
204
+ --color-secondary: #11243E;
205
+ --color-on-secondary: #FFFFFF;
206
+ --color-warning: #EA9E40;
207
+ --color-on-warning: #FFFFFF;
208
+ --color-positive: #69CB81;
209
+ --color-on-positive: #FFFFFF;
210
+ --color-negative: #DC576D;
211
+ --color-on-negative: #FFFFFF;
212
+ --color-surface: #FFFFFF;
213
+ --color-on-surface: #1A1A1A;
214
+ --color-surface-variant: #F8F8F8;
215
+ --color-surface-warning: #FBECD9;
216
+ --color-highlight: #3272DF;
217
+ --color-background: #EEEEEE;
218
+ --color-on-background: #1A1A1A;
219
+
220
+ --color-female: #E3798A;
221
+ --color-female-outline: #F4A3A7;
222
+ --color-female-surface: #FBE0E2;
223
+ --color-female-background: #FFF9F9;
224
+
225
+ --color-male: #3272DF;
226
+ --color-male-outline: #99B9EF;
227
+ --color-male-surface: #D6E3F9;
228
+ --color-male-background: #F6FAFF;
229
+
230
+ --color-neutral: #4D4D4D;
231
+ --color-neutral-outline: #B3B3B3;
232
+ --color-neutral-surface: #E6E6E6;
233
+ --color-neutral-background: #F8F8F8;
234
+
235
+ --color-text-primary: #1A1A1A;
236
+ --color-text-secondary: #666666;
237
+ --color-text-tertiary: #B3B3B3;
238
+ --color-text-highlight: #3272DF;
239
+ --color-icon-primary: #1A1A1A;
240
+ --color-icon-secondary: #666666;
241
+ --color-icon-highlight: #3272DF;
242
+ --color-divider: #E6E6E6;
243
+ --color-description: #555555;
244
+ --color-outline: #E6E6E6;
245
+ --color-outline-variant: #CCCCCC;
246
+ --color-disabled-background: #BBBBBB;
247
+ --color-disabled-text: #555555;
248
+ --color-disabled-icon: #555555;
249
+ --color-disabled-outline: #555555;
250
+
251
+ --color-button-solid-primary-background: var(--color-purple-500);
252
+ --color-button-solid-primary-text: #FFFFFF;
253
+ --color-button-solid-primary-icon: #FFFFFF;
254
+ --color-button-solid-secondary-background: #EFE6FD;
255
+ --color-button-solid-secondary-text: var(--color-purple-500);
256
+ --color-button-solid-secondary-icon: var(--color-purple-500);
257
+ --color-button-solid-tertiary-background: #F2F2F2;
258
+ --color-button-solid-tertiary-icon: #888888;
259
+ --color-button-solid-tertiary-text: #888888;
260
+ --color-button-outline-primary-outline: var(--color-purple-500);
261
+ --color-button-outline-primary-icon: var(--color-purple-500);
262
+ --color-button-outline-primary-text: var(--color-purple-500);
263
+ --color-button-solid-positive-background: #69CB81;
264
+ --color-button-solid-positive-icon: #FFFFFF;
265
+ --color-button-solid-positive-text: #FFFFFF;
266
+ --color-button-solid-warning-background: #EA9E40;
267
+ --color-button-solid-warning-icon: #FFFFFF;
268
+ --color-button-solid-warning-text: #FFFFFF;
269
+ --color-button-solid-negative-background: #DC576D;
270
+ --color-button-solid-negative-icon: #FFFFFF;
271
+ --color-button-solid-negative-text: #FFFFFF;
272
+
273
+ --color-button-text-hover-background: #E1E1E1;
274
+ --color-button-text-neutral-text: #333333;
275
+ --color-button-text-neutral-icon: #333333;
276
+ --color-button-text-negative-text: #DC576D;
277
+ --color-button-text-negative-icon: #DC576D;
278
+
279
+ --color-menu-background: #FFFFFF;
280
+ --color-menu-text: #1A1A1A;
281
+ --color-menu-border: #999999;
282
+
283
+ --color-tag-default-background: #F6FAFF;
284
+ --color-tag-default-text: #1A4080;
285
+ --color-tag-default-icon: #1A4080;
286
+ --color-tag-dark-background: #333333;
287
+ --color-tag-dark-text: #FFFFFF;
288
+ --color-tag-dark-icon: #FFFFFF;
289
+ --color-tag-green-background: #E2E9DB;
290
+ --color-tag-green-text: #7A977E;
291
+ --color-tag-green-icon: #7A977E;
292
+ --color-tag-yellow-background: #FEEACB;
293
+ --color-tag-yellow-text: #C79345;
294
+ --color-tag-yellow-icon: #C79345;
295
+ --color-tag-red-background: #FEE0DD;
296
+ --color-tag-red-text: #D67268;
297
+ --color-tag-red-icon: #D67268;
298
+ --color-tag-blue-background: #F6FAFF;
299
+ --color-tag-blue-text: #1A4080;
300
+ --color-tag-blue-icon: #1A4080;
301
+ --color-tag-pink-background: #CEA3B8;
302
+ --color-tag-pink-text: #CE75A0;
303
+ --color-tag-pink-icon: #CE75A0;
304
+
305
+ --color-tabel-header-background: #F6FAFF;
306
+
307
+ --color-text-image-primary-background: var(--color-purple-500);
308
+ --color-text-image-primary-text: #FFFFFF;
309
+ --color-text-image-secondary-background: #3171DE;
310
+ --color-text-image-secondary-text: #FFFFFF;
311
+ --color-text-image-dark-background: #11243E;
312
+ --color-text-image-dark-text: #FFFFFF;
313
+
314
+ /* breakpoints */
315
+ --breakpoint-tablet: 48rem; /* 768px for 16px font-size */
316
+ --breakpoint-desktop: 64rem; /* 1024px for 16px font-size*/
317
+ /* fonts */
318
+ --font-inter: 'Inter', sans-serif;
319
+ --font-space: 'Space Grotesk', sans-serif;
320
+ }
321
+
322
+ /* Overrides of the dark theme */
323
+ @layer base {
324
+ /* Here are overrides for the light theme */
325
+ @variant dark {
326
+ --color-primary: var(--color-purple-400);
327
+
328
+ --color-surface: #333333;
329
+ --color-on-surface: #EEEEEE;
330
+ --color-surface-variant: #494949;
331
+ --color-surface-warning: #FBECD9;
332
+ --color-highlight: #3272DF;
333
+ --color-background: #1A1A1A;
334
+ --color-on-background: #EEEEEE;
335
+
336
+ --color-neutral: #4D4D4D;
337
+ --color-neutral-outline: #B3B3B3;
338
+ --color-neutral-surface: #E6E6E6;
339
+ --color-neutral-background: #F8F8F8;
340
+
341
+ --color-text-primary: #EEEEEE;
342
+ --color-text-secondary: #666666;
343
+ --color-text-tertiary: #B3B3B3;
344
+ --color-text-highlight: #3272DF;
345
+ --color-icon-primary: #EEEEEE;
346
+ --color-icon-secondary: #666666;
347
+ --color-icon-highlight: #3272DF;
348
+ --color-description: #999999;
349
+
350
+ --color-disabled-background: #555555;
351
+ --color-disabled-text: #888888;
352
+
353
+ --color-button-text-hover-background: #383838;
354
+ --color-button-text-neutral-text: #EEEEEE;
355
+ --color-button-text-neutral-icon: #EEEEEE;
356
+
357
+ --color-menu-background: #333333;
358
+ --color-menu-text: #EEEEEE;
359
+ --color-menu-border: #999999;
360
+
361
+ --color-tabel-header-background: #F6FAFF;
362
+ }
363
+ }
364
+
365
+ /* Styles that can be overwritten by utils */
366
+ @layer components {
367
+ .btn-sm {
368
+ @apply row items-center justify-center gap-x-1 px-[0.625rem] py-[0.25rem] rounded-[0.375rem];
369
+ }
370
+
371
+ .btn-md {
372
+ @apply row items-center justify-center gap-x-2 px-[0.75rem] py-[0.5rem] rounded-[0.375rem];
373
+ }
374
+
375
+ .btn-lg {
376
+ @apply row items-center justify-center gap-x-2 px-[1rem] py-[0.675rem] rounded-[0.375rem];
377
+ }
378
+
379
+ .card-sm {
380
+ @apply col gap-y-2 bg-surface text-on-surface px-2 py-1 rounded;
381
+ }
382
+
383
+ .card-md {
384
+ @apply col gap-y-2 bg-surface text-on-surface px-4 py-2 rounded-md;
385
+ }
386
+
387
+ .card-lg {
388
+ @apply col gap-y-2 bg-surface text-on-surface px-8 py-4 rounded-lg;
389
+ }
390
+
391
+ .chip {
392
+ @apply row gap-x-1 items-center justify-center px-2 py-1 rounded-md;
393
+ }
394
+
395
+ .chip-full {
396
+ @apply row gap-x-1 items-center justify-center px-2 py-1 rounded-full;
397
+ }
398
+
399
+ .section-padding-x {
400
+ @apply px-6 tablet:px-12 desktop:px-24;
401
+ }
402
+
403
+ .section-padding-y {
404
+ @apply py-16;
405
+ }
406
+
407
+ .section-padding {
408
+ @apply px-6 tablet:px-12 desktop:px-24 py-16;
409
+ }
410
+ }
411
+
412
+ @layer components {
413
+ html, main {
414
+ @apply bg-background text-on-background;
415
+ }
416
+
417
+ button {
418
+ @apply cursor-pointer;
419
+ }
420
+
421
+ .textstyle-title-3xl {
422
+ @apply text-5xl font-space font-bold;
423
+ }
424
+
425
+ .textstyle-title-2xl {
426
+ @apply text-4xl font-space font-bold;
427
+ }
428
+
429
+ .textstyle-title-xl {
430
+ @apply text-3xl font-space font-bold;
431
+ }
432
+
433
+ .textstyle-title-lg {
434
+ @apply text-2xl font-space font-bold;
435
+ }
436
+
437
+ .textstyle-title-md {
438
+ @apply text-xl font-space font-bold;
439
+ }
440
+
441
+ .textstyle-title-normal {
442
+ @apply text-lg font-semibold;
443
+ }
444
+
445
+ .textstyle-title-sm {
446
+ @apply font-space font-bold;
447
+ }
448
+
449
+ .textstyle-accent {
450
+ @apply text-sm text-gray-600 font-bold;
451
+ }
452
+
453
+ .textstyle-description {
454
+ @apply text-gray-400;
455
+ }
456
+
457
+ .textstyle-label-lg {
458
+ @apply text-gray-700 text-lg font-semibold;
459
+ }
460
+
461
+ .textstyle-label-md {
462
+ @apply text-gray-700 font-semibold;
463
+ }
464
+
465
+ .textstyle-label-sm {
466
+ @apply text-sm text-gray-700 font-semibold;
467
+ }
468
+
469
+ .textstyle-table-name {
470
+ @apply text-lg font-space font-medium;
471
+ }
472
+
473
+ .textstyle-table-header {
474
+ @apply text-gray-600 font-bold;
475
+ }
476
+
477
+ .textstyle-navigation-item {
478
+ @apply font-bold font-space;
479
+ }
480
+
481
+ .textstyle-form-error {
482
+ @apply text-negative text-sm;
483
+ }
484
+
485
+ .textstyle-form-description {
486
+ @apply text-gray-500 text-sm;
487
+ }
488
+ }
@@ -0,0 +1,88 @@
1
+ import type { Dispatch, SetStateAction } from 'react'
2
+ import { useEffect, useState } from 'react'
3
+
4
+ type UseHoverStateProps = {
5
+ /**
6
+ * The delay after which the menu is closed in milliseconds
7
+ *
8
+ * default: 200ms
9
+ */
10
+ closingDelay: number,
11
+ /**
12
+ * Whether the hover state management should be disabled
13
+ *
14
+ * default: false
15
+ */
16
+ isDisabled: boolean,
17
+ }
18
+
19
+ type UseHoverStateReturnType = {
20
+ /**
21
+ * Whether the element is hovered
22
+ */
23
+ isHovered: boolean,
24
+ /**
25
+ * Function to change the current hover status
26
+ */
27
+ setIsHovered: Dispatch<SetStateAction<boolean>>,
28
+ /**
29
+ * Handlers to pass on to the component that should be hovered
30
+ */
31
+ handlers: {
32
+ onMouseEnter: () => void,
33
+ onMouseLeave: () => void,
34
+ },
35
+ }
36
+
37
+ const defaultUseHoverStateProps: UseHoverStateProps = {
38
+ closingDelay: 200,
39
+ isDisabled: false,
40
+ }
41
+
42
+ /**
43
+ * @param props See UseHoverStateProps
44
+ *
45
+ * A react hook for managing the hover state of a component. The handlers provided should be
46
+ * forwarded to the component which should be hovered over
47
+ */
48
+ export const useHoverState = (props: Partial<UseHoverStateProps> | undefined = undefined): UseHoverStateReturnType => {
49
+ const { closingDelay, isDisabled } = { ...defaultUseHoverStateProps, ...props }
50
+
51
+ const [isHovered, setIsHovered] = useState(false)
52
+ const [timer, setTimer] = useState<NodeJS.Timeout>()
53
+
54
+ const onMouseEnter = () => {
55
+ if (isDisabled) {
56
+ return
57
+ }
58
+ clearTimeout(timer)
59
+ setIsHovered(true)
60
+ }
61
+
62
+ const onMouseLeave = () => {
63
+ if (isDisabled) {
64
+ return
65
+ }
66
+ setTimer(setTimeout(() => {
67
+ setIsHovered(false)
68
+ }, closingDelay))
69
+ }
70
+
71
+ useEffect(() => {
72
+ if (timer) {
73
+ return () => {
74
+ clearTimeout(timer)
75
+ }
76
+ }
77
+ })
78
+
79
+ useEffect(() => {
80
+ if (timer) {
81
+ clearTimeout(timer)
82
+ }
83
+ }, [isDisabled]) // eslint-disable-line react-hooks/exhaustive-deps
84
+
85
+ return {
86
+ isHovered, setIsHovered, handlers: { onMouseEnter, onMouseLeave }
87
+ }
88
+ }
@@ -0,0 +1,78 @@
1
+ import type { Dispatch, PropsWithChildren, SetStateAction } from 'react'
2
+ import { createContext, useContext, useEffect, useState } from 'react'
3
+ import useLocalStorage from './useLocalStorage'
4
+
5
+ export const languages = ['en', 'de'] as const
6
+ export type Languages = typeof languages[number]
7
+ export const languagesLocalNames: Record<Languages, string> = {
8
+ en: 'English',
9
+ de: 'Deutsch',
10
+ }
11
+
12
+ export const DEFAULT_LANGUAGE = 'en'
13
+
14
+ export type LanguageContextValue = {
15
+ language: Languages,
16
+ setLanguage: Dispatch<SetStateAction<Languages>>,
17
+ }
18
+
19
+ export const LanguageContext = createContext<LanguageContextValue>({ language: DEFAULT_LANGUAGE, setLanguage: (v) => v })
20
+
21
+ export const useLanguage = () => useContext(LanguageContext)
22
+
23
+ export const useLocale = (overWriteLanguage?: Languages) => {
24
+ const { language } = useLanguage()
25
+ const mapping: Record<Languages, string> = {
26
+ en: 'en-US',
27
+ de: 'de-DE'
28
+ }
29
+ return mapping[overWriteLanguage ?? language]
30
+ }
31
+
32
+ type ProvideLanguageProps = {
33
+ initialLanguage?: Languages,
34
+ }
35
+
36
+ export const ProvideLanguage = ({ initialLanguage, children }: PropsWithChildren<ProvideLanguageProps>) => {
37
+ const [language, setLanguage] = useState<Languages>(initialLanguage ?? DEFAULT_LANGUAGE)
38
+ const [storedLanguage, setStoredLanguage] = useLocalStorage<Languages>('language', initialLanguage ?? DEFAULT_LANGUAGE)
39
+
40
+ useEffect(() => {
41
+ if(language !== initialLanguage && initialLanguage){
42
+ console.warn('LanguageProvider initial state changed: Prefer using useLanguages\'s setLanguage instead')
43
+ setLanguage(initialLanguage)
44
+ }
45
+ }, [initialLanguage]) // eslint-disable-line react-hooks/exhaustive-deps
46
+
47
+ useEffect(() => {
48
+ // TODO set locale of html tag here as well
49
+ setStoredLanguage(language)
50
+ }, [language, setStoredLanguage])
51
+
52
+ useEffect(() => {
53
+ if (storedLanguage !== null) {
54
+ setLanguage(storedLanguage)
55
+ return
56
+ }
57
+
58
+ const languagesToTestAgainst = Object.values(languages)
59
+
60
+ const matchingBrowserLanguages = window.navigator.languages
61
+ .map(language => languagesToTestAgainst.find((test) => language === test || language.split('-')[0] === test))
62
+ .filter(entry => entry !== undefined)
63
+
64
+ if (matchingBrowserLanguages.length === 0) return
65
+
66
+ const firstMatch = matchingBrowserLanguages[0] as Languages
67
+ setLanguage(firstMatch)
68
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
69
+
70
+ return (
71
+ <LanguageContext.Provider value={{
72
+ language,
73
+ setLanguage
74
+ }}>
75
+ {children}
76
+ </LanguageContext.Provider>
77
+ )
78
+ }
@@ -0,0 +1,33 @@
1
+ import type { Dispatch, SetStateAction } from 'react'
2
+ import { useCallback, useEffect, useState } from 'react'
3
+ import { LocalStorageService } from '../util/storage'
4
+
5
+ type SetValue<T> = Dispatch<SetStateAction<T>>
6
+ const useLocalStorage = <T, >(key: string, initValue: T): [T, SetValue<T>] => {
7
+ const get = useCallback((): T => {
8
+ if (typeof window === 'undefined') {
9
+ return initValue
10
+ }
11
+ const storageService = new LocalStorageService()
12
+ const value = storageService.get<T>(key)
13
+ return value || initValue
14
+ }, [initValue, key])
15
+
16
+ const [storedValue, setStoredValue] = useState<T>(get)
17
+
18
+ const setValue: SetValue<T> = useCallback(value => {
19
+ const newValue = value instanceof Function ? value(storedValue) : value
20
+ const storageService = new LocalStorageService()
21
+ storageService.set(key, value)
22
+
23
+ setStoredValue(newValue)
24
+ }, [storedValue, setStoredValue, key])
25
+
26
+ useEffect(() => {
27
+ setStoredValue(get())
28
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
29
+
30
+ return [storedValue, setValue]
31
+ }
32
+
33
+ export default useLocalStorage
@@ -0,0 +1,25 @@
1
+ import { useEffect } from 'react'
2
+ import type { RefObject } from 'react'
3
+
4
+ export const useOutsideClick = <Ts extends RefObject<HTMLElement>[]>(refs: Ts, handler: () => void) => {
5
+ useEffect(() => {
6
+ const listener = (event: MouseEvent | TouchEvent) => {
7
+ // returning means not "not clicking outside"
8
+
9
+ // if no target exists, return
10
+ if (event.target === null) return
11
+ // if the target is a ref's element or descendent thereof, return
12
+ if (refs.some((ref) => !ref.current || ref.current.contains(event.target as Node))) {
13
+ return
14
+ }
15
+
16
+ handler()
17
+ }
18
+ document.addEventListener('mousedown', listener)
19
+ document.addEventListener('touchstart', listener)
20
+ return () => {
21
+ document.removeEventListener('mousedown', listener)
22
+ document.removeEventListener('touchstart', listener)
23
+ }
24
+ }, [refs, handler])
25
+ }