@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.
Files changed (284) hide show
  1. package/.husky/pre-commit +6 -0
  2. package/.prettierrc.json +10 -0
  3. package/.storybook/StoryDecorator.tsx +22 -0
  4. package/.storybook/main.ts +20 -0
  5. package/.storybook/palettes/green.css +66 -0
  6. package/.storybook/palettes/red.css +66 -0
  7. package/.storybook/preview.css +39 -0
  8. package/.storybook/preview.tsx +31 -0
  9. package/.storybook/tailwind-theme/accentPalette.css +181 -0
  10. package/.storybook/tailwind-theme/backgrounds.css +11 -0
  11. package/.storybook/tailwind-theme/basePalette.css +178 -0
  12. package/dev/publish-alpha.sh +13 -0
  13. package/dev/publish-patch.sh +3 -0
  14. package/dist/ColorPicker/ColorPicker.d.ts +10 -0
  15. package/dist/ColorPicker/index.d.ts +1 -0
  16. package/dist/FileBadge/FileBadge.d.ts +7 -0
  17. package/dist/FileBadge/index.d.ts +1 -0
  18. package/dist/FileCard/FileCard.d.ts +8 -0
  19. package/dist/FileCard/index.d.ts +1 -0
  20. package/dist/FileIcon/FileIcon.d.ts +4 -0
  21. package/dist/FileIcon/index.d.ts +1 -0
  22. package/dist/FileViewer/FileViewerProvider.d.ts +2 -0
  23. package/dist/FileViewer/context.d.ts +4 -0
  24. package/dist/FileViewer/index.d.ts +3 -0
  25. package/dist/FileViewer/typings.d.ts +5 -0
  26. package/dist/ImageCard/ImageCard.d.ts +9 -0
  27. package/dist/ImageCard/index.d.ts +1 -0
  28. package/dist/ImageMarkup/ImageMarkup.d.ts +14 -0
  29. package/dist/ImageMarkup/index.d.ts +1 -0
  30. package/dist/ImageViewer/ImageViewer.d.ts +7 -0
  31. package/dist/ImageViewer/constants.d.ts +1 -0
  32. package/dist/ImageViewer/index.d.ts +2 -0
  33. package/dist/PDFViewer/PDFViewer.d.ts +7 -0
  34. package/dist/PDFViewer/constants.d.ts +1 -0
  35. package/dist/PDFViewer/index.d.ts +2 -0
  36. package/dist/SpreadsheetViewer/SpreadsheetViewer.d.ts +7 -0
  37. package/dist/SpreadsheetViewer/constants.d.ts +1 -0
  38. package/dist/SpreadsheetViewer/index.d.ts +2 -0
  39. package/dist/{builder → forms/builder}/DropDispatch.d.ts +2 -2
  40. package/dist/forms/builder/FieldActions.d.ts +13 -0
  41. package/dist/forms/builder/FieldBuilder.d.ts +10 -0
  42. package/dist/forms/builder/FieldSectionWithActions.d.ts +10 -0
  43. package/dist/forms/builder/FieldWithActions.d.ts +9 -0
  44. package/dist/forms/builder/FieldsEditor.d.ts +5 -0
  45. package/dist/forms/builder/FormBuilder.d.ts +25 -0
  46. package/dist/forms/builder/constants.d.ts +18 -0
  47. package/dist/forms/builder/hooks.d.ts +7 -0
  48. package/dist/forms/builder/index.d.ts +2 -0
  49. package/dist/{builder → forms/builder}/typings.d.ts +2 -1
  50. package/dist/forms/builder/utils.d.ts +23 -0
  51. package/dist/forms/constants.d.ts +3 -0
  52. package/dist/forms/constantsJsx.d.ts +9 -0
  53. package/dist/{fields → forms/fields}/BaseField/BaseField.d.ts +23 -10
  54. package/dist/forms/fields/BaseField/hooks.d.ts +388 -0
  55. package/dist/forms/fields/BaseField/index.d.ts +4 -0
  56. package/dist/{fields → forms/fields}/BaseField/layouts.d.ts +11 -5
  57. package/dist/{fields → forms/fields}/BaseField/typings.d.ts +2 -2
  58. package/dist/{fields → forms/fields}/BooleanField/BooleanField.d.ts +12 -6
  59. package/dist/forms/fields/BooleanField/BooleanInput.d.ts +3 -0
  60. package/dist/forms/fields/BooleanField/index.d.ts +2 -0
  61. package/dist/{fields → forms/fields}/CustomField/CustomField.d.ts +12 -6
  62. package/dist/{fields → forms/fields}/CustomField/FieldInputClonerField/FieldInputCloner.d.ts +2 -3
  63. package/dist/{fields → forms/fields}/CustomField/FieldInputClonerField/FieldInputClonerField.d.ts +3 -3
  64. package/dist/forms/fields/CustomField/FieldInputClonerField/index.d.ts +3 -0
  65. package/dist/forms/fields/CustomField/FieldInputClonerField/typings.d.ts +5 -0
  66. package/dist/forms/fields/CustomField/index.d.ts +1 -0
  67. package/dist/forms/fields/DateField/DateField.d.ts +22 -0
  68. package/dist/forms/fields/DateField/DateInput.d.ts +3 -0
  69. package/dist/forms/fields/DateField/index.d.ts +2 -0
  70. package/dist/{fields → forms/fields}/FieldSection/FieldSection.d.ts +13 -9
  71. package/dist/forms/fields/FieldSection/FieldSectionLayout.d.ts +6 -0
  72. package/dist/forms/fields/FieldSection/index.d.ts +1 -0
  73. package/dist/forms/fields/MultiStringField/MultiStringField.d.ts +40 -0
  74. package/dist/forms/fields/MultiStringField/MultiStringInput.d.ts +7 -0
  75. package/dist/forms/fields/MultiStringField/index.d.ts +2 -0
  76. package/dist/{fields → forms/fields}/NumberField/NumberField.d.ts +27 -10
  77. package/dist/forms/fields/NumberField/NumberInput.d.ts +3 -0
  78. package/dist/forms/fields/NumberField/index.d.ts +2 -0
  79. package/dist/forms/fields/QrField/QrField.d.ts +21 -0
  80. package/dist/forms/fields/QrField/QrInput.d.ts +9 -0
  81. package/dist/forms/fields/QrField/index.d.ts +2 -0
  82. package/dist/{fields → forms/fields}/SelectField/BaseSelectField.d.ts +12 -5
  83. package/dist/{fields → forms/fields}/SelectField/MultiSelectField.d.ts +13 -6
  84. package/dist/forms/fields/SelectField/MultiSelectInput.d.ts +3 -0
  85. package/dist/{fields → forms/fields}/SelectField/SelectField.d.ts +14 -7
  86. package/dist/forms/fields/SelectField/SelectInput.d.ts +3 -0
  87. package/dist/forms/fields/SelectField/index.d.ts +4 -0
  88. package/dist/forms/fields/StringOrTextFields/StringField/StringField.d.ts +26 -0
  89. package/dist/forms/fields/StringOrTextFields/StringField/StringInput.d.ts +3 -0
  90. package/dist/forms/fields/StringOrTextFields/StringField/index.d.ts +2 -0
  91. package/dist/{fields → forms/fields}/StringOrTextFields/StringOrTextField.d.ts +13 -8
  92. package/dist/forms/fields/StringOrTextFields/TextField/TextField.d.ts +22 -0
  93. package/dist/forms/fields/StringOrTextFields/TextField/TextInput.d.ts +3 -0
  94. package/dist/forms/fields/StringOrTextFields/TextField/index.d.ts +2 -0
  95. package/dist/forms/fields/StringOrTextFields/index.d.ts +2 -0
  96. package/dist/{fields → forms/fields}/UploadField/UploadField.d.ts +24 -9
  97. package/dist/forms/fields/UploadField/UploadInput.d.ts +3 -0
  98. package/dist/forms/fields/UploadField/index.d.ts +2 -0
  99. package/dist/forms/fields/constants.d.ts +106 -0
  100. package/dist/forms/fields/hooks.d.ts +6 -0
  101. package/dist/forms/fields/index.d.ts +12 -0
  102. package/dist/{fields → forms/fields}/typings.d.ts +9 -6
  103. package/dist/{fields → forms/fields}/utils.d.ts +7 -3
  104. package/dist/forms/index.d.ts +5 -0
  105. package/dist/{renderer → forms/renderer}/FormRenderer/FormRenderer.d.ts +4 -3
  106. package/dist/{renderer → forms/renderer}/PatchForm/Field.d.ts +5 -3
  107. package/dist/{renderer → forms/renderer}/PatchForm/Provider.d.ts +8 -4
  108. package/dist/forms/renderer/PatchForm/index.d.ts +2 -0
  109. package/dist/forms/renderer/index.d.ts +2 -0
  110. package/dist/forms/typings.d.ts +105 -0
  111. package/dist/forms/utils.d.ts +7 -0
  112. package/dist/forms.js +4450 -2478
  113. package/dist/forms.umd.cjs +44 -2777
  114. package/dist/index.d.ts +11 -3
  115. package/eslint.config.js +56 -0
  116. package/package.json +96 -94
  117. package/src/ColorPicker/ColorPicker.tsx +47 -0
  118. package/src/ColorPicker/index.ts +1 -0
  119. package/src/FileBadge/FileBadge.tsx +27 -0
  120. package/src/FileBadge/index.ts +1 -0
  121. package/src/FileCard/FileCard.stories.tsx +69 -0
  122. package/src/FileCard/FileCard.tsx +53 -0
  123. package/src/FileCard/index.ts +1 -0
  124. package/src/FileIcon/FileIcon.tsx +31 -0
  125. package/src/FileIcon/index.ts +1 -0
  126. package/src/FileViewer/FileViewerProvider.stories.tsx +50 -0
  127. package/src/FileViewer/FileViewerProvider.tsx +72 -0
  128. package/src/FileViewer/context.ts +11 -0
  129. package/src/FileViewer/index.ts +3 -0
  130. package/src/FileViewer/typings.ts +5 -0
  131. package/src/ImageCard/ImageCard.stories.tsx +94 -0
  132. package/src/ImageCard/ImageCard.tsx +82 -0
  133. package/src/ImageCard/index.ts +1 -0
  134. package/src/ImageMarkup/ImageMarkup.stories.tsx +65 -0
  135. package/src/ImageMarkup/ImageMarkup.tsx +268 -0
  136. package/src/ImageMarkup/index.ts +1 -0
  137. package/src/ImageViewer/ImageViewer.stories.tsx +57 -0
  138. package/src/ImageViewer/ImageViewer.tsx +124 -0
  139. package/src/ImageViewer/constants.ts +1 -0
  140. package/src/ImageViewer/index.ts +2 -0
  141. package/src/PDFViewer/PDFViewer.stories.tsx +55 -0
  142. package/src/PDFViewer/PDFViewer.tsx +170 -0
  143. package/src/PDFViewer/constants.ts +1 -0
  144. package/src/PDFViewer/index.ts +2 -0
  145. package/src/SpreadsheetViewer/SpreadsheetViewer.stories.tsx +55 -0
  146. package/src/SpreadsheetViewer/SpreadsheetViewer.tsx +162 -0
  147. package/src/SpreadsheetViewer/constants.ts +8 -0
  148. package/src/SpreadsheetViewer/index.ts +2 -0
  149. package/src/forms/builder/DropDispatch.ts +84 -0
  150. package/src/forms/builder/FieldActions.tsx +155 -0
  151. package/src/forms/builder/FieldBuilder.tsx +386 -0
  152. package/src/forms/builder/FieldSectionWithActions.tsx +260 -0
  153. package/src/forms/builder/FieldWithActions.tsx +129 -0
  154. package/src/forms/builder/FieldsEditor.tsx +180 -0
  155. package/src/forms/builder/FormBuilder.stories.tsx +105 -0
  156. package/src/forms/builder/FormBuilder.tsx +237 -0
  157. package/src/forms/builder/constants.ts +18 -0
  158. package/src/forms/builder/hooks.tsx +24 -0
  159. package/src/forms/builder/index.ts +2 -0
  160. package/src/forms/builder/typings.ts +18 -0
  161. package/src/forms/builder/utils.ts +229 -0
  162. package/src/forms/constants.ts +9 -0
  163. package/src/forms/constantsJsx.tsx +67 -0
  164. package/src/forms/fields/BaseField/BaseField.ts +152 -0
  165. package/src/forms/fields/BaseField/hooks.tsx +60 -0
  166. package/src/forms/fields/BaseField/index.ts +4 -0
  167. package/src/forms/fields/BaseField/layouts.tsx +100 -0
  168. package/src/forms/fields/BaseField/typings.ts +9 -0
  169. package/src/forms/fields/BooleanField/BooleanField.tsx +48 -0
  170. package/src/forms/fields/BooleanField/BooleanInput.tsx +54 -0
  171. package/src/forms/fields/BooleanField/index.ts +2 -0
  172. package/src/forms/fields/CustomField/CustomField.tsx +45 -0
  173. package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputCloner.tsx +25 -0
  174. package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputClonerField.tsx +26 -0
  175. package/src/forms/fields/CustomField/FieldInputClonerField/index.ts +3 -0
  176. package/src/forms/fields/CustomField/FieldInputClonerField/typings.ts +8 -0
  177. package/src/forms/fields/CustomField/index.ts +1 -0
  178. package/src/forms/fields/DateField/DateField.tsx +42 -0
  179. package/src/forms/fields/DateField/DateInput.tsx +39 -0
  180. package/src/forms/fields/DateField/index.ts +2 -0
  181. package/src/forms/fields/FieldSection/FieldSection.tsx +173 -0
  182. package/src/forms/fields/FieldSection/FieldSectionLayout.tsx +56 -0
  183. package/src/forms/fields/FieldSection/index.ts +1 -0
  184. package/src/forms/fields/MultiStringField/MultiStringField.tsx +90 -0
  185. package/src/forms/fields/MultiStringField/MultiStringInput.tsx +207 -0
  186. package/src/forms/fields/MultiStringField/index.ts +2 -0
  187. package/src/forms/fields/NumberField/NumberField.tsx +173 -0
  188. package/src/forms/fields/NumberField/NumberInput.tsx +44 -0
  189. package/src/forms/fields/NumberField/index.ts +2 -0
  190. package/src/forms/fields/QrField/QrField.tsx +38 -0
  191. package/src/forms/fields/QrField/QrInput.module.sass +5 -0
  192. package/src/forms/fields/QrField/QrInput.tsx +144 -0
  193. package/src/forms/fields/QrField/index.ts +2 -0
  194. package/src/forms/fields/SelectField/BaseSelectField.ts +73 -0
  195. package/src/forms/fields/SelectField/MultiSelectField.tsx +53 -0
  196. package/src/forms/fields/SelectField/MultiSelectInput.tsx +80 -0
  197. package/src/forms/fields/SelectField/SelectField.tsx +49 -0
  198. package/src/forms/fields/SelectField/SelectInput.tsx +69 -0
  199. package/src/forms/fields/SelectField/index.ts +4 -0
  200. package/src/forms/fields/StringOrTextFields/StringField/StringField.tsx +61 -0
  201. package/src/forms/fields/StringOrTextFields/StringField/StringInput.tsx +41 -0
  202. package/src/forms/fields/StringOrTextFields/StringField/index.ts +2 -0
  203. package/src/forms/fields/StringOrTextFields/StringOrTextField.ts +143 -0
  204. package/src/forms/fields/StringOrTextFields/TextField/TextField.tsx +52 -0
  205. package/src/forms/fields/StringOrTextFields/TextField/TextInput.tsx +42 -0
  206. package/src/forms/fields/StringOrTextFields/TextField/index.ts +2 -0
  207. package/src/forms/fields/StringOrTextFields/index.ts +2 -0
  208. package/src/forms/fields/UploadField/UploadField.tsx +156 -0
  209. package/src/forms/fields/UploadField/UploadInput.tsx +220 -0
  210. package/src/forms/fields/UploadField/index.ts +2 -0
  211. package/src/forms/fields/UploadField/utils.ts +17 -0
  212. package/src/forms/fields/constants.ts +43 -0
  213. package/src/forms/fields/hooks.tsx +26 -0
  214. package/src/forms/fields/index.ts +12 -0
  215. package/src/forms/fields/typings.ts +45 -0
  216. package/src/forms/fields/utils.ts +125 -0
  217. package/src/forms/index.ts +5 -0
  218. package/src/forms/renderer/FormRenderer/FormRenderer.stories.tsx +142 -0
  219. package/src/forms/renderer/FormRenderer/FormRenderer.tsx +135 -0
  220. package/src/forms/renderer/PatchForm/Field.tsx +41 -0
  221. package/src/forms/renderer/PatchForm/PatchForm.stories.tsx +91 -0
  222. package/src/forms/renderer/PatchForm/Provider.tsx +119 -0
  223. package/src/forms/renderer/PatchForm/index.ts +2 -0
  224. package/src/forms/renderer/index.ts +2 -0
  225. package/src/forms/typings.ts +162 -0
  226. package/src/forms/utils.ts +69 -0
  227. package/src/index.ts +11 -0
  228. package/src/vite-env.d.ts +1 -0
  229. package/tailwind.config.ts +8 -0
  230. package/tsconfig.json +26 -0
  231. package/vite.config.ts +23 -0
  232. package/README.md +0 -12
  233. package/dist/builder/FieldActions.d.ts +0 -12
  234. package/dist/builder/FieldBuilder.d.ts +0 -24
  235. package/dist/builder/FieldSectionWithActions.d.ts +0 -10
  236. package/dist/builder/FieldWithActions.d.ts +0 -11
  237. package/dist/builder/FieldsEditor.d.ts +0 -2
  238. package/dist/builder/FormBuilder.d.ts +0 -15
  239. package/dist/builder/constants.d.ts +0 -1
  240. package/dist/builder/index.d.ts +0 -2
  241. package/dist/builder/utils.d.ts +0 -13
  242. package/dist/fields/BaseField/hooks.d.ts +0 -374
  243. package/dist/fields/BaseField/index.d.ts +0 -4
  244. package/dist/fields/BooleanField/BooleanInput.d.ts +0 -4
  245. package/dist/fields/BooleanField/index.d.ts +0 -2
  246. package/dist/fields/CustomField/FieldInputClonerField/index.d.ts +0 -3
  247. package/dist/fields/CustomField/FieldInputClonerField/typings.d.ts +0 -5
  248. package/dist/fields/CustomField/index.d.ts +0 -1
  249. package/dist/fields/DateField/DateField.d.ts +0 -16
  250. package/dist/fields/DateField/DateInput.d.ts +0 -4
  251. package/dist/fields/DateField/index.d.ts +0 -2
  252. package/dist/fields/FieldSection/FieldSectionLayout.d.ts +0 -7
  253. package/dist/fields/FieldSection/index.d.ts +0 -1
  254. package/dist/fields/MultiStringField/MultiStringField.d.ts +0 -30
  255. package/dist/fields/MultiStringField/MultiStringInput.d.ts +0 -8
  256. package/dist/fields/MultiStringField/index.d.ts +0 -2
  257. package/dist/fields/NumberField/NumberInput.d.ts +0 -4
  258. package/dist/fields/NumberField/index.d.ts +0 -2
  259. package/dist/fields/SelectField/MultiSelectInput.d.ts +0 -4
  260. package/dist/fields/SelectField/SelectInput.d.ts +0 -4
  261. package/dist/fields/SelectField/index.d.ts +0 -4
  262. package/dist/fields/StringOrTextFields/StringField/StringField.d.ts +0 -19
  263. package/dist/fields/StringOrTextFields/StringField/StringInput.d.ts +0 -4
  264. package/dist/fields/StringOrTextFields/StringField/index.d.ts +0 -2
  265. package/dist/fields/StringOrTextFields/TextField/TextField.d.ts +0 -16
  266. package/dist/fields/StringOrTextFields/TextField/TextInput.d.ts +0 -4
  267. package/dist/fields/StringOrTextFields/TextField/index.d.ts +0 -2
  268. package/dist/fields/StringOrTextFields/index.d.ts +0 -2
  269. package/dist/fields/UploadField/UploadInput.d.ts +0 -4
  270. package/dist/fields/UploadField/index.d.ts +0 -2
  271. package/dist/fields/constants.d.ts +0 -20
  272. package/dist/fields/hooks.d.ts +0 -6
  273. package/dist/fields/index.d.ts +0 -11
  274. package/dist/forms.js.map +0 -1
  275. package/dist/forms.umd.cjs.map +0 -1
  276. package/dist/renderer/FormBrowser/FormBrowser.d.ts +0 -11
  277. package/dist/renderer/FormSubmissionBrowser/FormSubmissionBrowser.d.ts +0 -28
  278. package/dist/renderer/FormSubmissionViewer/FormSubmissionViewer.d.ts +0 -17
  279. package/dist/renderer/PatchForm/index.d.ts +0 -2
  280. package/dist/renderer/index.d.ts +0 -5
  281. package/dist/style.css +0 -34
  282. package/dist/typings.d.ts +0 -17
  283. package/dist/utils.d.ts +0 -7
  284. /package/dist/{fields → forms/fields}/UploadField/utils.d.ts +0 -0
@@ -0,0 +1,69 @@
1
+ import { Button, Menu, RiIcon } from "@overmap-ai/blocks"
2
+ import { memo, useCallback } from "react"
3
+
4
+ import { SEVERITY_COLOR_MAPPING } from "../../constants"
5
+ import { InputWithLabel, InputWithLabelAndHelpText, useFormikInput } from "../BaseField"
6
+ import { ComponentProps } from "../typings"
7
+ import { SelectField } from "./SelectField"
8
+
9
+ export const SelectInput = memo((props: ComponentProps<SelectField>) => {
10
+ const [{ inputId, labelId, size, severity, showInputOnly, field, fieldProps }, rest] = useFormikInput(props)
11
+ const { onChange, onBlur } = fieldProps
12
+ let [{ helpText, label }] = useFormikInput(props)
13
+ helpText = showInputOnly ? null : helpText
14
+ label = showInputOnly ? "" : label
15
+
16
+ const handleChange = useCallback(
17
+ (value: string | null) => {
18
+ onChange(value)
19
+ onBlur(value)
20
+ },
21
+ [onChange, onBlur],
22
+ )
23
+
24
+ const color = severity ? SEVERITY_COLOR_MAPPING[severity] : undefined
25
+
26
+ return (
27
+ <InputWithLabelAndHelpText helpText={helpText} severity={severity}>
28
+ <InputWithLabel
29
+ size={size}
30
+ severity={severity}
31
+ inputId={inputId}
32
+ labelId={labelId}
33
+ label={label}
34
+ image={showInputOnly ? undefined : field.image}
35
+ >
36
+ <Menu.Root>
37
+ <Menu.ClickTrigger>
38
+ <Button
39
+ {...fieldProps}
40
+ className="!justify-between"
41
+ id={inputId}
42
+ name={fieldProps.name}
43
+ accentColor={color}
44
+ variant="soft"
45
+ {...rest}
46
+ >
47
+ {fieldProps.value ? fieldProps.value : field.placeholder}
48
+ <RiIcon icon="RiArrowDownSLine" />
49
+ </Button>
50
+ </Menu.ClickTrigger>
51
+ <Menu.Content>
52
+ <Menu.SelectGroup value={fieldProps.value} onValueChange={handleChange}>
53
+ {field.options.map((option) => (
54
+ <Menu.SelectItem key={option.value} value={option.value}>
55
+ <Menu.SelectedIndicator>
56
+ <RiIcon icon="RiCheckLine" />
57
+ </Menu.SelectedIndicator>
58
+ {option.label}
59
+ </Menu.SelectItem>
60
+ ))}
61
+ </Menu.SelectGroup>
62
+ </Menu.Content>
63
+ </Menu.Root>
64
+ </InputWithLabel>
65
+ </InputWithLabelAndHelpText>
66
+ )
67
+ })
68
+
69
+ SelectInput.displayName = "SelectInput"
@@ -0,0 +1,4 @@
1
+ export * from "./MultiSelectField"
2
+ export * from "./MultiSelectInput"
3
+ export * from "./SelectField"
4
+ export * from "./SelectInput"
@@ -0,0 +1,61 @@
1
+ import { ReactNode } from "react"
2
+ import { RiInputField } from "react-icons/ri"
3
+
4
+ import { SHORT_TEXT_FIELD_MAX_LENGTH } from "../../../constants"
5
+ import { ISerializedField, SerializedStringField, StringInputType } from "../../../typings"
6
+ import { emptyBaseField } from "../../BaseField"
7
+ import { GetInputProps } from "../../typings"
8
+ import { StringOrTextField, StringOrTextFieldOptions } from "../StringOrTextField"
9
+ import { StringInput } from "./StringInput"
10
+
11
+ export interface StringFieldOptions extends Omit<StringOrTextFieldOptions, "type"> {
12
+ inputType?: StringInputType
13
+ }
14
+
15
+ export const emptyStringField = {
16
+ ...emptyBaseField,
17
+ type: "string",
18
+ maximum_length: SHORT_TEXT_FIELD_MAX_LENGTH,
19
+ input_type: "text",
20
+ }
21
+
22
+ export class StringField extends StringOrTextField<"string"> {
23
+ static readonly fieldTypeName = "Short Text"
24
+ static readonly fieldTypeDescription = `Short text fields can hold up to ${SHORT_TEXT_FIELD_MAX_LENGTH} characters on a single line.`
25
+ public readonly inputType: StringInputType
26
+
27
+ static Icon: typeof RiInputField = RiInputField
28
+
29
+ constructor(options: StringFieldOptions) {
30
+ const { inputType = "text", ...rest } = options
31
+ // the field supports a max length no larger than 500
32
+ const maxLength = options.maxLength
33
+ ? Math.min(SHORT_TEXT_FIELD_MAX_LENGTH, options.maxLength)
34
+ : SHORT_TEXT_FIELD_MAX_LENGTH
35
+ // the field supports a min length no larger than the max length
36
+ const minLength = options.minLength ? Math.min(options.minLength, maxLength) : undefined
37
+ super({ ...rest, maxLength, minLength, type: "string" })
38
+
39
+ this.inputType = inputType
40
+ }
41
+
42
+ serialize(): SerializedStringField {
43
+ return { ...super._serialize(), input_type: this.inputType }
44
+ }
45
+
46
+ static deserialize(data: ISerializedField): StringField {
47
+ if (data.type !== "string") throw new Error("Type mismatch.")
48
+ const { maximum_length, minimum_length, input_type, ...rest } = data
49
+ return new StringField({
50
+ ...rest,
51
+ maxLength: maximum_length,
52
+ minLength: minimum_length,
53
+ inputType: input_type,
54
+ placeholder: "Enter a short description",
55
+ })
56
+ }
57
+
58
+ getInput(props: GetInputProps<this>): ReactNode {
59
+ return <StringInput field={this} {...props} />
60
+ }
61
+ }
@@ -0,0 +1,41 @@
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 { StringField } from "./StringField"
8
+
9
+ export const StringInput = memo((props: ComponentProps<StringField>) => {
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
+ return (
18
+ <InputWithLabelAndHelpText helpText={helpText} severity={severity}>
19
+ <InputWithLabel
20
+ size={size}
21
+ severity={severity}
22
+ inputId={inputId}
23
+ labelId={labelId}
24
+ label={label}
25
+ image={showInputOnly ? undefined : field.image}
26
+ >
27
+ <Input.Root accentColor={color} variant="soft">
28
+ <Input.Field
29
+ {...rest}
30
+ {...fieldProps}
31
+ type={field.inputType}
32
+ id={inputId}
33
+ placeholder={field.placeholder}
34
+ />
35
+ </Input.Root>
36
+ </InputWithLabel>
37
+ </InputWithLabelAndHelpText>
38
+ )
39
+ })
40
+
41
+ StringInput.displayName = "StringInput"
@@ -0,0 +1,2 @@
1
+ export * from "./StringField"
2
+ export * from "./StringInput"
@@ -0,0 +1,143 @@
1
+ import get from "lodash.get"
2
+
3
+ import { FormikUserFormRevision } from "../../builder"
4
+ import { LONG_TEXT_FIELD_MAX_LENGTH } from "../../constants"
5
+ import { Form, SerializedStringField } from "../../typings"
6
+ import { BaseField, FieldOptions } from "../BaseField"
7
+ import { NumberField, NumberFieldValue } from "../NumberField"
8
+ import { InputFieldLevelValidator, InputValidator } from "../typings"
9
+
10
+ export interface StringOrTextFieldOptions extends FieldOptions<string> {
11
+ minLength?: NumberFieldValue
12
+ maxLength?: NumberFieldValue
13
+ placeholder?: string
14
+ }
15
+
16
+ // NOTE: If changing, also change it in NumberField.ts (avoid circular imports)
17
+ const valueIsFormikUserFormRevision = (form: FormikUserFormRevision | Form): form is FormikUserFormRevision => {
18
+ return "fields" in form
19
+ }
20
+
21
+ export type SerializedStringOrTextField<TIdentifier extends "string" | "text"> = Omit<SerializedStringField, "type"> & {
22
+ type: TIdentifier
23
+ }
24
+
25
+ export abstract class StringOrTextField<TIdentifier extends "string" | "text"> extends BaseField<string, TIdentifier> {
26
+ public readonly minLength?: number
27
+ public readonly maxLength: number
28
+ public readonly placeholder: string
29
+
30
+ protected constructor(options: StringOrTextFieldOptions) {
31
+ const { minLength, maxLength, placeholder = "", ...base } = options
32
+ super(base)
33
+ // lengths must be greater than or equal to 0
34
+ this.minLength = minLength ? Math.max(minLength, 0) : undefined
35
+ this.maxLength = maxLength ? Math.max(maxLength, 0) : LONG_TEXT_FIELD_MAX_LENGTH
36
+ this.placeholder = placeholder
37
+ }
38
+
39
+ /**
40
+ * This function returns a function that validates that the value given for "minimum length" (when creating a new field) is less than or
41
+ * equal to the value given for "maximum length".
42
+ */
43
+ static _validateMin: (path: string) => InputValidator<NumberFieldValue> = (path: string) => (value, allValues) => {
44
+ const field = valueIsFormikUserFormRevision(allValues)
45
+ ? (get(allValues, path) as SerializedStringField)
46
+ : allValues
47
+ if (typeof field.maximum_length === "number" && typeof value === "number" && field.maximum_length < value) {
48
+ return "Minimum cannot be greater than maximum."
49
+ }
50
+ return null
51
+ }
52
+
53
+ /**
54
+ * This function returns a function that validates that the value given for "maximum length" (when creating a new field) is greater than or
55
+ * equal to the value given for "minimum length".
56
+ */
57
+ static _validateMax: (path: string) => InputValidator<NumberFieldValue> = (path: string) => (value, allValues) => {
58
+ if (typeof value !== "number") return null
59
+
60
+ const { minimum_length: minimumLength } = valueIsFormikUserFormRevision(allValues)
61
+ ? (get(allValues, path) as SerializedStringField)
62
+ : allValues
63
+
64
+ if (typeof minimumLength !== "number") {
65
+ return null
66
+ }
67
+
68
+ if (minimumLength > value) {
69
+ return "Maximum cannot be less than minimum."
70
+ }
71
+ return null
72
+ }
73
+
74
+ static getFieldCreationSchema(parentPath = "") {
75
+ const path = parentPath && `${parentPath}.`
76
+ return [
77
+ {
78
+ field:
79
+ // min, max
80
+ new NumberField({
81
+ label: "Minimum length",
82
+ description: "Minimum number of characters",
83
+ required: false,
84
+ identifier: `${path}minimum_length`,
85
+ minimum: 0,
86
+ maximum: 100,
87
+ formValidators: [this._validateMin(parentPath)],
88
+ integers: true,
89
+ }),
90
+ showDirectly: false,
91
+ },
92
+ {
93
+ field: new NumberField({
94
+ label: "Maximum length",
95
+ description: "Maximum number of characters",
96
+ required: false,
97
+ identifier: `${path}maximum_length`,
98
+ minimum: 1,
99
+ maximum: LONG_TEXT_FIELD_MAX_LENGTH, // TODO: depends on short vs long text
100
+ formValidators: [this._validateMax(parentPath)],
101
+ // TODO: default: 500 (see: "Short text fields can hold up to 500 characters on a single line.")
102
+ integers: true,
103
+ }),
104
+ showDirectly: false,
105
+ },
106
+ ]
107
+ }
108
+
109
+ getFieldValidators(): InputFieldLevelValidator<string>[] {
110
+ const validators = super.getFieldValidators()
111
+
112
+ if (this.minLength) {
113
+ validators.push((value) => {
114
+ if (this.minLength && (!value || value.length < this.minLength)) {
115
+ // One exception to this rule:
116
+ if (!this.required && !value) return null
117
+ return `Minimum ${this.minLength} character(s).`
118
+ }
119
+ })
120
+ }
121
+ if (this.maxLength) {
122
+ validators.push((value) => {
123
+ if (typeof value === "string" && this.maxLength && value.length > this.maxLength) {
124
+ return `Maximum ${this.maxLength} character(s).`
125
+ }
126
+ })
127
+ }
128
+
129
+ return validators
130
+ }
131
+
132
+ protected _serialize(): SerializedStringOrTextField<TIdentifier> {
133
+ if (!this.identifier) {
134
+ throw new Error("Field identifier must be set before serializing.")
135
+ }
136
+ return {
137
+ ...super._serialize(),
138
+ minimum_length: this.minLength,
139
+ maximum_length: this.maxLength,
140
+ placeholder: this.placeholder,
141
+ }
142
+ }
143
+ }
@@ -0,0 +1,52 @@
1
+ import { ReactNode } from "react"
2
+ import { RiAlignJustify } from "react-icons/ri"
3
+
4
+ import { LONG_TEXT_FIELD_MAX_LENGTH } from "../../../constants"
5
+ import { ISerializedField, SerializedTextField } from "../../../typings"
6
+ import { emptyBaseField } from "../../BaseField"
7
+ import { GetInputProps } from "../../typings"
8
+ import { StringOrTextField, StringOrTextFieldOptions } from "../StringOrTextField"
9
+ import { TextInput } from "./TextInput"
10
+
11
+ export type TextFieldOptions = Omit<StringOrTextFieldOptions, "type">
12
+
13
+ export const emptyTextField = {
14
+ ...emptyBaseField,
15
+ type: "text",
16
+ maximum_length: LONG_TEXT_FIELD_MAX_LENGTH,
17
+ }
18
+
19
+ export class TextField extends StringOrTextField<"text"> {
20
+ static readonly fieldTypeName = "Paragraph"
21
+ static readonly fieldTypeDescription = `Paragraph fields can hold up to ${LONG_TEXT_FIELD_MAX_LENGTH} characters and can have multiple lines.`
22
+
23
+ static Icon: typeof RiAlignJustify = RiAlignJustify
24
+
25
+ constructor(options: TextFieldOptions) {
26
+ const maxLength = options.maxLength
27
+ ? Math.min(LONG_TEXT_FIELD_MAX_LENGTH, options.maxLength)
28
+ : LONG_TEXT_FIELD_MAX_LENGTH
29
+ // the field supports a min length no larger than the max length
30
+ const minLength = options.minLength ? Math.min(options.minLength, maxLength) : undefined
31
+ super({ ...options, maxLength, minLength, type: "text" })
32
+ }
33
+
34
+ serialize(): SerializedTextField {
35
+ return super._serialize()
36
+ }
37
+
38
+ static deserialize(data: ISerializedField) {
39
+ if (data.type !== "text") throw new Error("Type mismatch.")
40
+ const { maximum_length, minimum_length, ...rest } = data
41
+ return new TextField({
42
+ ...rest,
43
+ maxLength: maximum_length,
44
+ minLength: minimum_length,
45
+ placeholder: "Enter a description",
46
+ })
47
+ }
48
+
49
+ getInput(props: GetInputProps<this>): ReactNode {
50
+ return <TextInput field={this} {...props} />
51
+ }
52
+ }
@@ -0,0 +1,42 @@
1
+ import { TextArea } 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 { TextField } from "./TextField"
8
+
9
+ export const TextInput = memo((props: ComponentProps<TextField>) => {
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
+ return (
18
+ <InputWithLabelAndHelpText helpText={helpText} severity={severity}>
19
+ <InputWithLabel
20
+ size={size}
21
+ severity={severity}
22
+ inputId={inputId}
23
+ labelId={labelId}
24
+ label={label}
25
+ image={showInputOnly ? undefined : field.image}
26
+ >
27
+ <TextArea
28
+ {...rest}
29
+ {...fieldProps}
30
+ className="field-sizing-content"
31
+ resize="vertical"
32
+ id={inputId}
33
+ placeholder={field.placeholder}
34
+ accentColor={color}
35
+ variant="soft"
36
+ />
37
+ </InputWithLabel>
38
+ </InputWithLabelAndHelpText>
39
+ )
40
+ })
41
+
42
+ TextInput.displayName = "TextInput"
@@ -0,0 +1,2 @@
1
+ export * from "./TextField"
2
+ export * from "./TextInput"
@@ -0,0 +1,2 @@
1
+ export * from "./StringField"
2
+ export * from "./TextField"
@@ -0,0 +1,156 @@
1
+ import { ChangeEvent, ReactNode } from "react"
2
+ import { RiUpload2Line } from "react-icons/ri"
3
+
4
+ import { ISerializedField, SerializedUploadField } from "../../typings"
5
+ import { BaseField, ChildFieldOptions, emptyBaseField } from "../BaseField"
6
+ import { maxFileSizeMB } from "../constants"
7
+ import { NumberField } from "../NumberField"
8
+ import { MultiSelectField } from "../SelectField"
9
+ import { GetInputProps, InputFieldLevelValidator } from "../typings"
10
+ import { UploadInput } from "./UploadInput"
11
+
12
+ export interface UploadFieldOptions extends ChildFieldOptions<File[]> {
13
+ extensions?: string[]
14
+ maximum_size?: number | string
15
+ maximum_files?: number | string
16
+ }
17
+
18
+ export const emptyUploadField = {
19
+ ...emptyBaseField,
20
+ type: "upload",
21
+ extensions: [],
22
+ maximum_size: undefined,
23
+ maximum_files: 1,
24
+ }
25
+
26
+ export class UploadField extends BaseField<File[], "upload"> {
27
+ static readonly fieldTypeName = "Upload"
28
+ static readonly fieldTypeDescription = "Allows a file to be uploaded."
29
+
30
+ public readonly extensions?: string[]
31
+ public readonly maxFileSize: number | undefined
32
+ public readonly maxFiles: number
33
+ public readonly onlyValidateAfterTouched = false
34
+
35
+ static Icon: typeof RiUpload2Line = RiUpload2Line
36
+
37
+ constructor(options: UploadFieldOptions) {
38
+ const { extensions, maximum_files, maximum_size, ...base } = options
39
+ super({ ...base, type: "upload" })
40
+
41
+ this.maxFileSize = typeof maximum_size === "number" ? maximum_size : undefined
42
+ // if maximum_files is not a number or less than 1, default and clamp to 1
43
+ this.maxFiles = Math.max(typeof maximum_files === "number" ? maximum_files : 1, 1)
44
+
45
+ this.extensions = extensions
46
+ }
47
+
48
+ public getValueFromChangeEvent(event: ChangeEvent<HTMLInputElement>): File[] {
49
+ return Array.from(event.target.files || [])
50
+ }
51
+
52
+ protected isBlank(value: File[]): boolean {
53
+ return super.isBlank(value) || value.length === 0
54
+ }
55
+
56
+ static getFieldCreationSchema(parentPath = "") {
57
+ const path = parentPath && `${parentPath}.`
58
+ return [
59
+ {
60
+ field: new NumberField({
61
+ label: "How many files can be uploaded?",
62
+ description: "By default, only one file can be uploaded.",
63
+ required: false,
64
+ minimum: 1,
65
+ maximum: 10,
66
+ identifier: `${path}maximum_files`,
67
+ integers: true,
68
+ }),
69
+ showDirectly: false,
70
+ },
71
+ {
72
+ field: new NumberField({
73
+ // TODO: Default value
74
+ label: "What is the maximum size of each file?",
75
+ description: `Maximum file size in megabytes (between 1MB–${maxFileSizeMB}MB).`,
76
+ required: false,
77
+ identifier: `${path}maximum_size`,
78
+ minimum: 1,
79
+ maximum: maxFileSizeMB,
80
+ integers: true,
81
+ }),
82
+ showDirectly: false,
83
+ },
84
+ {
85
+ field: new MultiSelectField({
86
+ label: "Accepted file types",
87
+ description: "Types of allowed files to upload. If left blank, all files will be accepted.",
88
+ required: false,
89
+ identifier: `${path}extensions`,
90
+ options: [
91
+ {
92
+ value: "image/*",
93
+ label: "Images",
94
+ },
95
+ {
96
+ value: "audio/*",
97
+ label: "Audio files",
98
+ },
99
+ {
100
+ value: "video/*",
101
+ label: "Videos",
102
+ },
103
+ {
104
+ value: "text/*",
105
+ label: "Text files",
106
+ },
107
+ {
108
+ value: "application/*",
109
+ label: "Application files (includes PDFs and Word documents)",
110
+ },
111
+ ],
112
+ }),
113
+ showDirectly: false,
114
+ },
115
+ ]
116
+ }
117
+
118
+ getFieldValidators(): InputFieldLevelValidator<File[]>[] {
119
+ const validators = super.getFieldValidators()
120
+ const maxFileSizeInMB = this.maxFileSize ?? maxFileSizeMB
121
+ const maxFileSizeInB = maxFileSizeInMB * 1000 * 1000
122
+ const maxFiles = this.maxFiles || 1
123
+
124
+ validators.push((value) => {
125
+ if (value && value.some((file) => file.size > maxFileSizeInB)) {
126
+ return `Files must be at most ${maxFileSizeInMB}MB.`
127
+ }
128
+ })
129
+
130
+ validators.push((value) => {
131
+ if (value && value.length > maxFiles) {
132
+ return `You can only upload ${maxFiles} files.`
133
+ }
134
+ })
135
+
136
+ return validators
137
+ }
138
+
139
+ serialize(): SerializedUploadField {
140
+ return {
141
+ ...super._serialize(),
142
+ extensions: this.extensions,
143
+ maximum_size: this.maxFileSize,
144
+ maximum_files: this.maxFiles,
145
+ }
146
+ }
147
+
148
+ static deserialize(data: ISerializedField): UploadField {
149
+ if (data.type !== "upload") throw new Error("Type mismatch.")
150
+ return new UploadField(data)
151
+ }
152
+
153
+ getInput(props: GetInputProps<this>): ReactNode {
154
+ return <UploadInput field={this} {...props} />
155
+ }
156
+ }