@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.
Files changed (131) hide show
  1. package/package.json +4 -1
  2. package/.husky/pre-commit +0 -6
  3. package/.prettierrc.json +0 -10
  4. package/.storybook/StoryDecorator.tsx +0 -22
  5. package/.storybook/main.ts +0 -20
  6. package/.storybook/palettes/green.css +0 -66
  7. package/.storybook/palettes/red.css +0 -66
  8. package/.storybook/preview.css +0 -39
  9. package/.storybook/preview.tsx +0 -31
  10. package/.storybook/tailwind-theme/accentPalette.css +0 -181
  11. package/.storybook/tailwind-theme/backgrounds.css +0 -11
  12. package/.storybook/tailwind-theme/basePalette.css +0 -178
  13. package/dev/publish-alpha.sh +0 -13
  14. package/dev/publish-patch.sh +0 -3
  15. package/eslint.config.js +0 -56
  16. package/src/ColorPicker/ColorPicker.tsx +0 -47
  17. package/src/ColorPicker/index.ts +0 -1
  18. package/src/FileBadge/FileBadge.tsx +0 -27
  19. package/src/FileBadge/index.ts +0 -1
  20. package/src/FileCard/FileCard.stories.tsx +0 -69
  21. package/src/FileCard/FileCard.tsx +0 -53
  22. package/src/FileCard/index.ts +0 -1
  23. package/src/FileIcon/FileIcon.tsx +0 -31
  24. package/src/FileIcon/index.ts +0 -1
  25. package/src/FileViewer/FileViewerProvider.stories.tsx +0 -50
  26. package/src/FileViewer/FileViewerProvider.tsx +0 -72
  27. package/src/FileViewer/context.ts +0 -11
  28. package/src/FileViewer/index.ts +0 -3
  29. package/src/FileViewer/typings.ts +0 -5
  30. package/src/ImageCard/ImageCard.stories.tsx +0 -94
  31. package/src/ImageCard/ImageCard.tsx +0 -82
  32. package/src/ImageCard/index.ts +0 -1
  33. package/src/ImageMarkup/ImageMarkup.stories.tsx +0 -65
  34. package/src/ImageMarkup/ImageMarkup.tsx +0 -268
  35. package/src/ImageMarkup/index.ts +0 -1
  36. package/src/ImageViewer/ImageViewer.stories.tsx +0 -57
  37. package/src/ImageViewer/ImageViewer.tsx +0 -124
  38. package/src/ImageViewer/constants.ts +0 -1
  39. package/src/ImageViewer/index.ts +0 -2
  40. package/src/PDFViewer/PDFViewer.stories.tsx +0 -55
  41. package/src/PDFViewer/PDFViewer.tsx +0 -170
  42. package/src/PDFViewer/constants.ts +0 -1
  43. package/src/PDFViewer/index.ts +0 -2
  44. package/src/SpreadsheetViewer/SpreadsheetViewer.stories.tsx +0 -55
  45. package/src/SpreadsheetViewer/SpreadsheetViewer.tsx +0 -162
  46. package/src/SpreadsheetViewer/constants.ts +0 -8
  47. package/src/SpreadsheetViewer/index.ts +0 -2
  48. package/src/forms/builder/DropDispatch.ts +0 -84
  49. package/src/forms/builder/FieldActions.tsx +0 -155
  50. package/src/forms/builder/FieldBuilder.tsx +0 -386
  51. package/src/forms/builder/FieldSectionWithActions.tsx +0 -260
  52. package/src/forms/builder/FieldWithActions.tsx +0 -129
  53. package/src/forms/builder/FieldsEditor.tsx +0 -180
  54. package/src/forms/builder/FormBuilder.stories.tsx +0 -105
  55. package/src/forms/builder/FormBuilder.tsx +0 -237
  56. package/src/forms/builder/constants.ts +0 -18
  57. package/src/forms/builder/hooks.tsx +0 -24
  58. package/src/forms/builder/index.ts +0 -2
  59. package/src/forms/builder/typings.ts +0 -18
  60. package/src/forms/builder/utils.ts +0 -229
  61. package/src/forms/constants.ts +0 -9
  62. package/src/forms/constantsJsx.tsx +0 -67
  63. package/src/forms/fields/BaseField/BaseField.ts +0 -152
  64. package/src/forms/fields/BaseField/hooks.tsx +0 -60
  65. package/src/forms/fields/BaseField/index.ts +0 -4
  66. package/src/forms/fields/BaseField/layouts.tsx +0 -100
  67. package/src/forms/fields/BaseField/typings.ts +0 -9
  68. package/src/forms/fields/BooleanField/BooleanField.tsx +0 -48
  69. package/src/forms/fields/BooleanField/BooleanInput.tsx +0 -54
  70. package/src/forms/fields/BooleanField/index.ts +0 -2
  71. package/src/forms/fields/CustomField/CustomField.tsx +0 -45
  72. package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputCloner.tsx +0 -25
  73. package/src/forms/fields/CustomField/FieldInputClonerField/FieldInputClonerField.tsx +0 -26
  74. package/src/forms/fields/CustomField/FieldInputClonerField/index.ts +0 -3
  75. package/src/forms/fields/CustomField/FieldInputClonerField/typings.ts +0 -8
  76. package/src/forms/fields/CustomField/index.ts +0 -1
  77. package/src/forms/fields/DateField/DateField.tsx +0 -42
  78. package/src/forms/fields/DateField/DateInput.tsx +0 -39
  79. package/src/forms/fields/DateField/index.ts +0 -2
  80. package/src/forms/fields/FieldSection/FieldSection.tsx +0 -173
  81. package/src/forms/fields/FieldSection/FieldSectionLayout.tsx +0 -56
  82. package/src/forms/fields/FieldSection/index.ts +0 -1
  83. package/src/forms/fields/MultiStringField/MultiStringField.tsx +0 -90
  84. package/src/forms/fields/MultiStringField/MultiStringInput.tsx +0 -207
  85. package/src/forms/fields/MultiStringField/index.ts +0 -2
  86. package/src/forms/fields/NumberField/NumberField.tsx +0 -173
  87. package/src/forms/fields/NumberField/NumberInput.tsx +0 -44
  88. package/src/forms/fields/NumberField/index.ts +0 -2
  89. package/src/forms/fields/QrField/QrField.tsx +0 -38
  90. package/src/forms/fields/QrField/QrInput.module.sass +0 -5
  91. package/src/forms/fields/QrField/QrInput.tsx +0 -144
  92. package/src/forms/fields/QrField/index.ts +0 -2
  93. package/src/forms/fields/SelectField/BaseSelectField.ts +0 -73
  94. package/src/forms/fields/SelectField/MultiSelectField.tsx +0 -53
  95. package/src/forms/fields/SelectField/MultiSelectInput.tsx +0 -80
  96. package/src/forms/fields/SelectField/SelectField.tsx +0 -49
  97. package/src/forms/fields/SelectField/SelectInput.tsx +0 -69
  98. package/src/forms/fields/SelectField/index.ts +0 -4
  99. package/src/forms/fields/StringOrTextFields/StringField/StringField.tsx +0 -61
  100. package/src/forms/fields/StringOrTextFields/StringField/StringInput.tsx +0 -41
  101. package/src/forms/fields/StringOrTextFields/StringField/index.ts +0 -2
  102. package/src/forms/fields/StringOrTextFields/StringOrTextField.ts +0 -143
  103. package/src/forms/fields/StringOrTextFields/TextField/TextField.tsx +0 -52
  104. package/src/forms/fields/StringOrTextFields/TextField/TextInput.tsx +0 -42
  105. package/src/forms/fields/StringOrTextFields/TextField/index.ts +0 -2
  106. package/src/forms/fields/StringOrTextFields/index.ts +0 -2
  107. package/src/forms/fields/UploadField/UploadField.tsx +0 -156
  108. package/src/forms/fields/UploadField/UploadInput.tsx +0 -220
  109. package/src/forms/fields/UploadField/index.ts +0 -2
  110. package/src/forms/fields/UploadField/utils.ts +0 -17
  111. package/src/forms/fields/constants.ts +0 -43
  112. package/src/forms/fields/hooks.tsx +0 -26
  113. package/src/forms/fields/index.ts +0 -12
  114. package/src/forms/fields/typings.ts +0 -45
  115. package/src/forms/fields/utils.ts +0 -125
  116. package/src/forms/index.ts +0 -5
  117. package/src/forms/renderer/FormRenderer/FormRenderer.stories.tsx +0 -142
  118. package/src/forms/renderer/FormRenderer/FormRenderer.tsx +0 -135
  119. package/src/forms/renderer/PatchForm/Field.tsx +0 -41
  120. package/src/forms/renderer/PatchForm/PatchForm.stories.tsx +0 -91
  121. package/src/forms/renderer/PatchForm/Provider.tsx +0 -119
  122. package/src/forms/renderer/PatchForm/index.ts +0 -2
  123. package/src/forms/renderer/index.ts +0 -2
  124. package/src/forms/typings.ts +0 -162
  125. package/src/forms/utils.ts +0 -69
  126. package/src/index.ts +0 -11
  127. package/src/vite-env.d.ts +0 -1
  128. package/tailwind.config.ts +0 -8
  129. package/tsconfig.json +0 -26
  130. package/tsconfig.node.json +0 -10
  131. 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"
@@ -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"
@@ -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"
@@ -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"
@@ -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)
@@ -1,3 +0,0 @@
1
- export * from "./context"
2
- export * from "./FileViewerProvider"
3
- export * from "./typings"
@@ -1,5 +0,0 @@
1
- export interface FileViewerConfig {
2
- file: File
3
- onDelete?: (file: File) => void
4
- onClose?: () => void
5
- }
@@ -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
- )
@@ -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
- }