@genspectrum/dashboard-components 0.18.5 → 0.19.0
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/README.md +12 -2
- package/custom-elements.json +3 -3
- package/dist/assets/{mutationOverTimeWorker--b8ZHlji.js.map → mutationOverTimeWorker-ChQTFL68.js.map} +1 -1
- package/dist/components.d.ts +15 -14
- package/dist/components.js +1602 -332
- package/dist/components.js.map +1 -1
- package/dist/util.d.ts +14 -14
- package/package.json +3 -4
- package/src/preact/MutationAnnotationsContext.tsx +34 -27
- package/src/preact/components/dropdown.tsx +1 -1
- package/src/preact/components/info.tsx +1 -2
- package/src/preact/components/min-max-range-slider.tsx +0 -2
- package/src/preact/components/mutations-over-time-text-filter.stories.tsx +57 -0
- package/src/preact/components/mutations-over-time-text-filter.tsx +63 -0
- package/src/preact/components/segment-selector.tsx +1 -1
- package/src/preact/components/table.tsx +0 -2
- package/src/preact/dateRangeFilter/date-picker.tsx +15 -10
- package/src/preact/mutationFilter/mutation-filter.stories.tsx +169 -50
- package/src/preact/mutationFilter/mutation-filter.tsx +239 -234
- package/src/preact/mutationFilter/parseAndValidateMutation.ts +62 -10
- package/src/preact/mutationFilter/parseMutation.spec.ts +62 -47
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTime.spec.ts +128 -0
- package/src/preact/mutationsOverTime/getFilteredMutationsOverTimeData.ts +39 -2
- package/src/preact/mutationsOverTime/mutations-over-time-grid.tsx +8 -11
- package/src/preact/mutationsOverTime/mutations-over-time.stories.tsx +27 -0
- package/src/preact/mutationsOverTime/mutations-over-time.tsx +26 -5
- package/src/preact/shared/tanstackTable/pagination-context.tsx +30 -0
- package/src/preact/shared/tanstackTable/pagination.tsx +19 -6
- package/src/preact/shared/tanstackTable/tanstackTable.tsx +17 -3
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.stories.tsx +19 -1
- package/src/preact/wastewater/mutationsOverTime/wastewater-mutations-over-time.tsx +6 -1
- package/src/styles/replaceCssProperties.stories.tsx +49 -0
- package/src/styles/replaceCssProperties.ts +25 -0
- package/src/styles/tailwind.css +1 -0
- package/src/web-components/PreactLitAdapter.tsx +6 -3
- package/src/web-components/PreactLitAdapterWithGridJsStyles.tsx +0 -2
- package/src/web-components/gs-app.stories.ts +6 -2
- package/src/web-components/gs-app.ts +4 -1
- package/src/web-components/input/gs-date-range-filter.tsx +6 -0
- package/src/web-components/input/gs-mutation-filter.stories.ts +4 -4
- package/src/web-components/visualization/gs-prevalence-over-time.stories.ts +1 -1
- package/standalone-bundle/assets/mutationOverTimeWorker-jChgWnwp.js.map +1 -1
- package/standalone-bundle/dashboard-components.js +10836 -11289
- package/standalone-bundle/dashboard-components.js.map +1 -1
- package/dist/style.css +0 -392
- package/standalone-bundle/style.css +0 -1
package/dist/util.d.ts
CHANGED
|
@@ -918,7 +918,7 @@ declare global {
|
|
|
918
918
|
|
|
919
919
|
declare global {
|
|
920
920
|
interface HTMLElementTagNameMap {
|
|
921
|
-
'gs-
|
|
921
|
+
'gs-prevalence-over-time': PrevalenceOverTimeComponent;
|
|
922
922
|
}
|
|
923
923
|
}
|
|
924
924
|
|
|
@@ -926,7 +926,7 @@ declare global {
|
|
|
926
926
|
declare global {
|
|
927
927
|
namespace JSX {
|
|
928
928
|
interface IntrinsicElements {
|
|
929
|
-
'gs-
|
|
929
|
+
'gs-prevalence-over-time': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
930
930
|
}
|
|
931
931
|
}
|
|
932
932
|
}
|
|
@@ -934,7 +934,7 @@ declare global {
|
|
|
934
934
|
|
|
935
935
|
declare global {
|
|
936
936
|
interface HTMLElementTagNameMap {
|
|
937
|
-
'gs-
|
|
937
|
+
'gs-mutations-component': MutationsComponent;
|
|
938
938
|
}
|
|
939
939
|
}
|
|
940
940
|
|
|
@@ -942,7 +942,7 @@ declare global {
|
|
|
942
942
|
declare global {
|
|
943
943
|
namespace JSX {
|
|
944
944
|
interface IntrinsicElements {
|
|
945
|
-
'gs-
|
|
945
|
+
'gs-mutations-component': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
946
946
|
}
|
|
947
947
|
}
|
|
948
948
|
}
|
|
@@ -1062,11 +1062,10 @@ declare global {
|
|
|
1062
1062
|
|
|
1063
1063
|
declare global {
|
|
1064
1064
|
interface HTMLElementTagNameMap {
|
|
1065
|
-
'gs-
|
|
1065
|
+
'gs-location-filter': LocationFilterComponent;
|
|
1066
1066
|
}
|
|
1067
1067
|
interface HTMLElementEventMap {
|
|
1068
|
-
'gs-
|
|
1069
|
-
'gs-date-range-option-changed': DateRangeOptionChangedEvent;
|
|
1068
|
+
'gs-location-changed': LocationChangedEvent;
|
|
1070
1069
|
}
|
|
1071
1070
|
}
|
|
1072
1071
|
|
|
@@ -1074,7 +1073,7 @@ declare global {
|
|
|
1074
1073
|
declare global {
|
|
1075
1074
|
namespace JSX {
|
|
1076
1075
|
interface IntrinsicElements {
|
|
1077
|
-
'gs-
|
|
1076
|
+
'gs-location-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1078
1077
|
}
|
|
1079
1078
|
}
|
|
1080
1079
|
}
|
|
@@ -1082,10 +1081,10 @@ declare global {
|
|
|
1082
1081
|
|
|
1083
1082
|
declare global {
|
|
1084
1083
|
interface HTMLElementTagNameMap {
|
|
1085
|
-
'gs-
|
|
1084
|
+
'gs-text-filter': TextFilterComponent;
|
|
1086
1085
|
}
|
|
1087
1086
|
interface HTMLElementEventMap {
|
|
1088
|
-
'gs-
|
|
1087
|
+
'gs-text-filter-changed': TextFilterChangedEvent;
|
|
1089
1088
|
}
|
|
1090
1089
|
}
|
|
1091
1090
|
|
|
@@ -1093,7 +1092,7 @@ declare global {
|
|
|
1093
1092
|
declare global {
|
|
1094
1093
|
namespace JSX {
|
|
1095
1094
|
interface IntrinsicElements {
|
|
1096
|
-
'gs-
|
|
1095
|
+
'gs-text-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1097
1096
|
}
|
|
1098
1097
|
}
|
|
1099
1098
|
}
|
|
@@ -1101,10 +1100,11 @@ declare global {
|
|
|
1101
1100
|
|
|
1102
1101
|
declare global {
|
|
1103
1102
|
interface HTMLElementTagNameMap {
|
|
1104
|
-
'gs-
|
|
1103
|
+
'gs-date-range-filter': DateRangeFilterComponent;
|
|
1105
1104
|
}
|
|
1106
1105
|
interface HTMLElementEventMap {
|
|
1107
|
-
'gs-
|
|
1106
|
+
'gs-date-range-filter-changed': CustomEvent<Record<string, string>>;
|
|
1107
|
+
'gs-date-range-option-changed': DateRangeOptionChangedEvent;
|
|
1108
1108
|
}
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
@@ -1112,7 +1112,7 @@ declare global {
|
|
|
1112
1112
|
declare global {
|
|
1113
1113
|
namespace JSX {
|
|
1114
1114
|
interface IntrinsicElements {
|
|
1115
|
-
'gs-
|
|
1115
|
+
'gs-date-range-filter': DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
|
|
1116
1116
|
}
|
|
1117
1117
|
}
|
|
1118
1118
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genspectrum/dashboard-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "GenSpectrum web components for building dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
@@ -38,8 +38,7 @@
|
|
|
38
38
|
"require": "./dist/util.js"
|
|
39
39
|
},
|
|
40
40
|
"./custom-elements.json": "./custom-elements.json",
|
|
41
|
-
"./package.json": "./package.json"
|
|
42
|
-
"./style.css": "./dist/style.css"
|
|
41
|
+
"./package.json": "./package.json"
|
|
43
42
|
},
|
|
44
43
|
"files": [
|
|
45
44
|
"dist",
|
|
@@ -132,7 +131,7 @@
|
|
|
132
131
|
"eslint-config-preact": "^1.3.0",
|
|
133
132
|
"eslint-plugin-import": "^2.29.1",
|
|
134
133
|
"eslint-plugin-jest": "^28.2.0",
|
|
135
|
-
"eslint-plugin-storybook": "^0.
|
|
134
|
+
"eslint-plugin-storybook": "^0.12.0",
|
|
136
135
|
"happy-dom": "^17.1.1",
|
|
137
136
|
"http-server": "^14.1.1",
|
|
138
137
|
"lit-analyzer": "^2.0.3",
|
|
@@ -37,33 +37,7 @@ export const MutationAnnotationsContextProvider: FunctionalComponent<
|
|
|
37
37
|
return parseResult;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
const nucleotidePositions = new Map<string, MutationAnnotations>();
|
|
42
|
-
const aminoAcidMap = new Map<string, MutationAnnotations>();
|
|
43
|
-
const aminoAcidPositions = new Map<string, MutationAnnotations>();
|
|
44
|
-
|
|
45
|
-
value.forEach((annotation) => {
|
|
46
|
-
new Set(annotation.nucleotideMutations).forEach((code) => {
|
|
47
|
-
addAnnotationToMap(nucleotideMap, code, annotation);
|
|
48
|
-
});
|
|
49
|
-
new Set(annotation.aminoAcidMutations).forEach((code) => {
|
|
50
|
-
addAnnotationToMap(aminoAcidMap, code, annotation);
|
|
51
|
-
});
|
|
52
|
-
new Set(annotation.nucleotidePositions).forEach((position) => {
|
|
53
|
-
addAnnotationToMap(nucleotidePositions, position, annotation);
|
|
54
|
-
});
|
|
55
|
-
new Set(annotation.aminoAcidPositions).forEach((position) => {
|
|
56
|
-
addAnnotationToMap(aminoAcidPositions, position, annotation);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
success: true as const,
|
|
62
|
-
value: {
|
|
63
|
-
nucleotide: { mutation: nucleotideMap, position: nucleotidePositions },
|
|
64
|
-
'amino acid': { mutation: aminoAcidMap, position: aminoAcidPositions },
|
|
65
|
-
},
|
|
66
|
-
};
|
|
40
|
+
return { success: true as const, value: getMutationAnnotationsContext(value) };
|
|
67
41
|
}, [value]);
|
|
68
42
|
|
|
69
43
|
if (!parseResult.success) {
|
|
@@ -79,6 +53,33 @@ export const MutationAnnotationsContextProvider: FunctionalComponent<
|
|
|
79
53
|
);
|
|
80
54
|
};
|
|
81
55
|
|
|
56
|
+
export function getMutationAnnotationsContext(value: MutationAnnotations) {
|
|
57
|
+
const nucleotideMap = new Map<string, MutationAnnotations>();
|
|
58
|
+
const nucleotidePositions = new Map<string, MutationAnnotations>();
|
|
59
|
+
const aminoAcidMap = new Map<string, MutationAnnotations>();
|
|
60
|
+
const aminoAcidPositions = new Map<string, MutationAnnotations>();
|
|
61
|
+
|
|
62
|
+
value.forEach((annotation) => {
|
|
63
|
+
new Set(annotation.nucleotideMutations).forEach((code) => {
|
|
64
|
+
addAnnotationToMap(nucleotideMap, code, annotation);
|
|
65
|
+
});
|
|
66
|
+
new Set(annotation.aminoAcidMutations).forEach((code) => {
|
|
67
|
+
addAnnotationToMap(aminoAcidMap, code, annotation);
|
|
68
|
+
});
|
|
69
|
+
new Set(annotation.nucleotidePositions).forEach((position) => {
|
|
70
|
+
addAnnotationToMap(nucleotidePositions, position, annotation);
|
|
71
|
+
});
|
|
72
|
+
new Set(annotation.aminoAcidPositions).forEach((position) => {
|
|
73
|
+
addAnnotationToMap(aminoAcidPositions, position, annotation);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
nucleotide: { mutation: nucleotideMap, position: nucleotidePositions },
|
|
79
|
+
'amino acid': { mutation: aminoAcidMap, position: aminoAcidPositions },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
82
83
|
function addAnnotationToMap(map: Map<string, MutationAnnotations>, code: string, annotation: MutationAnnotation) {
|
|
83
84
|
const oldAnnotations = map.get(code.toUpperCase()) ?? [];
|
|
84
85
|
map.set(code.toUpperCase(), [...oldAnnotations, annotation]);
|
|
@@ -87,6 +88,12 @@ function addAnnotationToMap(map: Map<string, MutationAnnotations>, code: string,
|
|
|
87
88
|
export function useMutationAnnotationsProvider() {
|
|
88
89
|
const mutationAnnotations = useContext(MutationAnnotationsContext);
|
|
89
90
|
|
|
91
|
+
return getMutationAnnotationsProvider(mutationAnnotations);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getMutationAnnotationsProvider(
|
|
95
|
+
mutationAnnotations: Record<SequenceType, MutationAnnotationPerSequenceType>,
|
|
96
|
+
) {
|
|
90
97
|
return (mutation: Mutation, sequenceType: SequenceType) => {
|
|
91
98
|
const position =
|
|
92
99
|
mutation.segment === undefined
|
|
@@ -30,7 +30,7 @@ export const Dropdown: FunctionComponent<DropdownProps> = ({ children, buttonTit
|
|
|
30
30
|
return (
|
|
31
31
|
<>
|
|
32
32
|
<button type='button' className='btn btn-xs whitespace-nowrap w-full' onClick={toggle} ref={referenceRef}>
|
|
33
|
-
{buttonTitle}
|
|
33
|
+
<span className={'w-full truncate'}>{buttonTitle}</span>
|
|
34
34
|
</button>
|
|
35
35
|
<div ref={floatingRef} className={`${dropdownClass} ${showContent ? '' : 'hidden'}`}>
|
|
36
36
|
{children}
|
|
@@ -21,7 +21,7 @@ export const InfoHeadline2: FunctionComponent = ({ children }) => {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export const InfoParagraph: FunctionComponent = ({ children }) => {
|
|
24
|
-
return <p className='text-justify text-base font-normal my-1'>{children}</p>;
|
|
24
|
+
return <p className='text-justify text-base font-normal my-1 text-wrap'>{children}</p>;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
export const InfoLink: FunctionComponent<{ href: string }> = ({ children, href }) => {
|
|
@@ -103,7 +103,6 @@ function generateFullExampleCode(componentCode: string, componentName: string) {
|
|
|
103
103
|
return `<html>
|
|
104
104
|
<head>
|
|
105
105
|
<script type="module" src="https://unpkg.com/@genspectrum/dashboard-components@latest/standalone-bundle/dashboard-components.js"></script>
|
|
106
|
-
<link rel="stylesheet" href="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/style.css" />
|
|
107
106
|
</head>
|
|
108
107
|
|
|
109
108
|
<body>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type StoryObj } from '@storybook/preact';
|
|
2
|
+
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
|
3
|
+
import { type Meta } from '@storybook/web-components';
|
|
4
|
+
import { useState } from 'preact/hooks';
|
|
5
|
+
|
|
6
|
+
import { MutationsOverTimeTextFilter, type TextFilterProps } from './mutations-over-time-text-filter';
|
|
7
|
+
|
|
8
|
+
const meta: Meta = {
|
|
9
|
+
title: 'Component/Mutations over time text filter',
|
|
10
|
+
component: 'MutationsOverTimeTextFilter',
|
|
11
|
+
parameters: { fetchMock: {} },
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
|
|
16
|
+
const WrapperWithState = ({ setFilterValue, value }: { setFilterValue: (value: string) => void; value: string }) => {
|
|
17
|
+
const [state, setState] = useState(value);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<MutationsOverTimeTextFilter
|
|
21
|
+
setFilterValue={(value) => {
|
|
22
|
+
setFilterValue(value);
|
|
23
|
+
setState(value);
|
|
24
|
+
}}
|
|
25
|
+
value={state}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const MutationsOverTimeTextFilterStory: StoryObj<TextFilterProps> = {
|
|
31
|
+
render: (args) => {
|
|
32
|
+
return <WrapperWithState setFilterValue={args.setFilterValue} value={args.value} />;
|
|
33
|
+
},
|
|
34
|
+
args: {
|
|
35
|
+
setFilterValue: fn(),
|
|
36
|
+
value: 'Test',
|
|
37
|
+
},
|
|
38
|
+
play: async ({ canvasElement, step }) => {
|
|
39
|
+
const canvas = within(canvasElement);
|
|
40
|
+
|
|
41
|
+
await step('Expect initial value to show on the button', async () => {
|
|
42
|
+
const button = canvas.getByRole('button');
|
|
43
|
+
await expect(button).toHaveTextContent('Test');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await step('Change filter and expect it to show on the button', async () => {
|
|
47
|
+
const button = canvas.getByRole('button');
|
|
48
|
+
await userEvent.click(button);
|
|
49
|
+
|
|
50
|
+
const inputField = canvas.getByRole('textbox');
|
|
51
|
+
await userEvent.clear(inputField);
|
|
52
|
+
await userEvent.type(inputField, 'OtherText');
|
|
53
|
+
|
|
54
|
+
await waitFor(() => expect(button).toHaveTextContent('OtherText'));
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { h } from 'preact';
|
|
2
|
+
import { useCallback, useEffect, useState } from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
import { Dropdown } from './dropdown';
|
|
5
|
+
import { DeleteIcon } from '../shared/icons/DeleteIcon';
|
|
6
|
+
|
|
7
|
+
export type TextFilterProps = { setFilterValue: (newValue: string) => void; value: string };
|
|
8
|
+
|
|
9
|
+
export function MutationsOverTimeTextFilter({ setFilterValue, value }: TextFilterProps) {
|
|
10
|
+
const onInput = (newValue: string) => {
|
|
11
|
+
setFilterValue(newValue);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const onDeleteClick = () => setFilterValue('');
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className={'w-28 inline-flex'}>
|
|
18
|
+
<Dropdown buttonTitle={value === '' ? `Filter mutations` : value} placement={'bottom-start'}>
|
|
19
|
+
<div>
|
|
20
|
+
<label className='flex gap-1 input input-xs'>
|
|
21
|
+
<DebouncedInput placeholder={'Filter'} onInput={onInput} value={value} type='text' />
|
|
22
|
+
{value !== undefined && value !== '' && (
|
|
23
|
+
<button className={'cursor-pointer'} onClick={onDeleteClick}>
|
|
24
|
+
<DeleteIcon />
|
|
25
|
+
</button>
|
|
26
|
+
)}
|
|
27
|
+
</label>
|
|
28
|
+
</div>
|
|
29
|
+
</Dropdown>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function DebouncedInput({
|
|
35
|
+
value: initialValue,
|
|
36
|
+
onInput,
|
|
37
|
+
debounce = 500,
|
|
38
|
+
...props
|
|
39
|
+
}: {
|
|
40
|
+
onInput: (value: string) => void;
|
|
41
|
+
debounce?: number;
|
|
42
|
+
value?: string;
|
|
43
|
+
} & Omit<h.JSX.IntrinsicElements['input'], 'onInput'>) {
|
|
44
|
+
const [value, setValue] = useState<string | undefined>(initialValue);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
setValue(initialValue);
|
|
48
|
+
}, [initialValue]);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const timeout = setTimeout(() => {
|
|
52
|
+
onInput(value ?? '');
|
|
53
|
+
}, debounce);
|
|
54
|
+
|
|
55
|
+
return () => clearTimeout(timeout);
|
|
56
|
+
}, [value, debounce, onInput]);
|
|
57
|
+
|
|
58
|
+
const onChangeInput = useCallback((event: h.JSX.TargetedEvent<HTMLInputElement>) => {
|
|
59
|
+
setValue(event.currentTarget.value);
|
|
60
|
+
}, []);
|
|
61
|
+
|
|
62
|
+
return <input {...props} value={value} onInput={onChangeInput} />;
|
|
63
|
+
}
|
|
@@ -26,7 +26,7 @@ export const SegmentSelector: FunctionComponent<SegmentSelectorProps> = ({
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
|
-
<div className='w-
|
|
29
|
+
<div className='w-24 inline-flex'>
|
|
30
30
|
<CheckboxSelector
|
|
31
31
|
items={displayedSegments}
|
|
32
32
|
label={getSegmentSelectorLabel(displayedSegments, sequenceType)}
|
|
@@ -3,8 +3,6 @@ import { type OneDArray, type TColumn, type TData } from 'gridjs/dist/src/types'
|
|
|
3
3
|
import { type ComponentChild } from 'preact';
|
|
4
4
|
import { useEffect, useRef } from 'preact/hooks';
|
|
5
5
|
|
|
6
|
-
import 'gridjs/dist/theme/mermaid.css';
|
|
7
|
-
|
|
8
6
|
export const tableStyle = {
|
|
9
7
|
table: {
|
|
10
8
|
fontSize: '12px',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'flatpickr/dist/flatpickr.min.css';
|
|
2
1
|
import flatpickr from 'flatpickr';
|
|
3
2
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
4
3
|
|
|
@@ -22,8 +21,10 @@ export function DatePicker({
|
|
|
22
21
|
|
|
23
22
|
const [datePicker, setDatePicker] = useState<flatpickr.Instance | null>(null);
|
|
24
23
|
|
|
24
|
+
const calendarRef = useRef<HTMLDivElement>(null);
|
|
25
|
+
|
|
25
26
|
useEffect(() => {
|
|
26
|
-
if (!inputRef.current) {
|
|
27
|
+
if (!inputRef.current || !calendarRef.current) {
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -33,6 +34,7 @@ export function DatePicker({
|
|
|
33
34
|
defaultDate: value,
|
|
34
35
|
minDate,
|
|
35
36
|
maxDate,
|
|
37
|
+
appendTo: calendarRef.current,
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
setDatePicker(instance);
|
|
@@ -54,13 +56,16 @@ export function DatePicker({
|
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
return (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
<div className={'w-full'}>
|
|
60
|
+
<input
|
|
61
|
+
className={`input w-full ${className}`}
|
|
62
|
+
type='text'
|
|
63
|
+
placeholder={placeholderText}
|
|
64
|
+
ref={inputRef}
|
|
65
|
+
onChange={handleChange}
|
|
66
|
+
onBlur={handleChange}
|
|
67
|
+
/>
|
|
68
|
+
<div ref={calendarRef} />
|
|
69
|
+
</div>
|
|
65
70
|
);
|
|
66
71
|
}
|