@gxpl/sdk 0.0.36-0 → 0.0.36

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.
@@ -11,7 +11,8 @@ const TransitionMachineContext_1 = require("../provider/TransitionMachineContext
11
11
  const Scenes_1 = require("./Scenes/Scenes");
12
12
  const FixedLayer_1 = require("./fixedLayers/FixedLayer");
13
13
  const usePrelaodAssets_1 = require("../utils/usePrelaodAssets");
14
- const SectionVideoCacheContext_1 = require("./Section/SectionVideoCacheContext");
14
+ const PreviewWrapper_1 = require("./Preview/PreviewWrapper");
15
+ const PreviewListener_1 = require("./Preview/PreviewListener");
15
16
  const Page = ({ project, articlesData }) => {
16
17
  var _a, _b;
17
18
  const afterBodyOpen = (0, html_react_parser_1.default)(project.html.afterBodyOpen);
@@ -20,12 +21,12 @@ const Page = ({ project, articlesData }) => {
20
21
  const scenes = Object.values(articlesData).map(({ article }) => ({ id: article.id }));
21
22
  const { relations, scenesAssets } = project;
22
23
  (0, usePrelaodAssets_1.usePreloadAssets)(scenesAssets);
23
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Head_1.CNTRLHead, { project: project }), afterBodyOpen, (0, jsx_runtime_1.jsx)(TransitionMachineContext_1.TransitionMachineContext.Provider, { options: {
24
- input: {
25
- startScene,
26
- relations,
27
- scenes,
28
- }
29
- }, children: (0, jsx_runtime_1.jsxs)(SectionVideoCacheContext_1.SectionVideoCacheProvider, { assets: scenesAssets, children: [project.foreground && !project.foreground.hidden && (0, jsx_runtime_1.jsx)(FixedLayer_1.FixedLayer, { layer: project.foreground, type: "foreground" }), (0, jsx_runtime_1.jsx)(Scenes_1.Scenes, { articlesData: articlesData }), project.background && !project.background.hidden && (0, jsx_runtime_1.jsx)(FixedLayer_1.FixedLayer, { layer: project.background, type: "background" })] }) }), beforeBodyClose] }));
24
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Head_1.CNTRLHead, { project: project }), afterBodyOpen, (0, jsx_runtime_1.jsx)(PreviewWrapper_1.PreviewWrapper, { relations: relations, startScene: startScene, children: (0, jsx_runtime_1.jsxs)(TransitionMachineContext_1.TransitionMachineContext.Provider, { options: {
25
+ input: {
26
+ startScene,
27
+ relations,
28
+ scenes,
29
+ }
30
+ }, children: [(0, jsx_runtime_1.jsx)(PreviewListener_1.PreviewListener, {}), project.foreground && !project.foreground.hidden && (0, jsx_runtime_1.jsx)(FixedLayer_1.FixedLayer, { layer: project.foreground, type: "foreground" }), (0, jsx_runtime_1.jsx)(Scenes_1.Scenes, { articlesData: articlesData }), project.background && !project.background.hidden && (0, jsx_runtime_1.jsx)(FixedLayer_1.FixedLayer, { layer: project.background, type: "background" })] }) }), beforeBodyClose] }));
30
31
  };
31
32
  exports.Page = Page;
@@ -0,0 +1,3 @@
1
+ export declare const ChevronIcon: ({ className }: {
2
+ className?: string;
3
+ }) => JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChevronIcon = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ChevronIcon = ({ className }) => {
6
+ return ((0, jsx_runtime_1.jsxs)("svg", { width: "44px", height: "44px", viewBox: "0 0 44 44", version: "1.1", xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", children: [(0, jsx_runtime_1.jsx)("defs", { children: (0, jsx_runtime_1.jsx)("circle", { id: "path-1", cx: "21.5", cy: "21.5", r: "21" }) }), (0, jsx_runtime_1.jsx)("g", { id: "Page-1", stroke: "none", strokeWidth: "1", fill: "none", fillRule: "evenodd", children: (0, jsx_runtime_1.jsxs)("g", { id: "arrow", children: [(0, jsx_runtime_1.jsxs)("g", { id: "Oval", opacity: "0.2", children: [(0, jsx_runtime_1.jsx)("use", { fill: "#000000", xlinkHref: "#path-1" }), (0, jsx_runtime_1.jsx)("use", { fill: "#64686A", xlinkHref: "#path-1" })] }), (0, jsx_runtime_1.jsx)("polyline", { id: "Path", stroke: "#FFFFFF", strokeWidth: "2", points: "20 16.5 25 21.5 20 26.5" })] }) })] }));
7
+ };
8
+ exports.ChevronIcon = ChevronIcon;
@@ -0,0 +1,2 @@
1
+ export declare const IframePreviewWindowContext: import("react").Context<Window | null>;
2
+ export declare const useIframePreviewWindow: () => Window | null;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useIframePreviewWindow = exports.IframePreviewWindowContext = void 0;
4
+ const react_1 = require("react");
5
+ const react_2 = require("react");
6
+ exports.IframePreviewWindowContext = (0, react_2.createContext)(null);
7
+ const useIframePreviewWindow = () => (0, react_1.useContext)(exports.IframePreviewWindowContext);
8
+ exports.useIframePreviewWindow = useIframePreviewWindow;
@@ -0,0 +1,8 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { Relation } from '../../../sdk/types/project/Relation';
3
+ interface Props {
4
+ relations: Relation[];
5
+ startScene: string;
6
+ }
7
+ export declare const Preview: FC<PropsWithChildren<Props>>;
8
+ export {};
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Preview = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const client_1 = require("react-dom/client");
10
+ const findRelation_1 = require("../../../sdk/transitions/utils/findRelation");
11
+ const getAvailableTransitions_1 = require("../../../sdk/transitions/utils/getAvailableTransitions");
12
+ const style_1 = __importDefault(require("styled-jsx/style"));
13
+ const ChevronIcon_1 = require("./ChevronIcon");
14
+ const IframePreviewWindowContext_1 = require("./IframePreviewWindowContext");
15
+ const Preview = ({ children, relations, startScene }) => {
16
+ const id = (0, react_1.useId)();
17
+ const [iframeRef, setIframeRef] = (0, react_1.useState)(null);
18
+ const [isTransitioning, setIsTransitioning] = (0, react_1.useState)(false);
19
+ const [activeScene, setActiveScene] = (0, react_1.useState)(startScene);
20
+ const [activeSides, setActiveSides] = (0, react_1.useState)((0, getAvailableTransitions_1.getAvailableTransitions)(startScene, relations));
21
+ const handleSwipeToScene = (direction) => {
22
+ if (!iframeRef || !iframeRef.contentWindow || !iframeRef.contentDocument)
23
+ return;
24
+ setIsTransitioning(true);
25
+ const transition = (0, findRelation_1.findRelation)(relations, activeScene, direction);
26
+ const targetWindow = iframeRef.contentWindow;
27
+ const message = {
28
+ type: "TRANSITION_TRIGGER",
29
+ direction,
30
+ to: transition.to,
31
+ transitionType: transition.type,
32
+ };
33
+ targetWindow.postMessage(message, "*");
34
+ };
35
+ (0, react_1.useEffect)(() => {
36
+ if (!iframeRef)
37
+ return;
38
+ const handleMessage = (e) => {
39
+ var _a;
40
+ if (((_a = e.data) === null || _a === void 0 ? void 0 : _a.type) === "ACTIVE_SCENE_CHANGE") {
41
+ const activeScene = e.data.activeScene;
42
+ if (activeScene) {
43
+ const availableTransitions = (0, getAvailableTransitions_1.getAvailableTransitions)(activeScene, relations);
44
+ setActiveSides(availableTransitions);
45
+ setActiveScene(activeScene);
46
+ setIsTransitioning(false);
47
+ }
48
+ }
49
+ };
50
+ window.addEventListener("message", handleMessage);
51
+ return () => window.removeEventListener("message", handleMessage);
52
+ }, [iframeRef, relations]);
53
+ (0, react_1.useEffect)(() => {
54
+ if (!iframeRef)
55
+ return;
56
+ const iframeDocument = iframeRef.contentDocument;
57
+ if (!iframeDocument)
58
+ return;
59
+ const iframeWindow = iframeRef.contentWindow;
60
+ if (!iframeWindow)
61
+ return;
62
+ const iframeHead = iframeDocument.head;
63
+ if (!iframeHead)
64
+ return;
65
+ const copyStyleNode = (node) => {
66
+ if (node instanceof HTMLStyleElement) {
67
+ const cloned = node.cloneNode(true);
68
+ iframeHead.appendChild(cloned);
69
+ }
70
+ else if (node instanceof HTMLLinkElement && node.rel === 'stylesheet') {
71
+ const cloned = node.cloneNode(true);
72
+ iframeHead.appendChild(cloned);
73
+ }
74
+ };
75
+ const copyAllStyles = () => {
76
+ const style = iframeDocument.createElement("style");
77
+ style.innerHTML = `
78
+ html, body {
79
+ height: 100%;
80
+ margin: 0 !important;
81
+ padding: 0 !important;
82
+ }
83
+ `;
84
+ iframeHead.appendChild(style);
85
+ const parentHead = document.head;
86
+ if (!parentHead)
87
+ return;
88
+ Array.from(parentHead.querySelectorAll('style, link[rel="stylesheet"]')).forEach((node) => {
89
+ const href = node instanceof HTMLLinkElement ? node.href : null;
90
+ const existing = href
91
+ ? iframeHead.querySelector(`link[href="${href}"]`)
92
+ : null;
93
+ if (!existing) {
94
+ copyStyleNode(node);
95
+ }
96
+ });
97
+ };
98
+ copyAllStyles();
99
+ const observer = new MutationObserver((mutations) => {
100
+ mutations.forEach((mutation) => {
101
+ mutation.addedNodes.forEach((node) => {
102
+ if ((node instanceof HTMLStyleElement) ||
103
+ (node instanceof HTMLLinkElement && node.rel === 'stylesheet')) {
104
+ copyStyleNode(node);
105
+ }
106
+ });
107
+ });
108
+ });
109
+ observer.observe(document.head, {
110
+ childList: true,
111
+ subtree: true,
112
+ });
113
+ const mountNode = iframeDocument.createElement('div');
114
+ mountNode.id = 'react-root';
115
+ iframeDocument.body.appendChild(mountNode);
116
+ const root = (0, client_1.createRoot)(mountNode);
117
+ root.render((0, jsx_runtime_1.jsx)(IframePreviewWindowContext_1.IframePreviewWindowContext.Provider, { value: iframeWindow, children: children }));
118
+ return () => {
119
+ observer.disconnect();
120
+ };
121
+ }, [iframeRef, children]);
122
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "preview-wrapper", children: (0, jsx_runtime_1.jsxs)("div", { className: "preview-inner", children: [Object.entries(activeSides).map(([direction, isActive]) => (isActive ? ((0, jsx_runtime_1.jsx)("button", { onClick: () => handleSwipeToScene(direction), disabled: isTransitioning, className: `chevron-button chevron-button-${direction}`, children: (0, jsx_runtime_1.jsx)(ChevronIcon_1.ChevronIcon, { className: "chevron-icon" }) }, direction)) : null)), (0, jsx_runtime_1.jsx)("iframe", { className: "iframe-preview", ref: setIframeRef, style: {} })] }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
123
+ .preview-wrapper {
124
+ width: 100%;
125
+ height: 100%;
126
+ position: relative;
127
+ padding: 66px;
128
+ box-sizing: border-box;
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ background-color: #000000;
133
+ }
134
+ .preview-inner {
135
+ width: 100%;
136
+ max-height: 844px;
137
+ height: 100%;
138
+ position: relative;
139
+ display: flex;
140
+ width: 390px;
141
+ align-items: center;
142
+ justify-content: center;
143
+ }
144
+ .iframe-preview {
145
+ width: 390px;
146
+ max-height: 844px;
147
+ height: 100%;
148
+ border: none;
149
+ z-index: 1000;
150
+ background-color: #FFFFFF;
151
+ }
152
+ .chevron-icon {
153
+ width: 44px;
154
+ height: 44px;
155
+ }
156
+ .chevron-button {
157
+ border: none;
158
+ background: none;
159
+ padding: 0;
160
+ margin: 0;
161
+ cursor: pointer;
162
+ position: absolute;
163
+ }
164
+ .chevron-button-north {
165
+ top: 0;
166
+ left: 50%;
167
+ transform: translate(-50%, -125%) rotate(-90deg);
168
+ }
169
+ .chevron-button-south {
170
+ bottom: 0;
171
+ left: 50%;
172
+ transform: translate(-50%, 125%) rotate(90deg);
173
+ }
174
+ .chevron-button-east {
175
+ right: 0;
176
+ top: 50%;
177
+ transform: translate(125%, -50%);
178
+ }
179
+ .chevron-button-west {
180
+ left: 0;
181
+ top: 50%;
182
+ transform: translate(-125%, -50%) rotate(180deg);
183
+ }
184
+ ` })] }));
185
+ };
186
+ exports.Preview = Preview;
@@ -0,0 +1 @@
1
+ export declare const PreviewListener: () => null;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PreviewListener = void 0;
4
+ const react_1 = require("react");
5
+ const TransitionMachineContext_1 = require("../../provider/TransitionMachineContext");
6
+ const IframePreviewWindowContext_1 = require("./IframePreviewWindowContext");
7
+ const PreviewListener = () => {
8
+ const actorRef = TransitionMachineContext_1.TransitionMachineContext.useActorRef();
9
+ const { isActive } = TransitionMachineContext_1.TransitionMachineContext.useSelector((state) => ({
10
+ isActive: state.matches('active'),
11
+ }));
12
+ const iframePreviewWindow = (0, IframePreviewWindowContext_1.useIframePreviewWindow)();
13
+ (0, react_1.useEffect)(() => {
14
+ if (!iframePreviewWindow)
15
+ return;
16
+ const handleMessage = (e) => {
17
+ var _a;
18
+ if (((_a = e.data) === null || _a === void 0 ? void 0 : _a.type) === "TRANSITION_TRIGGER") {
19
+ const { direction, to, transitionType } = e.data;
20
+ actorRef.send({
21
+ type: 'TRANSITION_TRIGGER',
22
+ transition: transitionType,
23
+ to,
24
+ direction
25
+ });
26
+ }
27
+ };
28
+ iframePreviewWindow.addEventListener("message", handleMessage);
29
+ return () => {
30
+ if (iframePreviewWindow) {
31
+ iframePreviewWindow.removeEventListener("message", handleMessage);
32
+ }
33
+ };
34
+ }, [iframePreviewWindow]);
35
+ (0, react_1.useEffect)(() => {
36
+ if (isActive && actorRef && iframePreviewWindow) {
37
+ const { context } = actorRef.getSnapshot();
38
+ const { scenes } = context;
39
+ const [activeScene] = scenes;
40
+ iframePreviewWindow.parent.postMessage({
41
+ type: "ACTIVE_SCENE_CHANGE",
42
+ activeScene: activeScene.id,
43
+ }, "*");
44
+ }
45
+ }, [isActive, actorRef, iframePreviewWindow]);
46
+ return null;
47
+ };
48
+ exports.PreviewListener = PreviewListener;
@@ -0,0 +1,8 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { Relation } from '../../../sdk/types/project/Relation';
3
+ interface Props {
4
+ relations: Relation[];
5
+ startScene: string;
6
+ }
7
+ export declare const PreviewWrapper: FC<PropsWithChildren<Props>>;
8
+ export {};
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PreviewWrapper = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const Preview_1 = require("./Preview");
7
+ const PreviewWrapper = ({ children, relations, startScene }) => {
8
+ const [isDesktop, setIsDesktop] = (0, react_1.useState)(false);
9
+ const handleResize = () => {
10
+ if (window.innerWidth < 768) {
11
+ setIsDesktop(false);
12
+ }
13
+ else {
14
+ setIsDesktop(true);
15
+ }
16
+ };
17
+ (0, react_1.useEffect)(() => {
18
+ handleResize();
19
+ window.addEventListener('resize', handleResize);
20
+ return () => {
21
+ window.removeEventListener('resize', handleResize);
22
+ };
23
+ }, []);
24
+ if (isDesktop) {
25
+ return (0, jsx_runtime_1.jsx)(Preview_1.Preview, { relations: relations, startScene: startScene, children: children });
26
+ }
27
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
28
+ };
29
+ exports.PreviewWrapper = PreviewWrapper;
@@ -3,10 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SectionVideo = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
- const SectionVideoCacheContext_1 = require("./SectionVideoCacheContext");
7
6
  const SectionVideo = ({ container, sectionId, media }) => {
8
7
  const [video, setVideo] = (0, react_1.useState)(null);
9
- const { videoCache } = (0, react_1.useContext)(SectionVideoCacheContext_1.SectionVideoCacheContext);
10
8
  const [videoWrapper, setVideoWrapper] = (0, react_1.useState)(null);
11
9
  const [isVideoWidthOverflow, setIsVideoWidthOverflow] = (0, react_1.useState)(false);
12
10
  const { url, size, position, offsetX, coverUrl, play } = media;
@@ -26,42 +24,6 @@ const SectionVideo = ({ container, sectionId, media }) => {
26
24
  setUserPaused(false);
27
25
  }
28
26
  };
29
- (0, react_1.useEffect)(() => {
30
- if (!videoWrapper)
31
- return;
32
- const video = videoCache.get(url);
33
- if (!video)
34
- return;
35
- video.controls = play === "on-click";
36
- video.muted = play === "auto";
37
- video.autoplay = play === "auto";
38
- video.loop = true;
39
- const style = {
40
- objectFit: isContainHeight ? 'cover' : (size !== null && size !== void 0 ? size : 'cover'),
41
- width: isContainHeight && !isVideoWidthOverflow ? 'auto' : '100%',
42
- transform: isContainHeight ? 'translateX(-50%)' : 'none',
43
- left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
44
- height: '100%',
45
- position: 'relative'
46
- };
47
- Object.assign(video.style, style);
48
- if (!videoWrapper.contains(video)) {
49
- videoWrapper.appendChild(video);
50
- }
51
- setVideo(video);
52
- video.addEventListener('play', () => {
53
- setIsPlaying(true);
54
- });
55
- video.addEventListener('pause', () => {
56
- setIsPlaying(false);
57
- });
58
- if (play === "auto") {
59
- video.play().catch(() => { });
60
- }
61
- return () => {
62
- video.pause();
63
- };
64
- }, [url, play, videoWrapper, videoCache, sectionId, video, isVideoWidthOverflow]);
65
27
  (0, react_1.useEffect)(() => {
66
28
  if (!video || play !== 'on-click')
67
29
  return;
@@ -82,7 +44,6 @@ const SectionVideo = ({ container, sectionId, media }) => {
82
44
  if (!video || !videoWrapper)
83
45
  return;
84
46
  video.addEventListener('loadedmetadata', () => {
85
- console.log('loadedmetadata', video.videoHeight, video.videoWidth);
86
47
  const h = video.videoHeight;
87
48
  const w = video.videoWidth;
88
49
  const width = (videoWrapper.clientHeight / h) * w;
@@ -96,28 +57,35 @@ const SectionVideo = ({ container, sectionId, media }) => {
96
57
  }, [video, videoWrapper]);
97
58
  const isContainHeight = size === 'contain-height';
98
59
  const hasOffsetX = offsetX !== null && size === 'contain';
99
- return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)("div", { ref: setVideoWrapper, className: `section-video-wrapper-${sectionId}`, style: {
60
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)("div", { ref: setVideoWrapper, className: `section-video-wrapper-${sectionId}`, style: {
100
61
  position: position === 'fixed' ? 'sticky' : 'relative',
101
62
  height: position === 'fixed' ? '100vh' : '100%',
102
63
  top: position === 'fixed' ? '100vh' : '0',
103
64
  overflow: 'hidden',
104
- opacity: !isClickedOnCover && play === 'on-click' && coverUrl ? 0 : 1,
105
65
  width: '100%'
106
- }, children: play === 'on-click' && !isClickedOnCover && ((0, jsx_runtime_1.jsx)("div", { className: `video-background-${sectionId}-cover-container`, style: {
107
- position: 'absolute',
108
- left: 0,
109
- width: '100%',
110
- height: '100%',
111
- top: 0
112
- }, onClick: handleCoverClick, children: coverUrl && play === 'on-click' && ((0, jsx_runtime_1.jsx)("img", { src: coverUrl, alt: "Video cover", className: `video-background-${sectionId}-cover`, style: {
113
- opacity: isPlaying ? 0 : 1,
114
- left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
115
- width: isContainHeight ? 'auto' : '100%',
116
- objectFit: isContainHeight ? 'unset' : (size !== null && size !== void 0 ? size : 'cover'),
66
+ }, children: [(0, jsx_runtime_1.jsx)("video", { ref: setVideo, autoPlay: play === 'auto', loop: true, style: {
67
+ opacity: !isClickedOnCover && play === 'on-click' && coverUrl ? 0 : 1,
68
+ objectFit: isContainHeight ? 'cover' : (size !== null && size !== void 0 ? size : 'cover'),
69
+ width: isContainHeight && !isVideoWidthOverflow ? 'auto' : '100%',
117
70
  transform: isContainHeight ? 'translateX(-50%)' : 'none',
118
- position: 'relative',
71
+ left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
72
+ height: '100%',
73
+ position: 'relative'
74
+ }, controls: play === 'on-click', muted: play === 'auto', playsInline: true, preload: "auto", className: `video-background-${sectionId}`, onPlay: () => setIsPlaying(true), onPause: () => setIsPlaying(false), children: (0, jsx_runtime_1.jsx)("source", { src: `${url}` }) }), play === 'on-click' && !isClickedOnCover && ((0, jsx_runtime_1.jsx)("div", { className: `video-background-${sectionId}-cover-container`, style: {
75
+ position: 'absolute',
76
+ left: 0,
77
+ width: '100%',
119
78
  height: '100%',
120
- transition: 'opacity 0.1s ease-in-out'
121
- } })) })) }) }));
79
+ top: 0
80
+ }, onClick: handleCoverClick, children: coverUrl && play === 'on-click' && ((0, jsx_runtime_1.jsx)("img", { src: coverUrl, alt: "Video cover", className: `video-background-${sectionId}-cover`, style: {
81
+ opacity: isPlaying ? 0 : 1,
82
+ left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
83
+ width: isContainHeight ? 'auto' : '100%',
84
+ objectFit: isContainHeight ? 'unset' : (size !== null && size !== void 0 ? size : 'cover'),
85
+ transform: isContainHeight ? 'translateX(-50%)' : 'none',
86
+ position: 'relative',
87
+ height: '100%',
88
+ transition: 'opacity 0.1s ease-in-out'
89
+ } })) }))] }) }));
122
90
  };
123
91
  exports.SectionVideo = SectionVideo;
@@ -17,12 +17,10 @@ const getStyleFromItemStateAndParams_1 = require("../../../utils/getStyleFromIte
17
17
  const useItemFXData_1 = require("../../../common/useItemFXData");
18
18
  const getFill_1 = require("../../../utils/getFill");
19
19
  const useExemplary_1 = require("../../../common/useExemplary");
20
- const SectionVideoCacheContext_1 = require("../../Section/SectionVideoCacheContext");
21
20
  const ImageItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
22
21
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
23
22
  const id = (0, react_1.useId)();
24
23
  const { radius: itemRadius, strokeWidth: itemStrokeWidth, opacity: itemOpacity, strokeFill: itemStrokeFill, blur: itemBlur } = (0, useFileItem_1.useFileItem)(item, sectionId);
25
- const { imageCache } = (0, react_1.useContext)(SectionVideoCacheContext_1.SectionVideoCacheContext);
26
24
  const itemAngle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
27
25
  const [wrapperRef, setWrapperRef] = (0, react_1.useState)(null);
28
26
  (0, useRegisterResize_1.useRegisterResize)(wrapperRef, onResize);
@@ -67,19 +65,9 @@ const ImageItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityCha
67
65
  (0, react_1.useEffect)(() => {
68
66
  onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(isInteractive);
69
67
  }, [isInteractive, onVisibilityChange]);
70
- (0, react_1.useEffect)(() => {
71
- if (!wrapperRef)
72
- return;
73
- const image = imageCache.get(url);
74
- if (!image)
75
- return;
76
- image.className = `image image-${item.id}`;
77
- Object.assign(image.style, inlineStyles);
78
- if (!wrapperRef.contains(image)) {
79
- wrapperRef.appendChild(image);
80
- }
81
- }, [wrapperRef, imageCache, url, angle, blur, inlineStyles]);
82
- return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { link: item.link, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: `image-wrapper-${item.id}`, ref: setWrapperRef, style: Object.assign(Object.assign({ opacity, transform: `rotate(${angle}deg)` }, (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), { willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset', transition: (_o = wrapperStateParams === null || wrapperStateParams === void 0 ? void 0 : wrapperStateParams.transition) !== null && _o !== void 0 ? _o : 'none' }), children: [hasGLEffect && isFXAllowed && ((0, jsx_runtime_1.jsx)("canvas", { style: inlineStyles, ref: fxCanvas, className: `img-canvas image-${item.id}`, width: rectWidth, height: rectHeight })), !(hasGLEffect && isFXAllowed) && !imageCache.has(url) && ((0, jsx_runtime_1.jsx)("img", { alt: "", className: `image image-${item.id}`, style: inlineStyles, src: item.params.url }))] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
68
+ return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { link: item.link, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `image-wrapper-${item.id}`, ref: setWrapperRef, style: Object.assign(Object.assign({ opacity, transform: `rotate(${angle}deg)` }, (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), { willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset', transition: (_o = wrapperStateParams === null || wrapperStateParams === void 0 ? void 0 : wrapperStateParams.transition) !== null && _o !== void 0 ? _o : 'none' }), children: hasGLEffect && isFXAllowed
69
+ ? ((0, jsx_runtime_1.jsx)("canvas", { style: inlineStyles, ref: fxCanvas, className: `img-canvas image-${item.id}`, width: rectWidth, height: rectHeight }))
70
+ : ((0, jsx_runtime_1.jsx)("img", { alt: "", className: `image image-${item.id}`, style: inlineStyles, src: item.params.url })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
83
71
  .image-wrapper-${item.id} {
84
72
  position: absolute;
85
73
  width: 100%;
@@ -18,18 +18,16 @@ const useElementRect_1 = require("../../../utils/useElementRect");
18
18
  const useItemFXData_1 = require("../../../common/useItemFXData");
19
19
  const getFill_1 = require("../../../utils/getFill");
20
20
  const useExemplary_1 = require("../../../common/useExemplary");
21
- const SectionVideoCacheContext_1 = require("../../Section/SectionVideoCacheContext");
22
21
  const VideoItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
23
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
22
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
24
23
  const id = (0, react_1.useId)();
25
24
  const { radius: itemRadius, strokeWidth: itemStrokeWidth, strokeFill: itemStrokeFill, opacity: itemOpacity, blur: itemBlur } = (0, useFileItem_1.useFileItem)(item, sectionId);
26
- const { videoCache } = (0, react_1.useContext)(SectionVideoCacheContext_1.SectionVideoCacheContext);
27
25
  const [isVideoPlaying, setIsVideoPlaying] = (0, react_1.useState)(false);
28
26
  const isScrollPausedRef = (0, react_1.useRef)(false);
29
27
  const [userPaused, setUserPaused] = (0, react_1.useState)(false);
30
28
  const [isVideoInteracted, setIsVideoInteracted] = (0, react_1.useState)(false);
31
29
  const itemAngle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
32
- const [videoWrapper, setVideoWrapper] = (0, react_1.useState)(null);
30
+ const [ref, setRef] = (0, react_1.useState)(null);
33
31
  const [videoRef, setVideoRef] = (0, react_1.useState)(null);
34
32
  const fxCanvas = (0, react_1.useRef)(null);
35
33
  const { url, hasGLEffect } = item.params;
@@ -40,7 +38,7 @@ const VideoItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityCha
40
38
  const width = area && exemplary ? area.width * exemplary : 0;
41
39
  const height = area && exemplary ? area.height * exemplary : 0;
42
40
  const { controlsValues, fragmentShader } = (0, useItemFXData_1.useItemFXData)(item, sectionId);
43
- const rect = (0, useElementRect_1.useElementRect)(videoWrapper);
41
+ const rect = (0, useElementRect_1.useElementRect)(ref);
44
42
  const rectWidth = Math.floor((_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0);
45
43
  const rectHeight = Math.floor((_b = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _b !== void 0 ? _b : 0);
46
44
  const scrollPlayback = params.scrollPlayback;
@@ -64,7 +62,7 @@ const VideoItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityCha
64
62
  fragmentShader,
65
63
  controls: controlsValues
66
64
  }, width, height);
67
- (0, useRegisterResize_1.useRegisterResize)(videoWrapper, onResize);
65
+ (0, useRegisterResize_1.useRegisterResize)(ref, onResize);
68
66
  const inlineStyles = Object.assign(Object.assign({ borderRadius: `${radius * 100}vw` }, (strokeWidth !== undefined ? {
69
67
  borderWidth: `${strokeWidth * 100}vw`,
70
68
  borderColor: stroke,
@@ -76,53 +74,53 @@ const VideoItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityCha
76
74
  onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(isInteractive);
77
75
  }, [isInteractive, onVisibilityChange]);
78
76
  (0, react_1.useEffect)(() => {
79
- var _a;
80
- if (!videoWrapper || hasScrollPlayback || hasGLEffect)
77
+ if (!videoRef || params.play !== 'on-click' || !ref)
81
78
  return;
82
- const { play } = params;
83
- const video = videoCache.get(url);
84
- if (!video)
85
- return;
86
- video.controls = play === "on-click";
87
- video.muted = play === "auto";
88
- video.autoplay = play === "auto";
89
- video.loop = true;
90
- video.poster = (_a = item.params.coverUrl) !== null && _a !== void 0 ? _a : '';
91
- video.style.transform = `translateZ(0)`;
92
- video.className = `video video-${item.id}`;
93
- if (!videoWrapper.contains(video)) {
94
- videoWrapper.appendChild(video);
95
- }
96
- setVideoRef(video);
97
- video.addEventListener('play', () => {
98
- setIsVideoPlaying(true);
99
- setUserPaused(false);
100
- });
101
- video.addEventListener('pause', () => {
102
- if (!isScrollPausedRef.current) {
103
- setUserPaused(true);
79
+ const observer = new IntersectionObserver(([entry]) => {
80
+ if (userPaused || !isVideoInteracted)
81
+ return;
82
+ if (entry.isIntersecting) {
83
+ isScrollPausedRef.current = false;
84
+ videoRef.play();
85
+ }
86
+ else {
87
+ isScrollPausedRef.current = true;
88
+ videoRef.pause();
104
89
  }
105
- setIsVideoPlaying(false);
106
90
  });
107
- if (play === "auto") {
108
- video.play().catch(() => { });
109
- }
110
- return () => {
111
- video.pause();
112
- };
113
- }, [params, videoWrapper, videoCache]);
114
- return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { link: item.link, children: [(0, jsx_runtime_1.jsxs)("div", { className: `video-wrapper-${item.id}`, ref: setVideoWrapper, style: {
91
+ observer.observe(ref);
92
+ return () => observer.disconnect();
93
+ }, [videoRef, ref, userPaused, isVideoInteracted]);
94
+ return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { link: item.link, children: [(0, jsx_runtime_1.jsxs)("div", { className: `video-wrapper-${item.id}`, ref: setRef, style: {
115
95
  opacity,
116
- transform: `rotate(${angle}deg) translateZ(0)`,
96
+ transform: `rotate(${angle}deg)`,
117
97
  filter: `blur(${blur * 100}vw)`,
118
98
  willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset',
119
99
  transition: (_o = wrapperStateParams === null || wrapperStateParams === void 0 ? void 0 : wrapperStateParams.transition) !== null && _o !== void 0 ? _o : 'none'
120
- }, children: [hasScrollPlayback && ((0, jsx_runtime_1.jsx)(ScrollPlaybackVideo_1.ScrollPlaybackVideo, { sectionId: sectionId, src: item.params.url, playbackParams: scrollPlayback, style: inlineStyles, className: `video video-playback-wrapper video-${item.id}` })), hasGLEffect && isFXAllowed && ((0, jsx_runtime_1.jsx)("canvas", { style: inlineStyles, ref: fxCanvas, className: `video-canvas video-${item.id}`, width: rectWidth, height: rectHeight })), !hasScrollPlayback && !hasGLEffect && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(params.play === 'on-click' || params.play === 'on-hover') && item.params.coverUrl && !isVideoInteracted && ((0, jsx_runtime_1.jsx)("img", { onMouseEnter: () => {
100
+ }, children: [hasScrollPlayback && ((0, jsx_runtime_1.jsx)(ScrollPlaybackVideo_1.ScrollPlaybackVideo, { sectionId: sectionId, src: item.params.url, playbackParams: scrollPlayback, style: inlineStyles, className: `video video-playback-wrapper video-${item.id}` })), hasGLEffect && isFXAllowed && ((0, jsx_runtime_1.jsx)("canvas", { style: inlineStyles, ref: fxCanvas, className: `video-canvas video-${item.id}`, width: rectWidth, height: rectHeight })), !hasScrollPlayback && !hasGLEffect && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("video", { poster: (_p = item.params.coverUrl) !== null && _p !== void 0 ? _p : '', ref: setVideoRef, autoPlay: params.play === 'auto', preload: "auto", onClick: () => {
101
+ setIsVideoInteracted(true);
102
+ }, muted: params.muted, onPlay: () => {
103
+ setIsVideoPlaying(true);
104
+ setUserPaused(false);
105
+ }, onPause: () => {
106
+ if (!isScrollPausedRef.current) {
107
+ setUserPaused(true);
108
+ }
109
+ setIsVideoPlaying(false);
110
+ }, onMouseEnter: () => {
111
+ if (!videoRef || params.play !== 'on-hover')
112
+ return;
113
+ videoRef.play();
114
+ }, onMouseLeave: () => {
115
+ if (!videoRef || params.play !== 'on-hover')
116
+ return;
117
+ videoRef.pause();
118
+ }, loop: true, controls: params.controls, playsInline: true, className: `video video-${item.id}`, style: inlineStyles, children: (0, jsx_runtime_1.jsx)("source", { src: item.params.url }) }), (params.play === 'on-click' || params.play === 'on-hover') && item.params.coverUrl && !isVideoInteracted && ((0, jsx_runtime_1.jsx)("img", { onMouseEnter: () => {
121
119
  if (!videoRef || params.play !== 'on-hover')
122
120
  return;
123
121
  setIsVideoInteracted(true);
124
122
  videoRef.play();
125
- }, src: (_p = item.params.coverUrl) !== null && _p !== void 0 ? _p : '', className: `video-cover-${item.id}`, onClick: () => {
123
+ }, src: (_q = item.params.coverUrl) !== null && _q !== void 0 ? _q : '', className: `video-cover-${item.id}`, onClick: () => {
126
124
  if (!videoRef)
127
125
  return;
128
126
  setIsVideoInteracted(true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gxpl/sdk",
3
- "version": "0.0.36-0",
3
+ "version": "0.0.36",
4
4
  "description": "Generic SDK for use in public websites.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,10 +0,0 @@
1
- import { FC, PropsWithChildren } from 'react';
2
- export declare const SectionVideoCacheContext: import("react").Context<{
3
- videoCache: Map<string, HTMLVideoElement>;
4
- imageCache: Map<string, HTMLImageElement>;
5
- }>;
6
- interface Props {
7
- assets: string[];
8
- }
9
- export declare const SectionVideoCacheProvider: FC<PropsWithChildren<Props>>;
10
- export {};
@@ -1,43 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SectionVideoCacheProvider = exports.SectionVideoCacheContext = void 0;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_1 = require("react");
6
- exports.SectionVideoCacheContext = (0, react_1.createContext)({ videoCache: new Map(), imageCache: new Map() });
7
- const SectionVideoCacheProvider = ({ children, assets }) => {
8
- const [videoCache, setVideoCache] = (0, react_1.useState)(new Map());
9
- const [imageCache, setImageCache] = (0, react_1.useState)(new Map());
10
- (0, react_1.useEffect)(() => {
11
- assets.forEach(asset => {
12
- if (isVideoAsset(asset)) {
13
- const video = getSectionVideo(asset);
14
- setVideoCache(prev => prev.set(asset, video));
15
- }
16
- if (isImageAsset(asset)) {
17
- const img = new Image();
18
- img.src = asset;
19
- setImageCache(prev => prev.set(asset, img));
20
- }
21
- });
22
- }, [assets]);
23
- return (0, jsx_runtime_1.jsx)(exports.SectionVideoCacheContext.Provider, { value: { videoCache, imageCache }, children: children });
24
- };
25
- exports.SectionVideoCacheProvider = SectionVideoCacheProvider;
26
- function getSectionVideo(url) {
27
- const video = document.createElement('video');
28
- video.src = url;
29
- video.preload = 'auto';
30
- video.playsInline = true;
31
- video.load();
32
- return video;
33
- }
34
- function isVideoAsset(url) {
35
- const videoExtensions = ['.mp4', '.mov', '.webm'];
36
- const lowerUrl = url.toLowerCase();
37
- return videoExtensions.some(ext => lowerUrl.endsWith(ext));
38
- }
39
- function isImageAsset(url) {
40
- const imageExtensions = ['.gif', '.png', '.jpg', '.jpeg', '.webp', '.avif', '.svg'];
41
- const lowerUrl = url.toLowerCase();
42
- return imageExtensions.some(ext => lowerUrl.endsWith(ext));
43
- }