@overmap-ai/forms 1.0.17-master.1 → 1.0.17-master.3
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/package.json +4 -1
- package/.husky/pre-commit +0 -6
- package/.prettierrc.json +0 -10
- package/.storybook/StoryDecorator.tsx +0 -22
- package/.storybook/main.ts +0 -20
- package/.storybook/palettes/green.css +0 -66
- package/.storybook/palettes/red.css +0 -66
- package/.storybook/preview.css +0 -39
- package/.storybook/preview.tsx +0 -31
- package/.storybook/tailwind-theme/accentPalette.css +0 -181
- package/.storybook/tailwind-theme/backgrounds.css +0 -11
- package/.storybook/tailwind-theme/basePalette.css +0 -178
- package/dev/publish-alpha.sh +0 -13
- package/dev/publish-patch.sh +0 -3
- package/eslint.config.js +0 -56
- package/src/ColorPicker/ColorPicker.tsx +0 -47
- package/src/ColorPicker/index.ts +0 -1
- package/src/FileBadge/FileBadge.tsx +0 -27
- package/src/FileBadge/index.ts +0 -1
- package/src/FileCard/FileCard.stories.tsx +0 -69
- package/src/FileCard/FileCard.tsx +0 -53
- package/src/FileCard/index.ts +0 -1
- package/src/FileIcon/FileIcon.tsx +0 -31
- package/src/FileIcon/index.ts +0 -1
- package/src/FileViewer/FileViewerProvider.stories.tsx +0 -50
- package/src/FileViewer/FileViewerProvider.tsx +0 -72
- package/src/FileViewer/context.ts +0 -11
- package/src/FileViewer/index.ts +0 -3
- package/src/FileViewer/typings.ts +0 -5
- package/src/ImageCard/ImageCard.stories.tsx +0 -94
- package/src/ImageCard/ImageCard.tsx +0 -82
- package/src/ImageCard/index.ts +0 -1
- package/src/ImageMarkup/ImageMarkup.stories.tsx +0 -65
- package/src/ImageMarkup/ImageMarkup.tsx +0 -268
- package/src/ImageMarkup/index.ts +0 -1
- package/src/ImageViewer/ImageViewer.stories.tsx +0 -57
- package/src/ImageViewer/ImageViewer.tsx +0 -124
- package/src/ImageViewer/constants.ts +0 -1
- package/src/ImageViewer/index.ts +0 -2
- package/src/PDFViewer/PDFViewer.stories.tsx +0 -55
- package/src/PDFViewer/PDFViewer.tsx +0 -170
- package/src/PDFViewer/constants.ts +0 -1
- package/src/PDFViewer/index.ts +0 -2
- package/src/SpreadsheetViewer/SpreadsheetViewer.stories.tsx +0 -55
- package/src/SpreadsheetViewer/SpreadsheetViewer.tsx +0 -162
- package/src/SpreadsheetViewer/constants.ts +0 -8
- package/src/SpreadsheetViewer/index.ts +0 -2
- package/src/forms/builder/DropDispatch.ts +0 -84
- package/src/forms/builder/FieldActions.tsx +0 -155
- package/src/forms/builder/FieldBuilder.tsx +0 -386
- package/src/forms/builder/FieldSectionWithActions.tsx +0 -260
- package/src/forms/builder/FieldWithActions.tsx +0 -129
- package/src/forms/builder/FieldsEditor.tsx +0 -180
- package/src/forms/builder/FormBuilder.stories.tsx +0 -105
- package/src/forms/builder/FormBuilder.tsx +0 -237
- package/src/forms/builder/constants.ts +0 -18
- package/src/forms/builder/hooks.tsx +0 -24
- package/src/forms/builder/index.ts +0 -2
- package/src/forms/builder/typings.ts +0 -18
- package/src/forms/builder/utils.ts +0 -229
- package/src/forms/constants.ts +0 -9
- package/src/forms/constantsJsx.tsx +0 -67
- package/src/forms/fields/BaseField/BaseField.ts +0 -152
- package/src/forms/fields/BaseField/hooks.tsx +0 -60
- package/src/forms/fields/BaseField/index.ts +0 -4
- package/src/forms/fields/BaseField/layouts.tsx +0 -100
- package/src/forms/fields/BaseField/typings.ts +0 -9
- package/src/forms/fields/BooleanField/BooleanField.tsx +0 -48
- package/src/forms/fields/BooleanField/BooleanInput.tsx +0 -54
- package/src/forms/fields/BooleanField/index.ts +0 -2
- package/src/forms/fields/CustomField/CustomField.tsx +0 -45
- package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputCloner.tsx +0 -25
- package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputClonerField.tsx +0 -26
- package/src/forms/fields/CustomField/FieldInputClonerField/index.ts +0 -3
- package/src/forms/fields/CustomField/FieldInputClonerField/typings.ts +0 -8
- package/src/forms/fields/CustomField/index.ts +0 -1
- package/src/forms/fields/DateField/DateField.tsx +0 -42
- package/src/forms/fields/DateField/DateInput.tsx +0 -39
- package/src/forms/fields/DateField/index.ts +0 -2
- package/src/forms/fields/FieldSection/FieldSection.tsx +0 -173
- package/src/forms/fields/FieldSection/FieldSectionLayout.tsx +0 -56
- package/src/forms/fields/FieldSection/index.ts +0 -1
- package/src/forms/fields/MultiStringField/MultiStringField.tsx +0 -90
- package/src/forms/fields/MultiStringField/MultiStringInput.tsx +0 -207
- package/src/forms/fields/MultiStringField/index.ts +0 -2
- package/src/forms/fields/NumberField/NumberField.tsx +0 -173
- package/src/forms/fields/NumberField/NumberInput.tsx +0 -44
- package/src/forms/fields/NumberField/index.ts +0 -2
- package/src/forms/fields/QrField/QrField.tsx +0 -38
- package/src/forms/fields/QrField/QrInput.module.sass +0 -5
- package/src/forms/fields/QrField/QrInput.tsx +0 -144
- package/src/forms/fields/QrField/index.ts +0 -2
- package/src/forms/fields/SelectField/BaseSelectField.ts +0 -73
- package/src/forms/fields/SelectField/MultiSelectField.tsx +0 -53
- package/src/forms/fields/SelectField/MultiSelectInput.tsx +0 -80
- package/src/forms/fields/SelectField/SelectField.tsx +0 -49
- package/src/forms/fields/SelectField/SelectInput.tsx +0 -69
- package/src/forms/fields/SelectField/index.ts +0 -4
- package/src/forms/fields/StringOrTextFields/StringField/StringField.tsx +0 -61
- package/src/forms/fields/StringOrTextFields/StringField/StringInput.tsx +0 -41
- package/src/forms/fields/StringOrTextFields/StringField/index.ts +0 -2
- package/src/forms/fields/StringOrTextFields/StringOrTextField.ts +0 -143
- package/src/forms/fields/StringOrTextFields/TextField/TextField.tsx +0 -52
- package/src/forms/fields/StringOrTextFields/TextField/TextInput.tsx +0 -42
- package/src/forms/fields/StringOrTextFields/TextField/index.ts +0 -2
- package/src/forms/fields/StringOrTextFields/index.ts +0 -2
- package/src/forms/fields/UploadField/UploadField.tsx +0 -156
- package/src/forms/fields/UploadField/UploadInput.tsx +0 -220
- package/src/forms/fields/UploadField/index.ts +0 -2
- package/src/forms/fields/UploadField/utils.ts +0 -17
- package/src/forms/fields/constants.ts +0 -43
- package/src/forms/fields/hooks.tsx +0 -26
- package/src/forms/fields/index.ts +0 -12
- package/src/forms/fields/typings.ts +0 -45
- package/src/forms/fields/utils.ts +0 -125
- package/src/forms/index.ts +0 -5
- package/src/forms/renderer/FormRenderer/FormRenderer.stories.tsx +0 -142
- package/src/forms/renderer/FormRenderer/FormRenderer.tsx +0 -135
- package/src/forms/renderer/PatchForm/Field.tsx +0 -41
- package/src/forms/renderer/PatchForm/PatchForm.stories.tsx +0 -91
- package/src/forms/renderer/PatchForm/Provider.tsx +0 -119
- package/src/forms/renderer/PatchForm/index.ts +0 -2
- package/src/forms/renderer/index.ts +0 -2
- package/src/forms/typings.ts +0 -162
- package/src/forms/utils.ts +0 -69
- package/src/index.ts +0 -11
- package/src/vite-env.d.ts +0 -1
- package/tailwind.config.ts +0 -8
- package/tsconfig.json +0 -26
- package/tsconfig.node.json +0 -10
- package/vite.config.ts +0 -23
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Button } from "@overmap-ai/blocks"
|
|
2
|
-
import type { Meta } from "@storybook/react"
|
|
3
|
-
import { ChangeEvent, useCallback, useRef, useState } from "react"
|
|
4
|
-
|
|
5
|
-
import { SpreadsheetViewer } from "./SpreadsheetViewer"
|
|
6
|
-
|
|
7
|
-
const meta = {
|
|
8
|
-
title: "Components/SpreadsheetViewer",
|
|
9
|
-
component: SpreadsheetViewer,
|
|
10
|
-
tags: ["autodocs"],
|
|
11
|
-
parameters: {
|
|
12
|
-
disablePanel: true,
|
|
13
|
-
},
|
|
14
|
-
} satisfies Meta<typeof SpreadsheetViewer>
|
|
15
|
-
|
|
16
|
-
export default meta
|
|
17
|
-
|
|
18
|
-
export const Basic = () => {
|
|
19
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
20
|
-
const [file, setFile] = useState<File | null>(null)
|
|
21
|
-
const [open, setOpen] = useState(false)
|
|
22
|
-
|
|
23
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
24
|
-
if (e.target.files) {
|
|
25
|
-
const files = Array.from(e.target.files)
|
|
26
|
-
if (files.length > 0) {
|
|
27
|
-
setFile(files[0] || null)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}, [])
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<>
|
|
34
|
-
<input ref={inputRef} type="file" onChange={handleChange} />
|
|
35
|
-
<Button
|
|
36
|
-
onClick={() => {
|
|
37
|
-
setOpen(!open)
|
|
38
|
-
}}
|
|
39
|
-
>
|
|
40
|
-
open
|
|
41
|
-
</Button>
|
|
42
|
-
{file && open && (
|
|
43
|
-
<SpreadsheetViewer
|
|
44
|
-
file={file}
|
|
45
|
-
onClose={() => {
|
|
46
|
-
setOpen(false)
|
|
47
|
-
}}
|
|
48
|
-
onDelete={() => {
|
|
49
|
-
setFile(null)
|
|
50
|
-
}}
|
|
51
|
-
/>
|
|
52
|
-
)}
|
|
53
|
-
</>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { Badge, ButtonGroup, IconButton, RiIcon, Spinner, useViewportSize } from "@overmap-ai/blocks"
|
|
2
|
-
import { downloadFile } from "@overmap-ai/core"
|
|
3
|
-
import * as RadixDialog from "@radix-ui/react-dialog"
|
|
4
|
-
import { lazy, memo, Suspense, useCallback, useEffect, useMemo, useState } from "react"
|
|
5
|
-
import { read, utils, WorkSheet } from "xlsx"
|
|
6
|
-
|
|
7
|
-
import { FileBadge } from "../FileBadge"
|
|
8
|
-
|
|
9
|
-
interface SpreadsheetViewerProps {
|
|
10
|
-
file: File
|
|
11
|
-
onClose: () => void
|
|
12
|
-
onDelete?: (file: File) => void
|
|
13
|
-
}
|
|
14
|
-
export const SpreadsheetViewer = memo((props: SpreadsheetViewerProps) => {
|
|
15
|
-
const { file, onClose, onDelete } = props
|
|
16
|
-
const { md } = useViewportSize()
|
|
17
|
-
const [sheetNames, setSheetNames] = useState<string[]>([])
|
|
18
|
-
const [sheets, setSheets] = useState<Record<string, WorkSheet>>({})
|
|
19
|
-
const [activeSheet, setActiveSheet] = useState<string | undefined>()
|
|
20
|
-
const [data, setData] = useState<{
|
|
21
|
-
rowLabels: string[]
|
|
22
|
-
columnLabels: string[]
|
|
23
|
-
data: { value: unknown; readOnly: boolean }[][]
|
|
24
|
-
}>({
|
|
25
|
-
rowLabels: [],
|
|
26
|
-
columnLabels: [],
|
|
27
|
-
data: [[]],
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
const Spreadsheet = useMemo(() => {
|
|
31
|
-
return lazy(() => import("react-spreadsheet"))
|
|
32
|
-
}, [file, activeSheet])
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
void file.arrayBuffer().then((result) => {
|
|
36
|
-
const { SheetNames, Sheets } = read(result)
|
|
37
|
-
setSheetNames(SheetNames)
|
|
38
|
-
setSheets(Sheets)
|
|
39
|
-
setActiveSheet(SheetNames[0])
|
|
40
|
-
})
|
|
41
|
-
}, [file])
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (!activeSheet) return
|
|
45
|
-
const worksheet = sheets[activeSheet]!
|
|
46
|
-
const sheetAsJson = utils.sheet_to_json<NonNullable<unknown>>(worksheet)
|
|
47
|
-
const columnLabels = Object.keys(sheetAsJson[0] ?? {}).splice(1)
|
|
48
|
-
const rowLabels = sheetAsJson.map((row) => Object.values(row)[0] as string)
|
|
49
|
-
|
|
50
|
-
// Extract data based on column labels for each row, format as expected by "react-spreadsheet"
|
|
51
|
-
const data = sheetAsJson.map((row: Record<string, unknown>) => {
|
|
52
|
-
return columnLabels.map((columnLabel) => ({ value: row[columnLabel], readOnly: true }))
|
|
53
|
-
})
|
|
54
|
-
setData({ rowLabels, columnLabels, data })
|
|
55
|
-
}, [activeSheet, sheets])
|
|
56
|
-
|
|
57
|
-
const handleDelete = useCallback(() => {
|
|
58
|
-
if (onDelete) onDelete(file)
|
|
59
|
-
}, [file, onDelete])
|
|
60
|
-
|
|
61
|
-
const handleDownload = useCallback(() => {
|
|
62
|
-
downloadFile(file)
|
|
63
|
-
}, [file])
|
|
64
|
-
|
|
65
|
-
const handleNextSheet = useCallback(() => {
|
|
66
|
-
if (!activeSheet) throw new Error(`Expected an activeSheet, got ${activeSheet}`)
|
|
67
|
-
const activeSheetIndex = sheetNames.indexOf(activeSheet)
|
|
68
|
-
if (activeSheetIndex < 0)
|
|
69
|
-
throw new Error(
|
|
70
|
-
`Expected activeSheet to be present in sheets, ${activeSheet} not found in ${sheetNames.join(", ")}`,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
if (activeSheetIndex === sheetNames.length - 1) return
|
|
74
|
-
|
|
75
|
-
setActiveSheet(sheetNames[activeSheetIndex + 1])
|
|
76
|
-
}, [activeSheet, sheetNames])
|
|
77
|
-
|
|
78
|
-
const handlePreviousSheet = useCallback(() => {
|
|
79
|
-
if (!activeSheet) throw new Error(`Expected an activeSheet, got ${activeSheet}`)
|
|
80
|
-
const activeSheetIndex = sheetNames.indexOf(activeSheet)
|
|
81
|
-
if (activeSheetIndex < 0)
|
|
82
|
-
throw new Error(
|
|
83
|
-
`Expected activeSheet to be present in sheets, ${activeSheet} not found in ${sheetNames.join(", ")}`,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
if (activeSheetIndex === 0) return
|
|
87
|
-
|
|
88
|
-
setActiveSheet(sheetNames[activeSheetIndex - 1])
|
|
89
|
-
}, [activeSheet, sheetNames])
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<RadixDialog.Root open onOpenChange={onClose}>
|
|
93
|
-
<RadixDialog.Portal>
|
|
94
|
-
<RadixDialog.Overlay className="light:bg-(--black-a6) fixed inset-0 dark:bg-(--black-a8)" />
|
|
95
|
-
<RadixDialog.Content className="fixed inset-0">
|
|
96
|
-
<div className="flex h-full w-full flex-col overflow-hidden">
|
|
97
|
-
<div className="h-max w-full shrink-0 bg-(--color-background) p-2">
|
|
98
|
-
<ButtonGroup size="md" variant="soft" accentColor="base">
|
|
99
|
-
<div className="grid w-full grid-cols-3">
|
|
100
|
-
<div className="flex gap-2 items-center">
|
|
101
|
-
<IconButton onClick={onClose} aria-label="close">
|
|
102
|
-
<RiIcon icon="RiCloseLine" />
|
|
103
|
-
</IconButton>
|
|
104
|
-
<IconButton onClick={handleDownload} aria-label="download">
|
|
105
|
-
<RiIcon icon="RiDownload2Line" />
|
|
106
|
-
</IconButton>
|
|
107
|
-
</div>
|
|
108
|
-
<div className="flex justify-center">
|
|
109
|
-
<FileBadge
|
|
110
|
-
file={file}
|
|
111
|
-
accentColor="base"
|
|
112
|
-
truncateLength={!md ? 25 : undefined}
|
|
113
|
-
/>
|
|
114
|
-
</div>
|
|
115
|
-
<div className="flex justify-end gap-2">
|
|
116
|
-
{!!onDelete && (
|
|
117
|
-
<IconButton aria-label="delete" onClick={handleDelete}>
|
|
118
|
-
<RiIcon icon="RiDeleteBin2Line" />
|
|
119
|
-
</IconButton>
|
|
120
|
-
)}
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</ButtonGroup>
|
|
124
|
-
</div>
|
|
125
|
-
<div className="flex h-full w-full flex-col items-center gap-3 overflow-hidden p-2">
|
|
126
|
-
<Suspense fallback={<Spinner />}>
|
|
127
|
-
<div className="flex h-max max-h-[calc(100%-50px)] w-max max-w-full overflow-auto [scrollbar-color:var(--base-6)_transparent] [scrollbar-width:thin]">
|
|
128
|
-
<Spreadsheet
|
|
129
|
-
className="z-[1] h-full"
|
|
130
|
-
data={data.data}
|
|
131
|
-
rowLabels={data.rowLabels}
|
|
132
|
-
columnLabels={data.columnLabels}
|
|
133
|
-
// TODO: set darkMode
|
|
134
|
-
/>
|
|
135
|
-
</div>
|
|
136
|
-
<div className="flex h-max shrink-0 bg-(--base-2)">
|
|
137
|
-
<ButtonGroup
|
|
138
|
-
className="flex items-center"
|
|
139
|
-
variant="solid"
|
|
140
|
-
accentColor="base"
|
|
141
|
-
size="sm"
|
|
142
|
-
>
|
|
143
|
-
<IconButton aria-label="previous" onClick={handlePreviousSheet}>
|
|
144
|
-
<RiIcon icon="RiArrowLeftLine" />
|
|
145
|
-
</IconButton>
|
|
146
|
-
<Badge variant="solid" size="sm" style={{ borderRadius: 0 }} accentColor="base">
|
|
147
|
-
{activeSheet}
|
|
148
|
-
</Badge>
|
|
149
|
-
<IconButton aria-label="next" onClick={handleNextSheet}>
|
|
150
|
-
<RiIcon icon="RiArrowRightLine" />
|
|
151
|
-
</IconButton>
|
|
152
|
-
</ButtonGroup>
|
|
153
|
-
</div>
|
|
154
|
-
</Suspense>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
</RadixDialog.Content>
|
|
158
|
-
</RadixDialog.Portal>
|
|
159
|
-
</RadixDialog.Root>
|
|
160
|
-
)
|
|
161
|
-
})
|
|
162
|
-
SpreadsheetViewer.displayName = "SpreadsheetViewer"
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Reducer } from "react"
|
|
2
|
-
|
|
3
|
-
import { SerializedFieldSection } from "../typings"
|
|
4
|
-
|
|
5
|
-
export interface SmallFieldSection {
|
|
6
|
-
/** is dropping disabled in this section? */
|
|
7
|
-
disabled: boolean
|
|
8
|
-
/** id's of field that may not be dropped on this section */
|
|
9
|
-
conditionFields: Set<string>
|
|
10
|
-
/** the index of the section the condition is in */
|
|
11
|
-
conditionIndex?: number
|
|
12
|
-
/** the index of the section */
|
|
13
|
-
index: number
|
|
14
|
-
/** label of the section */
|
|
15
|
-
label: string | null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// section id -> section state
|
|
19
|
-
export type DropState = Record<string, SmallFieldSection>
|
|
20
|
-
|
|
21
|
-
export type DropAction = { type: "release" } | { type: "hold"; fieldId: string } | { type: "update"; state: DropState }
|
|
22
|
-
|
|
23
|
-
export const reducer: Reducer<DropState, DropAction> = (state, action) => {
|
|
24
|
-
const next = { ...state }
|
|
25
|
-
|
|
26
|
-
switch (action.type) {
|
|
27
|
-
case "release":
|
|
28
|
-
for (const sectionId in next) {
|
|
29
|
-
next[sectionId]!.disabled = false
|
|
30
|
-
}
|
|
31
|
-
return next
|
|
32
|
-
case "hold":
|
|
33
|
-
for (const sectionId in next) {
|
|
34
|
-
if (next[sectionId]?.conditionFields.has(action.fieldId)) {
|
|
35
|
-
next[sectionId]!.disabled = true
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return next
|
|
39
|
-
case "update":
|
|
40
|
-
return action.state
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** @returns the index of the section containing the field */
|
|
45
|
-
const getConditionIndex = (fields: SerializedFieldSection[], identifier?: string): number | undefined => {
|
|
46
|
-
if (!identifier) return undefined
|
|
47
|
-
|
|
48
|
-
for (let i = 0; i < fields.length; i++) {
|
|
49
|
-
const section = fields[i]
|
|
50
|
-
|
|
51
|
-
if (!section) continue
|
|
52
|
-
|
|
53
|
-
for (const field of section.fields) {
|
|
54
|
-
if (field.identifier === identifier) return i
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** creates a `DropState` from a list of `SerializedFieldSection` objects */
|
|
60
|
-
export const initializer = (fields: SerializedFieldSection[]): DropState => {
|
|
61
|
-
const acc: DropState = {}
|
|
62
|
-
|
|
63
|
-
for (let index = 0; index < fields.length; index++) {
|
|
64
|
-
const field = fields[index]
|
|
65
|
-
if (!field) throw new Error("Field is undefined.")
|
|
66
|
-
|
|
67
|
-
// include any fields that are disabled by the condition of the previous field
|
|
68
|
-
const previousConditionFields = index > 0 ? acc[fields[index - 1]!.identifier]?.conditionFields : undefined
|
|
69
|
-
const disabledFields = new Set<string>(previousConditionFields)
|
|
70
|
-
if (field.condition?.identifier) {
|
|
71
|
-
disabledFields.add(field.condition.identifier)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
acc[field.identifier] = {
|
|
75
|
-
disabled: false,
|
|
76
|
-
conditionFields: disabledFields,
|
|
77
|
-
conditionIndex: getConditionIndex(fields, field.condition?.identifier),
|
|
78
|
-
index,
|
|
79
|
-
label: field.label,
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return acc
|
|
84
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { IconButton, RiIcon } from "@overmap-ai/blocks"
|
|
2
|
-
import { Menu } from "@overmap-ai/blocks"
|
|
3
|
-
import { useFormikContext } from "formik"
|
|
4
|
-
import { ChangeEvent, ComponentProps, memo, ReactNode, useMemo, useRef } from "react"
|
|
5
|
-
import type { IconType } from "react-icons"
|
|
6
|
-
import { RiArrowDownLine, RiArrowUpLine, RiDeleteBin2Line, RiFileCopyLine, RiImageLine } from "react-icons/ri"
|
|
7
|
-
|
|
8
|
-
import { FieldTypeIdentifier } from "../typings"
|
|
9
|
-
import { FormikUserFormRevision } from "./typings"
|
|
10
|
-
|
|
11
|
-
interface FieldActionsProps {
|
|
12
|
-
index: number
|
|
13
|
-
type: FieldTypeIdentifier
|
|
14
|
-
sectionIndex?: number
|
|
15
|
-
remove: () => void
|
|
16
|
-
duplicate: () => void
|
|
17
|
-
move: (direction: "up" | "down") => void
|
|
18
|
-
upload?: (event: ChangeEvent<HTMLInputElement>) => void
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ActionsType {
|
|
22
|
-
Icon: IconType
|
|
23
|
-
buttonProps?: ComponentProps<"div">
|
|
24
|
-
key: string
|
|
25
|
-
text: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const FieldActions = memo((props: FieldActionsProps) => {
|
|
29
|
-
const { index, type, sectionIndex, remove, duplicate, move, upload } = props
|
|
30
|
-
// Upload function prop must be defined for non-section fields
|
|
31
|
-
if (type !== "section" && !upload) {
|
|
32
|
-
throw new Error("Upload function prop must be defined for non-section fields.")
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const { values } = useFormikContext<FormikUserFormRevision>()
|
|
36
|
-
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
37
|
-
|
|
38
|
-
const actions: ActionsType[] = useMemo(() => {
|
|
39
|
-
const actions = [
|
|
40
|
-
{
|
|
41
|
-
Icon: RiFileCopyLine,
|
|
42
|
-
key: "duplicate",
|
|
43
|
-
text: "Duplicate",
|
|
44
|
-
buttonProps: { onClick: duplicate },
|
|
45
|
-
},
|
|
46
|
-
]
|
|
47
|
-
// Show delete button only if not first section
|
|
48
|
-
if (index !== 0) {
|
|
49
|
-
actions.push({
|
|
50
|
-
Icon: RiDeleteBin2Line,
|
|
51
|
-
key: "delete",
|
|
52
|
-
text: "Delete",
|
|
53
|
-
buttonProps: { onClick: remove },
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Show upload button for non-section fields
|
|
58
|
-
if (type !== "section") {
|
|
59
|
-
actions.unshift({
|
|
60
|
-
Icon: RiImageLine,
|
|
61
|
-
key: "upload",
|
|
62
|
-
text: "Upload image",
|
|
63
|
-
buttonProps: {
|
|
64
|
-
onClick: () => {
|
|
65
|
-
fileInputRef.current?.click()
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
// Only show down arrow if it's not the last section or last field in the last section
|
|
71
|
-
if (
|
|
72
|
-
(sectionIndex === undefined && index !== values.fields.length - 1) ||
|
|
73
|
-
(sectionIndex !== undefined &&
|
|
74
|
-
(sectionIndex < values.fields.length - 1 || index !== values.fields[sectionIndex]!.fields.length - 1))
|
|
75
|
-
) {
|
|
76
|
-
actions.unshift({
|
|
77
|
-
Icon: RiArrowDownLine,
|
|
78
|
-
key: "moveDown",
|
|
79
|
-
text: "Move down",
|
|
80
|
-
buttonProps: {
|
|
81
|
-
onClick: () => {
|
|
82
|
-
move("down")
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
// Only show up arrow if it's not the first section or first field in the first section
|
|
88
|
-
if (
|
|
89
|
-
(sectionIndex === undefined && index !== 0) ||
|
|
90
|
-
(sectionIndex !== undefined && (sectionIndex !== 0 || index !== 0))
|
|
91
|
-
) {
|
|
92
|
-
actions.unshift({
|
|
93
|
-
Icon: RiArrowUpLine,
|
|
94
|
-
key: "moveUp",
|
|
95
|
-
text: "Move up",
|
|
96
|
-
buttonProps: {
|
|
97
|
-
onClick: () => {
|
|
98
|
-
move("up")
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return actions
|
|
105
|
-
}, [duplicate, index, move, remove, sectionIndex, type, values.fields])
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<>
|
|
109
|
-
{/* For tablet and up */}
|
|
110
|
-
<div className="mx-2 flex hidden flex-col gap-5 sm:block">
|
|
111
|
-
{actions.map((Action) => {
|
|
112
|
-
const Icon = Action.Icon as () => ReactNode
|
|
113
|
-
return (
|
|
114
|
-
<IconButton
|
|
115
|
-
key={Action.key}
|
|
116
|
-
type="button"
|
|
117
|
-
variant="ghost"
|
|
118
|
-
accentColor={Action.key.startsWith("move") ? "base" : undefined}
|
|
119
|
-
aria-label={Action.text}
|
|
120
|
-
{...(Action.buttonProps as Omit<ComponentProps<typeof IconButton>, "aria-label">)}
|
|
121
|
-
>
|
|
122
|
-
<Icon />
|
|
123
|
-
</IconButton>
|
|
124
|
-
)
|
|
125
|
-
})}
|
|
126
|
-
</div>
|
|
127
|
-
{/* For mobile devices */}
|
|
128
|
-
<div className="sm:hidden">
|
|
129
|
-
<Menu.Root>
|
|
130
|
-
<Menu.ClickTrigger>
|
|
131
|
-
<IconButton variant="ghost" aria-label="Actions menu">
|
|
132
|
-
<RiIcon icon="RiMore2Line" />
|
|
133
|
-
</IconButton>
|
|
134
|
-
</Menu.ClickTrigger>
|
|
135
|
-
<Menu.Content>
|
|
136
|
-
{actions.map((Action) => {
|
|
137
|
-
const Icon = Action.Icon as () => ReactNode
|
|
138
|
-
return (
|
|
139
|
-
<Menu.Item key={Action.key} onClick={Action.buttonProps?.onClick}>
|
|
140
|
-
<Icon />
|
|
141
|
-
{Action.text}
|
|
142
|
-
</Menu.Item>
|
|
143
|
-
)
|
|
144
|
-
})}
|
|
145
|
-
</Menu.Content>
|
|
146
|
-
</Menu.Root>
|
|
147
|
-
</div>
|
|
148
|
-
{type !== "section" && (
|
|
149
|
-
<input style={{ display: "none" }} ref={fileInputRef} type="file" accept="image/*" onChange={upload} />
|
|
150
|
-
)}
|
|
151
|
-
</>
|
|
152
|
-
)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
FieldActions.displayName = "FieldActions"
|