@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,39 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { MultiSelectExample } from '../../../../components/examples/MultiSelectExample'
|
|
3
|
+
import { action } from '@storybook/addon-actions'
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'User-Action/Input/Select',
|
|
7
|
+
component: MultiSelectExample,
|
|
8
|
+
} satisfies Meta<typeof MultiSelectExample>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const MultiSelectVariations: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
label: { name: 'Select-Label' },
|
|
16
|
+
options: [
|
|
17
|
+
{ value: '1', selected: false, label: 'Entry 1' },
|
|
18
|
+
{ value: '2', selected: false, label: 'Entry 2', disabled: true },
|
|
19
|
+
{ value: '3', selected: false, label: 'Different Entry 3' },
|
|
20
|
+
{ value: '4', selected: false, label: 'Custom styled Entry 4', className: '!text-red-400' },
|
|
21
|
+
{ value: '5', selected: false, label: 'Entry 5' },
|
|
22
|
+
{ value: '6', selected: false, label: 'Entry 6', disabled: true },
|
|
23
|
+
{ value: '7', selected: false, label: 'Long Entry 7' },
|
|
24
|
+
{ value: '8', selected: false, label: 'Long Entry 8' },
|
|
25
|
+
{ value: '9', selected: false, label: 'Very Long Entry 9' },
|
|
26
|
+
{ value: '10', selected: false, label: 'Long Entry 10' },
|
|
27
|
+
{ value: '11', selected: false, label: 'Very very Long Entry 11' },
|
|
28
|
+
{ value: '12', selected: false, label: 'Entry 12', disabled: true }
|
|
29
|
+
],
|
|
30
|
+
disabled: false,
|
|
31
|
+
hintText: undefined,
|
|
32
|
+
showDisabledOptions: true,
|
|
33
|
+
useChipDisplay: true,
|
|
34
|
+
enableSearch: false,
|
|
35
|
+
className: '',
|
|
36
|
+
triggerClassName: '',
|
|
37
|
+
onChange: action('onChange'),
|
|
38
|
+
},
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { SearchableSelectExample } from '../../../../components/examples/SearchableSelectExample'
|
|
3
|
+
import { action } from '@storybook/addon-actions'
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'User-Action/Input/Select',
|
|
7
|
+
component: SearchableSelectExample,
|
|
8
|
+
} satisfies Meta<typeof SearchableSelectExample>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const SearchableSelectVariations: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
label: { name: 'Select-Label', labelType: 'labelMedium' },
|
|
16
|
+
value: undefined,
|
|
17
|
+
options: [
|
|
18
|
+
{ value: 'Entry 1', label: 'Entry 1' },
|
|
19
|
+
{ value: 'Entry 2', label: 'Entry 2', disabled: true },
|
|
20
|
+
{ value: 'Entry 3', label: 'Entry 3' },
|
|
21
|
+
{ value: 'Custom styled', label: <span className="!text-red-400">Custom styled</span> },
|
|
22
|
+
{ value: 'Entry 5', label: 'Entry 5' },
|
|
23
|
+
{ value: 'Entry 6', label: 'Entry 6', disabled: true }
|
|
24
|
+
],
|
|
25
|
+
isDisabled: false,
|
|
26
|
+
hintText: 'Hinttext',
|
|
27
|
+
isHidingCurrentValue: false,
|
|
28
|
+
showDisabledOptions: true,
|
|
29
|
+
className: '',
|
|
30
|
+
onChange: action('onChange'),
|
|
31
|
+
},
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { SelectExample } from '../../../../components/examples/SelectExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Input/Select',
|
|
6
|
+
component: SelectExample<string>,
|
|
7
|
+
} satisfies Meta<typeof SelectExample<string>>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const SelectVariations: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
label: { name: 'Select-Label', labelType: 'labelMedium' },
|
|
15
|
+
value: undefined,
|
|
16
|
+
options: [
|
|
17
|
+
{ value: '1', label: 'Entry 1' },
|
|
18
|
+
{ value: '2', label: 'Entry 2', disabled: true },
|
|
19
|
+
{ value: '3', label: 'Entry 3' },
|
|
20
|
+
{ value: '4', label: <span className="!text-red-400">Custom styled</span> },
|
|
21
|
+
{ value: '5', label: 'Entry 5' },
|
|
22
|
+
{ value: '6', label: 'Entry 6', disabled: true }
|
|
23
|
+
],
|
|
24
|
+
isDisabled: false,
|
|
25
|
+
hintText: 'Hinttext',
|
|
26
|
+
isHidingCurrentValue: false,
|
|
27
|
+
showDisabledOptions: true,
|
|
28
|
+
className: '',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { CheckboxPropertyExample } from '../../../components/examples/properties/CheckboxPropertyExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Property',
|
|
6
|
+
component: CheckboxPropertyExample,
|
|
7
|
+
} satisfies Meta<typeof CheckboxPropertyExample>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const CheckboxPropertyVariation: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
name: 'Property',
|
|
15
|
+
softRequired: false,
|
|
16
|
+
value: false,
|
|
17
|
+
readOnly: false,
|
|
18
|
+
className: '',
|
|
19
|
+
},
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { DatePropertyExample } from '../../../components/examples/properties/DatePropertyExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Property',
|
|
6
|
+
component: DatePropertyExample,
|
|
7
|
+
} satisfies Meta<typeof DatePropertyExample>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const DatePropertyVariation: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
name: 'Property',
|
|
15
|
+
softRequired: false,
|
|
16
|
+
value: undefined,
|
|
17
|
+
readOnly: false,
|
|
18
|
+
type: 'dateTime',
|
|
19
|
+
className: '',
|
|
20
|
+
},
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { MultiSelectPropertyExample } from '../../../components/examples/properties/MultiSelectPropertyExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Property',
|
|
6
|
+
component: MultiSelectPropertyExample,
|
|
7
|
+
} satisfies Meta<typeof MultiSelectPropertyExample>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const MultiSelectPropertyVariation: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
name: 'Fruits',
|
|
15
|
+
softRequired: false,
|
|
16
|
+
options: [
|
|
17
|
+
{ value: 'apple', label: 'Apple', selected: false },
|
|
18
|
+
{ value: 'pear', label: 'Pear', selected: false },
|
|
19
|
+
{ value: 'plum', label: 'Plum', selected: false },
|
|
20
|
+
{ value: 'strawberry', label: 'Strawberry', selected: false, disabled: true },
|
|
21
|
+
{ value: 'orange', label: 'Orange', selected: false },
|
|
22
|
+
{ value: 'maracuja', label: 'Maracuja', selected: false },
|
|
23
|
+
{ value: 'lemon', label: 'Lemon', selected: false },
|
|
24
|
+
{ value: 'pineapple', label: 'Pineapple', selected: false },
|
|
25
|
+
{ value: 'kiwi', label: 'Kiwi', selected: false },
|
|
26
|
+
{ value: 'watermelon', label: 'Watermelon', selected: false },
|
|
27
|
+
],
|
|
28
|
+
readOnly: false,
|
|
29
|
+
hintText: 'Select',
|
|
30
|
+
enableSearch: true,
|
|
31
|
+
showDisabledOptions: true
|
|
32
|
+
},
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { NumberPropertyExample } from '../../../components/examples/properties/NumberPropertyExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Property',
|
|
6
|
+
component: NumberPropertyExample,
|
|
7
|
+
} satisfies Meta<typeof NumberPropertyExample>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const NumberPropertyVariation: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
name: 'Property',
|
|
15
|
+
softRequired: false,
|
|
16
|
+
value: undefined,
|
|
17
|
+
suffix: 'kg',
|
|
18
|
+
readOnly: false,
|
|
19
|
+
className: '',
|
|
20
|
+
},
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import clsx from 'clsx'
|
|
3
|
+
import { PropertyBase } from '../../../components/properties/PropertyBase'
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'User-Action/Property',
|
|
7
|
+
component: PropertyBase,
|
|
8
|
+
} satisfies Meta<typeof PropertyBase>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
type Story = StoryObj<typeof meta>;
|
|
12
|
+
|
|
13
|
+
export const PropertyBaseVariation: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
name: 'Property',
|
|
16
|
+
softRequired: false,
|
|
17
|
+
hasValue: true,
|
|
18
|
+
input: ({ softRequired, hasValue }) => (
|
|
19
|
+
<div
|
|
20
|
+
className={clsx('row grow py-2 px-4', { 'text-warning': softRequired && !hasValue })}
|
|
21
|
+
>
|
|
22
|
+
Value
|
|
23
|
+
</div>
|
|
24
|
+
),
|
|
25
|
+
className: '',
|
|
26
|
+
readOnly: false,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { SingleSelectPropertyExample } from '../../../components/examples/properties/SelectPropertyExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Property',
|
|
6
|
+
component: SingleSelectPropertyExample,
|
|
7
|
+
} satisfies Meta<typeof SingleSelectPropertyExample>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const SingleSelectPropertyVariation: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
value: undefined,
|
|
15
|
+
name: 'Fruits',
|
|
16
|
+
softRequired: false,
|
|
17
|
+
options: [
|
|
18
|
+
{ value: 'apple', label: 'Apple' },
|
|
19
|
+
{ value: 'pear', label: 'Pear' },
|
|
20
|
+
{ value: 'plum', label: 'Plum' },
|
|
21
|
+
{ value: 'strawberry', label: 'Strawberry', disabled: true },
|
|
22
|
+
{ value: 'orange', label: 'Orange' },
|
|
23
|
+
{ value: 'maracuja', label: 'Maracuja' },
|
|
24
|
+
{ value: 'lemon', label: 'Lemon' },
|
|
25
|
+
{ value: 'pineapple', label: 'Pineapple' },
|
|
26
|
+
{ value: 'kiwi', label: 'Kiwi' },
|
|
27
|
+
{ value: 'watermelon', label: 'Watermelon' },
|
|
28
|
+
],
|
|
29
|
+
readOnly: false,
|
|
30
|
+
hintText: 'Select',
|
|
31
|
+
showDisabledOptions: true,
|
|
32
|
+
isDisabled: false,
|
|
33
|
+
isHidingCurrentValue: true
|
|
34
|
+
},
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { TextPropertyExample } from '../../../components/examples/properties/TextPropertyExample'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'User-Action/Property',
|
|
6
|
+
component: TextPropertyExample,
|
|
7
|
+
} satisfies Meta<typeof TextPropertyExample>
|
|
8
|
+
|
|
9
|
+
export default meta
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
|
|
12
|
+
export const TextPropertyVariation: Story = {
|
|
13
|
+
args: {
|
|
14
|
+
name: 'Property',
|
|
15
|
+
softRequired: false,
|
|
16
|
+
value: undefined,
|
|
17
|
+
readOnly: false,
|
|
18
|
+
className: '',
|
|
19
|
+
},
|
|
20
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"jsx": "react-jsx",
|
|
14
|
+
"incremental": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
17
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
18
|
+
"noUncheckedIndexedAccess": true
|
|
19
|
+
}
|
|
20
|
+
}
|
package/util/array.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export const equalSizeGroups = <T >(array: T[], groupSize: number): T[][] => {
|
|
2
|
+
if (groupSize <= 0) {
|
|
3
|
+
console.warn(`group size should be greater than 0: groupSize = ${groupSize}`)
|
|
4
|
+
return [[...array]]
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const groups = []
|
|
8
|
+
for (let i = 0; i < array.length; i += groupSize) {
|
|
9
|
+
groups.push(array.slice(i, Math.min(i + groupSize, array.length)))
|
|
10
|
+
}
|
|
11
|
+
return groups
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param start
|
|
16
|
+
* @param end inclusive
|
|
17
|
+
* @param allowEmptyRange Whether the range can be defined empty via end < start
|
|
18
|
+
*/
|
|
19
|
+
export const range = (start: number, end: number, allowEmptyRange: boolean = false): number[] => {
|
|
20
|
+
if (end < start) {
|
|
21
|
+
if (!allowEmptyRange) {
|
|
22
|
+
console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set allowEmptyRange to true`)
|
|
23
|
+
}
|
|
24
|
+
return []
|
|
25
|
+
}
|
|
26
|
+
return Array.from({ length: end - start + 1 }, (_, index) => index + start)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Finds the closest match
|
|
30
|
+
* @param list The list of all possible matches
|
|
31
|
+
* @param firstCloser Return whether item1 is closer than item2
|
|
32
|
+
*/
|
|
33
|
+
export const closestMatch = <T >(list: T[], firstCloser: (item1: T, item2: T) => boolean) => {
|
|
34
|
+
return list.reduce((item1, item2) => {
|
|
35
|
+
return firstCloser(item1, item2) ? item1 : item2
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* returns the item in middle of a list and its neighbours before and after
|
|
41
|
+
* e.g. [1,2,3,4,5,6] for item = 1 would return [5,6,1,2,3]
|
|
42
|
+
*/
|
|
43
|
+
export const getNeighbours = <T >(list: T[], item: T, neighbourDistance: number = 2) => {
|
|
44
|
+
const index = list.indexOf(item)
|
|
45
|
+
const totalItems = neighbourDistance * 2 + 1
|
|
46
|
+
if (list.length < totalItems) {
|
|
47
|
+
console.warn('List is to short')
|
|
48
|
+
return list
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (index === -1) {
|
|
52
|
+
console.error('item not found in list')
|
|
53
|
+
return list.splice(0, totalItems)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let start = index - neighbourDistance
|
|
57
|
+
if (start < 0) {
|
|
58
|
+
start += list.length
|
|
59
|
+
}
|
|
60
|
+
const end = (index + neighbourDistance + 1) % list.length
|
|
61
|
+
|
|
62
|
+
const result: T[] = []
|
|
63
|
+
let ignoreOnce = list.length === totalItems
|
|
64
|
+
for (let i = start; i !== end || ignoreOnce; i = (i + 1) % list.length) {
|
|
65
|
+
result.push(list[i]!)
|
|
66
|
+
if (end === i && ignoreOnce) {
|
|
67
|
+
ignoreOnce = false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const createLoopingListWithIndex = <T >(list: T[], startIndex: number = 0, length: number = 0, forwards: boolean = true) => {
|
|
74
|
+
if (length < 0) {
|
|
75
|
+
console.warn(`createLoopingList: length must be >= 0, given ${length}`)
|
|
76
|
+
} else if (length === 0) {
|
|
77
|
+
length = list.length
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const returnList: [number, T][] = []
|
|
81
|
+
|
|
82
|
+
if (forwards) {
|
|
83
|
+
for (let i = startIndex; returnList.length < length; i = (i + 1) % list.length) {
|
|
84
|
+
returnList.push([i, list[i]!])
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
for (let i = startIndex; returnList.length < length; i = i === 0 ? i = list.length - 1 : i - 1) {
|
|
88
|
+
returnList.push([i, list[i]!])
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return returnList
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const createLoopingList = <T >(list: T[], startIndex: number = 0, length: number = 0, forwards: boolean = true) => {
|
|
96
|
+
return createLoopingListWithIndex(list, startIndex, length, forwards).map(([_, item]) => item)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const ArrayUtil = {
|
|
100
|
+
unique: <T>(list: T[]): T[] => {
|
|
101
|
+
const seen = new Set<T>()
|
|
102
|
+
return list.filter((item) => {
|
|
103
|
+
if (seen.has(item)) {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
seen.add(item)
|
|
107
|
+
return true
|
|
108
|
+
})
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
difference: <T>(list: T[], removeList: T[]): T[] => {
|
|
112
|
+
const remove = new Set<T>(removeList)
|
|
113
|
+
return list.filter((item) => !remove.has(item))
|
|
114
|
+
}
|
|
115
|
+
}
|
package/util/builder.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A simple function that implements the builder pattern
|
|
3
|
+
* @param value The value to update which gets return with the same reference
|
|
4
|
+
* @param update The updates to apply on the object
|
|
5
|
+
*/
|
|
6
|
+
export const builder = <T>(value: T, update: (value: T) => void): T => {
|
|
7
|
+
update(value)
|
|
8
|
+
return value
|
|
9
|
+
}
|
package/util/date.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { equalSizeGroups } from './array'
|
|
2
|
+
|
|
3
|
+
export const monthsList = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'] as const
|
|
4
|
+
export type Month = typeof monthsList[number]
|
|
5
|
+
|
|
6
|
+
export const weekDayList = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] as const
|
|
7
|
+
export type WeekDay = typeof weekDayList[number]
|
|
8
|
+
|
|
9
|
+
export const formatDate = (date: Date) => {
|
|
10
|
+
const year = date.getFullYear().toString().padStart(4, '0')
|
|
11
|
+
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
12
|
+
const day = (date.getDate()).toString().padStart(2, '0')
|
|
13
|
+
return `${year}-${month}-${day}`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const formatDateTime = (date: Date) => {
|
|
17
|
+
const dateString = formatDate(date)
|
|
18
|
+
const hours = date.getHours().toString().padStart(2, '0')
|
|
19
|
+
const minutes = date.getMinutes().toString().padStart(2, '0')
|
|
20
|
+
return `${dateString}T${hours}:${minutes}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const getDaysInMonth = (year: number, month: number): number => {
|
|
24
|
+
const lastDayOfMonth = new Date(year, month + 1, 0)
|
|
25
|
+
return lastDayOfMonth.getDate()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type Duration = {
|
|
29
|
+
years?: number,
|
|
30
|
+
months?: number,
|
|
31
|
+
days?: number,
|
|
32
|
+
hours?: number,
|
|
33
|
+
minutes?: number,
|
|
34
|
+
seconds?: number,
|
|
35
|
+
milliseconds?: number,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const changeDuration = (date: Date, duration: Duration, isAdding?: boolean): Date => {
|
|
39
|
+
const {
|
|
40
|
+
years = 0,
|
|
41
|
+
months = 0,
|
|
42
|
+
days = 0,
|
|
43
|
+
hours = 0,
|
|
44
|
+
minutes = 0,
|
|
45
|
+
seconds = 0,
|
|
46
|
+
milliseconds = 0,
|
|
47
|
+
} = duration
|
|
48
|
+
|
|
49
|
+
// Check ranges
|
|
50
|
+
if (years < 0) {
|
|
51
|
+
console.error(`Range error years must be greater than 0: received ${years}`)
|
|
52
|
+
return new Date(date)
|
|
53
|
+
}
|
|
54
|
+
if (months < 0 || months > 11) {
|
|
55
|
+
console.error(`Range error month must be 0 <= month <= 11: received ${months}`)
|
|
56
|
+
return new Date(date)
|
|
57
|
+
}
|
|
58
|
+
if (days < 0) {
|
|
59
|
+
console.error(`Range error days must be greater than 0: received ${days}`)
|
|
60
|
+
return new Date(date)
|
|
61
|
+
}
|
|
62
|
+
if (hours < 0 || hours > 23) {
|
|
63
|
+
console.error(`Range error hours must be 0 <= hours <= 23: received ${hours}`)
|
|
64
|
+
return new Date(date)
|
|
65
|
+
}
|
|
66
|
+
if (minutes < 0 || minutes > 59) {
|
|
67
|
+
console.error(`Range error minutes must be 0 <= minutes <= 59: received ${minutes}`)
|
|
68
|
+
return new Date(date)
|
|
69
|
+
}
|
|
70
|
+
if (seconds < 0 || seconds > 59) {
|
|
71
|
+
console.error(`Range error seconds must be 0 <= seconds <= 59: received ${seconds}`)
|
|
72
|
+
return new Date(date)
|
|
73
|
+
}
|
|
74
|
+
if (milliseconds < 0) {
|
|
75
|
+
console.error(`Range error seconds must be greater than 0: received ${milliseconds}`)
|
|
76
|
+
return new Date(date)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const multiplier = isAdding ? 1 : -1
|
|
80
|
+
|
|
81
|
+
const newDate = new Date(date)
|
|
82
|
+
|
|
83
|
+
newDate.setFullYear(newDate.getFullYear() + multiplier * years)
|
|
84
|
+
|
|
85
|
+
newDate.setMonth(newDate.getMonth() + multiplier * months)
|
|
86
|
+
|
|
87
|
+
newDate.setDate(newDate.getDate() + multiplier * days)
|
|
88
|
+
|
|
89
|
+
newDate.setHours(newDate.getHours() + multiplier * hours)
|
|
90
|
+
|
|
91
|
+
newDate.setMinutes(newDate.getMinutes() + multiplier * minutes)
|
|
92
|
+
|
|
93
|
+
newDate.setSeconds(newDate.getSeconds() + multiplier * seconds)
|
|
94
|
+
|
|
95
|
+
newDate.setMilliseconds(newDate.getMilliseconds() + multiplier * milliseconds)
|
|
96
|
+
|
|
97
|
+
return newDate
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const addDuration = (date: Date, duration: Duration): Date => {
|
|
101
|
+
return changeDuration(date, duration, true)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const subtractDuration = (date: Date, duration: Duration): Date => {
|
|
105
|
+
return changeDuration(date, duration, false)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const getBetweenDuration = (startDate: Date, endDate: Date): Duration => {
|
|
109
|
+
const durationInMilliseconds = endDate.getTime() - startDate.getTime()
|
|
110
|
+
|
|
111
|
+
const millisecondsInSecond = 1000
|
|
112
|
+
const millisecondsInMinute = 60 * millisecondsInSecond
|
|
113
|
+
const millisecondsInHour = 60 * millisecondsInMinute
|
|
114
|
+
const millisecondsInDay = 24 * millisecondsInHour
|
|
115
|
+
const millisecondsInMonth = 30 * millisecondsInDay // Rough estimation, can be adjusted
|
|
116
|
+
|
|
117
|
+
const years = Math.floor(durationInMilliseconds / (365.25 * millisecondsInDay))
|
|
118
|
+
const months = Math.floor(durationInMilliseconds / millisecondsInMonth)
|
|
119
|
+
const days = Math.floor(durationInMilliseconds / millisecondsInDay)
|
|
120
|
+
const hours = Math.floor((durationInMilliseconds % millisecondsInDay) / millisecondsInHour)
|
|
121
|
+
const seconds = Math.floor((durationInMilliseconds % millisecondsInHour) / millisecondsInSecond)
|
|
122
|
+
const milliseconds = durationInMilliseconds % millisecondsInSecond
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
years,
|
|
126
|
+
months,
|
|
127
|
+
days,
|
|
128
|
+
hours,
|
|
129
|
+
seconds,
|
|
130
|
+
milliseconds,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Checks if a given date is in the range of two dates
|
|
135
|
+
*
|
|
136
|
+
* An undefined value for startDate or endDate means no bound for the start or end respectively
|
|
137
|
+
*/
|
|
138
|
+
export const isInTimeSpan = (value: Date, startDate?: Date, endDate?: Date): boolean => {
|
|
139
|
+
if(startDate && endDate) {
|
|
140
|
+
console.assert(startDate <= endDate)
|
|
141
|
+
return startDate <= value && value <= endDate
|
|
142
|
+
} else if (startDate) {
|
|
143
|
+
return startDate <= value
|
|
144
|
+
} else if(endDate) {
|
|
145
|
+
return endDate >= value
|
|
146
|
+
} else {
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Compare two dates on the year, month, day */
|
|
152
|
+
export const equalDate = (date1: Date, date2: Date) => {
|
|
153
|
+
return date1.getFullYear() === date2.getFullYear()
|
|
154
|
+
&& date1.getMonth() === date2.getMonth()
|
|
155
|
+
&& date1.getDate() === date2.getDate()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const getWeeksForCalenderMonth = (date: Date, weekStart: WeekDay, weeks: number = 6) => {
|
|
159
|
+
const month = date.getMonth()
|
|
160
|
+
const year = date.getFullYear()
|
|
161
|
+
|
|
162
|
+
const dayList: Date[] = []
|
|
163
|
+
let currentDate = new Date(year, month, 1) // Start of month
|
|
164
|
+
const weekStartIndex = weekDayList.indexOf(weekStart)
|
|
165
|
+
|
|
166
|
+
// Move the current day to the week before
|
|
167
|
+
while (currentDate.getDay() !== weekStartIndex) {
|
|
168
|
+
currentDate = subtractDuration(currentDate, { days: 1 })
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
while (dayList.length < 7 * weeks) {
|
|
172
|
+
const date = new Date(currentDate)
|
|
173
|
+
date.setHours(date.getHours(), date.getMinutes()) // To make sure we are not overwriting the time
|
|
174
|
+
dayList.push(date)
|
|
175
|
+
currentDate = addDuration(currentDate, { days: 1 })
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// weeks
|
|
179
|
+
return equalSizeGroups(dayList, 7)
|
|
180
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { clamp } from './math'
|
|
2
|
+
|
|
3
|
+
export type EaseFunction = (t: number) => number
|
|
4
|
+
|
|
5
|
+
export class EaseFunctions {
|
|
6
|
+
static cubicBezierGeneric(x1: number, y1: number, x2: number, y2: number): { x: EaseFunction, y: EaseFunction } {
|
|
7
|
+
// Calculate the x and y coordinates using the cubic Bézier formula
|
|
8
|
+
const cx = 3 * x1
|
|
9
|
+
const bx = 3 * (x2 - x1) - cx
|
|
10
|
+
const ax = 1 - cx - bx
|
|
11
|
+
|
|
12
|
+
const cy = 3 * y1
|
|
13
|
+
const by = 3 * (y2 - y1) - cy
|
|
14
|
+
const ay = 1 - cy - by
|
|
15
|
+
|
|
16
|
+
// Compute x and y values at parameter t
|
|
17
|
+
const x = (t: number) => ((ax * t + bx) * t + cx) * t
|
|
18
|
+
const y = (t: number) => ((ay * t + by) * t + cy) * t
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
x,
|
|
22
|
+
y
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static cubicBezier(x1: number, y1: number, x2: number, y2: number): EaseFunction {
|
|
27
|
+
const { y } = EaseFunctions.cubicBezierGeneric(x1, y1, x2, y2)
|
|
28
|
+
return (t: number) => {
|
|
29
|
+
t = clamp(t)
|
|
30
|
+
return y(t) // <= equal to x(t) * 0 + y(t) * 1
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static easeInEaseOut(t: number): number {
|
|
35
|
+
return EaseFunctions.cubicBezier(0.65, 0, 0.35, 1)(t)
|
|
36
|
+
};
|
|
37
|
+
}
|