@overmap-ai/forms 1.0.15 → 1.0.17-master.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/.husky/pre-commit +6 -0
- package/.prettierrc.json +10 -0
- package/.storybook/StoryDecorator.tsx +22 -0
- package/.storybook/main.ts +20 -0
- package/.storybook/palettes/green.css +66 -0
- package/.storybook/palettes/red.css +66 -0
- package/.storybook/preview.css +39 -0
- package/.storybook/preview.tsx +31 -0
- package/.storybook/tailwind-theme/accentPalette.css +181 -0
- package/.storybook/tailwind-theme/backgrounds.css +11 -0
- package/.storybook/tailwind-theme/basePalette.css +178 -0
- package/dev/publish-alpha.sh +13 -0
- package/dev/publish-patch.sh +3 -0
- package/dist/ColorPicker/ColorPicker.d.ts +10 -0
- package/dist/ColorPicker/index.d.ts +1 -0
- package/dist/FileBadge/FileBadge.d.ts +7 -0
- package/dist/FileBadge/index.d.ts +1 -0
- package/dist/FileCard/FileCard.d.ts +8 -0
- package/dist/FileCard/index.d.ts +1 -0
- package/dist/FileIcon/FileIcon.d.ts +4 -0
- package/dist/FileIcon/index.d.ts +1 -0
- package/dist/FileViewer/FileViewerProvider.d.ts +2 -0
- package/dist/FileViewer/context.d.ts +4 -0
- package/dist/FileViewer/index.d.ts +3 -0
- package/dist/FileViewer/typings.d.ts +5 -0
- package/dist/ImageCard/ImageCard.d.ts +9 -0
- package/dist/ImageCard/index.d.ts +1 -0
- package/dist/ImageMarkup/ImageMarkup.d.ts +14 -0
- package/dist/ImageMarkup/index.d.ts +1 -0
- package/dist/ImageViewer/ImageViewer.d.ts +7 -0
- package/dist/ImageViewer/constants.d.ts +1 -0
- package/dist/ImageViewer/index.d.ts +2 -0
- package/dist/PDFViewer/PDFViewer.d.ts +7 -0
- package/dist/PDFViewer/constants.d.ts +1 -0
- package/dist/PDFViewer/index.d.ts +2 -0
- package/dist/SpreadsheetViewer/SpreadsheetViewer.d.ts +7 -0
- package/dist/SpreadsheetViewer/constants.d.ts +1 -0
- package/dist/SpreadsheetViewer/index.d.ts +2 -0
- package/dist/{builder → forms/builder}/DropDispatch.d.ts +2 -2
- package/dist/forms/builder/FieldActions.d.ts +13 -0
- package/dist/forms/builder/FieldBuilder.d.ts +10 -0
- package/dist/forms/builder/FieldSectionWithActions.d.ts +10 -0
- package/dist/forms/builder/FieldWithActions.d.ts +9 -0
- package/dist/forms/builder/FieldsEditor.d.ts +5 -0
- package/dist/forms/builder/FormBuilder.d.ts +25 -0
- package/dist/forms/builder/constants.d.ts +18 -0
- package/dist/forms/builder/hooks.d.ts +7 -0
- package/dist/forms/builder/index.d.ts +2 -0
- package/dist/{builder → forms/builder}/typings.d.ts +2 -1
- package/dist/forms/builder/utils.d.ts +23 -0
- package/dist/forms/constants.d.ts +3 -0
- package/dist/forms/constantsJsx.d.ts +9 -0
- package/dist/{fields → forms/fields}/BaseField/BaseField.d.ts +23 -10
- package/dist/forms/fields/BaseField/hooks.d.ts +388 -0
- package/dist/forms/fields/BaseField/index.d.ts +4 -0
- package/dist/{fields → forms/fields}/BaseField/layouts.d.ts +11 -5
- package/dist/{fields → forms/fields}/BaseField/typings.d.ts +2 -2
- package/dist/{fields → forms/fields}/BooleanField/BooleanField.d.ts +12 -6
- package/dist/forms/fields/BooleanField/BooleanInput.d.ts +3 -0
- package/dist/forms/fields/BooleanField/index.d.ts +2 -0
- package/dist/{fields → forms/fields}/CustomField/CustomField.d.ts +12 -6
- package/dist/{fields → forms/fields}/CustomField/FieldInputClonerField/FieldInputCloner.d.ts +2 -3
- package/dist/{fields → forms/fields}/CustomField/FieldInputClonerField/FieldInputClonerField.d.ts +3 -3
- package/dist/forms/fields/CustomField/FieldInputClonerField/index.d.ts +3 -0
- package/dist/forms/fields/CustomField/FieldInputClonerField/typings.d.ts +5 -0
- package/dist/forms/fields/CustomField/index.d.ts +1 -0
- package/dist/forms/fields/DateField/DateField.d.ts +22 -0
- package/dist/forms/fields/DateField/DateInput.d.ts +3 -0
- package/dist/forms/fields/DateField/index.d.ts +2 -0
- package/dist/{fields → forms/fields}/FieldSection/FieldSection.d.ts +13 -9
- package/dist/forms/fields/FieldSection/FieldSectionLayout.d.ts +6 -0
- package/dist/forms/fields/FieldSection/index.d.ts +1 -0
- package/dist/forms/fields/MultiStringField/MultiStringField.d.ts +40 -0
- package/dist/forms/fields/MultiStringField/MultiStringInput.d.ts +7 -0
- package/dist/forms/fields/MultiStringField/index.d.ts +2 -0
- package/dist/{fields → forms/fields}/NumberField/NumberField.d.ts +27 -10
- package/dist/forms/fields/NumberField/NumberInput.d.ts +3 -0
- package/dist/forms/fields/NumberField/index.d.ts +2 -0
- package/dist/forms/fields/QrField/QrField.d.ts +21 -0
- package/dist/forms/fields/QrField/QrInput.d.ts +9 -0
- package/dist/forms/fields/QrField/index.d.ts +2 -0
- package/dist/{fields → forms/fields}/SelectField/BaseSelectField.d.ts +12 -5
- package/dist/{fields → forms/fields}/SelectField/MultiSelectField.d.ts +13 -6
- package/dist/forms/fields/SelectField/MultiSelectInput.d.ts +3 -0
- package/dist/{fields → forms/fields}/SelectField/SelectField.d.ts +14 -7
- package/dist/forms/fields/SelectField/SelectInput.d.ts +3 -0
- package/dist/forms/fields/SelectField/index.d.ts +4 -0
- package/dist/forms/fields/StringOrTextFields/StringField/StringField.d.ts +26 -0
- package/dist/forms/fields/StringOrTextFields/StringField/StringInput.d.ts +3 -0
- package/dist/forms/fields/StringOrTextFields/StringField/index.d.ts +2 -0
- package/dist/{fields → forms/fields}/StringOrTextFields/StringOrTextField.d.ts +13 -8
- package/dist/forms/fields/StringOrTextFields/TextField/TextField.d.ts +22 -0
- package/dist/forms/fields/StringOrTextFields/TextField/TextInput.d.ts +3 -0
- package/dist/forms/fields/StringOrTextFields/TextField/index.d.ts +2 -0
- package/dist/forms/fields/StringOrTextFields/index.d.ts +2 -0
- package/dist/{fields → forms/fields}/UploadField/UploadField.d.ts +24 -9
- package/dist/forms/fields/UploadField/UploadInput.d.ts +3 -0
- package/dist/forms/fields/UploadField/index.d.ts +2 -0
- package/dist/forms/fields/constants.d.ts +106 -0
- package/dist/forms/fields/hooks.d.ts +6 -0
- package/dist/forms/fields/index.d.ts +12 -0
- package/dist/{fields → forms/fields}/typings.d.ts +9 -6
- package/dist/{fields → forms/fields}/utils.d.ts +7 -3
- package/dist/forms/index.d.ts +5 -0
- package/dist/{renderer → forms/renderer}/FormRenderer/FormRenderer.d.ts +4 -3
- package/dist/{renderer → forms/renderer}/PatchForm/Field.d.ts +5 -3
- package/dist/{renderer → forms/renderer}/PatchForm/Provider.d.ts +8 -4
- package/dist/forms/renderer/PatchForm/index.d.ts +2 -0
- package/dist/forms/renderer/index.d.ts +2 -0
- package/dist/forms/typings.d.ts +105 -0
- package/dist/forms/utils.d.ts +7 -0
- package/dist/forms.js +4450 -2478
- package/dist/forms.umd.cjs +44 -2777
- package/dist/index.d.ts +11 -3
- package/eslint.config.js +56 -0
- package/package.json +96 -94
- package/src/ColorPicker/ColorPicker.tsx +47 -0
- package/src/ColorPicker/index.ts +1 -0
- package/src/FileBadge/FileBadge.tsx +27 -0
- package/src/FileBadge/index.ts +1 -0
- package/src/FileCard/FileCard.stories.tsx +69 -0
- package/src/FileCard/FileCard.tsx +53 -0
- package/src/FileCard/index.ts +1 -0
- package/src/FileIcon/FileIcon.tsx +31 -0
- package/src/FileIcon/index.ts +1 -0
- package/src/FileViewer/FileViewerProvider.stories.tsx +50 -0
- package/src/FileViewer/FileViewerProvider.tsx +72 -0
- package/src/FileViewer/context.ts +11 -0
- package/src/FileViewer/index.ts +3 -0
- package/src/FileViewer/typings.ts +5 -0
- package/src/ImageCard/ImageCard.stories.tsx +94 -0
- package/src/ImageCard/ImageCard.tsx +82 -0
- package/src/ImageCard/index.ts +1 -0
- package/src/ImageMarkup/ImageMarkup.stories.tsx +65 -0
- package/src/ImageMarkup/ImageMarkup.tsx +268 -0
- package/src/ImageMarkup/index.ts +1 -0
- package/src/ImageViewer/ImageViewer.stories.tsx +57 -0
- package/src/ImageViewer/ImageViewer.tsx +124 -0
- package/src/ImageViewer/constants.ts +1 -0
- package/src/ImageViewer/index.ts +2 -0
- package/src/PDFViewer/PDFViewer.stories.tsx +55 -0
- package/src/PDFViewer/PDFViewer.tsx +170 -0
- package/src/PDFViewer/constants.ts +1 -0
- package/src/PDFViewer/index.ts +2 -0
- package/src/SpreadsheetViewer/SpreadsheetViewer.stories.tsx +55 -0
- package/src/SpreadsheetViewer/SpreadsheetViewer.tsx +162 -0
- package/src/SpreadsheetViewer/constants.ts +8 -0
- package/src/SpreadsheetViewer/index.ts +2 -0
- package/src/forms/builder/DropDispatch.ts +84 -0
- package/src/forms/builder/FieldActions.tsx +155 -0
- package/src/forms/builder/FieldBuilder.tsx +386 -0
- package/src/forms/builder/FieldSectionWithActions.tsx +260 -0
- package/src/forms/builder/FieldWithActions.tsx +129 -0
- package/src/forms/builder/FieldsEditor.tsx +180 -0
- package/src/forms/builder/FormBuilder.stories.tsx +105 -0
- package/src/forms/builder/FormBuilder.tsx +237 -0
- package/src/forms/builder/constants.ts +18 -0
- package/src/forms/builder/hooks.tsx +24 -0
- package/src/forms/builder/index.ts +2 -0
- package/src/forms/builder/typings.ts +18 -0
- package/src/forms/builder/utils.ts +229 -0
- package/src/forms/constants.ts +9 -0
- package/src/forms/constantsJsx.tsx +67 -0
- package/src/forms/fields/BaseField/BaseField.ts +152 -0
- package/src/forms/fields/BaseField/hooks.tsx +60 -0
- package/src/forms/fields/BaseField/index.ts +4 -0
- package/src/forms/fields/BaseField/layouts.tsx +100 -0
- package/src/forms/fields/BaseField/typings.ts +9 -0
- package/src/forms/fields/BooleanField/BooleanField.tsx +48 -0
- package/src/forms/fields/BooleanField/BooleanInput.tsx +54 -0
- package/src/forms/fields/BooleanField/index.ts +2 -0
- package/src/forms/fields/CustomField/CustomField.tsx +45 -0
- package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputCloner.tsx +25 -0
- package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputClonerField.tsx +26 -0
- package/src/forms/fields/CustomField/FieldInputClonerField/index.ts +3 -0
- package/src/forms/fields/CustomField/FieldInputClonerField/typings.ts +8 -0
- package/src/forms/fields/CustomField/index.ts +1 -0
- package/src/forms/fields/DateField/DateField.tsx +42 -0
- package/src/forms/fields/DateField/DateInput.tsx +39 -0
- package/src/forms/fields/DateField/index.ts +2 -0
- package/src/forms/fields/FieldSection/FieldSection.tsx +173 -0
- package/src/forms/fields/FieldSection/FieldSectionLayout.tsx +56 -0
- package/src/forms/fields/FieldSection/index.ts +1 -0
- package/src/forms/fields/MultiStringField/MultiStringField.tsx +90 -0
- package/src/forms/fields/MultiStringField/MultiStringInput.tsx +207 -0
- package/src/forms/fields/MultiStringField/index.ts +2 -0
- package/src/forms/fields/NumberField/NumberField.tsx +173 -0
- package/src/forms/fields/NumberField/NumberInput.tsx +44 -0
- package/src/forms/fields/NumberField/index.ts +2 -0
- package/src/forms/fields/QrField/QrField.tsx +38 -0
- package/src/forms/fields/QrField/QrInput.module.sass +5 -0
- package/src/forms/fields/QrField/QrInput.tsx +144 -0
- package/src/forms/fields/QrField/index.ts +2 -0
- package/src/forms/fields/SelectField/BaseSelectField.ts +73 -0
- package/src/forms/fields/SelectField/MultiSelectField.tsx +53 -0
- package/src/forms/fields/SelectField/MultiSelectInput.tsx +80 -0
- package/src/forms/fields/SelectField/SelectField.tsx +49 -0
- package/src/forms/fields/SelectField/SelectInput.tsx +69 -0
- package/src/forms/fields/SelectField/index.ts +4 -0
- package/src/forms/fields/StringOrTextFields/StringField/StringField.tsx +61 -0
- package/src/forms/fields/StringOrTextFields/StringField/StringInput.tsx +41 -0
- package/src/forms/fields/StringOrTextFields/StringField/index.ts +2 -0
- package/src/forms/fields/StringOrTextFields/StringOrTextField.ts +143 -0
- package/src/forms/fields/StringOrTextFields/TextField/TextField.tsx +52 -0
- package/src/forms/fields/StringOrTextFields/TextField/TextInput.tsx +42 -0
- package/src/forms/fields/StringOrTextFields/TextField/index.ts +2 -0
- package/src/forms/fields/StringOrTextFields/index.ts +2 -0
- package/src/forms/fields/UploadField/UploadField.tsx +156 -0
- package/src/forms/fields/UploadField/UploadInput.tsx +220 -0
- package/src/forms/fields/UploadField/index.ts +2 -0
- package/src/forms/fields/UploadField/utils.ts +17 -0
- package/src/forms/fields/constants.ts +43 -0
- package/src/forms/fields/hooks.tsx +26 -0
- package/src/forms/fields/index.ts +12 -0
- package/src/forms/fields/typings.ts +45 -0
- package/src/forms/fields/utils.ts +125 -0
- package/src/forms/index.ts +5 -0
- package/src/forms/renderer/FormRenderer/FormRenderer.stories.tsx +142 -0
- package/src/forms/renderer/FormRenderer/FormRenderer.tsx +135 -0
- package/src/forms/renderer/PatchForm/Field.tsx +41 -0
- package/src/forms/renderer/PatchForm/PatchForm.stories.tsx +91 -0
- package/src/forms/renderer/PatchForm/Provider.tsx +119 -0
- package/src/forms/renderer/PatchForm/index.ts +2 -0
- package/src/forms/renderer/index.ts +2 -0
- package/src/forms/typings.ts +162 -0
- package/src/forms/utils.ts +69 -0
- package/src/index.ts +11 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.ts +8 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +23 -0
- package/README.md +0 -12
- package/dist/builder/FieldActions.d.ts +0 -12
- package/dist/builder/FieldBuilder.d.ts +0 -24
- package/dist/builder/FieldSectionWithActions.d.ts +0 -10
- package/dist/builder/FieldWithActions.d.ts +0 -11
- package/dist/builder/FieldsEditor.d.ts +0 -2
- package/dist/builder/FormBuilder.d.ts +0 -15
- package/dist/builder/constants.d.ts +0 -1
- package/dist/builder/index.d.ts +0 -2
- package/dist/builder/utils.d.ts +0 -13
- package/dist/fields/BaseField/hooks.d.ts +0 -374
- package/dist/fields/BaseField/index.d.ts +0 -4
- package/dist/fields/BooleanField/BooleanInput.d.ts +0 -4
- package/dist/fields/BooleanField/index.d.ts +0 -2
- package/dist/fields/CustomField/FieldInputClonerField/index.d.ts +0 -3
- package/dist/fields/CustomField/FieldInputClonerField/typings.d.ts +0 -5
- package/dist/fields/CustomField/index.d.ts +0 -1
- package/dist/fields/DateField/DateField.d.ts +0 -16
- package/dist/fields/DateField/DateInput.d.ts +0 -4
- package/dist/fields/DateField/index.d.ts +0 -2
- package/dist/fields/FieldSection/FieldSectionLayout.d.ts +0 -7
- package/dist/fields/FieldSection/index.d.ts +0 -1
- package/dist/fields/MultiStringField/MultiStringField.d.ts +0 -30
- package/dist/fields/MultiStringField/MultiStringInput.d.ts +0 -8
- package/dist/fields/MultiStringField/index.d.ts +0 -2
- package/dist/fields/NumberField/NumberInput.d.ts +0 -4
- package/dist/fields/NumberField/index.d.ts +0 -2
- package/dist/fields/SelectField/MultiSelectInput.d.ts +0 -4
- package/dist/fields/SelectField/SelectInput.d.ts +0 -4
- package/dist/fields/SelectField/index.d.ts +0 -4
- package/dist/fields/StringOrTextFields/StringField/StringField.d.ts +0 -19
- package/dist/fields/StringOrTextFields/StringField/StringInput.d.ts +0 -4
- package/dist/fields/StringOrTextFields/StringField/index.d.ts +0 -2
- package/dist/fields/StringOrTextFields/TextField/TextField.d.ts +0 -16
- package/dist/fields/StringOrTextFields/TextField/TextInput.d.ts +0 -4
- package/dist/fields/StringOrTextFields/TextField/index.d.ts +0 -2
- package/dist/fields/StringOrTextFields/index.d.ts +0 -2
- package/dist/fields/UploadField/UploadInput.d.ts +0 -4
- package/dist/fields/UploadField/index.d.ts +0 -2
- package/dist/fields/constants.d.ts +0 -20
- package/dist/fields/hooks.d.ts +0 -6
- package/dist/fields/index.d.ts +0 -11
- package/dist/forms.js.map +0 -1
- package/dist/forms.umd.cjs.map +0 -1
- package/dist/renderer/FormBrowser/FormBrowser.d.ts +0 -11
- package/dist/renderer/FormSubmissionBrowser/FormSubmissionBrowser.d.ts +0 -28
- package/dist/renderer/FormSubmissionViewer/FormSubmissionViewer.d.ts +0 -17
- package/dist/renderer/PatchForm/index.d.ts +0 -2
- package/dist/renderer/index.d.ts +0 -5
- package/dist/style.css +0 -34
- package/dist/typings.d.ts +0 -17
- package/dist/utils.d.ts +0 -7
- /package/dist/{fields → forms/fields}/UploadField/utils.d.ts +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { ISerializedOnlyField } from "@overmap-ai/core"
|
|
2
|
+
import { ChangeEvent, ReactNode } from "react"
|
|
3
|
+
|
|
4
|
+
import { FormikUserFormRevision } from "../../builder"
|
|
5
|
+
import {
|
|
6
|
+
BaseSerializedField,
|
|
7
|
+
BaseSerializedObject,
|
|
8
|
+
FieldTypeIdentifier,
|
|
9
|
+
FieldValue,
|
|
10
|
+
Form,
|
|
11
|
+
ISerializedField,
|
|
12
|
+
} from "../../typings"
|
|
13
|
+
import { FieldSection } from "../FieldSection"
|
|
14
|
+
import { AnyField, GetInputProps, InputFieldLevelValidator, InputFormLevelValidator } from "../typings"
|
|
15
|
+
import { FieldOptions } from "./typings"
|
|
16
|
+
|
|
17
|
+
// TODO: These types redefine ISerializedField and related types.
|
|
18
|
+
// Looks like they can be merged.
|
|
19
|
+
|
|
20
|
+
// TODO: "Element" implies an instantiated component in React.
|
|
21
|
+
export abstract class BaseFormElement<TIdentifier extends FieldTypeIdentifier = FieldTypeIdentifier> {
|
|
22
|
+
public readonly type: TIdentifier
|
|
23
|
+
readonly identifier: string
|
|
24
|
+
public readonly description: string | null
|
|
25
|
+
|
|
26
|
+
protected constructor(options: BaseSerializedObject) {
|
|
27
|
+
const { description = null, identifier, type } = options
|
|
28
|
+
this.identifier = identifier
|
|
29
|
+
this.description = description
|
|
30
|
+
this.type = type as TIdentifier
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getId(): string {
|
|
34
|
+
return this.identifier
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
abstract getInput(props: GetInputProps<this>): ReactNode
|
|
38
|
+
|
|
39
|
+
static deserialize(_data: ISerializedField): AnyField | FieldSection {
|
|
40
|
+
throw new Error(`${this.name} must implement deserialize.`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected _serialize(): BaseSerializedObject<TIdentifier> {
|
|
44
|
+
if (!this.identifier) {
|
|
45
|
+
throw new Error("Field identifier must be set before serializing.")
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
type: this.type,
|
|
49
|
+
identifier: this.identifier,
|
|
50
|
+
description: this.description,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const emptyBaseField = {
|
|
56
|
+
label: "",
|
|
57
|
+
description: "",
|
|
58
|
+
required: false,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface FieldCreationSchemaObject {
|
|
62
|
+
field: AnyField
|
|
63
|
+
showDirectly: boolean
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export abstract class BaseField<
|
|
67
|
+
TValue extends FieldValue,
|
|
68
|
+
TIdentifier extends FieldTypeIdentifier = FieldTypeIdentifier,
|
|
69
|
+
> extends BaseFormElement<TIdentifier> {
|
|
70
|
+
static readonly fieldTypeName: string
|
|
71
|
+
static readonly fieldTypeDescription: string
|
|
72
|
+
|
|
73
|
+
public readonly required: boolean
|
|
74
|
+
private readonly formValidators: InputFormLevelValidator<TValue>[]
|
|
75
|
+
private readonly fieldValidators: InputFieldLevelValidator<TValue>[]
|
|
76
|
+
public readonly label: string
|
|
77
|
+
public readonly image: File | Promise<File> | undefined
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* By default, validation doesn't execute on `onChange` events when editing fields
|
|
81
|
+
* until the field has been `touched`. This can be overridden by setting this to `false`
|
|
82
|
+
* if you want to validate on every `onChange` event. This is important for fields like booleans
|
|
83
|
+
* which don't have a `onBlur` event (which is used to set the `touched` state).
|
|
84
|
+
*/
|
|
85
|
+
public readonly onlyValidateAfterTouched: boolean = true
|
|
86
|
+
|
|
87
|
+
protected constructor(options: FieldOptions<TValue>) {
|
|
88
|
+
const { label, required, image, fieldValidators = [], formValidators = [], ...base } = options
|
|
89
|
+
super(base)
|
|
90
|
+
this.label = label
|
|
91
|
+
this.required = required
|
|
92
|
+
this.image = image
|
|
93
|
+
this.fieldValidators = fieldValidators
|
|
94
|
+
this.formValidators = formValidators
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public static getFieldCreationSchema(): FieldCreationSchemaObject[] {
|
|
98
|
+
return []
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected isBlank(value: TValue | undefined): boolean {
|
|
102
|
+
return value === null || value === undefined || value === ""
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getValueFromChangeEvent(event: ChangeEvent<HTMLInputElement>): TValue {
|
|
106
|
+
return event.target.value as unknown as TValue
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public getError(value: TValue, allValues?: Form | FormikUserFormRevision): string | undefined {
|
|
110
|
+
if (this.required && this.isBlank(value)) {
|
|
111
|
+
return "This field is required."
|
|
112
|
+
}
|
|
113
|
+
for (const validator of this.getFieldValidators()) {
|
|
114
|
+
const error = validator(value)
|
|
115
|
+
if (error) return error
|
|
116
|
+
}
|
|
117
|
+
if (allValues) {
|
|
118
|
+
for (const validator of this.getFormValidators()) {
|
|
119
|
+
const error = validator(value, allValues)
|
|
120
|
+
if (error) return error
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// TODO: We can probably combine _serialize and serialize.
|
|
126
|
+
protected _serialize(): BaseSerializedField<TIdentifier> {
|
|
127
|
+
return {
|
|
128
|
+
...super._serialize(),
|
|
129
|
+
label: this.label,
|
|
130
|
+
required: this.required,
|
|
131
|
+
image: this.image,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
abstract serialize(): ISerializedOnlyField
|
|
136
|
+
|
|
137
|
+
public getFieldValidators(): InputFieldLevelValidator<TValue>[] {
|
|
138
|
+
return [...this.fieldValidators]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public getFormValidators(): InputFormLevelValidator<TValue>[] {
|
|
142
|
+
return [...this.formValidators]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public encodeValueToJson(value: TValue) {
|
|
146
|
+
return JSON.stringify(value)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public decodeJsonToValue(json: string): TValue {
|
|
150
|
+
return JSON.parse(json) as TValue
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useField } from "formik"
|
|
2
|
+
import { ChangeEventHandler, FocusEventHandler, useMemo } from "react"
|
|
3
|
+
|
|
4
|
+
import { Severity, ValueOfField } from "../../typings"
|
|
5
|
+
import { AnyField, ComponentProps } from "../typings"
|
|
6
|
+
|
|
7
|
+
// Wrapper for Formik's useField hook that returns the field's props, meta, and helpers.
|
|
8
|
+
export const useFormikInput = <TField extends AnyField>(props: ComponentProps<TField>) => {
|
|
9
|
+
const { id, field, formId, size, showInputOnly, internal, ...rest } = props
|
|
10
|
+
const [fieldProps, meta, helpers] = useField<ValueOfField<TField>>(field.getId())
|
|
11
|
+
const { touched } = meta
|
|
12
|
+
const helpText = meta.error ?? field.description
|
|
13
|
+
const severity: Severity | undefined = meta.error ? "danger" : undefined
|
|
14
|
+
const inputId = id ?? `${formId}-${field.getId()}-input`
|
|
15
|
+
const labelId = `${inputId}-label`
|
|
16
|
+
const label = field.required ? `${field.label} *` : field.label
|
|
17
|
+
|
|
18
|
+
// adds field-level validation to onChange and onBlur events
|
|
19
|
+
// this is necessary because Formik's useField hook does not support field-level validation
|
|
20
|
+
const fieldPropsWithValidation: typeof fieldProps = useMemo(() => {
|
|
21
|
+
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
|
22
|
+
const value = field.getValueFromChangeEvent(e) as ValueOfField<TField>
|
|
23
|
+
void helpers.setValue(value, false).then()
|
|
24
|
+
// only validate onChange if the field has been touched
|
|
25
|
+
if (touched || !field.onlyValidateAfterTouched) {
|
|
26
|
+
helpers.setError(field.getError(value))
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
|
|
31
|
+
void helpers.setTouched(true, false).then()
|
|
32
|
+
helpers.setError(field.getError(field.getValueFromChangeEvent(e)))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
...fieldProps,
|
|
37
|
+
onChange: handleChange,
|
|
38
|
+
onBlur: handleBlur,
|
|
39
|
+
}
|
|
40
|
+
}, [field, fieldProps, helpers, touched])
|
|
41
|
+
|
|
42
|
+
console.debug("severity", severity)
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
helpText,
|
|
47
|
+
size,
|
|
48
|
+
severity,
|
|
49
|
+
inputId,
|
|
50
|
+
labelId,
|
|
51
|
+
label,
|
|
52
|
+
showInputOnly,
|
|
53
|
+
internal,
|
|
54
|
+
fieldProps: fieldPropsWithValidation,
|
|
55
|
+
helpers,
|
|
56
|
+
field,
|
|
57
|
+
},
|
|
58
|
+
{ ...rest, "aria-labelledby": labelId },
|
|
59
|
+
] as const
|
|
60
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Text, TextSize } from "@overmap-ai/blocks"
|
|
2
|
+
import { cx } from "class-variance-authority"
|
|
3
|
+
import { ReactElement, ReactNode, useEffect, useState } from "react"
|
|
4
|
+
|
|
5
|
+
import { SEVERITY_COLOR_MAPPING } from "../../constants"
|
|
6
|
+
import { FullScreenImagePreview } from "../../constantsJsx"
|
|
7
|
+
import { Severity } from "../../typings"
|
|
8
|
+
|
|
9
|
+
interface InputWithLabelProps {
|
|
10
|
+
size?: TextSize
|
|
11
|
+
severity: Severity | undefined
|
|
12
|
+
inputId: string
|
|
13
|
+
labelId: string
|
|
14
|
+
label: string
|
|
15
|
+
image: File | Promise<File> | undefined
|
|
16
|
+
children: ReactNode
|
|
17
|
+
className?: string
|
|
18
|
+
}
|
|
19
|
+
export const InputWithLabel = (props: InputWithLabelProps) => {
|
|
20
|
+
const { className, label, children, size, severity, inputId, labelId, image } = props
|
|
21
|
+
const [resolvedImage, setResolvedImage] = useState<File | undefined>(undefined)
|
|
22
|
+
const [showImagePreview, setShowImagePreview] = useState<boolean>(false)
|
|
23
|
+
|
|
24
|
+
const color = severity ? SEVERITY_COLOR_MAPPING[severity] : "base"
|
|
25
|
+
|
|
26
|
+
console.debug(severity, color)
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (image instanceof Promise) {
|
|
30
|
+
image.then(setResolvedImage).catch(console.error)
|
|
31
|
+
} else {
|
|
32
|
+
setResolvedImage(image)
|
|
33
|
+
}
|
|
34
|
+
}, [image])
|
|
35
|
+
|
|
36
|
+
const resolvedImageURL = resolvedImage ? URL.createObjectURL(resolvedImage) : undefined
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="flex flex-col gap-2">
|
|
40
|
+
{resolvedImage && (
|
|
41
|
+
<>
|
|
42
|
+
<img
|
|
43
|
+
className="h-[100px] w-full min-w-[300px] cursor-pointer rounded-md object-cover"
|
|
44
|
+
src={resolvedImageURL}
|
|
45
|
+
alt={resolvedImage.name}
|
|
46
|
+
onClick={() => {
|
|
47
|
+
setShowImagePreview(true)
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
{showImagePreview && (
|
|
51
|
+
<FullScreenImagePreview
|
|
52
|
+
file={resolvedImage}
|
|
53
|
+
url={resolvedImageURL!}
|
|
54
|
+
name={resolvedImage.name}
|
|
55
|
+
setShowPreview={setShowImagePreview}
|
|
56
|
+
/>
|
|
57
|
+
)}
|
|
58
|
+
</>
|
|
59
|
+
)}
|
|
60
|
+
|
|
61
|
+
<label className={cx(className, "flex flex-col gap-1")} htmlFor={inputId}>
|
|
62
|
+
<Text accentColor={color} size={size} id={labelId} weight="medium">
|
|
63
|
+
{label}
|
|
64
|
+
</Text>
|
|
65
|
+
{children}
|
|
66
|
+
</label>
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface InputWithHelpTextProps {
|
|
72
|
+
severity: Severity | undefined
|
|
73
|
+
helpText: string | null
|
|
74
|
+
children: ReactElement
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const InputWithHelpText = (props: InputWithHelpTextProps) => {
|
|
78
|
+
const { helpText, children, severity } = props
|
|
79
|
+
|
|
80
|
+
const color = severity ? SEVERITY_COLOR_MAPPING[severity] : "base"
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="flex flex-col gap-1">
|
|
84
|
+
{children}
|
|
85
|
+
<div className="flex flex-col w-full">
|
|
86
|
+
<Text accentColor={color} size="xs">
|
|
87
|
+
{helpText}
|
|
88
|
+
</Text>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface InputWithLabelAndHelpTextProps extends InputWithHelpTextProps {
|
|
95
|
+
children: ReactElement<InputWithLabelProps>
|
|
96
|
+
}
|
|
97
|
+
export const InputWithLabelAndHelpText = (props: InputWithLabelAndHelpTextProps) => {
|
|
98
|
+
const { children, ...restProps } = props
|
|
99
|
+
return <InputWithHelpText {...restProps}>{children}</InputWithHelpText>
|
|
100
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseSerializedField } from "../../typings"
|
|
2
|
+
import { InputFieldLevelValidator, InputFormLevelValidator } from "../typings"
|
|
3
|
+
|
|
4
|
+
export interface FieldOptions<TValue> extends BaseSerializedField {
|
|
5
|
+
fieldValidators?: InputFieldLevelValidator<TValue>[]
|
|
6
|
+
formValidators?: InputFormLevelValidator<TValue>[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ChildFieldOptions<TValue> = Omit<FieldOptions<TValue>, "type">
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ChangeEvent, ReactNode } from "react"
|
|
2
|
+
import { RiCheckboxCircleLine } from "react-icons/ri"
|
|
3
|
+
|
|
4
|
+
import { ISerializedField, SerializedBooleanField } from "../../typings"
|
|
5
|
+
import { BaseField, ChildFieldOptions, emptyBaseField } from "../BaseField"
|
|
6
|
+
import { ComponentProps } from "../typings"
|
|
7
|
+
import { BooleanInput } from "./BooleanInput"
|
|
8
|
+
|
|
9
|
+
export const emptyBooleanField = {
|
|
10
|
+
...emptyBaseField,
|
|
11
|
+
type: "boolean",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class BooleanField extends BaseField<boolean, "boolean"> {
|
|
15
|
+
static readonly fieldTypeName = "Checkbox"
|
|
16
|
+
static readonly fieldTypeDescription = "Perfect for both optional and required yes/no questions."
|
|
17
|
+
|
|
18
|
+
public readonly onlyValidateAfterTouched = false
|
|
19
|
+
|
|
20
|
+
static Icon: typeof RiCheckboxCircleLine = RiCheckboxCircleLine
|
|
21
|
+
|
|
22
|
+
public constructor(options: ChildFieldOptions<boolean>) {
|
|
23
|
+
super({ ...options, type: "boolean" })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// if a BooleanField is required, `false` is considered blank
|
|
27
|
+
protected isBlank(value: boolean): boolean {
|
|
28
|
+
return this.required && !value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public getValueFromChangeEvent(event: ChangeEvent<HTMLInputElement> | boolean): boolean {
|
|
32
|
+
if (typeof event === "boolean") return event
|
|
33
|
+
return event.target.checked
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
serialize(): SerializedBooleanField {
|
|
37
|
+
return super._serialize()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static deserialize(data: ISerializedField): BooleanField {
|
|
41
|
+
if (data.type !== "boolean") throw new Error("Type mismatch.")
|
|
42
|
+
return new BooleanField(data)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getInput(props: ComponentProps<BooleanField>): ReactNode {
|
|
46
|
+
return <BooleanInput {...props} field={this} />
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Checkbox, RiIcon } from "@overmap-ai/blocks"
|
|
2
|
+
import { memo } from "react"
|
|
3
|
+
|
|
4
|
+
import { SEVERITY_COLOR_MAPPING } from "../../constants"
|
|
5
|
+
import { InputWithLabel, InputWithLabelAndHelpText } from "../BaseField"
|
|
6
|
+
import { useFormikInput } from "../BaseField"
|
|
7
|
+
import { ComponentProps } from "../typings"
|
|
8
|
+
import { BooleanField } from "./BooleanField"
|
|
9
|
+
|
|
10
|
+
const truthyValues = [true, "true"]
|
|
11
|
+
|
|
12
|
+
export const BooleanInput = memo((props: ComponentProps<BooleanField>) => {
|
|
13
|
+
const [{ inputId, labelId, size, severity, showInputOnly, field, fieldProps }, rest] = useFormikInput(props)
|
|
14
|
+
let [{ helpText, label }] = useFormikInput(props)
|
|
15
|
+
helpText = showInputOnly ? null : helpText
|
|
16
|
+
label = showInputOnly ? "" : label
|
|
17
|
+
|
|
18
|
+
const color = severity ? SEVERITY_COLOR_MAPPING[severity] : undefined
|
|
19
|
+
const value = truthyValues.includes(fieldProps.value)
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<InputWithLabelAndHelpText helpText={helpText} severity={severity}>
|
|
23
|
+
<InputWithLabel
|
|
24
|
+
size={size}
|
|
25
|
+
severity={severity}
|
|
26
|
+
inputId={inputId}
|
|
27
|
+
labelId={labelId}
|
|
28
|
+
label={label}
|
|
29
|
+
image={showInputOnly ? undefined : field.image}
|
|
30
|
+
className="align-center flex-row-reverse justify-end gap-2"
|
|
31
|
+
>
|
|
32
|
+
<Checkbox.Root
|
|
33
|
+
{...rest}
|
|
34
|
+
{...fieldProps}
|
|
35
|
+
id={inputId}
|
|
36
|
+
accentColor={color}
|
|
37
|
+
value={value.toString()}
|
|
38
|
+
checked={value}
|
|
39
|
+
onCheckedChange={fieldProps.onChange}
|
|
40
|
+
// disabled onChange and onBlur as that is handled by onCheckedChange
|
|
41
|
+
onChange={undefined}
|
|
42
|
+
onBlur={undefined}
|
|
43
|
+
variant="soft"
|
|
44
|
+
>
|
|
45
|
+
<Checkbox.Indicator>
|
|
46
|
+
<RiIcon icon="RiCheckLine" />
|
|
47
|
+
</Checkbox.Indicator>
|
|
48
|
+
</Checkbox.Root>
|
|
49
|
+
</InputWithLabel>
|
|
50
|
+
</InputWithLabelAndHelpText>
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
BooleanInput.displayName = "BooleanInput"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ISerializedOnlyField } from "@overmap-ai/core"
|
|
2
|
+
import { FC, ReactNode } from "react"
|
|
3
|
+
|
|
4
|
+
import { FieldTypeIdentifier, FieldValue } from "../../typings"
|
|
5
|
+
import { BaseField, ChildFieldOptions, emptyBaseField } from "../BaseField"
|
|
6
|
+
import { GetInputProps } from "../typings"
|
|
7
|
+
|
|
8
|
+
export type CustomFieldOptions<TValue> = ChildFieldOptions<TValue>
|
|
9
|
+
|
|
10
|
+
export const emptyCustomField = {
|
|
11
|
+
...emptyBaseField,
|
|
12
|
+
type: "custom",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class CustomField<
|
|
16
|
+
TValue extends FieldValue,
|
|
17
|
+
/** The options passed to constructor */
|
|
18
|
+
TFieldOptions extends CustomFieldOptions<TValue>,
|
|
19
|
+
/** The props passed to the custom component */
|
|
20
|
+
TComponentProps extends GetInputProps<CustomField<TValue, TFieldOptions, TComponentProps>>,
|
|
21
|
+
TIdentifier extends FieldTypeIdentifier = FieldTypeIdentifier,
|
|
22
|
+
> extends BaseField<TValue, TIdentifier> {
|
|
23
|
+
static readonly fieldTypeName = "Custom"
|
|
24
|
+
static readonly fieldTypeDescription = "Allows re-rendering of field already in the form"
|
|
25
|
+
|
|
26
|
+
public readonly Component: FC<TComponentProps>
|
|
27
|
+
|
|
28
|
+
// identifier of the field whose value is the label of the field to re-render
|
|
29
|
+
public readonly options: TFieldOptions
|
|
30
|
+
|
|
31
|
+
constructor(options: TFieldOptions, Component: FC<TComponentProps>) {
|
|
32
|
+
super({ ...options, type: "custom" })
|
|
33
|
+
this.options = options
|
|
34
|
+
this.Component = Component
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
serialize(): ISerializedOnlyField {
|
|
38
|
+
throw new Error("Serializing only supported for public input types.")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getInput(props: TComponentProps): ReactNode {
|
|
42
|
+
const CustomInput = this.Component
|
|
43
|
+
return <CustomInput field={this} {...props} />
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useField } from "formik"
|
|
2
|
+
import { memo, useMemo } from "react"
|
|
3
|
+
|
|
4
|
+
import { deserialize, useFieldInput } from "../../index"
|
|
5
|
+
import { FieldInputClonerProps } from "./typings"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Used to dynamically "clone" a field's input for use in conditional sections. When a field is selected for a
|
|
9
|
+
* condition, we need to render the same input in the condition section, so the user can input a value for the
|
|
10
|
+
* condition.
|
|
11
|
+
*/
|
|
12
|
+
export const FieldInputCloner = memo((props: FieldInputClonerProps) => {
|
|
13
|
+
const { field, ...rest } = props
|
|
14
|
+
const [{ value: identifier }] = useField<string>(field.options.clonedFieldIdentifier)
|
|
15
|
+
|
|
16
|
+
const deserializedField = useMemo(() => {
|
|
17
|
+
const options = field.options.getFieldToClone(identifier)
|
|
18
|
+
if (!options) return null
|
|
19
|
+
return deserialize(options)
|
|
20
|
+
}, [field.options, identifier])
|
|
21
|
+
|
|
22
|
+
return useFieldInput(deserializedField, rest)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
FieldInputCloner.displayName = "FieldInputCloner"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FieldValue, ISerializedField } from "../../../typings"
|
|
2
|
+
import { CustomField, CustomFieldOptions } from "../CustomField"
|
|
3
|
+
import { FieldInputCloner, FieldInputClonerProps } from "./index"
|
|
4
|
+
|
|
5
|
+
export interface FieldInputClonerFieldOptions extends CustomFieldOptions<unknown> {
|
|
6
|
+
/** Given an identifier, should return the options of the field with the
|
|
7
|
+
* corresponding identifier (the field being cloned) */
|
|
8
|
+
getFieldToClone: (identifier: string) => ISerializedField | null
|
|
9
|
+
/** The identifier of the field to clone */
|
|
10
|
+
clonedFieldIdentifier: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The purpose of this is to display a value input field in the condition of a section. The input field will look like
|
|
15
|
+
* the input field of the condition. For example, when specifying the conditional value of a SelectField, a SelectInput
|
|
16
|
+
* will be rendered.
|
|
17
|
+
*/
|
|
18
|
+
export class FieldInputClonerField extends CustomField<
|
|
19
|
+
FieldValue,
|
|
20
|
+
FieldInputClonerFieldOptions,
|
|
21
|
+
FieldInputClonerProps
|
|
22
|
+
> {
|
|
23
|
+
constructor(options: FieldInputClonerFieldOptions) {
|
|
24
|
+
super(options, FieldInputCloner)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FieldValue } from "../../../typings"
|
|
2
|
+
import { AnyField, ComponentProps, GetInputProps } from "../../typings"
|
|
3
|
+
import { CustomField } from "../CustomField"
|
|
4
|
+
import { FieldInputClonerFieldOptions } from "./FieldInputClonerField"
|
|
5
|
+
|
|
6
|
+
export type FieldInputClonerProps = ComponentProps<
|
|
7
|
+
CustomField<FieldValue, FieldInputClonerFieldOptions, GetInputProps<AnyField>>
|
|
8
|
+
>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./CustomField"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ChangeEvent, ReactNode } from "react"
|
|
2
|
+
import { RiCalendarLine } from "react-icons/ri"
|
|
3
|
+
|
|
4
|
+
import { ISerializedField, SerializedDateField } from "../../typings"
|
|
5
|
+
import { BaseField, ChildFieldOptions, emptyBaseField } from "../BaseField"
|
|
6
|
+
import { GetInputProps } from "../typings"
|
|
7
|
+
import { DateInput } from "./DateInput"
|
|
8
|
+
|
|
9
|
+
export const emptyDateField = {
|
|
10
|
+
...emptyBaseField,
|
|
11
|
+
type: "date",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class DateField extends BaseField<string, "date"> {
|
|
15
|
+
static readonly fieldTypeName = "Date"
|
|
16
|
+
static readonly fieldTypeDescription = "Allows specifying a date."
|
|
17
|
+
|
|
18
|
+
static Icon: typeof RiCalendarLine = RiCalendarLine
|
|
19
|
+
|
|
20
|
+
public readonly onlyValidateAfterTouched = false
|
|
21
|
+
|
|
22
|
+
public constructor(options: ChildFieldOptions<string>) {
|
|
23
|
+
super({ ...options, type: "date" })
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
serialize(): SerializedDateField {
|
|
27
|
+
return super._serialize()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public getValueFromChangeEvent(event: ChangeEvent<HTMLInputElement>): string {
|
|
31
|
+
return new Date(event.target.value).toISOString()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static deserialize(data: ISerializedField): DateField {
|
|
35
|
+
if (data.type !== "date") throw new Error("Type mismatch.")
|
|
36
|
+
return new DateField(data)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getInput(props: GetInputProps<this>): ReactNode {
|
|
40
|
+
return <DateInput field={this} {...props} />
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Input } from "@overmap-ai/blocks"
|
|
2
|
+
import { memo } from "react"
|
|
3
|
+
|
|
4
|
+
import { SEVERITY_COLOR_MAPPING } from "../../constants"
|
|
5
|
+
import { InputWithLabel, InputWithLabelAndHelpText, useFormikInput } from "../BaseField"
|
|
6
|
+
import { ComponentProps } from "../typings"
|
|
7
|
+
import { DateField } from "./DateField"
|
|
8
|
+
|
|
9
|
+
export const DateInput = memo((props: ComponentProps<DateField>) => {
|
|
10
|
+
const [{ inputId, labelId, size, severity, showInputOnly, field, fieldProps }, rest] = useFormikInput(props)
|
|
11
|
+
let [{ helpText, label }] = useFormikInput(props)
|
|
12
|
+
helpText = showInputOnly ? null : helpText
|
|
13
|
+
label = showInputOnly ? "" : label
|
|
14
|
+
|
|
15
|
+
const color = severity ? SEVERITY_COLOR_MAPPING[severity] : undefined
|
|
16
|
+
|
|
17
|
+
// TODO: Add timezone info
|
|
18
|
+
// remove the time from the date
|
|
19
|
+
const value: string = fieldProps.value ? fieldProps.value.split("T")[0]! : ""
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<InputWithLabelAndHelpText helpText={helpText} severity={severity}>
|
|
23
|
+
<InputWithLabel
|
|
24
|
+
size={size}
|
|
25
|
+
severity={severity}
|
|
26
|
+
inputId={inputId}
|
|
27
|
+
labelId={labelId}
|
|
28
|
+
label={label}
|
|
29
|
+
image={showInputOnly ? undefined : field.image}
|
|
30
|
+
>
|
|
31
|
+
<Input.Root accentColor={color} variant="soft">
|
|
32
|
+
<Input.Field {...rest} type="date" id={inputId} color={color} value={value} />
|
|
33
|
+
</Input.Root>
|
|
34
|
+
</InputWithLabel>
|
|
35
|
+
</InputWithLabelAndHelpText>
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
DateInput.displayName = "DateInput"
|