@seshuk/payload-media-preview 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +507 -0
  3. package/dist/components/Cell/Cell.client.d.ts +18 -0
  4. package/dist/components/Cell/Cell.client.js +131 -0
  5. package/dist/components/Cell/Cell.scss +43 -0
  6. package/dist/components/Cell/Cell.server.d.ts +11 -0
  7. package/dist/components/Cell/Cell.server.js +33 -0
  8. package/dist/components/ExternalLinkIcon/ExternalLinkIcon.d.ts +5 -0
  9. package/dist/components/ExternalLinkIcon/ExternalLinkIcon.js +17 -0
  10. package/dist/components/ExternalLinkIcon/ExternalLinkIcon.scss +19 -0
  11. package/dist/components/Field/Field.d.ts +18 -0
  12. package/dist/components/Field/Field.js +107 -0
  13. package/dist/components/Field/Field.scss +14 -0
  14. package/dist/components/MediaPreview.constants.d.ts +4 -0
  15. package/dist/components/MediaPreview.constants.js +28 -0
  16. package/dist/components/MediaPreview.d.ts +10 -0
  17. package/dist/components/MediaPreview.js +57 -0
  18. package/dist/components/MediaPreview.types.d.ts +2 -0
  19. package/dist/components/MediaPreview.types.js +1 -0
  20. package/dist/components/MediaPreview.utils.d.ts +6 -0
  21. package/dist/components/MediaPreview.utils.js +38 -0
  22. package/dist/components/Modal/Modal.constants.d.ts +16 -0
  23. package/dist/components/Modal/Modal.constants.js +16 -0
  24. package/dist/components/Modal/Modal.d.ts +19 -0
  25. package/dist/components/Modal/Modal.js +277 -0
  26. package/dist/components/Modal/Modal.scss +173 -0
  27. package/dist/components/Viewer/AudioViewer.d.ts +3 -0
  28. package/dist/components/Viewer/AudioViewer.js +24 -0
  29. package/dist/components/Viewer/IframeViewer.d.ts +3 -0
  30. package/dist/components/Viewer/IframeViewer.js +14 -0
  31. package/dist/components/Viewer/ImageViewer.d.ts +3 -0
  32. package/dist/components/Viewer/ImageViewer.js +10 -0
  33. package/dist/components/Viewer/VideoViewer.d.ts +3 -0
  34. package/dist/components/Viewer/VideoViewer.js +25 -0
  35. package/dist/components/Viewer/Viewer.d.ts +12 -0
  36. package/dist/components/Viewer/Viewer.js +50 -0
  37. package/dist/components/adapterResolver.d.ts +13 -0
  38. package/dist/components/adapterResolver.js +43 -0
  39. package/dist/exports/client.d.ts +5 -0
  40. package/dist/exports/client.js +5 -0
  41. package/dist/exports/rsc.d.ts +2 -0
  42. package/dist/exports/rsc.js +2 -0
  43. package/dist/field.d.ts +18 -0
  44. package/dist/field.js +36 -0
  45. package/dist/index.d.ts +6 -0
  46. package/dist/index.js +90 -0
  47. package/dist/translations/index.d.ts +8 -0
  48. package/dist/translations/index.js +90 -0
  49. package/dist/translations/locales/ar.d.ts +2 -0
  50. package/dist/translations/locales/ar.js +11 -0
  51. package/dist/translations/locales/az.d.ts +2 -0
  52. package/dist/translations/locales/az.js +11 -0
  53. package/dist/translations/locales/bg.d.ts +2 -0
  54. package/dist/translations/locales/bg.js +11 -0
  55. package/dist/translations/locales/bnBd.d.ts +2 -0
  56. package/dist/translations/locales/bnBd.js +11 -0
  57. package/dist/translations/locales/bnIn.d.ts +2 -0
  58. package/dist/translations/locales/bnIn.js +11 -0
  59. package/dist/translations/locales/ca.d.ts +2 -0
  60. package/dist/translations/locales/ca.js +11 -0
  61. package/dist/translations/locales/cs.d.ts +2 -0
  62. package/dist/translations/locales/cs.js +11 -0
  63. package/dist/translations/locales/da.d.ts +2 -0
  64. package/dist/translations/locales/da.js +11 -0
  65. package/dist/translations/locales/de.d.ts +2 -0
  66. package/dist/translations/locales/de.js +11 -0
  67. package/dist/translations/locales/en.d.ts +2 -0
  68. package/dist/translations/locales/en.js +11 -0
  69. package/dist/translations/locales/es.d.ts +2 -0
  70. package/dist/translations/locales/es.js +11 -0
  71. package/dist/translations/locales/et.d.ts +2 -0
  72. package/dist/translations/locales/et.js +11 -0
  73. package/dist/translations/locales/fa.d.ts +2 -0
  74. package/dist/translations/locales/fa.js +11 -0
  75. package/dist/translations/locales/fr.d.ts +2 -0
  76. package/dist/translations/locales/fr.js +11 -0
  77. package/dist/translations/locales/he.d.ts +2 -0
  78. package/dist/translations/locales/he.js +11 -0
  79. package/dist/translations/locales/hr.d.ts +2 -0
  80. package/dist/translations/locales/hr.js +11 -0
  81. package/dist/translations/locales/hu.d.ts +2 -0
  82. package/dist/translations/locales/hu.js +11 -0
  83. package/dist/translations/locales/hy.d.ts +2 -0
  84. package/dist/translations/locales/hy.js +11 -0
  85. package/dist/translations/locales/id.d.ts +2 -0
  86. package/dist/translations/locales/id.js +11 -0
  87. package/dist/translations/locales/is.d.ts +2 -0
  88. package/dist/translations/locales/is.js +11 -0
  89. package/dist/translations/locales/it.d.ts +2 -0
  90. package/dist/translations/locales/it.js +11 -0
  91. package/dist/translations/locales/ja.d.ts +2 -0
  92. package/dist/translations/locales/ja.js +11 -0
  93. package/dist/translations/locales/ko.d.ts +2 -0
  94. package/dist/translations/locales/ko.js +11 -0
  95. package/dist/translations/locales/lt.d.ts +2 -0
  96. package/dist/translations/locales/lt.js +11 -0
  97. package/dist/translations/locales/lv.d.ts +2 -0
  98. package/dist/translations/locales/lv.js +11 -0
  99. package/dist/translations/locales/my.d.ts +2 -0
  100. package/dist/translations/locales/my.js +11 -0
  101. package/dist/translations/locales/nb.d.ts +2 -0
  102. package/dist/translations/locales/nb.js +11 -0
  103. package/dist/translations/locales/nl.d.ts +2 -0
  104. package/dist/translations/locales/nl.js +11 -0
  105. package/dist/translations/locales/pl.d.ts +2 -0
  106. package/dist/translations/locales/pl.js +11 -0
  107. package/dist/translations/locales/pt.d.ts +2 -0
  108. package/dist/translations/locales/pt.js +11 -0
  109. package/dist/translations/locales/ro.d.ts +2 -0
  110. package/dist/translations/locales/ro.js +11 -0
  111. package/dist/translations/locales/rs.d.ts +2 -0
  112. package/dist/translations/locales/rs.js +11 -0
  113. package/dist/translations/locales/rsLatin.d.ts +2 -0
  114. package/dist/translations/locales/rsLatin.js +11 -0
  115. package/dist/translations/locales/ru.d.ts +2 -0
  116. package/dist/translations/locales/ru.js +11 -0
  117. package/dist/translations/locales/sk.d.ts +2 -0
  118. package/dist/translations/locales/sk.js +11 -0
  119. package/dist/translations/locales/sl.d.ts +2 -0
  120. package/dist/translations/locales/sl.js +11 -0
  121. package/dist/translations/locales/sv.d.ts +2 -0
  122. package/dist/translations/locales/sv.js +11 -0
  123. package/dist/translations/locales/ta.d.ts +2 -0
  124. package/dist/translations/locales/ta.js +11 -0
  125. package/dist/translations/locales/th.d.ts +2 -0
  126. package/dist/translations/locales/th.js +11 -0
  127. package/dist/translations/locales/tr.d.ts +2 -0
  128. package/dist/translations/locales/tr.js +11 -0
  129. package/dist/translations/locales/uk.d.ts +2 -0
  130. package/dist/translations/locales/uk.js +11 -0
  131. package/dist/translations/locales/vi.d.ts +2 -0
  132. package/dist/translations/locales/vi.js +11 -0
  133. package/dist/translations/locales/zh.d.ts +2 -0
  134. package/dist/translations/locales/zh.js +11 -0
  135. package/dist/translations/locales/zhTw.d.ts +2 -0
  136. package/dist/translations/locales/zhTw.js +11 -0
  137. package/dist/translations/types.d.ts +11 -0
  138. package/dist/translations/types.js +1 -0
  139. package/dist/types.d.ts +130 -0
  140. package/dist/types.js +2 -0
  141. package/dist/utils/insertField.d.ts +3 -0
  142. package/dist/utils/insertField.js +97 -0
  143. package/package.json +121 -0
@@ -0,0 +1,43 @@
1
+ @import '~@payloadcms/ui/scss';
2
+
3
+ td.cell-mediaPreview {
4
+ position: static !important;
5
+ z-index: auto !important;
6
+
7
+ &:has(.media-preview-cell) {
8
+ vertical-align: middle !important;
9
+ }
10
+ }
11
+
12
+ .media-preview-cell {
13
+ &__button-wrapper {
14
+ all: unset;
15
+ cursor: pointer;
16
+ display: inline-block;
17
+ }
18
+
19
+ &__pill {
20
+ color: var(--theme-elevation-800);
21
+ box-shadow: 0 0 0 1px var(--theme-elevation-150);
22
+ cursor: pointer;
23
+ gap: calc(var(--base) * 0.3) !important;
24
+ padding-inline: calc(var(--base) * 0.4) !important;
25
+ background-color: transparent;
26
+ transition: background-color 0.15s ease;
27
+
28
+ &:hover,
29
+ &--active {
30
+ background-color: var(--theme-elevation-100);
31
+ }
32
+ }
33
+
34
+ &__icon {
35
+ width: 1rem;
36
+ height: 1rem;
37
+ flex-shrink: 0;
38
+
39
+ .stroke {
40
+ stroke-width: 1;
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,11 @@
1
+ import type { MediaPreviewContentMode, MediaPreviewMode } from '@/types.js';
2
+ import type { DefaultServerCellComponentProps } from 'payload';
3
+ import React from 'react';
4
+ import './Cell.scss';
5
+ type Props = {
6
+ adapterNames?: string[];
7
+ contentMode?: Partial<MediaPreviewContentMode>;
8
+ mode?: MediaPreviewMode;
9
+ } & DefaultServerCellComponentProps;
10
+ export declare const MediaPreviewCell: React.FC<Props>;
11
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { resolveAdapter, resolveAdapterViewer } from "../adapterResolver.js";
4
+ import { MediaPreviewCellClient } from "./Cell.client.js";
5
+ import "./Cell.scss";
6
+ export const MediaPreviewCell = ({ adapterNames, contentMode, mode = 'auto', payload: payloadInstance, rowData })=>{
7
+ const fileSize = rowData?.filesize;
8
+ const height = rowData?.height;
9
+ const mimeType = rowData?.mimeType;
10
+ const url = rowData?.url;
11
+ const width = rowData?.width;
12
+ const adapterMatch = resolveAdapter(payloadInstance, adapterNames, {
13
+ doc: rowData || {},
14
+ mimeType,
15
+ url
16
+ });
17
+ const customViewer = resolveAdapterViewer(payloadInstance, adapterMatch);
18
+ const adapterNewTabUrl = adapterMatch?.result.mode === 'newTab' ? adapterMatch.result.url : undefined;
19
+ return /*#__PURE__*/ _jsx(MediaPreviewCellClient, {
20
+ adapterNewTabUrl: adapterNewTabUrl,
21
+ contentMode: contentMode,
22
+ customViewer: customViewer,
23
+ media: {
24
+ fileSize,
25
+ height,
26
+ mimeType,
27
+ url,
28
+ width
29
+ },
30
+ mode: mode,
31
+ rowId: rowData?.id
32
+ });
33
+ };
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import './ExternalLinkIcon.scss';
3
+ export declare const ExternalLinkIcon: React.FC<{
4
+ className?: string;
5
+ }>;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./ExternalLinkIcon.scss";
4
+ export const ExternalLinkIcon = ({ className })=>/*#__PURE__*/ _jsx("svg", {
5
+ className: [
6
+ className,
7
+ 'icon icon--external-link'
8
+ ].filter(Boolean).join(' '),
9
+ fill: "none",
10
+ viewBox: "0 0 16 16",
11
+ xmlns: "http://www.w3.org/2000/svg",
12
+ children: /*#__PURE__*/ _jsx("path", {
13
+ className: "stroke",
14
+ d: "M6 2H2C1.44772 2 1 2.44772 1 3V14C1 14.5523 1.44772 15 2 15H13C13.5523 15 14 14.5523 14 14V10M10 1H15M15 1V6M15 1L6 10",
15
+ strokeLinecap: "square"
16
+ })
17
+ });
@@ -0,0 +1,19 @@
1
+ @import '~@payloadcms/ui/scss';
2
+
3
+ .icon--external-link {
4
+ height: $baseline;
5
+ width: $baseline;
6
+ shape-rendering: auto;
7
+ vector-effect: non-scaling-stroke;
8
+ transform: scale(0.9);
9
+
10
+ .stroke {
11
+ fill: none;
12
+ stroke: currentColor;
13
+ stroke-width: 0.75;
14
+ }
15
+
16
+ .fill {
17
+ fill: currentColor;
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ import type { MediaPreviewContentMode, MediaPreviewMode } from '@/types.js';
2
+ import React from 'react';
3
+ import './Field.scss';
4
+ type Props = {
5
+ adapterNewTabUrl?: string;
6
+ contentMode?: Partial<MediaPreviewContentMode>;
7
+ customViewer?: React.ReactNode;
8
+ media: {
9
+ fileSize?: number;
10
+ height?: number;
11
+ mimeType?: string;
12
+ url?: string;
13
+ width?: number;
14
+ };
15
+ mode?: MediaPreviewMode;
16
+ };
17
+ export declare const MediaPreviewFieldClient: React.FC<Props>;
18
+ export {};
@@ -0,0 +1,107 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Button, useTranslation } from "@payloadcms/ui";
4
+ import { EyeIcon } from "@payloadcms/ui/icons/Eye";
5
+ import React, { useCallback, useMemo, useState } from "react";
6
+ import { ExternalLinkIcon } from "../ExternalLinkIcon/ExternalLinkIcon.js";
7
+ import { canPreviewDocument, getDocumentViewerType, getGoogleViewerUrl, getMicrosoftViewerUrl, getPreviewType } from "../MediaPreview.utils.js";
8
+ import { MediaPreviewModal } from "../Modal/Modal.js";
9
+ import "./Field.scss";
10
+ export const MediaPreviewFieldClient = ({ adapterNewTabUrl, contentMode, customViewer, media, mode: _mode = 'auto' })=>{
11
+ const { fileSize, height, mimeType, url, width } = media;
12
+ const [showModal, setShowModal] = useState(false);
13
+ const { t } = useTranslation();
14
+ const audioViewerMode = contentMode?.audio ?? 'inline';
15
+ const documentViewerMode = contentMode?.document ?? 'inline';
16
+ const imageViewerMode = contentMode?.image ?? 'inline';
17
+ const videoViewerMode = contentMode?.video ?? 'inline';
18
+ const previewType = useMemo(()=>getPreviewType(mimeType), [
19
+ mimeType
20
+ ]);
21
+ const isAudioFile = previewType === 'audio';
22
+ const isDocumentFile = previewType === 'document';
23
+ const isImageFile = previewType === 'image';
24
+ const isVideoFile = previewType === 'video';
25
+ const canPreview = !isDocumentFile || canPreviewDocument(fileSize);
26
+ const documentViewerUrl = useMemo(()=>{
27
+ if (isDocumentFile && url && mimeType) {
28
+ const viewerType = getDocumentViewerType(mimeType);
29
+ return viewerType === 'microsoft' ? getMicrosoftViewerUrl(url) : getGoogleViewerUrl(url);
30
+ }
31
+ return null;
32
+ }, [
33
+ isDocumentFile,
34
+ url,
35
+ mimeType
36
+ ]);
37
+ const handleToggleModal = useCallback(()=>{
38
+ setShowModal((prev)=>!prev);
39
+ }, []);
40
+ const renderNewTabButton = useCallback((href)=>/*#__PURE__*/ _jsx("div", {
41
+ className: "media-preview__button-wrapper",
42
+ children: /*#__PURE__*/ _jsx(Button, {
43
+ buttonStyle: "secondary",
44
+ el: "anchor",
45
+ icon: /*#__PURE__*/ _jsx(ExternalLinkIcon, {}),
46
+ iconPosition: "left",
47
+ newTab: true,
48
+ size: "medium",
49
+ url: href,
50
+ children: t('@seshuk/payload-media-preview:open')
51
+ })
52
+ }), [
53
+ t
54
+ ]);
55
+ if (previewType === 'unsupported' || !url || !canPreview) {
56
+ if (!customViewer) {
57
+ return null;
58
+ }
59
+ }
60
+ if (adapterNewTabUrl) {
61
+ return renderNewTabButton(adapterNewTabUrl);
62
+ }
63
+ if (!customViewer) {
64
+ if (isVideoFile && videoViewerMode === 'newTab' && url) {
65
+ return renderNewTabButton(url);
66
+ }
67
+ if (isAudioFile && audioViewerMode === 'newTab' && url) {
68
+ return renderNewTabButton(url);
69
+ }
70
+ if (isImageFile && imageViewerMode === 'newTab' && url) {
71
+ return renderNewTabButton(url);
72
+ }
73
+ if (isDocumentFile && documentViewerMode === 'newTab' && documentViewerUrl) {
74
+ return renderNewTabButton(documentViewerUrl);
75
+ }
76
+ }
77
+ return /*#__PURE__*/ _jsxs(_Fragment, {
78
+ children: [
79
+ /*#__PURE__*/ _jsx("div", {
80
+ className: "media-preview__button-wrapper",
81
+ children: /*#__PURE__*/ _jsx(Button, {
82
+ buttonStyle: "secondary",
83
+ icon: /*#__PURE__*/ _jsx(EyeIcon, {
84
+ active: false
85
+ }),
86
+ iconPosition: "left",
87
+ onClick: handleToggleModal,
88
+ size: "medium",
89
+ children: t('@seshuk/payload-media-preview:open')
90
+ })
91
+ }),
92
+ /*#__PURE__*/ _jsx(MediaPreviewModal, {
93
+ customViewer: customViewer,
94
+ media: {
95
+ documentViewerUrl,
96
+ height,
97
+ mimeType,
98
+ url,
99
+ width
100
+ },
101
+ mode: "fullscreen",
102
+ onClose: ()=>setShowModal(false),
103
+ show: showModal
104
+ })
105
+ ]
106
+ });
107
+ };
@@ -0,0 +1,14 @@
1
+ .media-preview__button-wrapper {
2
+ .btn {
3
+ margin-block: 0;
4
+
5
+ &__icon {
6
+ width: var(--base);
7
+ height: var(--base);
8
+
9
+ .stroke {
10
+ stroke-width: 1;
11
+ }
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,4 @@
1
+ export declare const FIELD_BASE_CLASS = "field-type";
2
+ export declare const GOOGLE_VIEWER_MAX_SIZE: number;
3
+ export declare const MICROSOFT_OFFICE_TYPES: string[];
4
+ export declare const GOOGLE_VIEWER_TYPES: string[];
@@ -0,0 +1,28 @@
1
+ export const FIELD_BASE_CLASS = 'field-type';
2
+ export const GOOGLE_VIEWER_MAX_SIZE = 25 * 1024 * 1024;
3
+ export const MICROSOFT_OFFICE_TYPES = [
4
+ 'application/msword',
5
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
6
+ 'application/vnd.ms-excel',
7
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
8
+ 'application/vnd.ms-powerpoint',
9
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
10
+ ];
11
+ export const GOOGLE_VIEWER_TYPES = [
12
+ 'application/pdf',
13
+ 'text/plain',
14
+ 'text/css',
15
+ 'text/html',
16
+ 'text/php',
17
+ 'text/javascript',
18
+ 'application/x-javascript',
19
+ 'text/x-c',
20
+ 'text/x-c++',
21
+ 'application/vnd.apple.pages',
22
+ 'application/postscript',
23
+ 'image/vnd.adobe.photoshop',
24
+ 'image/vnd.dxf',
25
+ 'application/dxf',
26
+ 'image/svg+xml',
27
+ 'application/vnd.ms-xpsdocument'
28
+ ];
@@ -0,0 +1,10 @@
1
+ import type { MediaPreviewContentMode, MediaPreviewMode } from '@/types.js';
2
+ import type { UIFieldServerProps } from 'payload';
3
+ import React from 'react';
4
+ type MediaPreviewProps = {
5
+ adapterNames?: string[];
6
+ contentMode?: Partial<MediaPreviewContentMode>;
7
+ mode?: MediaPreviewMode;
8
+ } & UIFieldServerProps;
9
+ export declare const MediaPreview: React.FC<MediaPreviewProps>;
10
+ export {};
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getTranslation } from "@payloadcms/translations";
3
+ import { FieldLabel } from "@payloadcms/ui";
4
+ import React from "react";
5
+ import { resolveAdapter, resolveAdapterViewer } from "./adapterResolver.js";
6
+ import { MediaPreviewFieldClient } from "./Field/Field.js";
7
+ import { FIELD_BASE_CLASS } from "./MediaPreview.constants.js";
8
+ import { getPreviewType } from "./MediaPreview.utils.js";
9
+ export const MediaPreview = (props)=>{
10
+ const { adapterNames, clientField: { label }, contentMode, data, mode = 'auto', path, req } = props;
11
+ const reqT = req.t;
12
+ const fileSize = data?.filesize;
13
+ const height = data?.height;
14
+ const mimeType = data?.mimeType;
15
+ const url = data?.url;
16
+ const width = data?.width;
17
+ const adapterMatch = resolveAdapter(req.payload, adapterNames, {
18
+ doc: data || {},
19
+ mimeType,
20
+ url
21
+ });
22
+ const previewType = getPreviewType(mimeType);
23
+ if (previewType === 'unsupported' && !adapterMatch) {
24
+ return null;
25
+ }
26
+ const customViewer = resolveAdapterViewer(req.payload, adapterMatch);
27
+ const adapterNewTabUrl = adapterMatch?.result.mode === 'newTab' ? adapterMatch.result.url : undefined;
28
+ let fieldLabel;
29
+ if (label === '@seshuk/payload-media-preview:label') {
30
+ fieldLabel = reqT('@seshuk/payload-media-preview:label');
31
+ } else if (label) {
32
+ const translatedLabel = getTranslation(label, req.i18n);
33
+ fieldLabel = translatedLabel && translatedLabel !== '' ? translatedLabel : undefined;
34
+ }
35
+ return /*#__PURE__*/ _jsxs("div", {
36
+ className: `${FIELD_BASE_CLASS} media-preview`,
37
+ children: [
38
+ fieldLabel && /*#__PURE__*/ _jsx(FieldLabel, {
39
+ htmlFor: path,
40
+ label: fieldLabel
41
+ }),
42
+ /*#__PURE__*/ _jsx(MediaPreviewFieldClient, {
43
+ adapterNewTabUrl: adapterNewTabUrl,
44
+ contentMode: contentMode,
45
+ customViewer: customViewer,
46
+ media: {
47
+ fileSize,
48
+ height,
49
+ mimeType,
50
+ url,
51
+ width
52
+ },
53
+ mode: mode
54
+ })
55
+ ]
56
+ });
57
+ };
@@ -0,0 +1,2 @@
1
+ export type PreviewType = 'audio' | 'document' | 'image' | 'unsupported' | 'video';
2
+ export type DocumentViewerType = 'google' | 'microsoft';
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,6 @@
1
+ import type { DocumentViewerType, PreviewType } from './MediaPreview.types.js';
2
+ export declare const getPreviewType: (mimeType?: string) => PreviewType;
3
+ export declare const getDocumentViewerType: (mimeType: string) => DocumentViewerType;
4
+ export declare const canPreviewDocument: (fileSize?: number) => boolean;
5
+ export declare const getMicrosoftViewerUrl: (fileUrl: string) => string;
6
+ export declare const getGoogleViewerUrl: (fileUrl: string) => string;
@@ -0,0 +1,38 @@
1
+ import { formatAbsoluteURL } from "@payloadcms/ui/utilities/formatAbsoluteURL";
2
+ import { GOOGLE_VIEWER_MAX_SIZE, GOOGLE_VIEWER_TYPES, MICROSOFT_OFFICE_TYPES } from "./MediaPreview.constants.js";
3
+ export const getPreviewType = (mimeType)=>{
4
+ if (!mimeType) {
5
+ return 'unsupported';
6
+ }
7
+ if (mimeType.startsWith('video/')) {
8
+ return 'video';
9
+ }
10
+ if (mimeType.startsWith('audio/')) {
11
+ return 'audio';
12
+ }
13
+ if (mimeType.startsWith('image/')) {
14
+ return 'image';
15
+ }
16
+ if (MICROSOFT_OFFICE_TYPES.includes(mimeType) || GOOGLE_VIEWER_TYPES.includes(mimeType)) {
17
+ return 'document';
18
+ }
19
+ return 'unsupported';
20
+ };
21
+ export const getDocumentViewerType = (mimeType)=>{
22
+ if (MICROSOFT_OFFICE_TYPES.includes(mimeType)) {
23
+ return 'microsoft';
24
+ }
25
+ return 'google';
26
+ };
27
+ export const canPreviewDocument = (fileSize)=>{
28
+ if (!fileSize) {
29
+ return true;
30
+ }
31
+ return fileSize <= GOOGLE_VIEWER_MAX_SIZE;
32
+ };
33
+ export const getMicrosoftViewerUrl = (fileUrl)=>{
34
+ return `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(formatAbsoluteURL(fileUrl))}`;
35
+ };
36
+ export const getGoogleViewerUrl = (fileUrl)=>{
37
+ return `https://docs.google.com/viewer?url=${encodeURIComponent(formatAbsoluteURL(fileUrl))}&embedded=true`;
38
+ };
@@ -0,0 +1,16 @@
1
+ export declare const POPUP_DIMENSIONS: {
2
+ readonly MAX_HEIGHT: 290;
3
+ readonly MAX_WIDTH: 480;
4
+ readonly MIN_HEIGHT: 200;
5
+ readonly MIN_WIDTH: 200;
6
+ };
7
+ export declare const AUDIO_DIMENSIONS: {
8
+ readonly HEIGHT: 40;
9
+ readonly WIDTH: 300;
10
+ };
11
+ export declare const SPACING: {
12
+ readonly BOTTOM: 4;
13
+ readonly CARET_SIZE: 10;
14
+ readonly TOP: 24;
15
+ readonly VIEWPORT_MARGIN: 16;
16
+ };
@@ -0,0 +1,16 @@
1
+ export const POPUP_DIMENSIONS = {
2
+ MAX_HEIGHT: 290,
3
+ MAX_WIDTH: 480,
4
+ MIN_HEIGHT: 200,
5
+ MIN_WIDTH: 200
6
+ };
7
+ export const AUDIO_DIMENSIONS = {
8
+ HEIGHT: 40,
9
+ WIDTH: 300
10
+ };
11
+ export const SPACING = {
12
+ BOTTOM: 4,
13
+ CARET_SIZE: 10,
14
+ TOP: 24,
15
+ VIEWPORT_MARGIN: 16
16
+ };
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import './Modal.scss';
3
+ type MediaPreviewModalProps = {
4
+ customViewer?: React.ReactNode;
5
+ media: {
6
+ documentViewerUrl?: null | string;
7
+ height?: number;
8
+ mimeType?: string;
9
+ url?: string;
10
+ width?: number;
11
+ };
12
+ mode: 'fullscreen' | 'popup';
13
+ onClose?: () => void;
14
+ rowId?: number | string;
15
+ show: boolean;
16
+ triggerRef?: React.RefObject<HTMLButtonElement | HTMLElement | null>;
17
+ };
18
+ export declare const MediaPreviewModal: React.FC<MediaPreviewModalProps>;
19
+ export {};