feat: folder selection support in useMediaAsset and file widget (#706)

This commit is contained in:
Denys Konovalov 2023-04-19 06:42:18 +02:00 committed by GitHub
parent 0fae2ce73d
commit 23df691a0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 98 additions and 25 deletions

View File

@ -51,6 +51,7 @@ export function openMediaLibrary<EF extends BaseField = UnknownField>(
payload: {
controlID?: string;
forImage?: boolean;
forFolder?: boolean;
value?: string | string[];
alt?: string;
allowMultiple?: boolean;
@ -69,6 +70,7 @@ export function openMediaLibrary<EF extends BaseField = UnknownField>(
config = {},
allowMultiple,
forImage,
forFolder,
replaceIndex,
collection,
collectionFile,
@ -81,6 +83,7 @@ export function openMediaLibrary<EF extends BaseField = UnknownField>(
payload: {
controlID,
forImage,
forFolder,
value,
alt,
allowMultiple,

View File

@ -79,6 +79,7 @@ const MediaLibrary: FC<TranslatedProps<MediaLibraryProps>> = ({
dynamicSearch,
dynamicSearchActive,
forImage = false,
forFolder = false,
isLoading,
hasNextPage,
isPaginating,
@ -182,7 +183,12 @@ const MediaLibrary: FC<TranslatedProps<MediaLibraryProps>> = ({
*/
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;
}

View File

@ -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 {

View File

@ -20,13 +20,21 @@ export default function useMediaInsert<T extends string | string[], F extends Me
field: F;
controlID?: string;
forImage?: boolean;
forFolder?: boolean;
insertOptions?: MediaLibrarInsertOptions;
},
callback: (newValue: MediaPath<T>) => 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<T extends string | string[], F extends Me
openMediaLibrary({
controlID: finalControlID,
forImage,
forFolder,
value: value.path,
alt: value.alt,
replaceIndex,
@ -62,7 +71,17 @@ export default function useMediaInsert<T extends string | string[], F extends Me
);
setSelected(false);
},
[dispatch, finalControlID, forImage, value.path, value.alt, collection, field, insertOptions],
[
dispatch,
finalControlID,
forImage,
forFolder,
value.path,
value.alt,
collection,
field,
insertOptions,
],
);
return handleOpenMediaLibrary;

View File

@ -162,20 +162,28 @@ const de: LocalePhrasesRoot = {
markdown: 'Markdown',
},
image: {
choose: 'Wähle ein Bild',
choose: 'Bild wählen',
chooseUrl: 'Von URL hinzufügen',
replaceUrl: 'Von URL ersetzen',
replaceUrl: 'Mit URL ersetzen',
promptUrl: 'Bild-URL eingeben',
chooseDifferent: 'Wähle ein anderes Bild',
remove: 'Entferne Bild',
chooseDifferent: 'Anderes Bild wählen',
remove: 'Bild entfernen',
},
file: {
choose: 'Wählen Sie eine Datei',
choose: 'Datei wählen',
chooseUrl: 'Von URL hinzufügen',
replaceUrl: 'Von URL ersetzen',
replaceUrl: 'Mit URL ersetzen',
promptUrl: 'Datei-URL eingeben',
chooseDifferent: 'Wählen Sie eine andere Datei',
remove: 'Datei löschen',
chooseDifferent: 'Andere Datei wählen',
remove: 'Datei entfernen',
},
folder: {
choose: 'Ordner wählen',
chooseUrl: 'Ordner-Pfad eingeben',
replaceUrl: 'Mit Pfad ersetzen',
promptUrl: 'Ordner-Pfad eingeben',
chooseDifferent: 'Anderen Ordner wählen',
remove: 'Ordner entfernen',
},
unknownControl: {
noControl: "Kein Bedienelement für Widget '%{widget}'.",

View File

@ -191,6 +191,17 @@ const en: LocalePhrasesRoot = {
remove: 'Remove file',
removeAll: 'Remove all files',
},
folder: {
choose: 'Choose a folder',
chooseUrl: 'Insert folder path',
chooseMultiple: 'Choose folders',
replaceUrl: 'Replace with path',
promptUrl: 'Enter path of the folder',
chooseDifferent: 'Choose different folder',
addMore: 'Add more folders',
remove: 'Remove folder',
removeAll: 'Remove all folders',
},
unknownControl: {
noControl: "No control for widget '%{widget}'.",
},

View File

@ -52,6 +52,7 @@ export type MediaLibraryState = {
dynamicSearchActive?: boolean;
dynamicSearchQuery?: string;
forImage?: boolean;
forFolder?: boolean;
isPersisting?: boolean;
isDeleting?: boolean;
hasNextPage?: boolean;
@ -76,6 +77,7 @@ function mediaLibrary(
const {
controlID,
forImage,
forFolder,
config,
collection,
collectionFile,
@ -91,6 +93,7 @@ function mediaLibrary(
...state,
isVisible: true,
forImage: Boolean(forImage),
forFolder: Boolean(forFolder),
controlID,
config: libConfig,
collection,

View File

@ -14,6 +14,7 @@ export default {
},
media_folder: { type: 'string' },
public_folder: { type: 'string' },
select_folder: { type: 'boolean' },
choose_url: { type: 'boolean' },
multiple: { type: 'boolean' },
media_library: {

View File

@ -62,6 +62,8 @@ const withFileControl = ({ forImage = false }: WithFileControlProps = {}) => {
const uploadButtonRef = useRef<HTMLButtonElement | null>(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 (

View File

@ -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.
<CodeTabs>

View File

@ -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
<CodeTabs>
```yaml
@ -40,3 +41,20 @@ default: '/uploads/general-manual.pdf',
```
</CodeTabs>
<CodeTabs>
```yaml
name: gallery_folder
label: Gallery Folder
widget: file
select_folder: true
```
```js
name: 'gallery_folder',
label: 'Gallery Folder',
widget: 'file',
select_folder: true,
```
</CodeTabs>

View File

@ -218,7 +218,7 @@ CMS.registerShortcode<YouTubeShortcodeProps>('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.
<CodeTabs>