feat: folder selection support in useMediaAsset and file widget (#706)
This commit is contained in:
parent
0fae2ce73d
commit
23df691a0a
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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}'.",
|
||||
|
@ -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}'.",
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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: {
|
||||
|
@ -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 (
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user