@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
package/eslint.config.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import pluginJs from "@eslint/js"
|
|
2
|
-
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended"
|
|
3
|
-
import pluginReact from "eslint-plugin-react"
|
|
4
|
-
import simpleImportSort from "eslint-plugin-simple-import-sort"
|
|
5
|
-
import pluginStorybook from "eslint-plugin-storybook"
|
|
6
|
-
import globals from "globals"
|
|
7
|
-
import tseslint from "typescript-eslint"
|
|
8
|
-
|
|
9
|
-
/** @type {import('eslint').Linter.Config[]} */
|
|
10
|
-
export default [
|
|
11
|
-
{ files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
|
|
12
|
-
{ languageOptions: { globals: globals.browser } },
|
|
13
|
-
pluginJs.configs.recommended,
|
|
14
|
-
...tseslint.configs.recommended,
|
|
15
|
-
{
|
|
16
|
-
rules: {
|
|
17
|
-
"no-unused-vars": "off",
|
|
18
|
-
"@typescript-eslint/no-unused-vars": [
|
|
19
|
-
"error",
|
|
20
|
-
{
|
|
21
|
-
varsIgnorePattern: "^_",
|
|
22
|
-
argsIgnorePattern: "^_",
|
|
23
|
-
caughtErrorsIgnorePattern: "^_",
|
|
24
|
-
},
|
|
25
|
-
],
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
plugins: {
|
|
30
|
-
"simple-import-sort": simpleImportSort,
|
|
31
|
-
},
|
|
32
|
-
rules: {
|
|
33
|
-
"simple-import-sort/imports": "error",
|
|
34
|
-
"simple-import-sort/exports": "error",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
...pluginReact.configs.flat.recommended,
|
|
39
|
-
settings: {
|
|
40
|
-
react: { version: "detect" },
|
|
41
|
-
},
|
|
42
|
-
rules: {
|
|
43
|
-
"react/react-in-jsx-scope": "off",
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
...pluginStorybook.configs["flat/recommended"],
|
|
47
|
-
eslintPluginPrettierRecommended,
|
|
48
|
-
{
|
|
49
|
-
linterOptions: {
|
|
50
|
-
reportUnusedDisableDirectives: "error",
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
ignores: ["!.storybook", "dist"],
|
|
55
|
-
},
|
|
56
|
-
]
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { IconButton, Popover, RiIcon } from "@overmap-ai/blocks"
|
|
2
|
-
import { CSSColor } from "@overmap-ai/core"
|
|
3
|
-
import { memo, ReactElement, useCallback } from "react"
|
|
4
|
-
|
|
5
|
-
interface ColorPickerProps {
|
|
6
|
-
selectedColor: CSSColor | null
|
|
7
|
-
allColors: CSSColor[]
|
|
8
|
-
onFinish: (color: CSSColor) => void
|
|
9
|
-
trigger: ReactElement
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const ColorPicker = memo((props: ColorPickerProps) => {
|
|
13
|
-
const { selectedColor, allColors, onFinish, trigger } = props
|
|
14
|
-
|
|
15
|
-
const handleSelectedColorChange = useCallback(
|
|
16
|
-
(color: CSSColor) => () => {
|
|
17
|
-
onFinish(color)
|
|
18
|
-
},
|
|
19
|
-
[onFinish],
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<Popover.Root>
|
|
24
|
-
<Popover.Trigger>{trigger}</Popover.Trigger>
|
|
25
|
-
<Popover.Content size="sm">
|
|
26
|
-
<div className="grid w-max grid-cols-7 gap-x-1 gap-y-1">
|
|
27
|
-
{allColors.map((color) => (
|
|
28
|
-
<IconButton
|
|
29
|
-
key={color}
|
|
30
|
-
onClick={handleSelectedColorChange(color)}
|
|
31
|
-
style={{
|
|
32
|
-
backgroundColor: color,
|
|
33
|
-
}}
|
|
34
|
-
type="button"
|
|
35
|
-
variant="solid"
|
|
36
|
-
aria-label={color}
|
|
37
|
-
>
|
|
38
|
-
{selectedColor === color && <RiIcon icon="RiCheckLine" />}
|
|
39
|
-
</IconButton>
|
|
40
|
-
))}
|
|
41
|
-
</div>
|
|
42
|
-
</Popover.Content>
|
|
43
|
-
</Popover.Root>
|
|
44
|
-
)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
ColorPicker.displayName = "ColorPicker"
|
package/src/ColorPicker/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./ColorPicker"
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Badge, BadgeProps } from "@overmap-ai/blocks"
|
|
2
|
-
import { truncate } from "@overmap-ai/core"
|
|
3
|
-
import { memo, useMemo } from "react"
|
|
4
|
-
|
|
5
|
-
import { FileIcon } from "../FileIcon"
|
|
6
|
-
|
|
7
|
-
export interface FileBadgeProps extends Omit<BadgeProps, "children"> {
|
|
8
|
-
file: File
|
|
9
|
-
truncateLength?: number
|
|
10
|
-
hideName?: boolean
|
|
11
|
-
}
|
|
12
|
-
export const FileBadge = memo((props: FileBadgeProps) => {
|
|
13
|
-
const { file, truncateLength, hideName = false, ...rest } = props
|
|
14
|
-
|
|
15
|
-
const fileName = useMemo(
|
|
16
|
-
() => (truncateLength !== undefined ? truncate(file.name, truncateLength) : file.name),
|
|
17
|
-
[file.name, truncateLength],
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<Badge {...rest}>
|
|
22
|
-
<FileIcon fileType={file.type} />
|
|
23
|
-
{!hideName && fileName}
|
|
24
|
-
</Badge>
|
|
25
|
-
)
|
|
26
|
-
})
|
|
27
|
-
FileBadge.displayName = "FileBadge"
|
package/src/FileBadge/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./FileBadge"
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { Meta } from "@storybook/react"
|
|
2
|
-
import { ChangeEvent, useCallback, useRef, useState } from "react"
|
|
3
|
-
|
|
4
|
-
import { FileCard } from "./FileCard"
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: "Components/FileCard",
|
|
8
|
-
component: FileCard,
|
|
9
|
-
tags: ["autodocs"],
|
|
10
|
-
parameters: {
|
|
11
|
-
disablePanel: true,
|
|
12
|
-
},
|
|
13
|
-
} satisfies Meta<typeof FileCard>
|
|
14
|
-
|
|
15
|
-
export default meta
|
|
16
|
-
|
|
17
|
-
export const Basic = () => {
|
|
18
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
19
|
-
const [files, setFiles] = useState<File[]>([])
|
|
20
|
-
|
|
21
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
22
|
-
if (e.target.files) {
|
|
23
|
-
const files = Array.from(e.target.files)
|
|
24
|
-
if (files.length > 0) {
|
|
25
|
-
setFiles(files)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}, [])
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<>
|
|
32
|
-
<input ref={inputRef} multiple type="file" onChange={handleChange} />
|
|
33
|
-
<div className="flex flex-col gap-1">
|
|
34
|
-
{files.map((file, index) => (
|
|
35
|
-
<FileCard key={index} file={file} />
|
|
36
|
-
))}
|
|
37
|
-
</div>
|
|
38
|
-
</>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const Loading = () => {
|
|
43
|
-
return <FileCard file={null} />
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const Errored = () => {
|
|
47
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
48
|
-
const [files, setFiles] = useState<File[]>([])
|
|
49
|
-
|
|
50
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
51
|
-
if (e.target.files) {
|
|
52
|
-
const files = Array.from(e.target.files)
|
|
53
|
-
if (files.length > 0) {
|
|
54
|
-
setFiles(files)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}, [])
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<>
|
|
61
|
-
<input ref={inputRef} multiple type="file" onChange={handleChange} />
|
|
62
|
-
<div className="flex flex-col gap-1">
|
|
63
|
-
{files.map((file, index) => (
|
|
64
|
-
<FileCard key={index} file={file} error={file.name} />
|
|
65
|
-
))}
|
|
66
|
-
</div>
|
|
67
|
-
</>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { RiIcon, Spinner } from "@overmap-ai/blocks"
|
|
2
|
-
import { truncate } from "@overmap-ai/core"
|
|
3
|
-
import { cx } from "class-variance-authority"
|
|
4
|
-
import { ComponentProps, forwardRef, memo, PropsWithoutRef, ReactNode, useMemo } from "react"
|
|
5
|
-
|
|
6
|
-
import { FileIcon } from "../FileIcon"
|
|
7
|
-
|
|
8
|
-
export interface FileCardProps extends Omit<PropsWithoutRef<ComponentProps<"div">>, "children"> {
|
|
9
|
-
file: File | null
|
|
10
|
-
truncateLength?: number
|
|
11
|
-
error?: string
|
|
12
|
-
rightSlot?: ReactNode
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const FileCard = memo(
|
|
16
|
-
forwardRef<HTMLDivElement, FileCardProps>((props, ref) => {
|
|
17
|
-
const { file, className, error, truncateLength, rightSlot, ...rest } = props
|
|
18
|
-
|
|
19
|
-
const fileName = useMemo(() => {
|
|
20
|
-
if (!file) return
|
|
21
|
-
return truncateLength !== undefined ? truncate(file.name, truncateLength) : file.name
|
|
22
|
-
}, [file, truncateLength])
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<div
|
|
26
|
-
className={cx(
|
|
27
|
-
className,
|
|
28
|
-
"flex h-5 w-full items-center gap-2 rounded-md border border-(--base-a6) bg-(--base-2) text-sm py-1 px-2 h-max",
|
|
29
|
-
{
|
|
30
|
-
"text-(--gray-11)": error,
|
|
31
|
-
},
|
|
32
|
-
)}
|
|
33
|
-
ref={ref}
|
|
34
|
-
{...rest}
|
|
35
|
-
>
|
|
36
|
-
{error ? <RiIcon icon="RiFileWarningLine" /> : <FileIcon fileType={file?.type ?? ""} />}
|
|
37
|
-
{!error ? (
|
|
38
|
-
file ? (
|
|
39
|
-
fileName
|
|
40
|
-
) : (
|
|
41
|
-
<div className="flex w-full justify-center">
|
|
42
|
-
<Spinner />
|
|
43
|
-
</div>
|
|
44
|
-
)
|
|
45
|
-
) : (
|
|
46
|
-
error
|
|
47
|
-
)}
|
|
48
|
-
{rightSlot}
|
|
49
|
-
</div>
|
|
50
|
-
)
|
|
51
|
-
}),
|
|
52
|
-
)
|
|
53
|
-
FileCard.displayName = "FileCard"
|
package/src/FileCard/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./FileCard"
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { RiIcon } from "@overmap-ai/blocks"
|
|
2
|
-
import { memo, useMemo } from "react"
|
|
3
|
-
|
|
4
|
-
import { SUPPORTED_IMAGE_FILE_TYPES } from "../ImageViewer"
|
|
5
|
-
import { SUPPORTED_PDF_FILE_TYPES } from "../PDFViewer"
|
|
6
|
-
import { SUPPORTED_SPREADSHEET_FILE_EXTENSIONS } from "../SpreadsheetViewer"
|
|
7
|
-
|
|
8
|
-
// TODO: create a mapping for each supported file extensions to a appropriate icon.
|
|
9
|
-
// May need to outsource some icons for this.
|
|
10
|
-
|
|
11
|
-
export interface FileIconProps {
|
|
12
|
-
fileType: string
|
|
13
|
-
}
|
|
14
|
-
export const FileIcon = memo((props: FileIconProps) => {
|
|
15
|
-
const { fileType } = props
|
|
16
|
-
const icon = useMemo(() => {
|
|
17
|
-
if (SUPPORTED_SPREADSHEET_FILE_EXTENSIONS.includes(fileType)) {
|
|
18
|
-
return <RiIcon icon="RiFileExcelLine" />
|
|
19
|
-
}
|
|
20
|
-
if (SUPPORTED_PDF_FILE_TYPES.includes(fileType)) {
|
|
21
|
-
return <RiIcon icon="RiFilePdfLine" />
|
|
22
|
-
}
|
|
23
|
-
if (SUPPORTED_IMAGE_FILE_TYPES.includes(fileType)) {
|
|
24
|
-
return <RiIcon icon="RiFileImageLine" />
|
|
25
|
-
}
|
|
26
|
-
return <RiIcon icon="RiFileLine" />
|
|
27
|
-
}, [fileType])
|
|
28
|
-
|
|
29
|
-
return <div className="h-max w-max">{icon}</div>
|
|
30
|
-
})
|
|
31
|
-
FileIcon.displayName = "FileIcon"
|
package/src/FileIcon/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./FileIcon"
|
|
@@ -1,50 +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 { useFileViewer } from "./context"
|
|
6
|
-
import { FileViewerProvider } from "./FileViewerProvider"
|
|
7
|
-
|
|
8
|
-
const meta = {
|
|
9
|
-
title: "Contexts/FileViewer",
|
|
10
|
-
component: FileViewerProvider,
|
|
11
|
-
tags: ["autodocs"],
|
|
12
|
-
parameters: {
|
|
13
|
-
disablePanel: true,
|
|
14
|
-
},
|
|
15
|
-
} satisfies Meta<typeof FileViewerProvider>
|
|
16
|
-
|
|
17
|
-
export default meta
|
|
18
|
-
|
|
19
|
-
export const Basic = () => {
|
|
20
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
21
|
-
const [file, setFile] = useState<File | null>(null)
|
|
22
|
-
const openFileViewer = useFileViewer()
|
|
23
|
-
|
|
24
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
25
|
-
if (e.target.files) {
|
|
26
|
-
const files = Array.from(e.target.files)
|
|
27
|
-
if (files.length > 0) {
|
|
28
|
-
setFile(files[0] || null)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}, [])
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<>
|
|
35
|
-
<input ref={inputRef} type="file" onChange={handleChange} />
|
|
36
|
-
{file && (
|
|
37
|
-
<Button
|
|
38
|
-
onClick={() => {
|
|
39
|
-
openFileViewer((close) => ({
|
|
40
|
-
file: file,
|
|
41
|
-
onDelete: close,
|
|
42
|
-
}))
|
|
43
|
-
}}
|
|
44
|
-
>
|
|
45
|
-
Open
|
|
46
|
-
</Button>
|
|
47
|
-
)}
|
|
48
|
-
</>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { memo, PropsWithChildren, useCallback, useEffect, useMemo, useState } from "react"
|
|
2
|
-
|
|
3
|
-
import { ImageViewer, SUPPORTED_IMAGE_FILE_TYPES } from "../ImageViewer"
|
|
4
|
-
import { PDFViewer, SUPPORTED_PDF_FILE_TYPES } from "../PDFViewer"
|
|
5
|
-
import { SpreadsheetViewer, SUPPORTED_SPREADSHEET_FILE_EXTENSIONS } from "../SpreadsheetViewer"
|
|
6
|
-
import { FileViewerContext } from "./context"
|
|
7
|
-
import { FileViewerConfig } from "./typings"
|
|
8
|
-
|
|
9
|
-
type FileViewerType = "image" | "spreadsheet" | "pdf"
|
|
10
|
-
export const FileViewerProvider = memo((props: PropsWithChildren) => {
|
|
11
|
-
const { children } = props
|
|
12
|
-
const [config, setConfig] = useState<FileViewerConfig | null>(null)
|
|
13
|
-
const [fileType, setFileType] = useState<FileViewerType | null>(null)
|
|
14
|
-
|
|
15
|
-
const closeFileViewer = useCallback(() => {
|
|
16
|
-
setConfig(null)
|
|
17
|
-
setFileType(null)
|
|
18
|
-
}, [])
|
|
19
|
-
|
|
20
|
-
const openFileViewer = useCallback(
|
|
21
|
-
(func: (closeFileViewer: () => void) => FileViewerConfig) => {
|
|
22
|
-
setConfig(func(closeFileViewer))
|
|
23
|
-
},
|
|
24
|
-
[closeFileViewer],
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
const handleClose = useCallback(() => {
|
|
28
|
-
if (!config) return
|
|
29
|
-
if (config.onClose) config.onClose()
|
|
30
|
-
closeFileViewer()
|
|
31
|
-
}, [closeFileViewer, config])
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (!config) return
|
|
35
|
-
const { file } = config
|
|
36
|
-
|
|
37
|
-
if (SUPPORTED_SPREADSHEET_FILE_EXTENSIONS.includes(file.type)) {
|
|
38
|
-
setFileType("spreadsheet")
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
if (SUPPORTED_PDF_FILE_TYPES.includes(file.type)) {
|
|
42
|
-
setFileType("pdf")
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
if (SUPPORTED_IMAGE_FILE_TYPES.includes(file.type)) {
|
|
46
|
-
setFileType("image")
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
}, [config])
|
|
50
|
-
|
|
51
|
-
const value = useMemo(() => openFileViewer, [openFileViewer])
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<FileViewerContext.Provider value={value}>
|
|
55
|
-
{children}
|
|
56
|
-
{config && (
|
|
57
|
-
<>
|
|
58
|
-
{fileType === "spreadsheet" && (
|
|
59
|
-
<SpreadsheetViewer file={config.file} onDelete={config.onDelete} onClose={handleClose} />
|
|
60
|
-
)}
|
|
61
|
-
{fileType === "pdf" && (
|
|
62
|
-
<PDFViewer file={config.file} onDelete={config.onDelete} onClose={handleClose} />
|
|
63
|
-
)}
|
|
64
|
-
{fileType === "image" && (
|
|
65
|
-
<ImageViewer file={config.file} onDelete={config.onDelete} onClose={handleClose} />
|
|
66
|
-
)}
|
|
67
|
-
</>
|
|
68
|
-
)}
|
|
69
|
-
</FileViewerContext.Provider>
|
|
70
|
-
)
|
|
71
|
-
})
|
|
72
|
-
FileViewerProvider.displayName = "FileViewerProvider"
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext } from "react"
|
|
2
|
-
|
|
3
|
-
import { FileViewerConfig } from "./typings"
|
|
4
|
-
|
|
5
|
-
export type FileViewerContextType = (func: (close: () => void) => FileViewerConfig) => void
|
|
6
|
-
|
|
7
|
-
export const FileViewerContext = createContext<FileViewerContextType>(() => {
|
|
8
|
-
throw new Error("No FileViewerProvider found")
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
export const useFileViewer = () => useContext(FileViewerContext)
|
package/src/FileViewer/index.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import type { Meta } from "@storybook/react"
|
|
2
|
-
import { ChangeEvent, useCallback, useRef, useState } from "react"
|
|
3
|
-
|
|
4
|
-
import { ImageCard } from "./ImageCard"
|
|
5
|
-
|
|
6
|
-
const meta = {
|
|
7
|
-
title: "Components/ImageCard",
|
|
8
|
-
component: ImageCard,
|
|
9
|
-
tags: ["autodocs"],
|
|
10
|
-
parameters: {
|
|
11
|
-
disablePanel: true,
|
|
12
|
-
},
|
|
13
|
-
} satisfies Meta<typeof ImageCard>
|
|
14
|
-
|
|
15
|
-
export default meta
|
|
16
|
-
|
|
17
|
-
export const Basic = () => {
|
|
18
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
19
|
-
const [files, setFiles] = useState<File[]>([])
|
|
20
|
-
|
|
21
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
22
|
-
if (e.target.files) {
|
|
23
|
-
const files = Array.from(e.target.files)
|
|
24
|
-
if (files.length > 0) {
|
|
25
|
-
setFiles(files)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}, [])
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<>
|
|
32
|
-
<input ref={inputRef} multiple type="file" onChange={handleChange} />
|
|
33
|
-
<div className="flex flex-col gap-1">
|
|
34
|
-
{files.map((file, index) => (
|
|
35
|
-
<ImageCard key={index} file={file} />
|
|
36
|
-
))}
|
|
37
|
-
</div>
|
|
38
|
-
</>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const Loading = () => {
|
|
43
|
-
return <ImageCard file={null} />
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const Errored = () => {
|
|
47
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
48
|
-
const [files, setFiles] = useState<File[]>([])
|
|
49
|
-
|
|
50
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
51
|
-
if (e.target.files) {
|
|
52
|
-
const files = Array.from(e.target.files)
|
|
53
|
-
if (files.length > 0) {
|
|
54
|
-
setFiles(files)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}, [])
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<>
|
|
61
|
-
<input ref={inputRef} multiple type="file" onChange={handleChange} />
|
|
62
|
-
<div className="flex flex-col gap-1">
|
|
63
|
-
{files.map((file, index) => (
|
|
64
|
-
<ImageCard key={index} file={file} error={file.name} />
|
|
65
|
-
))}
|
|
66
|
-
</div>
|
|
67
|
-
</>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export const Responsive = () => {
|
|
72
|
-
const inputRef = useRef<HTMLInputElement>(null)
|
|
73
|
-
const [files, setFiles] = useState<File[]>([])
|
|
74
|
-
|
|
75
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
76
|
-
if (e.target.files) {
|
|
77
|
-
const files = Array.from(e.target.files)
|
|
78
|
-
if (files.length > 0) {
|
|
79
|
-
setFiles(files)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}, [])
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<>
|
|
86
|
-
<input ref={inputRef} multiple type="file" onChange={handleChange} />
|
|
87
|
-
<div className="flex flex-col gap-1">
|
|
88
|
-
{files.map((file, index) => (
|
|
89
|
-
<ImageCard key={index} file={file} />
|
|
90
|
-
))}
|
|
91
|
-
</div>
|
|
92
|
-
</>
|
|
93
|
-
)
|
|
94
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { RiIcon, Spinner, useSize } from "@overmap-ai/blocks"
|
|
2
|
-
import { truncate } from "@overmap-ai/core"
|
|
3
|
-
import { cx } from "class-variance-authority"
|
|
4
|
-
import {
|
|
5
|
-
ComponentProps,
|
|
6
|
-
forwardRef,
|
|
7
|
-
memo,
|
|
8
|
-
PropsWithoutRef,
|
|
9
|
-
ReactNode,
|
|
10
|
-
RefObject,
|
|
11
|
-
useLayoutEffect,
|
|
12
|
-
useMemo,
|
|
13
|
-
useRef,
|
|
14
|
-
} from "react"
|
|
15
|
-
|
|
16
|
-
import { FileIcon } from "../FileIcon"
|
|
17
|
-
|
|
18
|
-
export interface ImageCardProps extends Omit<PropsWithoutRef<ComponentProps<"div">>, "children"> {
|
|
19
|
-
file: File | null
|
|
20
|
-
alt?: string
|
|
21
|
-
error?: string
|
|
22
|
-
truncateLength?: number
|
|
23
|
-
rightSlot?: ReactNode
|
|
24
|
-
}
|
|
25
|
-
export const ImageCard = memo(
|
|
26
|
-
forwardRef<HTMLDivElement, ImageCardProps>((props, forwardedRef) => {
|
|
27
|
-
const { file, alt, error, rightSlot, className, truncateLength, ...rest } = props
|
|
28
|
-
const fileCardRef = useRef<HTMLDivElement>(null)
|
|
29
|
-
const imageInsetRef = useRef<HTMLDivElement>(null)
|
|
30
|
-
const fileCardSize = useSize(fileCardRef as RefObject<HTMLDivElement>)
|
|
31
|
-
|
|
32
|
-
useLayoutEffect(() => {
|
|
33
|
-
if (!imageInsetRef.current || !fileCardSize) return
|
|
34
|
-
imageInsetRef.current.style.height = `${fileCardSize.height * 4}px`
|
|
35
|
-
}, [fileCardSize])
|
|
36
|
-
|
|
37
|
-
const fileName = useMemo(() => {
|
|
38
|
-
if (!file) return
|
|
39
|
-
return truncateLength !== undefined ? truncate(file.name, truncateLength) : file.name
|
|
40
|
-
}, [file, truncateLength])
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<div
|
|
44
|
-
className={cx(
|
|
45
|
-
className,
|
|
46
|
-
"relative flex h-max w-full flex-col gap-0 overflow-hidden rounded-md border items-center border-(--base-a6)",
|
|
47
|
-
)}
|
|
48
|
-
ref={forwardedRef}
|
|
49
|
-
{...rest}
|
|
50
|
-
>
|
|
51
|
-
{!file && !error && (
|
|
52
|
-
<div className="absolute flex h-full w-full flex-col items-center justify-center">
|
|
53
|
-
<Spinner />
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
56
|
-
|
|
57
|
-
<div
|
|
58
|
-
ref={imageInsetRef}
|
|
59
|
-
className="-m-4 flex max-w-full items-center justify-center overflow-hidden bg-clip-padding"
|
|
60
|
-
>
|
|
61
|
-
{file && !error && (
|
|
62
|
-
<img
|
|
63
|
-
className="max-w-full object-cover"
|
|
64
|
-
src={URL.createObjectURL(file)}
|
|
65
|
-
alt={alt ?? file.name}
|
|
66
|
-
/>
|
|
67
|
-
)}
|
|
68
|
-
</div>
|
|
69
|
-
<div
|
|
70
|
-
className={cx("flex h-max w-full items-center gap-1 bg-(--base-2) px-2 py-1", {
|
|
71
|
-
"bg-transparent": !file,
|
|
72
|
-
})}
|
|
73
|
-
ref={fileCardRef}
|
|
74
|
-
>
|
|
75
|
-
{error ? <RiIcon icon="RiFileWarningLine" /> : file && <FileIcon fileType={file.type} />}
|
|
76
|
-
{error ?? fileName}
|
|
77
|
-
{rightSlot}
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
}),
|
|
82
|
-
)
|
package/src/ImageCard/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./ImageCard"
|
|
@@ -1,65 +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 { ImageMarkup } from "./ImageMarkup"
|
|
6
|
-
|
|
7
|
-
const meta = {
|
|
8
|
-
title: "Components/ImageMarkup",
|
|
9
|
-
component: ImageMarkup,
|
|
10
|
-
tags: ["autodocs"],
|
|
11
|
-
parameters: {
|
|
12
|
-
disablePanel: true,
|
|
13
|
-
},
|
|
14
|
-
} satisfies Meta<typeof ImageMarkup>
|
|
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
|
-
const [dirty, setDirty] = useState(false)
|
|
23
|
-
|
|
24
|
-
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
|
25
|
-
if (e.target.files) {
|
|
26
|
-
const files = Array.from(e.target.files)
|
|
27
|
-
if (files.length > 0) {
|
|
28
|
-
setFile(files[0] || null)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}, [])
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<>
|
|
35
|
-
<input ref={inputRef} type="file" onChange={handleChange} />
|
|
36
|
-
<Button
|
|
37
|
-
onClick={() => {
|
|
38
|
-
setOpen(!open)
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
Open
|
|
42
|
-
</Button>
|
|
43
|
-
{file && open && (
|
|
44
|
-
<ImageMarkup
|
|
45
|
-
dirty={dirty}
|
|
46
|
-
onDirty={(dirty) => {
|
|
47
|
-
setDirty(dirty)
|
|
48
|
-
}}
|
|
49
|
-
file={file}
|
|
50
|
-
onClose={() => {
|
|
51
|
-
setOpen(false)
|
|
52
|
-
}}
|
|
53
|
-
onDelete={() => {
|
|
54
|
-
setFile(null)
|
|
55
|
-
alert("deleted")
|
|
56
|
-
}}
|
|
57
|
-
onSave={(file: File) => {
|
|
58
|
-
setFile(file)
|
|
59
|
-
alert("saved")
|
|
60
|
-
}}
|
|
61
|
-
/>
|
|
62
|
-
)}
|
|
63
|
-
</>
|
|
64
|
-
)
|
|
65
|
-
}
|