@ynput/ayon-frontend-shared 0.3.14 → 0.3.15

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.
@@ -1,11 +1,11 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../../../../_virtual/jsx-runtime.cjs.js"),i=require("@ynput/ayon-react-components"),r=require("react"),t=require("styled-components"),l=require("clsx"),m=t.keyframes`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("../../../../_virtual/jsx-runtime.cjs.js"),g=require("@ynput/ayon-react-components"),i=require("react"),l=require("styled-components"),j=require("clsx"),I=l.keyframes`
2
2
  from {
3
3
  transform: rotate(0deg);
4
4
  }
5
5
  to {
6
6
  transform: rotate(360deg);
7
7
  }
8
- `,f=t.div`
8
+ `,T=l.div`
9
9
  position: relative;
10
10
  display: flex;
11
11
  justify-content: center;
@@ -14,10 +14,10 @@
14
14
  overflow: hidden;
15
15
 
16
16
  background-color: var(--md-sys-color-surface-container-low);
17
- `,x=t(i.Icon)`
18
- animation: ${m} 1s linear infinite;
17
+ `,L=l(g.Icon)`
18
+ animation: ${I} 1s linear infinite;
19
19
  opacity: 0.6;
20
- `,g=t.img`
20
+ `,q=l.img`
21
21
  position: absolute;
22
22
  inset: 0;
23
23
  object-fit: cover;
@@ -29,5 +29,5 @@
29
29
  &.hidden {
30
30
  opacity: 0;
31
31
  }
32
- `,y=({mimetype:u="",src:a,...o})=>{const[d,s]=r.useState(!1),[c,n]=r.useState(!1);return r.useEffect(()=>{n(!1),s(!1)},[a]),e.jsxRuntimeExports.jsxs(f,{...o,className:l("file-thumbnail",o.className),children:[c?e.jsxRuntimeExports.jsx(i.Icon,{icon:i.getMimeTypeIcon(u)}):e.jsxRuntimeExports.jsx(x,{icon:"progress_activity"}),e.jsxRuntimeExports.jsx(g,{src:a,...o,onError:()=>{n(!0),s(!0)},onLoad:()=>{n(!0),s(!1)},className:l({hidden:!c||d})})]})};exports.FileThumbnail=y;
32
+ `,F=({mimetype:E="",src:p,...u})=>{const[R,r]=i.useState(!1),[w,t]=i.useState(!1),s=i.useRef(null),a=i.useRef(0);return i.useEffect(()=>{const n=a.current+1;a.current=n,t(!1),r(!1);const e=s.current;let h=!1,d=0,m=0,f=0;const o=b=>{h||n!==a.current||(h=!0,cancelAnimationFrame(d),window.clearInterval(m),window.clearTimeout(f),b())},v=()=>{!e||n!==a.current||e.complete&&e.naturalWidth>0&&o(()=>{t(!0),r(!1)})},x=()=>o(()=>{t(!0),r(!1)}),y=()=>o(()=>{t(!0),r(!0)});return e&&(e.addEventListener("load",x),e.addEventListener("error",y),typeof e.decode=="function"&&e.decode().then(()=>o(()=>{t(!0),r(!1)})).catch(()=>{v()})),d=requestAnimationFrame(v),m=window.setInterval(v,250),f=window.setTimeout(()=>{o(()=>{e&&e.naturalWidth>0?(t(!0),r(!1)):t(!0)})},1e4),()=>{cancelAnimationFrame(d),window.clearInterval(m),window.clearTimeout(f),e&&(e.removeEventListener("load",x),e.removeEventListener("error",y))}},[p]),c.jsxRuntimeExports.jsxs(T,{...u,className:j("file-thumbnail",u.className),children:[w?c.jsxRuntimeExports.jsx(g.Icon,{icon:g.getMimeTypeIcon(E)}):c.jsxRuntimeExports.jsx(L,{icon:"progress_activity"}),c.jsxRuntimeExports.jsx(q,{ref:s,src:p,...u,onError:n=>{n.currentTarget===s.current&&(t(!0),r(!0))},onLoad:n=>{n.currentTarget===s.current&&(t(!0),r(!1))},className:j({hidden:!w||R})})]})};exports.FileThumbnail=F;
33
33
  //# sourceMappingURL=FileThumbnail.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileThumbnail.cjs.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n\n useEffect(() => {\n // reset loaded state when src changes\n setLoaded(false)\n setError(false)\n }, [src])\n\n return (\n <Wrapper {...props} className={clsx('file-thumbnail', props.className)}>\n {!loaded ? (\n <LoadingIcon icon=\"progress_activity\" />\n ) : (\n <Icon icon={getMimeTypeIcon(mimetype)} />\n )}\n <Image\n src={src}\n {...props}\n onError={() => {\n setLoaded(true)\n setError(true)\n }}\n onLoad={() => {\n setLoaded(true)\n setError(false)\n }}\n className={clsx({ hidden: !loaded || error })}\n />\n </Wrapper>\n )\n}\n"],"names":["spin","keyframes","Wrapper","styled","LoadingIcon","Icon","Image","FileThumbnail","mimetype","src","props","error","setError","useState","loaded","setLoaded","useEffect","jsxs","clsx","jsx","getMimeTypeIcon"],"mappings":"yPAKMA,EAAOC,EAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA;AAAAA,EASPC,EAAUC,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWjBC,EAAcD,EAAOE,MAAI;AAAA,eAChBL,CAAI;AAAA;AAAA,EAIbM,EAAQH,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBRI,EAAwC,CAAC,CAAE,SAAAC,EAAW,GAAI,IAAAC,EAAK,GAAGC,KAAY,CACzF,KAAM,CAACC,EAAOC,CAAQ,EAAIC,EAAAA,SAAS,EAAK,EAClC,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAS,EAAK,EAE1CG,OAAAA,EAAAA,UAAU,IAAM,CAEdD,EAAU,EAAK,EACfH,EAAS,EAAK,CAChB,EAAG,CAACH,CAAG,CAAC,EAGNQ,yBAACf,GAAS,GAAGQ,EAAO,UAAWQ,EAAK,iBAAkBR,EAAM,SAAS,EAClE,SAAA,CAACI,EAGAK,EAAAA,kBAAAA,IAACd,EAAAA,KAAA,CAAK,KAAMe,EAAAA,gBAAgBZ,CAAQ,CAAA,CAAG,EAFvCW,EAAAA,kBAAAA,IAACf,EAAA,CAAY,KAAK,mBAAA,CAAoB,EAIxCe,EAAAA,kBAAAA,IAACb,EAAA,CACC,IAAAG,EACC,GAAGC,EACJ,QAAS,IAAM,CACbK,EAAU,EAAI,EACdH,EAAS,EAAI,CACf,EACA,OAAQ,IAAM,CACZG,EAAU,EAAI,EACdH,EAAS,EAAK,CAChB,EACA,UAAWM,EAAK,CAAE,OAAQ,CAACJ,GAAUH,EAAO,CAAA,CAAA,CAC9C,EACF,CAEJ"}
1
+ {"version":3,"file":"FileThumbnail.cjs.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState, useRef } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const loadAttemptRef = useRef(0)\n\n // Robust loading effect: attempt to detect when the image is actually ready.\n // Uses `decode()` when available, falls back to `complete` + `naturalWidth` checks,\n // adds RAF + polling and a watchdog timeout to avoid stuck states.\n useEffect(() => {\n const attemptId = loadAttemptRef.current + 1\n loadAttemptRef.current = attemptId\n\n // reset visible state for this attempt\n setLoaded(false)\n setError(false)\n\n const img = imageRef.current\n let resolved = false\n let frame = 0\n let interval = 0\n let timeout = 0\n\n const resolveOnce = (resolver: () => void) => {\n if (resolved || attemptId !== loadAttemptRef.current) return\n resolved = true\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n resolver()\n }\n\n const checkComplete = () => {\n if (!img || attemptId !== loadAttemptRef.current) return\n if (img.complete && img.naturalWidth > 0) {\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n }\n }\n\n const onLoad = () =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n const onError = () =>\n resolveOnce(() => {\n // keep behavior: mark loaded so placeholder swaps to icon, and flag error\n setLoaded(true)\n setError(true)\n })\n\n if (img) {\n img.addEventListener('load', onLoad)\n img.addEventListener('error', onError)\n\n if (typeof img.decode === 'function') {\n img\n .decode()\n .then(() =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n }),\n )\n .catch(() => {\n checkComplete()\n })\n }\n }\n\n frame = requestAnimationFrame(checkComplete)\n interval = window.setInterval(checkComplete, 250)\n\n timeout = window.setTimeout(() => {\n // Force-resolve after a timeout to avoid infinite spinner states.\n resolveOnce(() => {\n if (img && img.naturalWidth > 0) {\n setLoaded(true)\n setError(false)\n } else {\n setLoaded(true)\n }\n })\n }, 10000)\n\n return () => {\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n if (img) {\n img.removeEventListener('load', onLoad)\n img.removeEventListener('error', onError)\n }\n }\n }, [src])\n\n return (\n <Wrapper {...props} className={clsx('file-thumbnail', props.className)}>\n {!loaded ? (\n <LoadingIcon icon=\"progress_activity\" />\n ) : (\n <Icon icon={getMimeTypeIcon(mimetype)} />\n )}\n <Image\n ref={imageRef}\n src={src}\n {...props}\n onError={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(true)\n }\n }}\n onLoad={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(false)\n }\n }}\n className={clsx({ hidden: !loaded || error })}\n />\n </Wrapper>\n )\n}\n"],"names":["spin","keyframes","Wrapper","styled","LoadingIcon","Icon","Image","FileThumbnail","mimetype","src","props","error","setError","useState","loaded","setLoaded","imageRef","useRef","loadAttemptRef","useEffect","attemptId","img","resolved","frame","interval","timeout","resolveOnce","resolver","checkComplete","onLoad","onError","jsxs","clsx","jsx","getMimeTypeIcon","event"],"mappings":"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"}
@@ -1,16 +1,16 @@
1
- import { j as o } from "../../../../_virtual/jsx-runtime.es.js";
2
- import { Icon as l, getMimeTypeIcon as d } from "@ynput/ayon-react-components";
3
- import { useState as a, useEffect as u } from "react";
4
- import s, { keyframes as p } from "styled-components";
5
- import c from "clsx";
6
- const g = p`
1
+ import { j as s } from "../../../../_virtual/jsx-runtime.es.js";
2
+ import { Icon as j, getMimeTypeIcon as b } from "@ynput/ayon-react-components";
3
+ import { useState as y, useRef as x, useEffect as k } from "react";
4
+ import u, { keyframes as A } from "styled-components";
5
+ import E from "clsx";
6
+ const F = A`
7
7
  from {
8
8
  transform: rotate(0deg);
9
9
  }
10
10
  to {
11
11
  transform: rotate(360deg);
12
12
  }
13
- `, y = s.div`
13
+ `, R = u.div`
14
14
  position: relative;
15
15
  display: flex;
16
16
  justify-content: center;
@@ -19,10 +19,10 @@ const g = p`
19
19
  overflow: hidden;
20
20
 
21
21
  background-color: var(--md-sys-color-surface-container-low);
22
- `, h = s(l)`
23
- animation: ${g} 1s linear infinite;
22
+ `, N = u(j)`
23
+ animation: ${F} 1s linear infinite;
24
24
  opacity: 0.6;
25
- `, x = s.img`
25
+ `, W = u.img`
26
26
  position: absolute;
27
27
  inset: 0;
28
28
  object-fit: cover;
@@ -34,29 +34,57 @@ const g = p`
34
34
  &.hidden {
35
35
  opacity: 0;
36
36
  }
37
- `, I = ({ mimetype: m = "", src: i, ...e }) => {
38
- const [f, t] = a(!1), [n, r] = a(!1);
39
- return u(() => {
40
- r(!1), t(!1);
41
- }, [i]), /* @__PURE__ */ o.jsxs(y, { ...e, className: c("file-thumbnail", e.className), children: [
42
- n ? /* @__PURE__ */ o.jsx(l, { icon: d(m) }) : /* @__PURE__ */ o.jsx(h, { icon: "progress_activity" }),
43
- /* @__PURE__ */ o.jsx(
44
- x,
37
+ `, _ = ({ mimetype: I = "", src: v, ...c }) => {
38
+ const [L, r] = y(!1), [p, t] = y(!1), a = x(null), i = x(0);
39
+ return k(() => {
40
+ const o = i.current + 1;
41
+ i.current = o, t(!1), r(!1);
42
+ const e = a.current;
43
+ let w = !1, l = 0, m = 0, d = 0;
44
+ const n = (T) => {
45
+ w || o !== i.current || (w = !0, cancelAnimationFrame(l), window.clearInterval(m), window.clearTimeout(d), T());
46
+ }, f = () => {
47
+ !e || o !== i.current || e.complete && e.naturalWidth > 0 && n(() => {
48
+ t(!0), r(!1);
49
+ });
50
+ }, g = () => n(() => {
51
+ t(!0), r(!1);
52
+ }), h = () => n(() => {
53
+ t(!0), r(!0);
54
+ });
55
+ return e && (e.addEventListener("load", g), e.addEventListener("error", h), typeof e.decode == "function" && e.decode().then(
56
+ () => n(() => {
57
+ t(!0), r(!1);
58
+ })
59
+ ).catch(() => {
60
+ f();
61
+ })), l = requestAnimationFrame(f), m = window.setInterval(f, 250), d = window.setTimeout(() => {
62
+ n(() => {
63
+ e && e.naturalWidth > 0 ? (t(!0), r(!1)) : t(!0);
64
+ });
65
+ }, 1e4), () => {
66
+ cancelAnimationFrame(l), window.clearInterval(m), window.clearTimeout(d), e && (e.removeEventListener("load", g), e.removeEventListener("error", h));
67
+ };
68
+ }, [v]), /* @__PURE__ */ s.jsxs(R, { ...c, className: E("file-thumbnail", c.className), children: [
69
+ p ? /* @__PURE__ */ s.jsx(j, { icon: b(I) }) : /* @__PURE__ */ s.jsx(N, { icon: "progress_activity" }),
70
+ /* @__PURE__ */ s.jsx(
71
+ W,
45
72
  {
46
- src: i,
47
- ...e,
48
- onError: () => {
49
- r(!0), t(!0);
73
+ ref: a,
74
+ src: v,
75
+ ...c,
76
+ onError: (o) => {
77
+ o.currentTarget === a.current && (t(!0), r(!0));
50
78
  },
51
- onLoad: () => {
52
- r(!0), t(!1);
79
+ onLoad: (o) => {
80
+ o.currentTarget === a.current && (t(!0), r(!1));
53
81
  },
54
- className: c({ hidden: !n || f })
82
+ className: E({ hidden: !p || L })
55
83
  }
56
84
  )
57
85
  ] });
58
86
  };
59
87
  export {
60
- I as FileThumbnail
88
+ _ as FileThumbnail
61
89
  };
62
90
  //# sourceMappingURL=FileThumbnail.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileThumbnail.es.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n\n useEffect(() => {\n // reset loaded state when src changes\n setLoaded(false)\n setError(false)\n }, [src])\n\n return (\n <Wrapper {...props} className={clsx('file-thumbnail', props.className)}>\n {!loaded ? (\n <LoadingIcon icon=\"progress_activity\" />\n ) : (\n <Icon icon={getMimeTypeIcon(mimetype)} />\n )}\n <Image\n src={src}\n {...props}\n onError={() => {\n setLoaded(true)\n setError(true)\n }}\n onLoad={() => {\n setLoaded(true)\n setError(false)\n }}\n className={clsx({ hidden: !loaded || error })}\n />\n </Wrapper>\n )\n}\n"],"names":["spin","keyframes","Wrapper","styled","LoadingIcon","Icon","Image","FileThumbnail","mimetype","src","props","error","setError","useState","loaded","setLoaded","useEffect","jsxs","clsx","jsx","getMimeTypeIcon"],"mappings":";;;;;AAKA,MAAMA,IAAOC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASPC,IAAUC,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWjBC,IAAcD,EAAOE,CAAI;AAAA,eAChBL,CAAI;AAAA;AAAA,GAIbM,IAAQH,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAqBRI,IAAwC,CAAC,EAAE,UAAAC,IAAW,IAAI,KAAAC,GAAK,GAAGC,QAAY;AACzF,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAAS,EAAK,GAClC,CAACC,GAAQC,CAAS,IAAIF,EAAS,EAAK;AAE1C,SAAAG,EAAU,MAAM;AAEd,IAAAD,EAAU,EAAK,GACfH,EAAS,EAAK;AAAA,EAChB,GAAG,CAACH,CAAG,CAAC,GAGNQ,gBAAAA,OAACf,KAAS,GAAGQ,GAAO,WAAWQ,EAAK,kBAAkBR,EAAM,SAAS,GAClE,UAAA;AAAA,IAACI,IAGAK,gBAAAA,EAAAA,IAACd,GAAA,EAAK,MAAMe,EAAgBZ,CAAQ,EAAA,CAAG,IAFvCW,gBAAAA,EAAAA,IAACf,GAAA,EAAY,MAAK,oBAAA,CAAoB;AAAA,IAIxCe,gBAAAA,EAAAA;AAAAA,MAACb;AAAA,MAAA;AAAA,QACC,KAAAG;AAAA,QACC,GAAGC;AAAA,QACJ,SAAS,MAAM;AACb,UAAAK,EAAU,EAAI,GACdH,EAAS,EAAI;AAAA,QACf;AAAA,QACA,QAAQ,MAAM;AACZ,UAAAG,EAAU,EAAI,GACdH,EAAS,EAAK;AAAA,QAChB;AAAA,QACA,WAAWM,EAAK,EAAE,QAAQ,CAACJ,KAAUH,GAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAC9C,GACF;AAEJ;"}
1
+ {"version":3,"file":"FileThumbnail.es.js","sources":["../../../../../src/components/FileThumbnail/FileThumbnail.tsx"],"sourcesContent":["import { getMimeTypeIcon, Icon } from '@ynput/ayon-react-components'\nimport { FC, HTMLAttributes, ImgHTMLAttributes, useEffect, useState, useRef } from 'react'\nimport styled, { keyframes } from 'styled-components'\nimport clsx from 'clsx'\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`\n\nconst Wrapper = styled.div`\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n\n overflow: hidden;\n\n background-color: var(--md-sys-color-surface-container-low);\n`\n\nconst LoadingIcon = styled(Icon)`\n animation: ${spin} 1s linear infinite;\n opacity: 0.6;\n`\n\nconst Image = styled.img`\n position: absolute;\n inset: 0;\n object-fit: cover;\n background-color: var(--md-sys-color-surface-container-low);\n\n width: 100%;\n height: 100%;\n\n &.hidden {\n opacity: 0;\n }\n`\n\nexport interface FileThumbnailProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'onError' | 'onLoad'> {\n src: string\n mimetype?: string\n imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'onError' | 'onLoad'>\n}\n\nexport const FileThumbnail: FC<FileThumbnailProps> = ({ mimetype = '', src, ...props }) => {\n const [error, setError] = useState(false)\n const [loaded, setLoaded] = useState(false)\n const imageRef = useRef<HTMLImageElement | null>(null)\n const loadAttemptRef = useRef(0)\n\n // Robust loading effect: attempt to detect when the image is actually ready.\n // Uses `decode()` when available, falls back to `complete` + `naturalWidth` checks,\n // adds RAF + polling and a watchdog timeout to avoid stuck states.\n useEffect(() => {\n const attemptId = loadAttemptRef.current + 1\n loadAttemptRef.current = attemptId\n\n // reset visible state for this attempt\n setLoaded(false)\n setError(false)\n\n const img = imageRef.current\n let resolved = false\n let frame = 0\n let interval = 0\n let timeout = 0\n\n const resolveOnce = (resolver: () => void) => {\n if (resolved || attemptId !== loadAttemptRef.current) return\n resolved = true\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n resolver()\n }\n\n const checkComplete = () => {\n if (!img || attemptId !== loadAttemptRef.current) return\n if (img.complete && img.naturalWidth > 0) {\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n }\n }\n\n const onLoad = () =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n })\n const onError = () =>\n resolveOnce(() => {\n // keep behavior: mark loaded so placeholder swaps to icon, and flag error\n setLoaded(true)\n setError(true)\n })\n\n if (img) {\n img.addEventListener('load', onLoad)\n img.addEventListener('error', onError)\n\n if (typeof img.decode === 'function') {\n img\n .decode()\n .then(() =>\n resolveOnce(() => {\n setLoaded(true)\n setError(false)\n }),\n )\n .catch(() => {\n checkComplete()\n })\n }\n }\n\n frame = requestAnimationFrame(checkComplete)\n interval = window.setInterval(checkComplete, 250)\n\n timeout = window.setTimeout(() => {\n // Force-resolve after a timeout to avoid infinite spinner states.\n resolveOnce(() => {\n if (img && img.naturalWidth > 0) {\n setLoaded(true)\n setError(false)\n } else {\n setLoaded(true)\n }\n })\n }, 10000)\n\n return () => {\n cancelAnimationFrame(frame)\n window.clearInterval(interval)\n window.clearTimeout(timeout)\n if (img) {\n img.removeEventListener('load', onLoad)\n img.removeEventListener('error', onError)\n }\n }\n }, [src])\n\n return (\n <Wrapper {...props} className={clsx('file-thumbnail', props.className)}>\n {!loaded ? (\n <LoadingIcon icon=\"progress_activity\" />\n ) : (\n <Icon icon={getMimeTypeIcon(mimetype)} />\n )}\n <Image\n ref={imageRef}\n src={src}\n {...props}\n onError={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(true)\n }\n }}\n onLoad={(event) => {\n if (event.currentTarget === imageRef.current) {\n setLoaded(true)\n setError(false)\n }\n }}\n className={clsx({ hidden: !loaded || error })}\n />\n </Wrapper>\n )\n}\n"],"names":["spin","keyframes","Wrapper","styled","LoadingIcon","Icon","Image","FileThumbnail","mimetype","src","props","error","setError","useState","loaded","setLoaded","imageRef","useRef","loadAttemptRef","useEffect","attemptId","img","resolved","frame","interval","timeout","resolveOnce","resolver","checkComplete","onLoad","onError","jsxs","clsx","jsx","getMimeTypeIcon","event"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynput/ayon-frontend-shared",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "description": "Shared React components for AYON frontend",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",