Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/editor/src/view/hooks/useFilesGallery/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_:
Expand Down Expand Up @@ -122,3 +123,21 @@ const {openFilesGallery} = useFilesGallery(undefined, {overrideItemProps:getGall
<YfmStaticView {...props} />
</div>;
```

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});

<div onClick={openFilesGallery}>
<YfmStaticView {...props} />
</div>;
```
8 changes: 7 additions & 1 deletion packages/editor/src/view/hooks/useFilesGallery/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
};
117 changes: 64 additions & 53 deletions packages/editor/src/view/hooks/useFilesGallery/useFilesGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export function useFilesGallery(
download: getItemDownloladUrl,
overrideItemProps,
copyUrl: getItemCopyUrl,
resolveCustomItem,
}: UseFilesGalleryOptions = {},
) {
const {openGallery} = useGallery();
Expand All @@ -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;
Expand All @@ -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));
}
}
}

Expand All @@ -140,6 +150,7 @@ export function useFilesGallery(
getItemCopyUrl,
getItemDownloladUrl,
overrideItemProps,
resolveCustomItem,
toaster,
openGallery,
],
Expand Down
Loading