diff --git a/packages/editor/src/view/hooks/useFilesGallery/README.md b/packages/editor/src/view/hooks/useFilesGallery/README.md
index 6b421b0b8..93ce67518 100644
--- a/packages/editor/src/view/hooks/useFilesGallery/README.md
+++ b/packages/editor/src/view/hooks/useFilesGallery/README.md
@@ -27,6 +27,7 @@ _UseFilesGalleryOptions_
| download | `(url: string, type: FilesGalleryItemType, element: Element) => string or undefined` | | | | The file download link getter (if you want to show the download action) |
| copyUrl | `(url: string, type: FilesGalleryItemType, element: Element) => string or undefined` | | | | The file copy link getter (if you want to show the copy link action) |
| overrideItemProps | `(url: string, type: FilesGalleryItemType, element: Element, currentProps: GalleryItemProps) => GalleryItemProps` | | | | The custom gallery item props getter (if you want to override the default gallery item props) |
+| resolveCustomItem | `(url: string, type: 'file', element: Element, linkObj: {name?: string or null; mimetype?: string or null}) => GalleryItemProps or undefined` | | | | Resolves base `GalleryItemProps` for elements not handled by the default image/video logic (e.g. arbitrary file links). Return `undefined` to skip the element. The returned props go through the same `download`/`copyUrl`/`overrideItemProps` pipeline with `type: 'file'`. If the returned props contain `actions`, they are merged with the auto-generated download/copy actions. Note: `FilesGalleryItemType` is now `'image' \| 'video' \| 'file'` — callers doing exhaustive `switch` on `type` in other options may need to handle the new `'file'` case. |
_useFilesGallery returns function `openFilesGallery` with the following args_:
@@ -122,3 +123,21 @@ const {openFilesGallery} = useFilesGallery(undefined, {overrideItemProps:getGall
;
```
+
+If you want to handle custom file types (e.g. PDF links) that are not images or videos, provide the `resolveCustomItem` option
+
+```tsx
+import {YfmStaticView, useFilesGallery} from '@gravity-ui/markdown-editor/view';
+import {getGalleryItemImage} from '@gravity-ui/components';
+
+function resolveCustomItem(url: string, type: 'file', element: Element, {name, mimetype}: {name?: string | null; mimetype?: string | null}) {
+ if (mimetype !== 'application/pdf') return undefined;
+ return getGalleryItemImage({src: '/icons/pdf.svg', name: name ?? url});
+}
+
+const {openFilesGallery} = useFilesGallery(undefined, {resolveCustomItem});
+
+
+
+
;
+```
diff --git a/packages/editor/src/view/hooks/useFilesGallery/types.ts b/packages/editor/src/view/hooks/useFilesGallery/types.ts
index d20448adf..cc00d4a71 100644
--- a/packages/editor/src/view/hooks/useFilesGallery/types.ts
+++ b/packages/editor/src/view/hooks/useFilesGallery/types.ts
@@ -5,7 +5,7 @@ export type GalleryItemPropsWithUrl = GalleryItemProps & {
url?: string;
};
-export type FilesGalleryItemType = 'image' | 'video';
+export type FilesGalleryItemType = 'image' | 'video' | 'file';
export type UseFilesGalleryOptions = {
download?: (url: string, type: FilesGalleryItemType, element: Element) => string | undefined;
@@ -16,4 +16,10 @@ export type UseFilesGalleryOptions = {
element: Element,
currentProps: GalleryItemProps,
) => GalleryItemProps;
+ resolveCustomItem?: (
+ url: string,
+ type: 'file',
+ element: Element,
+ linkObj: {name?: string | null; mimetype?: string | null},
+ ) => GalleryItemProps | undefined;
};
diff --git a/packages/editor/src/view/hooks/useFilesGallery/useFilesGallery.tsx b/packages/editor/src/view/hooks/useFilesGallery/useFilesGallery.tsx
index 2fa93562b..6382e4d9e 100644
--- a/packages/editor/src/view/hooks/useFilesGallery/useFilesGallery.tsx
+++ b/packages/editor/src/view/hooks/useFilesGallery/useFilesGallery.tsx
@@ -22,6 +22,7 @@ export function useFilesGallery(
download: getItemDownloladUrl,
overrideItemProps,
copyUrl: getItemCopyUrl,
+ resolveCustomItem,
}: UseFilesGalleryOptions = {},
) {
const {openGallery} = useGallery();
@@ -42,6 +43,48 @@ export function useFilesGallery(
return false;
}
+ const buildItem = (
+ link: string,
+ type: FilesGalleryItemType,
+ element: Element,
+ baseProps: GalleryItemPropsWithUrl,
+ ): GalleryItemPropsWithUrl => {
+ const galleryItemActions: GalleryItemAction[] = [...(baseProps.actions ?? [])];
+
+ const itemCopyUrl = getItemCopyUrl?.(link, type, element);
+ if (itemCopyUrl) {
+ const handleLinkCopied = () => {
+ toaster.add({
+ theme: 'success',
+ name: 'g-md-editor-gallery-copy-link',
+ title: i18n('link_copied'),
+ });
+ };
+ galleryItemActions.push(
+ getGalleryItemCopyLinkAction({
+ copyUrl: itemCopyUrl,
+ onCopy: handleLinkCopied,
+ }),
+ );
+ }
+
+ const downloadUrl = getItemDownloladUrl?.(link, type, element);
+ if (downloadUrl) {
+ galleryItemActions.push(getGalleryItemDownloadAction({downloadUrl}));
+ }
+
+ const galleryItemProps: GalleryItemPropsWithUrl = {
+ ...baseProps,
+ url: link,
+ actions: galleryItemActions,
+ };
+
+ return {
+ ...galleryItemProps,
+ ...overrideItemProps?.(link, type, element, galleryItemProps),
+ };
+ };
+
const targetFile = buildLinkObject(event.target);
if (!targetFile || !targetFile.link) return false;
@@ -60,63 +103,30 @@ export function useFilesGallery(
if (linkObj.type === 'image' || supportedExtensions.includes(extension)) {
const link = linkObj.link;
const name = linkObj.name || '';
-
- const filesGalleryItemType: FilesGalleryItemType =
- supportedVideoExtensions.includes(extension) ? 'video' : 'image';
- const galleryItemActions: GalleryItemAction[] = [];
-
- const itemCopyUrl = getItemCopyUrl?.(
- link,
- filesGalleryItemType,
- element,
- );
-
- if (itemCopyUrl) {
- const handleLinkCopied = () => {
- toaster.add({
- theme: 'success',
- name: 'g-md-editor-gallery-copy-link',
- title: i18n('link_copied'),
- });
- };
-
- galleryItemActions.push(
- getGalleryItemCopyLinkAction({
- copyUrl: itemCopyUrl,
- onCopy: handleLinkCopied,
- }),
- );
- }
-
- const downloadUrl = getItemDownloladUrl?.(
- link,
- filesGalleryItemType,
- element,
- );
-
- if (downloadUrl) {
- galleryItemActions.push(
- getGalleryItemDownloadAction({downloadUrl}),
- );
- }
-
- const galleryItemProps = {
- ...(filesGalleryItemType === 'video'
- ? getGalleryItemVideo({src: link, name: name})
- : getGalleryItemImage({src: link, name: name})),
+ const type: FilesGalleryItemType = supportedVideoExtensions.includes(
+ extension,
+ )
+ ? 'video'
+ : 'image';
+ const baseProps: GalleryItemPropsWithUrl = {
+ ...(type === 'video'
+ ? getGalleryItemVideo({src: link, name})
+ : getGalleryItemImage({src: link, name})),
url: link,
- actions: galleryItemActions,
+ actions: undefined,
};
- result.push({
- ...galleryItemProps,
- ...overrideItemProps?.(
- link,
- filesGalleryItemType,
- element,
- galleryItemProps,
- ),
+ result.push(buildItem(link, type, element, baseProps));
+ } else if (resolveCustomItem) {
+ const link = linkObj.link;
+ const baseProps = resolveCustomItem(link, 'file', element, {
+ name: linkObj.name,
+ mimetype: linkObj.mimetype,
});
+
+ if (baseProps) {
+ result.push(buildItem(link, 'file', element, baseProps));
+ }
}
}
@@ -140,6 +150,7 @@ export function useFilesGallery(
getItemCopyUrl,
getItemDownloladUrl,
overrideItemProps,
+ resolveCustomItem,
toaster,
openGallery,
],