Skip to content

Commit ae2bcd8

Browse files
committed
feat: #203 allow users to specify maximum upload size
1 parent 8c566b3 commit ae2bcd8

File tree

5 files changed

+82
-16
lines changed

5 files changed

+82
-16
lines changed

src/components/Tool/index.tsx

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import {Flex} from '@sanity/ui'
2-
import React from 'react'
2+
import React, {ComponentProps} from 'react'
33
import Browser from '../Browser'
4+
import {ToolOptionsProvider} from '../../contexts/ToolOptionsContext'
5+
import {Tool as SanityTool} from 'sanity'
6+
import {MediaToolOptions} from '@types'
47

5-
const Tool = () => {
8+
const Tool = ({tool: {options}}: ComponentProps<SanityTool<MediaToolOptions>['component']>) => {
69
return (
7-
<Flex direction="column" height="fill" flex={1}>
8-
<Browser />
9-
</Flex>
10+
<ToolOptionsProvider options={options}>
11+
<Flex direction="column" height="fill" flex={1}>
12+
<Browser />
13+
</Flex>
14+
</ToolOptionsProvider>
1015
)
1116
}
1217

src/components/UploadDropzone/index.tsx

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import {white} from '@sanity/color'
22
import {Flex, Text} from '@sanity/ui'
33
import React, {ReactNode} from 'react'
4-
import {DropEvent, useDropzone} from 'react-dropzone'
4+
import {DropEvent, DropzoneOptions, useDropzone} from 'react-dropzone'
55
import {useDispatch} from 'react-redux'
66
import styled from 'styled-components'
77
import {useAssetSourceActions} from '../../contexts/AssetSourceDispatchContext'
88
import {DropzoneDispatchProvider} from '../../contexts/DropzoneDispatchContext'
99
import useTypedSelector from '../../hooks/useTypedSelector'
1010
import {notificationsActions} from '../../modules/notifications'
1111
import {uploadsActions} from '../../modules/uploads'
12+
import {useToolOptions} from '../../contexts/ToolOptionsContext'
1213

1314
type Props = {
1415
children: ReactNode
@@ -62,6 +63,10 @@ async function filterFiles(fileList: FileList) {
6263
const UploadDropzone = (props: Props) => {
6364
const {children} = props
6465

66+
const {
67+
dropzone: {maxSize}
68+
} = useToolOptions()
69+
6570
const {onSelect} = useAssetSourceActions()
6671

6772
// Redux
@@ -82,6 +87,19 @@ const UploadDropzone = (props: Props) => {
8287
)
8388
}
8489

90+
const handleDropRejected: DropzoneOptions['onDropRejected'] = rejections => {
91+
const errorCodes = rejections.flatMap(({errors}) => errors.map(({code}) => code))
92+
93+
if (errorCodes.includes('file-too-large')) {
94+
dispatch(
95+
notificationsActions.add({
96+
status: 'error',
97+
title: 'One or more files exceed the maximum upload size.'
98+
})
99+
)
100+
}
101+
}
102+
85103
// Use custom file selector to obtain files on file drop + change events (excluding folders and packages)
86104
const handleFileGetter = async (event: DropEvent) => {
87105
let fileList: FileList | undefined
@@ -123,7 +141,9 @@ const UploadDropzone = (props: Props) => {
123141
// HACK: Disable drag and drop functionality when in a selecting context
124142
// (This is currently due to Sanity's native image input taking precedence with drag and drop)
125143
noDrag: !!onSelect,
126-
onDrop: handleDrop
144+
onDrop: handleDrop,
145+
maxSize,
146+
onDropRejected: handleDropRejected
127147
})
128148

129149
return (

src/contexts/ToolOptionsContext.tsx

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {MediaToolOptions} from '@types'
2+
import React, {PropsWithChildren, createContext, useContext, useMemo} from 'react'
3+
import {DropzoneOptions} from 'react-dropzone'
4+
5+
type ContextProps = {
6+
dropzone: Pick<DropzoneOptions, 'maxSize'>
7+
}
8+
9+
const ToolOptionsContext = createContext<ContextProps | null>(null)
10+
11+
type Props = {
12+
options?: MediaToolOptions
13+
}
14+
15+
export const ToolOptionsProvider = ({options, children}: PropsWithChildren<Props>) => {
16+
const value = useMemo<ContextProps>(
17+
() => ({dropzone: {maxSize: options?.maximumUploadSize}}),
18+
[options?.maximumUploadSize]
19+
)
20+
21+
return <ToolOptionsContext.Provider value={value}>{children}</ToolOptionsContext.Provider>
22+
}
23+
24+
export const useToolOptions = () => {
25+
const context = useContext(ToolOptionsContext)
26+
27+
if (!context) {
28+
throw new Error('useToolOptions must be used within an ToolOptionsProvider')
29+
}
30+
31+
return context
32+
}
33+
34+
export default ToolOptionsContext

src/index.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import {definePlugin, Tool as SanityTool} from 'sanity'
1+
import {definePlugin} from 'sanity'
22
import {ImageIcon} from '@sanity/icons'
33
import type {AssetSource} from 'sanity'
44
import FormBuilderTool from './components/FormBuilderTool'
55
import Tool from './components/Tool'
66
import mediaTag from './schemas/tag'
7+
import {MediaToolOptions} from '@types'
78

89
const plugin = {
910
icon: ImageIcon,
@@ -16,12 +17,7 @@ export const mediaAssetSource: AssetSource = {
1617
component: FormBuilderTool
1718
}
1819

19-
const tool = {
20-
...plugin,
21-
component: Tool
22-
} as SanityTool
23-
24-
export const media = definePlugin({
20+
export const media = definePlugin<MediaToolOptions | void>(options => ({
2521
name: 'media',
2622
form: {
2723
file: {
@@ -39,6 +35,13 @@ export const media = definePlugin({
3935
types: [mediaTag]
4036
},
4137
tools: prev => {
42-
return [...prev, tool]
38+
return [
39+
...prev,
40+
{
41+
...plugin,
42+
options,
43+
component: Tool
44+
}
45+
]
4346
}
44-
})
47+
}))

src/types/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import * as z from 'zod'
1010
import {assetFormSchema, tagFormSchema, tagOptionSchema} from '../formSchema'
1111
import {RootReducerState} from '../modules/types'
1212

13+
export type MediaToolOptions = {
14+
maximumUploadSize?: number
15+
}
16+
1317
type CustomFields = {
1418
altText?: string
1519
description?: string

0 commit comments

Comments
 (0)