@chayns-components/gallery 5.2.6 → 5.2.8-alpha.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.
- package/lib/cjs/components/media-content/MediaContent.js +28 -5
- package/lib/cjs/components/media-content/MediaContent.js.map +1 -1
- package/lib/cjs/components/media-content/MediaContent.utils.js +74 -1
- package/lib/cjs/components/media-content/MediaContent.utils.js.map +1 -1
- package/lib/cjs/components/media-content/useMediaContentSize.js +72 -0
- package/lib/cjs/components/media-content/useMediaContentSize.js.map +1 -0
- package/lib/esm/components/media-content/MediaContent.js +28 -6
- package/lib/esm/components/media-content/MediaContent.js.map +1 -1
- package/lib/esm/components/media-content/MediaContent.utils.js +70 -0
- package/lib/esm/components/media-content/MediaContent.utils.js.map +1 -1
- package/lib/esm/components/media-content/useMediaContentSize.js +66 -0
- package/lib/esm/components/media-content/useMediaContentSize.js.map +1 -0
- package/lib/types/components/media-content/MediaContent.utils.d.ts +16 -0
- package/lib/types/components/media-content/useMediaContentSize.d.ts +6 -0
- package/package.json +3 -3
|
@@ -9,6 +9,8 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
9
9
|
var _MediaContent = require("./MediaContent.styles");
|
|
10
10
|
var _MediaContent2 = require("./MediaContent.utils");
|
|
11
11
|
var _MediaContent3 = require("./MediaContent.constants");
|
|
12
|
+
var _useMediaContentSize = _interopRequireDefault(require("./useMediaContentSize"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
13
15
|
const MediaContent = ({
|
|
14
16
|
file,
|
|
@@ -22,10 +24,14 @@ const MediaContent = ({
|
|
|
22
24
|
const sourceKey = (0, _MediaContent2.getMediaSourceUrl)(file);
|
|
23
25
|
const previewSourceUrl = (0, _MediaContent2.getMediaPreviewUrl)(file, previewUrl);
|
|
24
26
|
const [hasLoadedFinalMedia, setHasLoadedFinalMedia] = (0, _react.useState)(false);
|
|
27
|
+
const [resolvedFinalSourceUrl, setResolvedFinalSourceUrl] = (0, _react.useState)();
|
|
28
|
+
const [containerElement, setContainerElement] = (0, _react.useState)(null);
|
|
25
29
|
const imageRef = (0, _react.useRef)(null);
|
|
26
30
|
const videoRef = (0, _react.useRef)(null);
|
|
27
|
-
const
|
|
28
|
-
const
|
|
31
|
+
const renderSize = (0, _useMediaContentSize.default)(containerElement);
|
|
32
|
+
const devicePixelRatio = typeof window !== 'undefined' && Number.isFinite(window.devicePixelRatio) && window.devicePixelRatio > 0 ? window.devicePixelRatio : 1;
|
|
33
|
+
const displayPreviewUrl = (0, _react.useMemo)(() => previewSourceUrl ? (0, _MediaContent2.getResponsiveImageServiceUrl)(previewSourceUrl, renderSize, devicePixelRatio) : undefined, [devicePixelRatio, previewSourceUrl, renderSize]);
|
|
34
|
+
const finalSourceUrl = isVideo ? sourceKey : resolvedFinalSourceUrl;
|
|
29
35
|
const shouldHidePreview = shouldLoadImages && hasLoadedFinalMedia;
|
|
30
36
|
const previewLayerStyle = {
|
|
31
37
|
opacity: shouldHidePreview ? 0 : 1,
|
|
@@ -35,12 +41,27 @@ const MediaContent = ({
|
|
|
35
41
|
opacity: hasLoadedFinalMedia || !displayPreviewUrl ? 1 : 0,
|
|
36
42
|
transition: `opacity ${_MediaContent3.MEDIA_CONTENT_IMAGE_FADE_DURATION_MS}ms ease`
|
|
37
43
|
};
|
|
38
|
-
(0, _react.
|
|
44
|
+
(0, _react.useEffect)(() => {
|
|
39
45
|
setHasLoadedFinalMedia(false);
|
|
40
46
|
}, [sourceKey]);
|
|
41
|
-
(0, _react.
|
|
42
|
-
var _imageRef$current, _videoRef$current;
|
|
47
|
+
(0, _react.useEffect)(() => {
|
|
43
48
|
if (!shouldLoadImages) {
|
|
49
|
+
setResolvedFinalSourceUrl(undefined);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (isVideo) {
|
|
53
|
+
setResolvedFinalSourceUrl(sourceKey);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!renderSize) {
|
|
57
|
+
setResolvedFinalSourceUrl(undefined);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setResolvedFinalSourceUrl((0, _MediaContent2.getResponsiveImageServiceUrl)(sourceKey, renderSize, devicePixelRatio));
|
|
61
|
+
}, [devicePixelRatio, isVideo, renderSize, shouldLoadImages, sourceKey]);
|
|
62
|
+
(0, _react.useEffect)(() => {
|
|
63
|
+
var _imageRef$current, _videoRef$current;
|
|
64
|
+
if (!shouldLoadImages || !finalSourceUrl) {
|
|
44
65
|
return;
|
|
45
66
|
}
|
|
46
67
|
if (!isVideo && (_imageRef$current = imageRef.current) !== null && _imageRef$current !== void 0 && _imageRef$current.complete && imageRef.current.naturalWidth > 0) {
|
|
@@ -54,6 +75,7 @@ const MediaContent = ({
|
|
|
54
75
|
const shouldShowPreview = Boolean(displayPreviewUrl);
|
|
55
76
|
if (isVideo) {
|
|
56
77
|
return /*#__PURE__*/_react.default.createElement(_MediaContent.StyledMediaContentVideoWrapper, {
|
|
78
|
+
ref: setContainerElement,
|
|
57
79
|
onClick: onClick,
|
|
58
80
|
$ratio: ratio
|
|
59
81
|
}, displayPreviewUrl && /*#__PURE__*/_react.default.createElement(_MediaContent.StyledMediaContentPreviewImage, {
|
|
@@ -77,6 +99,7 @@ const MediaContent = ({
|
|
|
77
99
|
})));
|
|
78
100
|
}
|
|
79
101
|
return /*#__PURE__*/_react.default.createElement(_MediaContent.StyledMediaContentImageWrapper, {
|
|
102
|
+
ref: setContainerElement,
|
|
80
103
|
onClick: onClick,
|
|
81
104
|
$ratio: ratio
|
|
82
105
|
}, shouldShowPreview && /*#__PURE__*/_react.default.createElement(_MediaContent.StyledMediaContentPreviewImage, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaContent.js","names":["_core","require","_react","_interopRequireWildcard","_MediaContent","_MediaContent2","_MediaContent3","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","MediaContent","file","previewUrl","ratio","onClick","shouldLoadImages","playIconSize","isVideo","isVideoFile","sourceKey","getMediaSourceUrl","previewSourceUrl","getMediaPreviewUrl","hasLoadedFinalMedia","setHasLoadedFinalMedia","useState","imageRef","useRef","videoRef","finalSourceUrl","displayPreviewUrl","shouldHidePreview","previewLayerStyle","opacity","filter","undefined","finalMediaStyle","transition","MEDIA_CONTENT_IMAGE_FADE_DURATION_MS","useLayoutEffect","_imageRef$current","_videoRef$current","current","complete","naturalWidth","readyState","shouldRenderFinalImage","Boolean","shouldShowPreview","createElement","StyledMediaContentVideoWrapper","$ratio","StyledMediaContentPreviewImage","draggable","src","alt","style","StyledMediaContentPlayIcon","Icon","size","icons","StyledMediaContentVideo","ref","poster","preload","onLoadedData","type","StyledMediaContentImageWrapper","StyledMediaContentImage","onLoad","displayName","_default","exports","memo"],"sources":["../../../../src/components/media-content/MediaContent.tsx"],"sourcesContent":["import { Icon } from '@chayns-components/core';\nimport React, { FC, memo, useLayoutEffect, useRef, useState } from 'react';\nimport {\n StyledMediaContentImage,\n StyledMediaContentImageWrapper,\n StyledMediaContentPlayIcon,\n StyledMediaContentPreviewImage,\n StyledMediaContentVideo,\n StyledMediaContentVideoWrapper,\n} from './MediaContent.styles';\nimport type { MediaContentProps } from './MediaContent.types';\nimport { getMediaPreviewUrl, getMediaSourceUrl, isVideoFile } from './MediaContent.utils';\nimport { MEDIA_CONTENT_IMAGE_FADE_DURATION_MS } from './MediaContent.constants';\n\nconst MediaContent: FC<MediaContentProps> = ({\n file,\n previewUrl,\n ratio,\n onClick,\n shouldLoadImages = true,\n playIconSize = 50,\n}) => {\n const isVideo = isVideoFile(file);\n const sourceKey = getMediaSourceUrl(file);\n const previewSourceUrl = getMediaPreviewUrl(file, previewUrl);\n const [hasLoadedFinalMedia, setHasLoadedFinalMedia] = useState(false);\n const imageRef = useRef<HTMLImageElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const finalSourceUrl = sourceKey;\n const displayPreviewUrl = previewSourceUrl;\n const shouldHidePreview = shouldLoadImages && hasLoadedFinalMedia;\n const previewLayerStyle = {\n opacity: shouldHidePreview ? 0 : 1,\n filter: shouldHidePreview ? 'blur(0px)' : undefined,\n };\n const finalMediaStyle = {\n opacity: hasLoadedFinalMedia || !displayPreviewUrl ? 1 : 0,\n transition: `opacity ${MEDIA_CONTENT_IMAGE_FADE_DURATION_MS}ms ease`,\n };\n\n useLayoutEffect(() => {\n setHasLoadedFinalMedia(false);\n }, [sourceKey]);\n\n useLayoutEffect(() => {\n if (!shouldLoadImages) {\n return;\n }\n\n if (!isVideo && imageRef.current?.complete && imageRef.current.naturalWidth > 0) {\n setHasLoadedFinalMedia(true);\n }\n\n if (isVideo && videoRef.current?.readyState && videoRef.current.readyState >= 2) {\n setHasLoadedFinalMedia(true);\n }\n }, [finalSourceUrl, isVideo, shouldLoadImages]);\n\n const shouldRenderFinalImage = shouldLoadImages && Boolean(finalSourceUrl);\n const shouldShowPreview = Boolean(displayPreviewUrl);\n\n if (isVideo) {\n return (\n <StyledMediaContentVideoWrapper onClick={onClick} $ratio={ratio}>\n {displayPreviewUrl && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n <StyledMediaContentPlayIcon>\n <Icon size={playIconSize} icons={['fa fa-play']} />\n </StyledMediaContentPlayIcon>\n {shouldRenderFinalImage && (\n <StyledMediaContentVideo\n ref={videoRef}\n poster={displayPreviewUrl}\n preload=\"metadata\"\n onLoadedData={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n >\n <source src={finalSourceUrl} type=\"video/mp4\" />\n </StyledMediaContentVideo>\n )}\n </StyledMediaContentVideoWrapper>\n );\n }\n\n return (\n <StyledMediaContentImageWrapper onClick={onClick} $ratio={ratio}>\n {shouldShowPreview && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n {shouldRenderFinalImage && (\n <StyledMediaContentImage\n ref={imageRef}\n draggable={false}\n src={finalSourceUrl}\n alt=\"\"\n onLoad={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n />\n )}\n </StyledMediaContentImageWrapper>\n );\n};\n\nMediaContent.displayName = 'MediaContent';\n\nexport default memo(MediaContent);\n"],"mappings":";;;;;;AAAA,IAAAA,KAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAC,uBAAA,CAAAF,OAAA;AACA,IAAAG,aAAA,GAAAH,OAAA;AASA,IAAAI,cAAA,GAAAJ,OAAA;AACA,IAAAK,cAAA,GAAAL,OAAA;AAAgF,SAAAE,wBAAAI,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAN,uBAAA,YAAAA,CAAAI,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;AAEhF,MAAMkB,YAAmC,GAAGA,CAAC;EACzCC,IAAI;EACJC,UAAU;EACVC,KAAK;EACLC,OAAO;EACPC,gBAAgB,GAAG,IAAI;EACvBC,YAAY,GAAG;AACnB,CAAC,KAAK;EACF,MAAMC,OAAO,GAAG,IAAAC,0BAAW,EAACP,IAAI,CAAC;EACjC,MAAMQ,SAAS,GAAG,IAAAC,gCAAiB,EAACT,IAAI,CAAC;EACzC,MAAMU,gBAAgB,GAAG,IAAAC,iCAAkB,EAACX,IAAI,EAAEC,UAAU,CAAC;EAC7D,MAAM,CAACW,mBAAmB,EAAEC,sBAAsB,CAAC,GAAG,IAAAC,eAAQ,EAAC,KAAK,CAAC;EACrE,MAAMC,QAAQ,GAAG,IAAAC,aAAM,EAAmB,IAAI,CAAC;EAC/C,MAAMC,QAAQ,GAAG,IAAAD,aAAM,EAAmB,IAAI,CAAC;EAC/C,MAAME,cAAc,GAAGV,SAAS;EAChC,MAAMW,iBAAiB,GAAGT,gBAAgB;EAC1C,MAAMU,iBAAiB,GAAGhB,gBAAgB,IAAIQ,mBAAmB;EACjE,MAAMS,iBAAiB,GAAG;IACtBC,OAAO,EAAEF,iBAAiB,GAAG,CAAC,GAAG,CAAC;IAClCG,MAAM,EAAEH,iBAAiB,GAAG,WAAW,GAAGI;EAC9C,CAAC;EACD,MAAMC,eAAe,GAAG;IACpBH,OAAO,EAAEV,mBAAmB,IAAI,CAACO,iBAAiB,GAAG,CAAC,GAAG,CAAC;IAC1DO,UAAU,EAAE,WAAWC,mDAAoC;EAC/D,CAAC;EAED,IAAAC,sBAAe,EAAC,MAAM;IAClBf,sBAAsB,CAAC,KAAK,CAAC;EACjC,CAAC,EAAE,CAACL,SAAS,CAAC,CAAC;EAEf,IAAAoB,sBAAe,EAAC,MAAM;IAAA,IAAAC,iBAAA,EAAAC,iBAAA;IAClB,IAAI,CAAC1B,gBAAgB,EAAE;MACnB;IACJ;IAEA,IAAI,CAACE,OAAO,KAAAuB,iBAAA,GAAId,QAAQ,CAACgB,OAAO,cAAAF,iBAAA,eAAhBA,iBAAA,CAAkBG,QAAQ,IAAIjB,QAAQ,CAACgB,OAAO,CAACE,YAAY,GAAG,CAAC,EAAE;MAC7EpB,sBAAsB,CAAC,IAAI,CAAC;IAChC;IAEA,IAAIP,OAAO,KAAAwB,iBAAA,GAAIb,QAAQ,CAACc,OAAO,cAAAD,iBAAA,eAAhBA,iBAAA,CAAkBI,UAAU,IAAIjB,QAAQ,CAACc,OAAO,CAACG,UAAU,IAAI,CAAC,EAAE;MAC7ErB,sBAAsB,CAAC,IAAI,CAAC;IAChC;EACJ,CAAC,EAAE,CAACK,cAAc,EAAEZ,OAAO,EAAEF,gBAAgB,CAAC,CAAC;EAE/C,MAAM+B,sBAAsB,GAAG/B,gBAAgB,IAAIgC,OAAO,CAAClB,cAAc,CAAC;EAC1E,MAAMmB,iBAAiB,GAAGD,OAAO,CAACjB,iBAAiB,CAAC;EAEpD,IAAIb,OAAO,EAAE;IACT,oBACI/B,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAA8D,8BAA8B;MAACpC,OAAO,EAAEA,OAAQ;MAACqC,MAAM,EAAEtC;IAAM,GAC3DiB,iBAAiB,iBACd5C,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAAgE,8BAA8B;MAC3BC,SAAS,EAAE,KAAM;MACjBC,GAAG,EAAExB,iBAAkB;MACvByB,GAAG,EAAC,EAAE;MACN,eAAY,MAAM;MAClBC,KAAK,EAAExB;IAAkB,CAC5B,CACJ,eACD9C,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAAqE,0BAA0B,qBACvBvE,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAACjE,KAAA,CAAA0E,IAAI;MAACC,IAAI,EAAE3C,YAAa;MAAC4C,KAAK,EAAE,CAAC,YAAY;IAAE,CAAE,CAC1B,CAAC,EAC5Bd,sBAAsB,iBACnB5D,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAAyE,uBAAuB;MACpBC,GAAG,EAAElC,QAAS;MACdmC,MAAM,EAAEjC,iBAAkB;MAC1BkC,OAAO,EAAC,UAAU;MAClBC,YAAY,EAAEA,CAAA,KAAMzC,sBAAsB,CAAC,IAAI,CAAE;MACjDgC,KAAK,EAAEpB;IAAgB,gBAEvBlD,MAAA,CAAAe,OAAA,CAAAgD,aAAA;MAAQK,GAAG,EAAEzB,cAAe;MAACqC,IAAI,EAAC;IAAW,CAAE,CAC1B,CAED,CAAC;EAEzC;EAEA,oBACIhF,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAA+E,8BAA8B;IAACrD,OAAO,EAAEA,OAAQ;IAACqC,MAAM,EAAEtC;EAAM,GAC3DmC,iBAAiB,iBACd9D,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAAgE,8BAA8B;IAC3BC,SAAS,EAAE,KAAM;IACjBC,GAAG,EAAExB,iBAAkB;IACvByB,GAAG,EAAC,EAAE;IACN,eAAY,MAAM;IAClBC,KAAK,EAAExB;EAAkB,CAC5B,CACJ,EACAc,sBAAsB,iBACnB5D,MAAA,CAAAe,OAAA,CAAAgD,aAAA,CAAC7D,aAAA,CAAAgF,uBAAuB;IACpBN,GAAG,EAAEpC,QAAS;IACd2B,SAAS,EAAE,KAAM;IACjBC,GAAG,EAAEzB,cAAe;IACpB0B,GAAG,EAAC,EAAE;IACNc,MAAM,EAAEA,CAAA,KAAM7C,sBAAsB,CAAC,IAAI,CAAE;IAC3CgC,KAAK,EAAEpB;EAAgB,CAC1B,CAEuB,CAAC;AAEzC,CAAC;AAED1B,YAAY,CAAC4D,WAAW,GAAG,cAAc;AAAC,IAAAC,QAAA,GAAAC,OAAA,CAAAvE,OAAA,gBAE3B,IAAAwE,WAAI,EAAC/D,YAAY,CAAC","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"MediaContent.js","names":["_core","require","_react","_interopRequireWildcard","_MediaContent","_MediaContent2","_MediaContent3","_useMediaContentSize","_interopRequireDefault","e","__esModule","default","t","WeakMap","r","n","o","i","f","__proto__","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","MediaContent","file","previewUrl","ratio","onClick","shouldLoadImages","playIconSize","isVideo","isVideoFile","sourceKey","getMediaSourceUrl","previewSourceUrl","getMediaPreviewUrl","hasLoadedFinalMedia","setHasLoadedFinalMedia","useState","resolvedFinalSourceUrl","setResolvedFinalSourceUrl","containerElement","setContainerElement","imageRef","useRef","videoRef","renderSize","useMediaContentSize","devicePixelRatio","window","Number","isFinite","displayPreviewUrl","useMemo","getResponsiveImageServiceUrl","undefined","finalSourceUrl","shouldHidePreview","previewLayerStyle","opacity","filter","finalMediaStyle","transition","MEDIA_CONTENT_IMAGE_FADE_DURATION_MS","useEffect","_imageRef$current","_videoRef$current","current","complete","naturalWidth","readyState","shouldRenderFinalImage","Boolean","shouldShowPreview","createElement","StyledMediaContentVideoWrapper","ref","$ratio","StyledMediaContentPreviewImage","draggable","src","alt","style","StyledMediaContentPlayIcon","Icon","size","icons","StyledMediaContentVideo","poster","preload","onLoadedData","type","StyledMediaContentImageWrapper","StyledMediaContentImage","onLoad","displayName","_default","exports","memo"],"sources":["../../../../src/components/media-content/MediaContent.tsx"],"sourcesContent":["import { Icon } from '@chayns-components/core';\nimport React, { FC, memo, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n StyledMediaContentImage,\n StyledMediaContentImageWrapper,\n StyledMediaContentPlayIcon,\n StyledMediaContentPreviewImage,\n StyledMediaContentVideo,\n StyledMediaContentVideoWrapper,\n} from './MediaContent.styles';\nimport type { MediaContentProps } from './MediaContent.types';\nimport {\n getMediaPreviewUrl,\n getMediaSourceUrl,\n getResponsiveImageServiceUrl,\n isVideoFile,\n} from './MediaContent.utils';\nimport { MEDIA_CONTENT_IMAGE_FADE_DURATION_MS } from './MediaContent.constants';\nimport useMediaContentSize from './useMediaContentSize';\n\nconst MediaContent: FC<MediaContentProps> = ({\n file,\n previewUrl,\n ratio,\n onClick,\n shouldLoadImages = true,\n playIconSize = 50,\n}) => {\n const isVideo = isVideoFile(file);\n const sourceKey = getMediaSourceUrl(file);\n const previewSourceUrl = getMediaPreviewUrl(file, previewUrl);\n const [hasLoadedFinalMedia, setHasLoadedFinalMedia] = useState(false);\n const [resolvedFinalSourceUrl, setResolvedFinalSourceUrl] = useState<string>();\n const [containerElement, setContainerElement] = useState<HTMLDivElement | null>(null);\n const imageRef = useRef<HTMLImageElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const renderSize = useMediaContentSize(containerElement);\n const devicePixelRatio =\n typeof window !== 'undefined' &&\n Number.isFinite(window.devicePixelRatio) &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1;\n const displayPreviewUrl = useMemo(\n () =>\n previewSourceUrl\n ? getResponsiveImageServiceUrl(previewSourceUrl, renderSize, devicePixelRatio)\n : undefined,\n [devicePixelRatio, previewSourceUrl, renderSize],\n );\n const finalSourceUrl = isVideo ? sourceKey : resolvedFinalSourceUrl;\n const shouldHidePreview = shouldLoadImages && hasLoadedFinalMedia;\n const previewLayerStyle = {\n opacity: shouldHidePreview ? 0 : 1,\n filter: shouldHidePreview ? 'blur(0px)' : undefined,\n };\n const finalMediaStyle = {\n opacity: hasLoadedFinalMedia || !displayPreviewUrl ? 1 : 0,\n transition: `opacity ${MEDIA_CONTENT_IMAGE_FADE_DURATION_MS}ms ease`,\n };\n\n useEffect(() => {\n setHasLoadedFinalMedia(false);\n }, [sourceKey]);\n\n useEffect(() => {\n if (!shouldLoadImages) {\n setResolvedFinalSourceUrl(undefined);\n return;\n }\n\n if (isVideo) {\n setResolvedFinalSourceUrl(sourceKey);\n return;\n }\n\n if (!renderSize) {\n setResolvedFinalSourceUrl(undefined);\n return;\n }\n\n setResolvedFinalSourceUrl(\n getResponsiveImageServiceUrl(sourceKey, renderSize, devicePixelRatio),\n );\n }, [devicePixelRatio, isVideo, renderSize, shouldLoadImages, sourceKey]);\n\n useEffect(() => {\n if (!shouldLoadImages || !finalSourceUrl) {\n return;\n }\n\n if (!isVideo && imageRef.current?.complete && imageRef.current.naturalWidth > 0) {\n setHasLoadedFinalMedia(true);\n }\n\n if (isVideo && videoRef.current?.readyState && videoRef.current.readyState >= 2) {\n setHasLoadedFinalMedia(true);\n }\n }, [finalSourceUrl, isVideo, shouldLoadImages]);\n\n const shouldRenderFinalImage = shouldLoadImages && Boolean(finalSourceUrl);\n const shouldShowPreview = Boolean(displayPreviewUrl);\n\n if (isVideo) {\n return (\n <StyledMediaContentVideoWrapper\n ref={setContainerElement}\n onClick={onClick}\n $ratio={ratio}\n >\n {displayPreviewUrl && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n <StyledMediaContentPlayIcon>\n <Icon size={playIconSize} icons={['fa fa-play']} />\n </StyledMediaContentPlayIcon>\n {shouldRenderFinalImage && (\n <StyledMediaContentVideo\n ref={videoRef}\n poster={displayPreviewUrl}\n preload=\"metadata\"\n onLoadedData={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n >\n <source src={finalSourceUrl} type=\"video/mp4\" />\n </StyledMediaContentVideo>\n )}\n </StyledMediaContentVideoWrapper>\n );\n }\n\n return (\n <StyledMediaContentImageWrapper ref={setContainerElement} onClick={onClick} $ratio={ratio}>\n {shouldShowPreview && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n {shouldRenderFinalImage && (\n <StyledMediaContentImage\n ref={imageRef}\n draggable={false}\n src={finalSourceUrl}\n alt=\"\"\n onLoad={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n />\n )}\n </StyledMediaContentImageWrapper>\n );\n};\n\nMediaContent.displayName = 'MediaContent';\n\nexport default memo(MediaContent);\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.isVideoFile = exports.getMediaSourceUrl = exports.getMediaPreviewUrl = void 0;
|
|
6
|
+
exports.isVideoFile = exports.isImageServiceUrl = exports.hasImageServiceTransformParameters = exports.getResponsiveImageServiceUrl = exports.getMediaSourceUrl = exports.getMediaPreviewUrl = void 0;
|
|
7
|
+
const IMAGE_SERVICE_ORIGINS = new Set(['https://tsimg.cloud', 'https://tsimg.space']);
|
|
8
|
+
const IMAGE_SERVICE_PARAM_PATTERN = /^(?:m(?:scale|crop|shortedgescale)|[whsbd]\d+)$/i;
|
|
9
|
+
const IMAGE_SERVICE_RESIZE_OVERSCAN = 1.25;
|
|
7
10
|
const isVideoFile = file => 'thumbnailUrl' in file;
|
|
8
11
|
exports.isVideoFile = isVideoFile;
|
|
9
12
|
const getMediaSourceUrl = file => file.url.replace('_0.mp4', '.mp4');
|
|
@@ -18,5 +21,75 @@ const getMediaPreviewUrl = (file, previewUrl) => {
|
|
|
18
21
|
}
|
|
19
22
|
return (_file$meta = file.meta) === null || _file$meta === void 0 ? void 0 : _file$meta.preview;
|
|
20
23
|
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks whether the given URL belongs to the chayns image service.
|
|
27
|
+
*/
|
|
21
28
|
exports.getMediaPreviewUrl = getMediaPreviewUrl;
|
|
29
|
+
const isImageServiceUrl = url => {
|
|
30
|
+
try {
|
|
31
|
+
return IMAGE_SERVICE_ORIGINS.has(new URL(url).origin);
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detects whether the given image-service URL already contains resize parameters.
|
|
39
|
+
*/
|
|
40
|
+
exports.isImageServiceUrl = isImageServiceUrl;
|
|
41
|
+
const hasImageServiceTransformParameters = url => {
|
|
42
|
+
if (!isImageServiceUrl(url)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const urlObject = new URL(url);
|
|
47
|
+
const fileName = urlObject.pathname.split('/').pop();
|
|
48
|
+
if (!fileName) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const extensionIndex = fileName.lastIndexOf('.');
|
|
52
|
+
const nameWithoutExtension = extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;
|
|
53
|
+
const parameterSegment = nameWithoutExtension.split('_').pop();
|
|
54
|
+
if (!parameterSegment || parameterSegment === nameWithoutExtension) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return parameterSegment.split('-').every(parameter => IMAGE_SERVICE_PARAM_PATTERN.test(parameter));
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.hasImageServiceTransformParameters = hasImageServiceTransformParameters;
|
|
63
|
+
const getImageServiceParameterSegment = (size, devicePixelRatio) => {
|
|
64
|
+
const scaleFactor = Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? devicePixelRatio : 1;
|
|
65
|
+
const width = Math.max(1, Math.ceil(size.width * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN));
|
|
66
|
+
const height = Math.max(1, Math.ceil(size.height * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN));
|
|
67
|
+
return `w${width}-h${height}`;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Expands a chayns image-service URL to a display-appropriate size when no transform is present.
|
|
72
|
+
*/
|
|
73
|
+
const getResponsiveImageServiceUrl = (url, size, devicePixelRatio = 1) => {
|
|
74
|
+
if (!size || hasImageServiceTransformParameters(url) || !isImageServiceUrl(url)) {
|
|
75
|
+
return url;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const urlObject = new URL(url);
|
|
79
|
+
const pathSegments = urlObject.pathname.split('/');
|
|
80
|
+
const fileName = pathSegments.pop();
|
|
81
|
+
if (!fileName) {
|
|
82
|
+
return url;
|
|
83
|
+
}
|
|
84
|
+
const extensionIndex = fileName.lastIndexOf('.');
|
|
85
|
+
const extension = extensionIndex > -1 ? fileName.slice(extensionIndex) : '';
|
|
86
|
+
const fileBaseName = extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;
|
|
87
|
+
pathSegments.push(`${fileBaseName}_${getImageServiceParameterSegment(size, devicePixelRatio)}${extension}`);
|
|
88
|
+
urlObject.pathname = pathSegments.join('/');
|
|
89
|
+
return urlObject.toString();
|
|
90
|
+
} catch {
|
|
91
|
+
return url;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
exports.getResponsiveImageServiceUrl = getResponsiveImageServiceUrl;
|
|
22
95
|
//# sourceMappingURL=MediaContent.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaContent.utils.js","names":["isVideoFile","file","exports","getMediaSourceUrl","url","replace","getMediaPreviewUrl","previewUrl","_file$meta","thumbnailUrl","meta","preview"],"sources":["../../../../src/components/media-content/MediaContent.utils.ts"],"sourcesContent":["import type { FileItem, Video } from '@chayns-components/core';\n\nexport type GalleryMediaFile = FileItem['file'];\n\nexport const isVideoFile = (file: GalleryMediaFile): file is Video => 'thumbnailUrl' in file;\n\nexport const getMediaSourceUrl = (file: GalleryMediaFile): string =>\n file.url.replace('_0.mp4', '.mp4');\n\nexport const getMediaPreviewUrl = (\n file: GalleryMediaFile,\n previewUrl?: string,\n): string | undefined => {\n if (previewUrl) {\n return previewUrl;\n }\n\n if (isVideoFile(file)) {\n return file.thumbnailUrl;\n }\n\n return file.meta?.preview;\n};\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"MediaContent.utils.js","names":["IMAGE_SERVICE_ORIGINS","Set","IMAGE_SERVICE_PARAM_PATTERN","IMAGE_SERVICE_RESIZE_OVERSCAN","isVideoFile","file","exports","getMediaSourceUrl","url","replace","getMediaPreviewUrl","previewUrl","_file$meta","thumbnailUrl","meta","preview","isImageServiceUrl","has","URL","origin","hasImageServiceTransformParameters","urlObject","fileName","pathname","split","pop","extensionIndex","lastIndexOf","nameWithoutExtension","slice","parameterSegment","every","parameter","test","getImageServiceParameterSegment","size","devicePixelRatio","scaleFactor","Number","isFinite","width","Math","max","ceil","height","getResponsiveImageServiceUrl","pathSegments","extension","fileBaseName","push","join","toString"],"sources":["../../../../src/components/media-content/MediaContent.utils.ts"],"sourcesContent":["import type { FileItem, Video } from '@chayns-components/core';\n\nexport type GalleryMediaFile = FileItem['file'];\nexport type MediaContentSize = {\n height: number;\n width: number;\n};\n\nconst IMAGE_SERVICE_ORIGINS = new Set(['https://tsimg.cloud', 'https://tsimg.space']);\nconst IMAGE_SERVICE_PARAM_PATTERN = /^(?:m(?:scale|crop|shortedgescale)|[whsbd]\\d+)$/i;\nconst IMAGE_SERVICE_RESIZE_OVERSCAN = 1.25;\n\nexport const isVideoFile = (file: GalleryMediaFile): file is Video => 'thumbnailUrl' in file;\n\nexport const getMediaSourceUrl = (file: GalleryMediaFile): string =>\n file.url.replace('_0.mp4', '.mp4');\n\nexport const getMediaPreviewUrl = (\n file: GalleryMediaFile,\n previewUrl?: string,\n): string | undefined => {\n if (previewUrl) {\n return previewUrl;\n }\n\n if (isVideoFile(file)) {\n return file.thumbnailUrl;\n }\n\n return file.meta?.preview;\n};\n\n/**\n * Checks whether the given URL belongs to the chayns image service.\n */\nexport const isImageServiceUrl = (url: string): boolean => {\n try {\n return IMAGE_SERVICE_ORIGINS.has(new URL(url).origin);\n } catch {\n return false;\n }\n};\n\n/**\n * Detects whether the given image-service URL already contains resize parameters.\n */\nexport const hasImageServiceTransformParameters = (url: string): boolean => {\n if (!isImageServiceUrl(url)) {\n return false;\n }\n\n try {\n const urlObject = new URL(url);\n const fileName = urlObject.pathname.split('/').pop();\n\n if (!fileName) {\n return false;\n }\n\n const extensionIndex = fileName.lastIndexOf('.');\n const nameWithoutExtension =\n extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;\n const parameterSegment = nameWithoutExtension.split('_').pop();\n\n if (!parameterSegment || parameterSegment === nameWithoutExtension) {\n return false;\n }\n\n return parameterSegment\n .split('-')\n .every((parameter) => IMAGE_SERVICE_PARAM_PATTERN.test(parameter));\n } catch {\n return false;\n }\n};\n\nconst getImageServiceParameterSegment = (size: MediaContentSize, devicePixelRatio: number) => {\n const scaleFactor =\n Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? devicePixelRatio : 1;\n const width = Math.max(1, Math.ceil(size.width * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN));\n const height = Math.max(\n 1,\n Math.ceil(size.height * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN),\n );\n\n return `w${width}-h${height}`;\n};\n\n/**\n * Expands a chayns image-service URL to a display-appropriate size when no transform is present.\n */\nexport const getResponsiveImageServiceUrl = (\n url: string,\n size?: MediaContentSize,\n devicePixelRatio = 1,\n): string => {\n if (!size || hasImageServiceTransformParameters(url) || !isImageServiceUrl(url)) {\n return url;\n }\n\n try {\n const urlObject = new URL(url);\n const pathSegments = urlObject.pathname.split('/');\n const fileName = pathSegments.pop();\n\n if (!fileName) {\n return url;\n }\n\n const extensionIndex = fileName.lastIndexOf('.');\n const extension = extensionIndex > -1 ? fileName.slice(extensionIndex) : '';\n const fileBaseName = extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;\n\n pathSegments.push(\n `${fileBaseName}_${getImageServiceParameterSegment(size, devicePixelRatio)}${extension}`,\n );\n urlObject.pathname = pathSegments.join('/');\n\n return urlObject.toString();\n } catch {\n return url;\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
const MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS = 100;
|
|
9
|
+
const getSizeFromElement = element => {
|
|
10
|
+
if (!element) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const {
|
|
14
|
+
width,
|
|
15
|
+
height
|
|
16
|
+
} = element.getBoundingClientRect();
|
|
17
|
+
if (width <= 0 || height <= 0) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
width,
|
|
22
|
+
height
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Measures the media tile size so responsive image-service URLs can be generated without layout shifts.
|
|
28
|
+
*/
|
|
29
|
+
const useMediaContentSize = element => {
|
|
30
|
+
const [size, setSize] = (0, _react.useState)();
|
|
31
|
+
(0, _react.useEffect)(() => {
|
|
32
|
+
if (!element) {
|
|
33
|
+
setSize(undefined);
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
let timeoutId;
|
|
37
|
+
const updateSize = () => {
|
|
38
|
+
const nextSize = getSizeFromElement(element);
|
|
39
|
+
if (!nextSize) {
|
|
40
|
+
setSize(undefined);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (timeoutId) {
|
|
44
|
+
clearTimeout(timeoutId);
|
|
45
|
+
}
|
|
46
|
+
timeoutId = setTimeout(() => {
|
|
47
|
+
setSize(nextSize);
|
|
48
|
+
}, MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS);
|
|
49
|
+
};
|
|
50
|
+
updateSize();
|
|
51
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
52
|
+
return () => {
|
|
53
|
+
if (timeoutId) {
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
59
|
+
updateSize();
|
|
60
|
+
});
|
|
61
|
+
resizeObserver.observe(element);
|
|
62
|
+
return () => {
|
|
63
|
+
resizeObserver.disconnect();
|
|
64
|
+
if (timeoutId) {
|
|
65
|
+
clearTimeout(timeoutId);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}, [element]);
|
|
69
|
+
return size;
|
|
70
|
+
};
|
|
71
|
+
var _default = exports.default = useMediaContentSize;
|
|
72
|
+
//# sourceMappingURL=useMediaContentSize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMediaContentSize.js","names":["_react","require","MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS","getSizeFromElement","element","undefined","width","height","getBoundingClientRect","useMediaContentSize","size","setSize","useState","useEffect","timeoutId","updateSize","nextSize","clearTimeout","setTimeout","ResizeObserver","resizeObserver","observe","disconnect","_default","exports","default"],"sources":["../../../../src/components/media-content/useMediaContentSize.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { MediaContentSize } from './MediaContent.utils';\n\nconst MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS = 100;\n\nconst getSizeFromElement = (element: HTMLElement | null): MediaContentSize | undefined => {\n if (!element) {\n return undefined;\n }\n\n const { width, height } = element.getBoundingClientRect();\n\n if (width <= 0 || height <= 0) {\n return undefined;\n }\n\n return {\n width,\n height,\n };\n};\n\n/**\n * Measures the media tile size so responsive image-service URLs can be generated without layout shifts.\n */\nconst useMediaContentSize = (element: HTMLElement | null) => {\n const [size, setSize] = useState<MediaContentSize>();\n\n useEffect(() => {\n if (!element) {\n setSize(undefined);\n return undefined;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const updateSize = () => {\n const nextSize = getSizeFromElement(element);\n\n if (!nextSize) {\n setSize(undefined);\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(() => {\n setSize(nextSize);\n }, MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS);\n };\n\n updateSize();\n\n if (typeof ResizeObserver === 'undefined') {\n return () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n }\n\n const resizeObserver = new ResizeObserver(() => {\n updateSize();\n });\n\n resizeObserver.observe(element);\n\n return () => {\n resizeObserver.disconnect();\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n }, [element]);\n\n return size;\n};\n\nexport default useMediaContentSize;\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAGA,MAAMC,qCAAqC,GAAG,GAAG;AAEjD,MAAMC,kBAAkB,GAAIC,OAA2B,IAAmC;EACtF,IAAI,CAACA,OAAO,EAAE;IACV,OAAOC,SAAS;EACpB;EAEA,MAAM;IAAEC,KAAK;IAAEC;EAAO,CAAC,GAAGH,OAAO,CAACI,qBAAqB,CAAC,CAAC;EAEzD,IAAIF,KAAK,IAAI,CAAC,IAAIC,MAAM,IAAI,CAAC,EAAE;IAC3B,OAAOF,SAAS;EACpB;EAEA,OAAO;IACHC,KAAK;IACLC;EACJ,CAAC;AACL,CAAC;;AAED;AACA;AACA;AACA,MAAME,mBAAmB,GAAIL,OAA2B,IAAK;EACzD,MAAM,CAACM,IAAI,EAAEC,OAAO,CAAC,GAAG,IAAAC,eAAQ,EAAmB,CAAC;EAEpD,IAAAC,gBAAS,EAAC,MAAM;IACZ,IAAI,CAACT,OAAO,EAAE;MACVO,OAAO,CAACN,SAAS,CAAC;MAClB,OAAOA,SAAS;IACpB;IAEA,IAAIS,SAAoD;IAExD,MAAMC,UAAU,GAAGA,CAAA,KAAM;MACrB,MAAMC,QAAQ,GAAGb,kBAAkB,CAACC,OAAO,CAAC;MAE5C,IAAI,CAACY,QAAQ,EAAE;QACXL,OAAO,CAACN,SAAS,CAAC;QAClB;MACJ;MAEA,IAAIS,SAAS,EAAE;QACXG,YAAY,CAACH,SAAS,CAAC;MAC3B;MAEAA,SAAS,GAAGI,UAAU,CAAC,MAAM;QACzBP,OAAO,CAACK,QAAQ,CAAC;MACrB,CAAC,EAAEd,qCAAqC,CAAC;IAC7C,CAAC;IAEDa,UAAU,CAAC,CAAC;IAEZ,IAAI,OAAOI,cAAc,KAAK,WAAW,EAAE;MACvC,OAAO,MAAM;QACT,IAAIL,SAAS,EAAE;UACXG,YAAY,CAACH,SAAS,CAAC;QAC3B;MACJ,CAAC;IACL;IAEA,MAAMM,cAAc,GAAG,IAAID,cAAc,CAAC,MAAM;MAC5CJ,UAAU,CAAC,CAAC;IAChB,CAAC,CAAC;IAEFK,cAAc,CAACC,OAAO,CAACjB,OAAO,CAAC;IAE/B,OAAO,MAAM;MACTgB,cAAc,CAACE,UAAU,CAAC,CAAC;MAE3B,IAAIR,SAAS,EAAE;QACXG,YAAY,CAACH,SAAS,CAAC;MAC3B;IACJ,CAAC;EACL,CAAC,EAAE,CAACV,OAAO,CAAC,CAAC;EAEb,OAAOM,IAAI;AACf,CAAC;AAAC,IAAAa,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAEahB,mBAAmB","ignoreList":[]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Icon } from '@chayns-components/core';
|
|
2
|
-
import React, { memo,
|
|
2
|
+
import React, { memo, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { StyledMediaContentImage, StyledMediaContentImageWrapper, StyledMediaContentPlayIcon, StyledMediaContentPreviewImage, StyledMediaContentVideo, StyledMediaContentVideoWrapper } from './MediaContent.styles';
|
|
4
|
-
import { getMediaPreviewUrl, getMediaSourceUrl, isVideoFile } from './MediaContent.utils';
|
|
4
|
+
import { getMediaPreviewUrl, getMediaSourceUrl, getResponsiveImageServiceUrl, isVideoFile } from './MediaContent.utils';
|
|
5
5
|
import { MEDIA_CONTENT_IMAGE_FADE_DURATION_MS } from './MediaContent.constants';
|
|
6
|
+
import useMediaContentSize from './useMediaContentSize';
|
|
6
7
|
const MediaContent = ({
|
|
7
8
|
file,
|
|
8
9
|
previewUrl,
|
|
@@ -15,10 +16,14 @@ const MediaContent = ({
|
|
|
15
16
|
const sourceKey = getMediaSourceUrl(file);
|
|
16
17
|
const previewSourceUrl = getMediaPreviewUrl(file, previewUrl);
|
|
17
18
|
const [hasLoadedFinalMedia, setHasLoadedFinalMedia] = useState(false);
|
|
19
|
+
const [resolvedFinalSourceUrl, setResolvedFinalSourceUrl] = useState();
|
|
20
|
+
const [containerElement, setContainerElement] = useState(null);
|
|
18
21
|
const imageRef = useRef(null);
|
|
19
22
|
const videoRef = useRef(null);
|
|
20
|
-
const
|
|
21
|
-
const
|
|
23
|
+
const renderSize = useMediaContentSize(containerElement);
|
|
24
|
+
const devicePixelRatio = typeof window !== 'undefined' && Number.isFinite(window.devicePixelRatio) && window.devicePixelRatio > 0 ? window.devicePixelRatio : 1;
|
|
25
|
+
const displayPreviewUrl = useMemo(() => previewSourceUrl ? getResponsiveImageServiceUrl(previewSourceUrl, renderSize, devicePixelRatio) : undefined, [devicePixelRatio, previewSourceUrl, renderSize]);
|
|
26
|
+
const finalSourceUrl = isVideo ? sourceKey : resolvedFinalSourceUrl;
|
|
22
27
|
const shouldHidePreview = shouldLoadImages && hasLoadedFinalMedia;
|
|
23
28
|
const previewLayerStyle = {
|
|
24
29
|
opacity: shouldHidePreview ? 0 : 1,
|
|
@@ -28,11 +33,26 @@ const MediaContent = ({
|
|
|
28
33
|
opacity: hasLoadedFinalMedia || !displayPreviewUrl ? 1 : 0,
|
|
29
34
|
transition: `opacity ${MEDIA_CONTENT_IMAGE_FADE_DURATION_MS}ms ease`
|
|
30
35
|
};
|
|
31
|
-
|
|
36
|
+
useEffect(() => {
|
|
32
37
|
setHasLoadedFinalMedia(false);
|
|
33
38
|
}, [sourceKey]);
|
|
34
|
-
|
|
39
|
+
useEffect(() => {
|
|
35
40
|
if (!shouldLoadImages) {
|
|
41
|
+
setResolvedFinalSourceUrl(undefined);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (isVideo) {
|
|
45
|
+
setResolvedFinalSourceUrl(sourceKey);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!renderSize) {
|
|
49
|
+
setResolvedFinalSourceUrl(undefined);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
setResolvedFinalSourceUrl(getResponsiveImageServiceUrl(sourceKey, renderSize, devicePixelRatio));
|
|
53
|
+
}, [devicePixelRatio, isVideo, renderSize, shouldLoadImages, sourceKey]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!shouldLoadImages || !finalSourceUrl) {
|
|
36
56
|
return;
|
|
37
57
|
}
|
|
38
58
|
if (!isVideo && imageRef.current?.complete && imageRef.current.naturalWidth > 0) {
|
|
@@ -46,6 +66,7 @@ const MediaContent = ({
|
|
|
46
66
|
const shouldShowPreview = Boolean(displayPreviewUrl);
|
|
47
67
|
if (isVideo) {
|
|
48
68
|
return /*#__PURE__*/React.createElement(StyledMediaContentVideoWrapper, {
|
|
69
|
+
ref: setContainerElement,
|
|
49
70
|
onClick: onClick,
|
|
50
71
|
$ratio: ratio
|
|
51
72
|
}, displayPreviewUrl && /*#__PURE__*/React.createElement(StyledMediaContentPreviewImage, {
|
|
@@ -69,6 +90,7 @@ const MediaContent = ({
|
|
|
69
90
|
})));
|
|
70
91
|
}
|
|
71
92
|
return /*#__PURE__*/React.createElement(StyledMediaContentImageWrapper, {
|
|
93
|
+
ref: setContainerElement,
|
|
72
94
|
onClick: onClick,
|
|
73
95
|
$ratio: ratio
|
|
74
96
|
}, shouldShowPreview && /*#__PURE__*/React.createElement(StyledMediaContentPreviewImage, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaContent.js","names":["Icon","React","memo","useLayoutEffect","useRef","useState","StyledMediaContentImage","StyledMediaContentImageWrapper","StyledMediaContentPlayIcon","StyledMediaContentPreviewImage","StyledMediaContentVideo","StyledMediaContentVideoWrapper","getMediaPreviewUrl","getMediaSourceUrl","isVideoFile","MEDIA_CONTENT_IMAGE_FADE_DURATION_MS","MediaContent","file","previewUrl","ratio","onClick","shouldLoadImages","playIconSize","isVideo","sourceKey","previewSourceUrl","hasLoadedFinalMedia","setHasLoadedFinalMedia","imageRef","videoRef","finalSourceUrl","displayPreviewUrl","shouldHidePreview","previewLayerStyle","opacity","filter","undefined","finalMediaStyle","transition","current","complete","naturalWidth","readyState","shouldRenderFinalImage","Boolean","shouldShowPreview","createElement","$ratio","draggable","src","alt","style","size","icons","ref","poster","preload","onLoadedData","type","onLoad","displayName"],"sources":["../../../../src/components/media-content/MediaContent.tsx"],"sourcesContent":["import { Icon } from '@chayns-components/core';\nimport React, { FC, memo, useLayoutEffect, useRef, useState } from 'react';\nimport {\n StyledMediaContentImage,\n StyledMediaContentImageWrapper,\n StyledMediaContentPlayIcon,\n StyledMediaContentPreviewImage,\n StyledMediaContentVideo,\n StyledMediaContentVideoWrapper,\n} from './MediaContent.styles';\nimport type { MediaContentProps } from './MediaContent.types';\nimport { getMediaPreviewUrl, getMediaSourceUrl, isVideoFile } from './MediaContent.utils';\nimport { MEDIA_CONTENT_IMAGE_FADE_DURATION_MS } from './MediaContent.constants';\n\nconst MediaContent: FC<MediaContentProps> = ({\n file,\n previewUrl,\n ratio,\n onClick,\n shouldLoadImages = true,\n playIconSize = 50,\n}) => {\n const isVideo = isVideoFile(file);\n const sourceKey = getMediaSourceUrl(file);\n const previewSourceUrl = getMediaPreviewUrl(file, previewUrl);\n const [hasLoadedFinalMedia, setHasLoadedFinalMedia] = useState(false);\n const imageRef = useRef<HTMLImageElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const finalSourceUrl = sourceKey;\n const displayPreviewUrl = previewSourceUrl;\n const shouldHidePreview = shouldLoadImages && hasLoadedFinalMedia;\n const previewLayerStyle = {\n opacity: shouldHidePreview ? 0 : 1,\n filter: shouldHidePreview ? 'blur(0px)' : undefined,\n };\n const finalMediaStyle = {\n opacity: hasLoadedFinalMedia || !displayPreviewUrl ? 1 : 0,\n transition: `opacity ${MEDIA_CONTENT_IMAGE_FADE_DURATION_MS}ms ease`,\n };\n\n useLayoutEffect(() => {\n setHasLoadedFinalMedia(false);\n }, [sourceKey]);\n\n useLayoutEffect(() => {\n if (!shouldLoadImages) {\n return;\n }\n\n if (!isVideo && imageRef.current?.complete && imageRef.current.naturalWidth > 0) {\n setHasLoadedFinalMedia(true);\n }\n\n if (isVideo && videoRef.current?.readyState && videoRef.current.readyState >= 2) {\n setHasLoadedFinalMedia(true);\n }\n }, [finalSourceUrl, isVideo, shouldLoadImages]);\n\n const shouldRenderFinalImage = shouldLoadImages && Boolean(finalSourceUrl);\n const shouldShowPreview = Boolean(displayPreviewUrl);\n\n if (isVideo) {\n return (\n <StyledMediaContentVideoWrapper onClick={onClick} $ratio={ratio}>\n {displayPreviewUrl && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n <StyledMediaContentPlayIcon>\n <Icon size={playIconSize} icons={['fa fa-play']} />\n </StyledMediaContentPlayIcon>\n {shouldRenderFinalImage && (\n <StyledMediaContentVideo\n ref={videoRef}\n poster={displayPreviewUrl}\n preload=\"metadata\"\n onLoadedData={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n >\n <source src={finalSourceUrl} type=\"video/mp4\" />\n </StyledMediaContentVideo>\n )}\n </StyledMediaContentVideoWrapper>\n );\n }\n\n return (\n <StyledMediaContentImageWrapper onClick={onClick} $ratio={ratio}>\n {shouldShowPreview && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n {shouldRenderFinalImage && (\n <StyledMediaContentImage\n ref={imageRef}\n draggable={false}\n src={finalSourceUrl}\n alt=\"\"\n onLoad={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n />\n )}\n </StyledMediaContentImageWrapper>\n );\n};\n\nMediaContent.displayName = 'MediaContent';\n\nexport default memo(MediaContent);\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"MediaContent.js","names":["Icon","React","memo","useEffect","useMemo","useRef","useState","StyledMediaContentImage","StyledMediaContentImageWrapper","StyledMediaContentPlayIcon","StyledMediaContentPreviewImage","StyledMediaContentVideo","StyledMediaContentVideoWrapper","getMediaPreviewUrl","getMediaSourceUrl","getResponsiveImageServiceUrl","isVideoFile","MEDIA_CONTENT_IMAGE_FADE_DURATION_MS","useMediaContentSize","MediaContent","file","previewUrl","ratio","onClick","shouldLoadImages","playIconSize","isVideo","sourceKey","previewSourceUrl","hasLoadedFinalMedia","setHasLoadedFinalMedia","resolvedFinalSourceUrl","setResolvedFinalSourceUrl","containerElement","setContainerElement","imageRef","videoRef","renderSize","devicePixelRatio","window","Number","isFinite","displayPreviewUrl","undefined","finalSourceUrl","shouldHidePreview","previewLayerStyle","opacity","filter","finalMediaStyle","transition","current","complete","naturalWidth","readyState","shouldRenderFinalImage","Boolean","shouldShowPreview","createElement","ref","$ratio","draggable","src","alt","style","size","icons","poster","preload","onLoadedData","type","onLoad","displayName"],"sources":["../../../../src/components/media-content/MediaContent.tsx"],"sourcesContent":["import { Icon } from '@chayns-components/core';\nimport React, { FC, memo, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n StyledMediaContentImage,\n StyledMediaContentImageWrapper,\n StyledMediaContentPlayIcon,\n StyledMediaContentPreviewImage,\n StyledMediaContentVideo,\n StyledMediaContentVideoWrapper,\n} from './MediaContent.styles';\nimport type { MediaContentProps } from './MediaContent.types';\nimport {\n getMediaPreviewUrl,\n getMediaSourceUrl,\n getResponsiveImageServiceUrl,\n isVideoFile,\n} from './MediaContent.utils';\nimport { MEDIA_CONTENT_IMAGE_FADE_DURATION_MS } from './MediaContent.constants';\nimport useMediaContentSize from './useMediaContentSize';\n\nconst MediaContent: FC<MediaContentProps> = ({\n file,\n previewUrl,\n ratio,\n onClick,\n shouldLoadImages = true,\n playIconSize = 50,\n}) => {\n const isVideo = isVideoFile(file);\n const sourceKey = getMediaSourceUrl(file);\n const previewSourceUrl = getMediaPreviewUrl(file, previewUrl);\n const [hasLoadedFinalMedia, setHasLoadedFinalMedia] = useState(false);\n const [resolvedFinalSourceUrl, setResolvedFinalSourceUrl] = useState<string>();\n const [containerElement, setContainerElement] = useState<HTMLDivElement | null>(null);\n const imageRef = useRef<HTMLImageElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const renderSize = useMediaContentSize(containerElement);\n const devicePixelRatio =\n typeof window !== 'undefined' &&\n Number.isFinite(window.devicePixelRatio) &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1;\n const displayPreviewUrl = useMemo(\n () =>\n previewSourceUrl\n ? getResponsiveImageServiceUrl(previewSourceUrl, renderSize, devicePixelRatio)\n : undefined,\n [devicePixelRatio, previewSourceUrl, renderSize],\n );\n const finalSourceUrl = isVideo ? sourceKey : resolvedFinalSourceUrl;\n const shouldHidePreview = shouldLoadImages && hasLoadedFinalMedia;\n const previewLayerStyle = {\n opacity: shouldHidePreview ? 0 : 1,\n filter: shouldHidePreview ? 'blur(0px)' : undefined,\n };\n const finalMediaStyle = {\n opacity: hasLoadedFinalMedia || !displayPreviewUrl ? 1 : 0,\n transition: `opacity ${MEDIA_CONTENT_IMAGE_FADE_DURATION_MS}ms ease`,\n };\n\n useEffect(() => {\n setHasLoadedFinalMedia(false);\n }, [sourceKey]);\n\n useEffect(() => {\n if (!shouldLoadImages) {\n setResolvedFinalSourceUrl(undefined);\n return;\n }\n\n if (isVideo) {\n setResolvedFinalSourceUrl(sourceKey);\n return;\n }\n\n if (!renderSize) {\n setResolvedFinalSourceUrl(undefined);\n return;\n }\n\n setResolvedFinalSourceUrl(\n getResponsiveImageServiceUrl(sourceKey, renderSize, devicePixelRatio),\n );\n }, [devicePixelRatio, isVideo, renderSize, shouldLoadImages, sourceKey]);\n\n useEffect(() => {\n if (!shouldLoadImages || !finalSourceUrl) {\n return;\n }\n\n if (!isVideo && imageRef.current?.complete && imageRef.current.naturalWidth > 0) {\n setHasLoadedFinalMedia(true);\n }\n\n if (isVideo && videoRef.current?.readyState && videoRef.current.readyState >= 2) {\n setHasLoadedFinalMedia(true);\n }\n }, [finalSourceUrl, isVideo, shouldLoadImages]);\n\n const shouldRenderFinalImage = shouldLoadImages && Boolean(finalSourceUrl);\n const shouldShowPreview = Boolean(displayPreviewUrl);\n\n if (isVideo) {\n return (\n <StyledMediaContentVideoWrapper\n ref={setContainerElement}\n onClick={onClick}\n $ratio={ratio}\n >\n {displayPreviewUrl && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n <StyledMediaContentPlayIcon>\n <Icon size={playIconSize} icons={['fa fa-play']} />\n </StyledMediaContentPlayIcon>\n {shouldRenderFinalImage && (\n <StyledMediaContentVideo\n ref={videoRef}\n poster={displayPreviewUrl}\n preload=\"metadata\"\n onLoadedData={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n >\n <source src={finalSourceUrl} type=\"video/mp4\" />\n </StyledMediaContentVideo>\n )}\n </StyledMediaContentVideoWrapper>\n );\n }\n\n return (\n <StyledMediaContentImageWrapper ref={setContainerElement} onClick={onClick} $ratio={ratio}>\n {shouldShowPreview && (\n <StyledMediaContentPreviewImage\n draggable={false}\n src={displayPreviewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n style={previewLayerStyle}\n />\n )}\n {shouldRenderFinalImage && (\n <StyledMediaContentImage\n ref={imageRef}\n draggable={false}\n src={finalSourceUrl}\n alt=\"\"\n onLoad={() => setHasLoadedFinalMedia(true)}\n style={finalMediaStyle}\n />\n )}\n </StyledMediaContentImageWrapper>\n );\n};\n\nMediaContent.displayName = 'MediaContent';\n\nexport default memo(MediaContent);\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const IMAGE_SERVICE_ORIGINS = new Set(['https://tsimg.cloud', 'https://tsimg.space']);
|
|
2
|
+
const IMAGE_SERVICE_PARAM_PATTERN = /^(?:m(?:scale|crop|shortedgescale)|[whsbd]\d+)$/i;
|
|
3
|
+
const IMAGE_SERVICE_RESIZE_OVERSCAN = 1.25;
|
|
1
4
|
export const isVideoFile = file => 'thumbnailUrl' in file;
|
|
2
5
|
export const getMediaSourceUrl = file => file.url.replace('_0.mp4', '.mp4');
|
|
3
6
|
export const getMediaPreviewUrl = (file, previewUrl) => {
|
|
@@ -9,4 +12,71 @@ export const getMediaPreviewUrl = (file, previewUrl) => {
|
|
|
9
12
|
}
|
|
10
13
|
return file.meta?.preview;
|
|
11
14
|
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether the given URL belongs to the chayns image service.
|
|
18
|
+
*/
|
|
19
|
+
export const isImageServiceUrl = url => {
|
|
20
|
+
try {
|
|
21
|
+
return IMAGE_SERVICE_ORIGINS.has(new URL(url).origin);
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Detects whether the given image-service URL already contains resize parameters.
|
|
29
|
+
*/
|
|
30
|
+
export const hasImageServiceTransformParameters = url => {
|
|
31
|
+
if (!isImageServiceUrl(url)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const urlObject = new URL(url);
|
|
36
|
+
const fileName = urlObject.pathname.split('/').pop();
|
|
37
|
+
if (!fileName) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const extensionIndex = fileName.lastIndexOf('.');
|
|
41
|
+
const nameWithoutExtension = extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;
|
|
42
|
+
const parameterSegment = nameWithoutExtension.split('_').pop();
|
|
43
|
+
if (!parameterSegment || parameterSegment === nameWithoutExtension) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return parameterSegment.split('-').every(parameter => IMAGE_SERVICE_PARAM_PATTERN.test(parameter));
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const getImageServiceParameterSegment = (size, devicePixelRatio) => {
|
|
52
|
+
const scaleFactor = Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? devicePixelRatio : 1;
|
|
53
|
+
const width = Math.max(1, Math.ceil(size.width * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN));
|
|
54
|
+
const height = Math.max(1, Math.ceil(size.height * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN));
|
|
55
|
+
return `w${width}-h${height}`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Expands a chayns image-service URL to a display-appropriate size when no transform is present.
|
|
60
|
+
*/
|
|
61
|
+
export const getResponsiveImageServiceUrl = (url, size, devicePixelRatio = 1) => {
|
|
62
|
+
if (!size || hasImageServiceTransformParameters(url) || !isImageServiceUrl(url)) {
|
|
63
|
+
return url;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const urlObject = new URL(url);
|
|
67
|
+
const pathSegments = urlObject.pathname.split('/');
|
|
68
|
+
const fileName = pathSegments.pop();
|
|
69
|
+
if (!fileName) {
|
|
70
|
+
return url;
|
|
71
|
+
}
|
|
72
|
+
const extensionIndex = fileName.lastIndexOf('.');
|
|
73
|
+
const extension = extensionIndex > -1 ? fileName.slice(extensionIndex) : '';
|
|
74
|
+
const fileBaseName = extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;
|
|
75
|
+
pathSegments.push(`${fileBaseName}_${getImageServiceParameterSegment(size, devicePixelRatio)}${extension}`);
|
|
76
|
+
urlObject.pathname = pathSegments.join('/');
|
|
77
|
+
return urlObject.toString();
|
|
78
|
+
} catch {
|
|
79
|
+
return url;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
12
82
|
//# sourceMappingURL=MediaContent.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaContent.utils.js","names":["isVideoFile","file","getMediaSourceUrl","url","replace","getMediaPreviewUrl","previewUrl","thumbnailUrl","meta","preview"],"sources":["../../../../src/components/media-content/MediaContent.utils.ts"],"sourcesContent":["import type { FileItem, Video } from '@chayns-components/core';\n\nexport type GalleryMediaFile = FileItem['file'];\n\nexport const isVideoFile = (file: GalleryMediaFile): file is Video => 'thumbnailUrl' in file;\n\nexport const getMediaSourceUrl = (file: GalleryMediaFile): string =>\n file.url.replace('_0.mp4', '.mp4');\n\nexport const getMediaPreviewUrl = (\n file: GalleryMediaFile,\n previewUrl?: string,\n): string | undefined => {\n if (previewUrl) {\n return previewUrl;\n }\n\n if (isVideoFile(file)) {\n return file.thumbnailUrl;\n }\n\n return file.meta?.preview;\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"MediaContent.utils.js","names":["IMAGE_SERVICE_ORIGINS","Set","IMAGE_SERVICE_PARAM_PATTERN","IMAGE_SERVICE_RESIZE_OVERSCAN","isVideoFile","file","getMediaSourceUrl","url","replace","getMediaPreviewUrl","previewUrl","thumbnailUrl","meta","preview","isImageServiceUrl","has","URL","origin","hasImageServiceTransformParameters","urlObject","fileName","pathname","split","pop","extensionIndex","lastIndexOf","nameWithoutExtension","slice","parameterSegment","every","parameter","test","getImageServiceParameterSegment","size","devicePixelRatio","scaleFactor","Number","isFinite","width","Math","max","ceil","height","getResponsiveImageServiceUrl","pathSegments","extension","fileBaseName","push","join","toString"],"sources":["../../../../src/components/media-content/MediaContent.utils.ts"],"sourcesContent":["import type { FileItem, Video } from '@chayns-components/core';\n\nexport type GalleryMediaFile = FileItem['file'];\nexport type MediaContentSize = {\n height: number;\n width: number;\n};\n\nconst IMAGE_SERVICE_ORIGINS = new Set(['https://tsimg.cloud', 'https://tsimg.space']);\nconst IMAGE_SERVICE_PARAM_PATTERN = /^(?:m(?:scale|crop|shortedgescale)|[whsbd]\\d+)$/i;\nconst IMAGE_SERVICE_RESIZE_OVERSCAN = 1.25;\n\nexport const isVideoFile = (file: GalleryMediaFile): file is Video => 'thumbnailUrl' in file;\n\nexport const getMediaSourceUrl = (file: GalleryMediaFile): string =>\n file.url.replace('_0.mp4', '.mp4');\n\nexport const getMediaPreviewUrl = (\n file: GalleryMediaFile,\n previewUrl?: string,\n): string | undefined => {\n if (previewUrl) {\n return previewUrl;\n }\n\n if (isVideoFile(file)) {\n return file.thumbnailUrl;\n }\n\n return file.meta?.preview;\n};\n\n/**\n * Checks whether the given URL belongs to the chayns image service.\n */\nexport const isImageServiceUrl = (url: string): boolean => {\n try {\n return IMAGE_SERVICE_ORIGINS.has(new URL(url).origin);\n } catch {\n return false;\n }\n};\n\n/**\n * Detects whether the given image-service URL already contains resize parameters.\n */\nexport const hasImageServiceTransformParameters = (url: string): boolean => {\n if (!isImageServiceUrl(url)) {\n return false;\n }\n\n try {\n const urlObject = new URL(url);\n const fileName = urlObject.pathname.split('/').pop();\n\n if (!fileName) {\n return false;\n }\n\n const extensionIndex = fileName.lastIndexOf('.');\n const nameWithoutExtension =\n extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;\n const parameterSegment = nameWithoutExtension.split('_').pop();\n\n if (!parameterSegment || parameterSegment === nameWithoutExtension) {\n return false;\n }\n\n return parameterSegment\n .split('-')\n .every((parameter) => IMAGE_SERVICE_PARAM_PATTERN.test(parameter));\n } catch {\n return false;\n }\n};\n\nconst getImageServiceParameterSegment = (size: MediaContentSize, devicePixelRatio: number) => {\n const scaleFactor =\n Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? devicePixelRatio : 1;\n const width = Math.max(1, Math.ceil(size.width * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN));\n const height = Math.max(\n 1,\n Math.ceil(size.height * scaleFactor * IMAGE_SERVICE_RESIZE_OVERSCAN),\n );\n\n return `w${width}-h${height}`;\n};\n\n/**\n * Expands a chayns image-service URL to a display-appropriate size when no transform is present.\n */\nexport const getResponsiveImageServiceUrl = (\n url: string,\n size?: MediaContentSize,\n devicePixelRatio = 1,\n): string => {\n if (!size || hasImageServiceTransformParameters(url) || !isImageServiceUrl(url)) {\n return url;\n }\n\n try {\n const urlObject = new URL(url);\n const pathSegments = urlObject.pathname.split('/');\n const fileName = pathSegments.pop();\n\n if (!fileName) {\n return url;\n }\n\n const extensionIndex = fileName.lastIndexOf('.');\n const extension = extensionIndex > -1 ? fileName.slice(extensionIndex) : '';\n const fileBaseName = extensionIndex > -1 ? fileName.slice(0, extensionIndex) : fileName;\n\n pathSegments.push(\n `${fileBaseName}_${getImageServiceParameterSegment(size, devicePixelRatio)}${extension}`,\n );\n urlObject.pathname = pathSegments.join('/');\n\n return urlObject.toString();\n } catch {\n return url;\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
const MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS = 100;
|
|
3
|
+
const getSizeFromElement = element => {
|
|
4
|
+
if (!element) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const {
|
|
8
|
+
width,
|
|
9
|
+
height
|
|
10
|
+
} = element.getBoundingClientRect();
|
|
11
|
+
if (width <= 0 || height <= 0) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
width,
|
|
16
|
+
height
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Measures the media tile size so responsive image-service URLs can be generated without layout shifts.
|
|
22
|
+
*/
|
|
23
|
+
const useMediaContentSize = element => {
|
|
24
|
+
const [size, setSize] = useState();
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!element) {
|
|
27
|
+
setSize(undefined);
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
let timeoutId;
|
|
31
|
+
const updateSize = () => {
|
|
32
|
+
const nextSize = getSizeFromElement(element);
|
|
33
|
+
if (!nextSize) {
|
|
34
|
+
setSize(undefined);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (timeoutId) {
|
|
38
|
+
clearTimeout(timeoutId);
|
|
39
|
+
}
|
|
40
|
+
timeoutId = setTimeout(() => {
|
|
41
|
+
setSize(nextSize);
|
|
42
|
+
}, MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS);
|
|
43
|
+
};
|
|
44
|
+
updateSize();
|
|
45
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
46
|
+
return () => {
|
|
47
|
+
if (timeoutId) {
|
|
48
|
+
clearTimeout(timeoutId);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
53
|
+
updateSize();
|
|
54
|
+
});
|
|
55
|
+
resizeObserver.observe(element);
|
|
56
|
+
return () => {
|
|
57
|
+
resizeObserver.disconnect();
|
|
58
|
+
if (timeoutId) {
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}, [element]);
|
|
63
|
+
return size;
|
|
64
|
+
};
|
|
65
|
+
export default useMediaContentSize;
|
|
66
|
+
//# sourceMappingURL=useMediaContentSize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMediaContentSize.js","names":["useEffect","useState","MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS","getSizeFromElement","element","undefined","width","height","getBoundingClientRect","useMediaContentSize","size","setSize","timeoutId","updateSize","nextSize","clearTimeout","setTimeout","ResizeObserver","resizeObserver","observe","disconnect"],"sources":["../../../../src/components/media-content/useMediaContentSize.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport type { MediaContentSize } from './MediaContent.utils';\n\nconst MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS = 100;\n\nconst getSizeFromElement = (element: HTMLElement | null): MediaContentSize | undefined => {\n if (!element) {\n return undefined;\n }\n\n const { width, height } = element.getBoundingClientRect();\n\n if (width <= 0 || height <= 0) {\n return undefined;\n }\n\n return {\n width,\n height,\n };\n};\n\n/**\n * Measures the media tile size so responsive image-service URLs can be generated without layout shifts.\n */\nconst useMediaContentSize = (element: HTMLElement | null) => {\n const [size, setSize] = useState<MediaContentSize>();\n\n useEffect(() => {\n if (!element) {\n setSize(undefined);\n return undefined;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const updateSize = () => {\n const nextSize = getSizeFromElement(element);\n\n if (!nextSize) {\n setSize(undefined);\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(() => {\n setSize(nextSize);\n }, MEDIA_CONTENT_SIZE_RESIZE_DEBOUNCE_MS);\n };\n\n updateSize();\n\n if (typeof ResizeObserver === 'undefined') {\n return () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n }\n\n const resizeObserver = new ResizeObserver(() => {\n updateSize();\n });\n\n resizeObserver.observe(element);\n\n return () => {\n resizeObserver.disconnect();\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n };\n }, [element]);\n\n return size;\n};\n\nexport default useMediaContentSize;\n"],"mappings":"AAAA,SAASA,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAG3C,MAAMC,qCAAqC,GAAG,GAAG;AAEjD,MAAMC,kBAAkB,GAAIC,OAA2B,IAAmC;EACtF,IAAI,CAACA,OAAO,EAAE;IACV,OAAOC,SAAS;EACpB;EAEA,MAAM;IAAEC,KAAK;IAAEC;EAAO,CAAC,GAAGH,OAAO,CAACI,qBAAqB,CAAC,CAAC;EAEzD,IAAIF,KAAK,IAAI,CAAC,IAAIC,MAAM,IAAI,CAAC,EAAE;IAC3B,OAAOF,SAAS;EACpB;EAEA,OAAO;IACHC,KAAK;IACLC;EACJ,CAAC;AACL,CAAC;;AAED;AACA;AACA;AACA,MAAME,mBAAmB,GAAIL,OAA2B,IAAK;EACzD,MAAM,CAACM,IAAI,EAAEC,OAAO,CAAC,GAAGV,QAAQ,CAAmB,CAAC;EAEpDD,SAAS,CAAC,MAAM;IACZ,IAAI,CAACI,OAAO,EAAE;MACVO,OAAO,CAACN,SAAS,CAAC;MAClB,OAAOA,SAAS;IACpB;IAEA,IAAIO,SAAoD;IAExD,MAAMC,UAAU,GAAGA,CAAA,KAAM;MACrB,MAAMC,QAAQ,GAAGX,kBAAkB,CAACC,OAAO,CAAC;MAE5C,IAAI,CAACU,QAAQ,EAAE;QACXH,OAAO,CAACN,SAAS,CAAC;QAClB;MACJ;MAEA,IAAIO,SAAS,EAAE;QACXG,YAAY,CAACH,SAAS,CAAC;MAC3B;MAEAA,SAAS,GAAGI,UAAU,CAAC,MAAM;QACzBL,OAAO,CAACG,QAAQ,CAAC;MACrB,CAAC,EAAEZ,qCAAqC,CAAC;IAC7C,CAAC;IAEDW,UAAU,CAAC,CAAC;IAEZ,IAAI,OAAOI,cAAc,KAAK,WAAW,EAAE;MACvC,OAAO,MAAM;QACT,IAAIL,SAAS,EAAE;UACXG,YAAY,CAACH,SAAS,CAAC;QAC3B;MACJ,CAAC;IACL;IAEA,MAAMM,cAAc,GAAG,IAAID,cAAc,CAAC,MAAM;MAC5CJ,UAAU,CAAC,CAAC;IAChB,CAAC,CAAC;IAEFK,cAAc,CAACC,OAAO,CAACf,OAAO,CAAC;IAE/B,OAAO,MAAM;MACTc,cAAc,CAACE,UAAU,CAAC,CAAC;MAE3B,IAAIR,SAAS,EAAE;QACXG,YAAY,CAACH,SAAS,CAAC;MAC3B;IACJ,CAAC;EACL,CAAC,EAAE,CAACR,OAAO,CAAC,CAAC;EAEb,OAAOM,IAAI;AACf,CAAC;AAED,eAAeD,mBAAmB","ignoreList":[]}
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { FileItem, Video } from '@chayns-components/core';
|
|
2
2
|
export type GalleryMediaFile = FileItem['file'];
|
|
3
|
+
export type MediaContentSize = {
|
|
4
|
+
height: number;
|
|
5
|
+
width: number;
|
|
6
|
+
};
|
|
3
7
|
export declare const isVideoFile: (file: GalleryMediaFile) => file is Video;
|
|
4
8
|
export declare const getMediaSourceUrl: (file: GalleryMediaFile) => string;
|
|
5
9
|
export declare const getMediaPreviewUrl: (file: GalleryMediaFile, previewUrl?: string) => string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether the given URL belongs to the chayns image service.
|
|
12
|
+
*/
|
|
13
|
+
export declare const isImageServiceUrl: (url: string) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Detects whether the given image-service URL already contains resize parameters.
|
|
16
|
+
*/
|
|
17
|
+
export declare const hasImageServiceTransformParameters: (url: string) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Expands a chayns image-service URL to a display-appropriate size when no transform is present.
|
|
20
|
+
*/
|
|
21
|
+
export declare const getResponsiveImageServiceUrl: (url: string, size?: MediaContentSize, devicePixelRatio?: number) => string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MediaContentSize } from './MediaContent.utils';
|
|
2
|
+
/**
|
|
3
|
+
* Measures the media tile size so responsive image-service URLs can be generated without layout shifts.
|
|
4
|
+
*/
|
|
5
|
+
declare const useMediaContentSize: (element: HTMLElement | null) => MediaContentSize | undefined;
|
|
6
|
+
export default useMediaContentSize;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chayns-components/gallery",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.8-alpha.0",
|
|
4
4
|
"description": "A set of beautiful React components for developing your own applications with chayns.",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"browserslist": [
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"typescript": "^5.9.3"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@chayns-components/core": "^5.2.
|
|
73
|
+
"@chayns-components/core": "^5.2.8-alpha.0",
|
|
74
74
|
"react-compiler-runtime": "^1.0.0",
|
|
75
75
|
"uuid": "^10.0.0"
|
|
76
76
|
},
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"publishConfig": {
|
|
85
85
|
"access": "public"
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "1c8fe6448a297b7837d271b23e00347b68f2214a"
|
|
88
88
|
}
|