@vryzel/file-next-ui 0.6.0 → 0.6.2
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/dist/index.cjs +155 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -56
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2771,6 +2771,15 @@ function clampPreviewZoom(scale) {
|
|
|
2771
2771
|
function canPreviewFile(file) {
|
|
2772
2772
|
return previewKind(file.mimeType, file.name) !== "none";
|
|
2773
2773
|
}
|
|
2774
|
+
function pdfFrameMode(src, pageOrigin) {
|
|
2775
|
+
try {
|
|
2776
|
+
const url = new URL(src, pageOrigin);
|
|
2777
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return "blob";
|
|
2778
|
+
return url.origin === new URL(pageOrigin).origin ? "blob" : "direct";
|
|
2779
|
+
} catch {
|
|
2780
|
+
return "blob";
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2774
2783
|
function previewKind(mimeType, name) {
|
|
2775
2784
|
const mime = mimeType.toLowerCase();
|
|
2776
2785
|
const ext = name.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -2786,6 +2795,74 @@ function previewKind(mimeType, name) {
|
|
|
2786
2795
|
if (mime.startsWith("text/") || ["txt", "csv", "md"].includes(ext)) return "text";
|
|
2787
2796
|
return "none";
|
|
2788
2797
|
}
|
|
2798
|
+
function PreviewSpinner({ label }) {
|
|
2799
|
+
return /* @__PURE__ */ jsxs("div", { role: "status", "aria-label": label, className: "absolute inset-0 z-10 flex items-center justify-center", children: [
|
|
2800
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
2801
|
+
/* @__PURE__ */ jsx(
|
|
2802
|
+
"span",
|
|
2803
|
+
{
|
|
2804
|
+
"aria-hidden": "true",
|
|
2805
|
+
className: "size-8 rounded-full border-2 border-white/25 border-t-white motion-safe:animate-spin"
|
|
2806
|
+
}
|
|
2807
|
+
)
|
|
2808
|
+
] });
|
|
2809
|
+
}
|
|
2810
|
+
function PdfFrame({
|
|
2811
|
+
src,
|
|
2812
|
+
title,
|
|
2813
|
+
onClick
|
|
2814
|
+
}) {
|
|
2815
|
+
const labels = useExplorerLabels();
|
|
2816
|
+
const [frameSrc, setFrameSrc] = useState(null);
|
|
2817
|
+
const [failed, setFailed] = useState(false);
|
|
2818
|
+
const [painted, setPainted] = useState(false);
|
|
2819
|
+
useEffect(() => {
|
|
2820
|
+
setPainted(false);
|
|
2821
|
+
if (pdfFrameMode(src, window.location.origin) === "direct") {
|
|
2822
|
+
setFrameSrc(src);
|
|
2823
|
+
setFailed(false);
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
const controller = new AbortController();
|
|
2827
|
+
let cancelled = false;
|
|
2828
|
+
let objectUrl = null;
|
|
2829
|
+
setFrameSrc(null);
|
|
2830
|
+
setFailed(false);
|
|
2831
|
+
void fetch(src, { credentials: "same-origin", signal: controller.signal }).then(async (response) => {
|
|
2832
|
+
if (!response.ok) throw new Error(String(response.status));
|
|
2833
|
+
const blob = new Blob([await response.arrayBuffer()], { type: "application/pdf" });
|
|
2834
|
+
if (cancelled) return;
|
|
2835
|
+
objectUrl = URL.createObjectURL(blob);
|
|
2836
|
+
setFrameSrc(objectUrl);
|
|
2837
|
+
}).catch((error) => {
|
|
2838
|
+
if (cancelled || error instanceof DOMException && error.name === "AbortError") return;
|
|
2839
|
+
setFailed(true);
|
|
2840
|
+
});
|
|
2841
|
+
return () => {
|
|
2842
|
+
cancelled = true;
|
|
2843
|
+
controller.abort();
|
|
2844
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
2845
|
+
};
|
|
2846
|
+
}, [src]);
|
|
2847
|
+
if (failed) {
|
|
2848
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-white/70", children: labels.previewUnavailable });
|
|
2849
|
+
}
|
|
2850
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative h-full w-full max-w-5xl", children: [
|
|
2851
|
+
!painted ? /* @__PURE__ */ jsx(PreviewSpinner, { label: labels.loading }) : null,
|
|
2852
|
+
frameSrc ? /* @__PURE__ */ jsx(
|
|
2853
|
+
"iframe",
|
|
2854
|
+
{
|
|
2855
|
+
src: frameSrc,
|
|
2856
|
+
title,
|
|
2857
|
+
referrerPolicy: "no-referrer",
|
|
2858
|
+
className: "h-full w-full bg-white",
|
|
2859
|
+
onClick,
|
|
2860
|
+
onLoad: () => setPainted(true),
|
|
2861
|
+
onError: () => setPainted(true)
|
|
2862
|
+
}
|
|
2863
|
+
) : null
|
|
2864
|
+
] });
|
|
2865
|
+
}
|
|
2789
2866
|
function FilePreviewDialog({
|
|
2790
2867
|
file,
|
|
2791
2868
|
src,
|
|
@@ -2795,11 +2872,13 @@ function FilePreviewDialog({
|
|
|
2795
2872
|
const labels = useExplorerLabels();
|
|
2796
2873
|
const [scale, setScale] = useState(1);
|
|
2797
2874
|
const [pan, setPan] = useState({ x: 0, y: 0 });
|
|
2875
|
+
const [ready, setReady] = useState(false);
|
|
2798
2876
|
const drag = useRef(null);
|
|
2799
2877
|
const closeRef = useRef(null);
|
|
2800
2878
|
useEffect(() => {
|
|
2801
2879
|
setScale(1);
|
|
2802
2880
|
setPan({ x: 0, y: 0 });
|
|
2881
|
+
setReady(false);
|
|
2803
2882
|
closeRef.current?.focus();
|
|
2804
2883
|
}, [file?.id, src]);
|
|
2805
2884
|
useEffect(() => {
|
|
@@ -2849,63 +2928,83 @@ function FilePreviewDialog({
|
|
|
2849
2928
|
}
|
|
2850
2929
|
)
|
|
2851
2930
|
] }),
|
|
2852
|
-
/* @__PURE__ */
|
|
2853
|
-
"
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2931
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex h-full w-full items-center justify-center px-4 pt-12 pb-16", children: [
|
|
2932
|
+
kind !== "pdf" && !ready ? /* @__PURE__ */ jsx(PreviewSpinner, { label: labels.loading }) : null,
|
|
2933
|
+
kind === "image" ? /* @__PURE__ */ jsx(
|
|
2934
|
+
"img",
|
|
2935
|
+
{
|
|
2936
|
+
src,
|
|
2937
|
+
alt: file.name,
|
|
2938
|
+
draggable: false,
|
|
2939
|
+
className: `max-h-full max-w-full cursor-zoom-in select-none object-contain ${ready ? "" : "absolute h-px w-px opacity-0"}`,
|
|
2940
|
+
onLoad: () => setReady(true),
|
|
2941
|
+
onError: () => setReady(true),
|
|
2942
|
+
style: { transform: `translate(${pan.x}px, ${pan.y}px) scale(${scale})` },
|
|
2943
|
+
onClick: stop,
|
|
2944
|
+
onDoubleClick: (event) => {
|
|
2945
|
+
stop(event);
|
|
2946
|
+
zoom(scale === 1 ? 2 : 1);
|
|
2947
|
+
},
|
|
2948
|
+
onWheel: (event) => {
|
|
2949
|
+
event.preventDefault();
|
|
2950
|
+
stop(event);
|
|
2951
|
+
zoom(scale + (event.deltaY < 0 ? 0.25 : -0.25));
|
|
2952
|
+
},
|
|
2953
|
+
onPointerDown: (event) => {
|
|
2954
|
+
if (scale === 1) return;
|
|
2955
|
+
stop(event);
|
|
2956
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2957
|
+
drag.current = { x: event.clientX, y: event.clientY, px: pan.x, py: pan.y };
|
|
2958
|
+
},
|
|
2959
|
+
onPointerMove: (event) => {
|
|
2960
|
+
if (!drag.current) return;
|
|
2961
|
+
setPan({
|
|
2962
|
+
x: drag.current.px + event.clientX - drag.current.x,
|
|
2963
|
+
y: drag.current.py + event.clientY - drag.current.y
|
|
2964
|
+
});
|
|
2965
|
+
},
|
|
2966
|
+
onPointerUp: () => {
|
|
2967
|
+
drag.current = null;
|
|
2968
|
+
}
|
|
2885
2969
|
}
|
|
2886
|
-
}
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
"video"
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2970
|
+
) : kind === "pdf" ? /* @__PURE__ */ jsx(PdfFrame, { src, title: file.name, onClick: stop }) : kind === "text" ? /* @__PURE__ */ jsx(
|
|
2971
|
+
"iframe",
|
|
2972
|
+
{
|
|
2973
|
+
src,
|
|
2974
|
+
title: file.name,
|
|
2975
|
+
sandbox: "",
|
|
2976
|
+
referrerPolicy: "no-referrer",
|
|
2977
|
+
className: ready ? "h-full w-full max-w-5xl bg-white" : "absolute h-px w-px max-w-5xl opacity-0",
|
|
2978
|
+
onClick: stop,
|
|
2979
|
+
onLoad: () => setReady(true),
|
|
2980
|
+
onError: () => setReady(true)
|
|
2981
|
+
}
|
|
2982
|
+
) : kind === "video" ? /* @__PURE__ */ jsx(
|
|
2983
|
+
"video",
|
|
2984
|
+
{
|
|
2985
|
+
controls: true,
|
|
2986
|
+
playsInline: true,
|
|
2987
|
+
preload: "metadata",
|
|
2988
|
+
src,
|
|
2989
|
+
className: `max-h-full max-w-full ${ready ? "" : "absolute h-px w-px opacity-0"}`,
|
|
2990
|
+
onClick: stop,
|
|
2991
|
+
onLoadedData: () => setReady(true),
|
|
2992
|
+
onError: () => setReady(true),
|
|
2993
|
+
children: labels.previewUnavailable
|
|
2994
|
+
}
|
|
2995
|
+
) : kind === "audio" ? /* @__PURE__ */ jsx(
|
|
2996
|
+
"audio",
|
|
2997
|
+
{
|
|
2998
|
+
controls: true,
|
|
2999
|
+
preload: "metadata",
|
|
3000
|
+
src,
|
|
3001
|
+
className: `w-full max-w-md ${ready ? "" : "absolute h-px w-px opacity-0"}`,
|
|
3002
|
+
onClick: stop,
|
|
3003
|
+
onLoadedMetadata: () => setReady(true),
|
|
3004
|
+
onError: () => setReady(true)
|
|
3005
|
+
}
|
|
3006
|
+
) : /* @__PURE__ */ jsx("p", { className: "text-sm text-white/70", children: labels.previewUnavailable })
|
|
3007
|
+
] }),
|
|
2909
3008
|
kind === "image" ? /* @__PURE__ */ jsxs(
|
|
2910
3009
|
"div",
|
|
2911
3010
|
{
|