diff --git a/packages/core/src/actions/mediaLibrary.ts b/packages/core/src/actions/mediaLibrary.ts index c9a89439..142e2874 100644 --- a/packages/core/src/actions/mediaLibrary.ts +++ b/packages/core/src/actions/mediaLibrary.ts @@ -51,6 +51,7 @@ export function openMediaLibrary( payload: { controlID?: string; forImage?: boolean; + forFolder?: boolean; value?: string | string[]; alt?: string; allowMultiple?: boolean; @@ -69,6 +70,7 @@ export function openMediaLibrary( config = {}, allowMultiple, forImage, + forFolder, replaceIndex, collection, collectionFile, @@ -81,6 +83,7 @@ export function openMediaLibrary( payload: { controlID, forImage, + forFolder, value, alt, allowMultiple, diff --git a/packages/core/src/components/media-library/common/MediaLibrary.tsx b/packages/core/src/components/media-library/common/MediaLibrary.tsx index fd4287d2..188e0732 100644 --- a/packages/core/src/components/media-library/common/MediaLibrary.tsx +++ b/packages/core/src/components/media-library/common/MediaLibrary.tsx @@ -79,6 +79,7 @@ const MediaLibrary: FC> = ({ dynamicSearch, dynamicSearchActive, forImage = false, + forFolder = false, isLoading, hasNextPage, isPaginating, @@ -182,7 +183,12 @@ const MediaLibrary: FC> = ({ */ const handleAssetSelect = useCallback( (asset: MediaFile) => { - if (!canInsert || selectedFile?.key === asset.key || asset.isDirectory) { + if ( + !canInsert || + selectedFile?.key === asset.key || + (!forFolder && asset.isDirectory) || + (forFolder && !asset.isDirectory) + ) { return; } diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 0cc5a65e..e99ec573 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -562,6 +562,7 @@ export interface MediaField extends BaseField { choose_url?: boolean; multiple?: boolean; media_library?: MediaLibraryConfig; + select_folder?: boolean; } export interface BooleanField extends BaseField { diff --git a/packages/core/src/lib/hooks/useMediaInsert.ts b/packages/core/src/lib/hooks/useMediaInsert.ts index cc3cc9da..2a3ded0d 100644 --- a/packages/core/src/lib/hooks/useMediaInsert.ts +++ b/packages/core/src/lib/hooks/useMediaInsert.ts @@ -20,13 +20,21 @@ export default function useMediaInsert) => void, ): (e?: MouseEvent) => void { const dispatch = useAppDispatch(); - const { controlID, collection, field, forImage = false, insertOptions } = options; + const { + controlID, + collection, + field, + forImage = false, + forFolder = false, + insertOptions, + } = options; const finalControlID = useMemo(() => controlID ?? uuid(), [controlID]); const mediaPathSelector = useMemo(() => selectMediaPath(finalControlID), [finalControlID]); @@ -50,6 +58,7 @@ export default function useMediaInsert { const uploadButtonRef = useRef(null); + const forFolder = useMemo(() => field.select_folder ?? false, [field.select_folder]); + const handleOnChange = useCallback( ({ path: newValue }: MediaPath) => { if (newValue !== internalValue) { @@ -76,7 +78,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { const handleOpenMediaLibrary = useMediaInsert( { path: internalValue }, - { collection, field, controlID, forImage }, + { collection, field, controlID, forImage, forFolder }, handleOnChange, ); @@ -87,7 +89,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { const chooseUrl = useMemo(() => field.choose_url ?? false, [field.choose_url]); const handleUrl = useCallback( - (subject: 'image' | 'file') => (e: MouseEvent) => { + (subject: 'image' | 'folder' | 'file') => (e: MouseEvent) => { e.preventDefault(); const url = window.prompt(t(`editor.editorWidgets.${subject}.promptUrl`)); @@ -122,6 +124,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { return openMediaLibrary({ controlID, forImage, + forFolder, value: internalValue, replaceIndex: index, allowMultiple: false, @@ -130,7 +133,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { field, }); }, - [openMediaLibrary, controlID, internalValue, collection, field], + [openMediaLibrary, controlID, internalValue, collection, field, forFolder], ); // TODO Readd when multiple uploads is supported @@ -212,7 +215,7 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => { }, [collection, field, internalValue, onRemoveOne, onReplaceOne, renderFileLink]); const content: JSX.Element = useMemo(() => { - const subject = forImage ? 'image' : 'file'; + const subject = forImage ? 'image' : forFolder ? 'folder' : 'file'; if (Array.isArray(internalValue) ? internalValue.length === 0 : isEmpty(internalValue)) { return ( diff --git a/packages/docs/content/docs/custom-widgets.mdx b/packages/docs/content/docs/custom-widgets.mdx index 1fd605eb..09dfd730 100644 --- a/packages/docs/content/docs/custom-widgets.mdx +++ b/packages/docs/content/docs/custom-widgets.mdx @@ -372,7 +372,7 @@ const validator = () => { If you want to use the media library in your custom widget you will need to use the `useMediaInsert` and `useMediaAsset` hooks. -- `useMediaInsert` - Takes the current url to your media, details about your field (including a unique ID) and a callback method for when new media is uploaded. +- `useMediaInsert` - Takes the current url to your media, details about your field (including a unique ID) and a callback method for when new media is uploaded. If you want to select folders instead of files, set the `forFolder` variable in options. - `useMediaAsset` - Transforms your stored url into a usable url for displaying as a preview. diff --git a/packages/docs/content/docs/widget-file.mdx b/packages/docs/content/docs/widget-file.mdx index a2eb00fd..881201d9 100644 --- a/packages/docs/content/docs/widget-file.mdx +++ b/packages/docs/content/docs/widget-file.mdx @@ -14,15 +14,16 @@ The file widget allows editors to upload a file or select an existing one from t For common options, see [Common widget options](/docs/widgets#common-widget-options). -| Name | Type | Default | Description | -| ------------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| default | string | `null` | _Optional_. The default value for the field. Accepts a string. | -| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo | -| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site | -| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when the media library is opened by the current widget. See [Media Library](/docs/configuration-options#media-library) | -| choose_url | boolean | `false` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden | +| Name | Type | Default | Description | +| ------------- | --------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| default | string | `null` | _Optional_. The default value for the field. Accepts a string. | +| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo | +| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site | +| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when the media library is opened by the current widget. See [Media Library](/docs/configuration-options#media-library) | +| choose_url | boolean | `false` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden | +| select_folder | boolean | `false` | _Optional_. When set to `true`, selecting folders instead of files will be possible. See [Media Library](/docs/configuration-options#media-library) for folder support. | -## Example +## Examples ```yaml @@ -40,3 +41,20 @@ default: '/uploads/general-manual.pdf', ``` + + +```yaml +name: gallery_folder +label: Gallery Folder +widget: file +select_folder: true +``` + +```js +name: 'gallery_folder', +label: 'Gallery Folder', +widget: 'file', +select_folder: true, +``` + + diff --git a/packages/docs/content/docs/widget-markdown.mdx b/packages/docs/content/docs/widget-markdown.mdx index d72932b6..502fff5d 100644 --- a/packages/docs/content/docs/widget-markdown.mdx +++ b/packages/docs/content/docs/widget-markdown.mdx @@ -218,7 +218,7 @@ CMS.registerShortcode('youtube', { If you want to use the media library in your shortcode you will need to use the `useMediaInsert` and `useMediaAsset` hooks. -- `useMediaInsert` - Takes the current url to your media, details about your field and a callback method for when new media is uploaded. +- `useMediaInsert` - Takes the current url to your media, details about your field and a callback method for when new media is uploaded. If you want to select folders instead of files, set the `forFolder`variable in options. - `useMediaAsset` - Transforms your stored url into a usable url for displaying as a preview.