@cntrl-site/sdk-nextjs 1.8.9-alpha.10 → 1.8.9-alpha.13
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/components/items/LinkWrapper.js +1 -9
- package/lib/interactions/InteractionsRegistry.js +101 -2
- package/lib/interactions/ItemInteractionCtrl.js +1 -1
- package/lib/provider/InteractionsContext.js +21 -0
- package/package.json +2 -2
- package/src/components/ScrollPlaybackVideo.tsx +1 -1
- package/src/components/items/LinkWrapper.tsx +1 -11
- package/src/interactions/InteractionsRegistry.ts +98 -6
- package/src/interactions/ItemInteractionCtrl.ts +4 -3
- package/src/interactions/types.ts +4 -4
- package/src/provider/InteractionsContext.tsx +24 -1
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LinkWrapper = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
|
|
6
5
|
const LinkWrapper = ({ url, children, target }) => {
|
|
7
|
-
const validUrl = url &&
|
|
6
|
+
const validUrl = url && buildValidUrl(url);
|
|
8
7
|
const targetParams = target === '_blank' ? { target, rel: 'noreferrer' } : {};
|
|
9
8
|
return url ? ((0, jsx_runtime_1.jsx)("a", Object.assign({ href: validUrl }, targetParams, { children: children }))) : ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children }));
|
|
10
9
|
};
|
|
@@ -26,10 +25,3 @@ function buildValidUrl(url) {
|
|
|
26
25
|
return url;
|
|
27
26
|
return `//${url}`;
|
|
28
27
|
}
|
|
29
|
-
function prependBasePath(url) {
|
|
30
|
-
// Only prepend for internal links (starting with '/')
|
|
31
|
-
if (url && url.startsWith('/') && basePath && !url.startsWith(basePath)) {
|
|
32
|
-
return `${basePath}${url}`;
|
|
33
|
-
}
|
|
34
|
-
return url;
|
|
35
|
-
}
|
|
@@ -93,6 +93,8 @@ class InteractionsRegistry {
|
|
|
93
93
|
for (const interaction of this.interactions) {
|
|
94
94
|
const { triggers } = interaction;
|
|
95
95
|
for (const trigger of triggers) {
|
|
96
|
+
if (!('itemId' in trigger))
|
|
97
|
+
continue;
|
|
96
98
|
if (trigger.itemId !== itemId)
|
|
97
99
|
continue;
|
|
98
100
|
if (activeStates.includes(trigger.from)) {
|
|
@@ -102,12 +104,107 @@ class InteractionsRegistry {
|
|
|
102
104
|
}
|
|
103
105
|
return available;
|
|
104
106
|
}
|
|
105
|
-
|
|
107
|
+
notifyLoad() {
|
|
106
108
|
var _a, _b;
|
|
107
109
|
const timestamp = Date.now();
|
|
108
110
|
for (const interaction of this.interactions) {
|
|
109
111
|
const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
|
|
110
|
-
const matchingTrigger = interaction.triggers.find(
|
|
112
|
+
const matchingTrigger = interaction.triggers.find(trigger => 'position' in trigger && trigger.position === 0 && trigger.from === currentStateId);
|
|
113
|
+
if (!matchingTrigger)
|
|
114
|
+
continue;
|
|
115
|
+
const activeStateId = this.getActiveInteractionState(interaction.id);
|
|
116
|
+
const isNewStateActive = matchingTrigger.to === activeStateId;
|
|
117
|
+
this.setCurrentStateForInteraction(interaction.id, matchingTrigger.to);
|
|
118
|
+
const transitioningItems = (_a = this.stateItemsIdsMap[activeStateId]) !== null && _a !== void 0 ? _a : [];
|
|
119
|
+
const state = interaction.states.find((state) => state.id === matchingTrigger.to);
|
|
120
|
+
const actions = (_b = state === null || state === void 0 ? void 0 : state.actions) !== null && _b !== void 0 ? _b : [];
|
|
121
|
+
for (const action of actions) {
|
|
122
|
+
const ctrl = this.ctrls.get(action.itemId);
|
|
123
|
+
if (!ctrl)
|
|
124
|
+
continue;
|
|
125
|
+
ctrl.receiveAction(action.type);
|
|
126
|
+
}
|
|
127
|
+
this.itemsStages = this.itemsStages.map((stage) => {
|
|
128
|
+
if (stage.interactionId !== interaction.id)
|
|
129
|
+
return stage;
|
|
130
|
+
return {
|
|
131
|
+
itemId: stage.itemId,
|
|
132
|
+
interactionId: stage.interactionId,
|
|
133
|
+
type: 'transitioning',
|
|
134
|
+
from: stage.type === 'transitioning' ? stage.to : stage.stateId,
|
|
135
|
+
to: matchingTrigger.to,
|
|
136
|
+
direction: isNewStateActive ? 'in' : 'out',
|
|
137
|
+
updated: timestamp
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
const itemsToNotify = new Set(transitioningItems);
|
|
141
|
+
for (const trigger of interaction.triggers) {
|
|
142
|
+
if (!('itemId' in trigger))
|
|
143
|
+
continue;
|
|
144
|
+
itemsToNotify.add(trigger.itemId);
|
|
145
|
+
}
|
|
146
|
+
this.notifyItemCtrlsChange(Array.from(itemsToNotify));
|
|
147
|
+
this.notifyTransitionStartForItems(transitioningItems, activeStateId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
notifyScrollTrigger(position) {
|
|
151
|
+
var _a, _b;
|
|
152
|
+
const timestamp = Date.now();
|
|
153
|
+
for (const interaction of this.interactions) {
|
|
154
|
+
const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
|
|
155
|
+
const matchingTrigger = interaction.triggers.find((trigger) => {
|
|
156
|
+
if (!('position' in trigger) || trigger.position === 0)
|
|
157
|
+
return false;
|
|
158
|
+
const isScrollingDown = trigger.position < position;
|
|
159
|
+
const relevantState = isScrollingDown ? trigger.from : trigger.to;
|
|
160
|
+
return relevantState === currentStateId;
|
|
161
|
+
});
|
|
162
|
+
if (!matchingTrigger || !('position' in matchingTrigger))
|
|
163
|
+
continue;
|
|
164
|
+
const activeStateId = this.getActiveInteractionState(interaction.id);
|
|
165
|
+
const targetStateId = matchingTrigger.position > position ? matchingTrigger.from : matchingTrigger.to;
|
|
166
|
+
const isNewStateActive = targetStateId === activeStateId;
|
|
167
|
+
this.setCurrentStateForInteraction(interaction.id, targetStateId !== null && targetStateId !== void 0 ? targetStateId : activeStateId);
|
|
168
|
+
const transitioningItems = (_a = this.stateItemsIdsMap[activeStateId]) !== null && _a !== void 0 ? _a : [];
|
|
169
|
+
const state = interaction.states.find((state) => state.id === targetStateId);
|
|
170
|
+
const actions = (_b = state === null || state === void 0 ? void 0 : state.actions) !== null && _b !== void 0 ? _b : [];
|
|
171
|
+
for (const action of actions) {
|
|
172
|
+
const ctrl = this.ctrls.get(action.itemId);
|
|
173
|
+
if (!ctrl)
|
|
174
|
+
continue;
|
|
175
|
+
ctrl.receiveAction(action.type);
|
|
176
|
+
}
|
|
177
|
+
const itemsStages = this.itemsStages.map((stage) => {
|
|
178
|
+
if (stage.interactionId !== interaction.id)
|
|
179
|
+
return stage;
|
|
180
|
+
return {
|
|
181
|
+
itemId: stage.itemId,
|
|
182
|
+
interactionId: stage.interactionId,
|
|
183
|
+
type: 'transitioning',
|
|
184
|
+
from: stage.type === 'transitioning' ? stage.to : stage.stateId,
|
|
185
|
+
to: targetStateId,
|
|
186
|
+
direction: isNewStateActive ? 'in' : 'out',
|
|
187
|
+
updated: timestamp
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
this.itemsStages = itemsStages;
|
|
191
|
+
const itemsToNotify = new Set(transitioningItems);
|
|
192
|
+
for (const trigger of interaction.triggers) {
|
|
193
|
+
if (!('itemId' in trigger))
|
|
194
|
+
continue;
|
|
195
|
+
itemsToNotify.add(trigger.itemId);
|
|
196
|
+
}
|
|
197
|
+
this.notifyItemCtrlsChange(Array.from(itemsToNotify));
|
|
198
|
+
this.notifyTransitionStartForItems(transitioningItems, activeStateId);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
notifyItemTrigger(itemId, triggerType) {
|
|
202
|
+
var _a, _b;
|
|
203
|
+
const timestamp = Date.now();
|
|
204
|
+
for (const interaction of this.interactions) {
|
|
205
|
+
const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
|
|
206
|
+
const matchingTrigger = interaction.triggers.find((trigger) => 'itemId' in trigger
|
|
207
|
+
&& trigger.itemId === itemId
|
|
111
208
|
&& trigger.from === currentStateId
|
|
112
209
|
&& trigger.type === triggerType);
|
|
113
210
|
if (!matchingTrigger)
|
|
@@ -139,6 +236,8 @@ class InteractionsRegistry {
|
|
|
139
236
|
});
|
|
140
237
|
const itemsToNotify = new Set(transitioningItems);
|
|
141
238
|
for (const trigger of interaction.triggers) {
|
|
239
|
+
if (!('itemId' in trigger))
|
|
240
|
+
continue;
|
|
142
241
|
itemsToNotify.add(trigger.itemId);
|
|
143
242
|
}
|
|
144
243
|
this.notifyItemCtrlsChange(Array.from(itemsToNotify));
|
|
@@ -5,14 +5,35 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
5
5
|
const react_1 = require("react");
|
|
6
6
|
const InteractionsRegistry_1 = require("../interactions/InteractionsRegistry");
|
|
7
7
|
const useCurrentLayout_1 = require("../common/useCurrentLayout");
|
|
8
|
+
const ArticleRectContext_1 = require("./ArticleRectContext");
|
|
8
9
|
exports.InteractionsContext = (0, react_1.createContext)(undefined);
|
|
9
10
|
const InteractionsProvider = ({ article, children }) => {
|
|
10
11
|
const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
|
|
12
|
+
const articleRectObserver = (0, react_1.useContext)(ArticleRectContext_1.ArticleRectContext);
|
|
11
13
|
const registry = (0, react_1.useMemo)(() => {
|
|
12
14
|
if (!layoutId)
|
|
13
15
|
return;
|
|
14
16
|
return new InteractionsRegistry_1.InteractionsRegistry(article, layoutId);
|
|
15
17
|
}, [layoutId]);
|
|
18
|
+
(0, react_1.useEffect)(() => {
|
|
19
|
+
if (!registry || !articleRectObserver)
|
|
20
|
+
return;
|
|
21
|
+
const handleScroll = () => {
|
|
22
|
+
const scrollY = window.scrollY;
|
|
23
|
+
registry.notifyScrollTrigger(scrollY);
|
|
24
|
+
};
|
|
25
|
+
return articleRectObserver.on('scroll', handleScroll);
|
|
26
|
+
}, [registry, articleRectObserver]);
|
|
27
|
+
(0, react_1.useEffect)(() => {
|
|
28
|
+
if (!registry)
|
|
29
|
+
return;
|
|
30
|
+
registry.notifyLoad();
|
|
31
|
+
const handleLoad = () => {
|
|
32
|
+
registry.notifyLoad();
|
|
33
|
+
};
|
|
34
|
+
window.addEventListener('load', handleLoad);
|
|
35
|
+
return () => window.removeEventListener('load', handleLoad);
|
|
36
|
+
}, [registry]);
|
|
16
37
|
return ((0, jsx_runtime_1.jsx)(exports.InteractionsContext.Provider, { value: registry, children: children }));
|
|
17
38
|
};
|
|
18
39
|
exports.InteractionsProvider = InteractionsProvider;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.8.9-alpha.
|
|
3
|
+
"version": "1.8.9-alpha.13",
|
|
4
4
|
"description": "SDK for Next.js",
|
|
5
5
|
"author": "arsen@momdesign.nyc",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@antfu/eslint-config": "^3.8.0",
|
|
32
32
|
"@cntrl-site/color": "^1.0.0",
|
|
33
33
|
"@cntrl-site/effects": "^1.3.2",
|
|
34
|
-
"@cntrl-site/sdk": "^1.22.
|
|
34
|
+
"@cntrl-site/sdk": "^1.22.9-alpha.2",
|
|
35
35
|
"@types/vimeo__player": "^2.18.0",
|
|
36
36
|
"@vimeo/player": "^2.25.0",
|
|
37
37
|
"html-react-parser": "^3.0.1",
|
|
@@ -24,7 +24,7 @@ export const ScrollPlaybackVideo: FC<Props> = ({ sectionId, src, playbackParams,
|
|
|
24
24
|
const scrollPos = articleRectObserver.getSectionScroll(sectionId);
|
|
25
25
|
const time = rangeMap(scrollPos, playbackParams.from, playbackParams.to, 0, 1, true);
|
|
26
26
|
setTime(toFixed(time));
|
|
27
|
-
})
|
|
27
|
+
});
|
|
28
28
|
}, [playbackParams?.from, playbackParams?.to, time]);
|
|
29
29
|
|
|
30
30
|
const scrollVideoManager = useMemo<ScrollPlaybackVideoManager | null>(() => {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import React, { ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
|
|
4
|
-
|
|
5
3
|
interface Props {
|
|
6
4
|
url?: string;
|
|
7
5
|
children: ReactElement | ReactNode[];
|
|
@@ -9,7 +7,7 @@ interface Props {
|
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
export const LinkWrapper: React.FC<Props> = ({ url, children, target }) => {
|
|
12
|
-
const validUrl = url &&
|
|
10
|
+
const validUrl = url && buildValidUrl(url);
|
|
13
11
|
const targetParams = target === '_blank' ? { target, rel: 'noreferrer' } : {};
|
|
14
12
|
return url ? (
|
|
15
13
|
<a
|
|
@@ -39,11 +37,3 @@ function buildValidUrl(url: string): string {
|
|
|
39
37
|
if (protocolCheck) return url;
|
|
40
38
|
return `//${url}`;
|
|
41
39
|
}
|
|
42
|
-
|
|
43
|
-
function prependBasePath(url: string): string {
|
|
44
|
-
// Only prepend for internal links (starting with '/')
|
|
45
|
-
if (url && url.startsWith('/') && basePath && !url.startsWith(basePath)) {
|
|
46
|
-
return `${basePath}${url}`;
|
|
47
|
-
}
|
|
48
|
-
return url;
|
|
49
|
-
}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
Article,
|
|
4
4
|
ArticleItemType,
|
|
5
5
|
Interaction,
|
|
6
|
-
|
|
6
|
+
InteractionItemTrigger,
|
|
7
7
|
ItemAny,
|
|
8
8
|
} from '@cntrl-site/sdk';
|
|
9
9
|
import { isItemType } from '../utils/isItemType';
|
|
@@ -96,12 +96,13 @@ export class InteractionsRegistry implements InteractionsRegistryPort {
|
|
|
96
96
|
return itemStyles;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
getItemAvailableTriggers(itemId: string): Set<
|
|
100
|
-
const available = new Set<
|
|
99
|
+
getItemAvailableTriggers(itemId: string): Set<InteractionItemTrigger['type']> {
|
|
100
|
+
const available = new Set<InteractionItemTrigger['type']>();
|
|
101
101
|
const activeStates = Object.values(this.interactionStateMap);
|
|
102
102
|
for (const interaction of this.interactions) {
|
|
103
103
|
const { triggers } = interaction;
|
|
104
104
|
for (const trigger of triggers) {
|
|
105
|
+
if (!('itemId' in trigger)) continue;
|
|
105
106
|
if (trigger.itemId !== itemId) continue;
|
|
106
107
|
if (activeStates.includes(trigger.from)) {
|
|
107
108
|
available.add(trigger.type);
|
|
@@ -111,12 +112,102 @@ export class InteractionsRegistry implements InteractionsRegistryPort {
|
|
|
111
112
|
return available;
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
|
|
115
|
+
notifyLoad() {
|
|
116
|
+
const timestamp = Date.now();
|
|
117
|
+
for (const interaction of this.interactions) {
|
|
118
|
+
const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
|
|
119
|
+
const matchingTrigger = interaction.triggers.find(trigger =>
|
|
120
|
+
'position' in trigger && trigger.position === 0 && trigger.from === currentStateId
|
|
121
|
+
);
|
|
122
|
+
if (!matchingTrigger) continue;
|
|
123
|
+
const activeStateId = this.getActiveInteractionState(interaction.id);
|
|
124
|
+
const isNewStateActive = matchingTrigger.to === activeStateId;
|
|
125
|
+
this.setCurrentStateForInteraction(interaction.id, matchingTrigger.to);
|
|
126
|
+
const transitioningItems = this.stateItemsIdsMap[activeStateId] ?? [];
|
|
127
|
+
const state = interaction.states.find((state) => state.id === matchingTrigger.to);
|
|
128
|
+
const actions = state?.actions ?? [];
|
|
129
|
+
for (const action of actions) {
|
|
130
|
+
const ctrl = this.ctrls.get(action.itemId);
|
|
131
|
+
if (!ctrl) continue;
|
|
132
|
+
ctrl.receiveAction(action.type);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.itemsStages = this.itemsStages.map((stage) => {
|
|
136
|
+
if (stage.interactionId !== interaction.id) return stage;
|
|
137
|
+
return {
|
|
138
|
+
itemId: stage.itemId,
|
|
139
|
+
interactionId: stage.interactionId,
|
|
140
|
+
type: 'transitioning',
|
|
141
|
+
from: stage.type === 'transitioning' ? stage.to : stage.stateId!,
|
|
142
|
+
to: matchingTrigger.to,
|
|
143
|
+
direction: isNewStateActive ? 'in' : 'out',
|
|
144
|
+
updated: timestamp
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const itemsToNotify = new Set<ItemId>(transitioningItems);
|
|
149
|
+
for (const trigger of interaction.triggers) {
|
|
150
|
+
if (!('itemId' in trigger)) continue;
|
|
151
|
+
itemsToNotify.add(trigger.itemId);
|
|
152
|
+
}
|
|
153
|
+
this.notifyItemCtrlsChange(Array.from(itemsToNotify));
|
|
154
|
+
this.notifyTransitionStartForItems(transitioningItems, activeStateId);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
notifyScrollTrigger(position: number) {
|
|
159
|
+
const timestamp = Date.now();
|
|
160
|
+
for (const interaction of this.interactions) {
|
|
161
|
+
const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
|
|
162
|
+
const matchingTrigger = interaction.triggers.find((trigger) => {
|
|
163
|
+
if (!('position' in trigger) || trigger.position === 0) return false;
|
|
164
|
+
const isScrollingDown = trigger.position < position;
|
|
165
|
+
const relevantState = isScrollingDown ? trigger.from : trigger.to;
|
|
166
|
+
return relevantState === currentStateId;
|
|
167
|
+
});
|
|
168
|
+
if (!matchingTrigger || !('position' in matchingTrigger)) continue;
|
|
169
|
+
const activeStateId = this.getActiveInteractionState(interaction.id);
|
|
170
|
+
const targetStateId = matchingTrigger.position > position ? matchingTrigger.from : matchingTrigger.to;
|
|
171
|
+
const isNewStateActive = targetStateId === activeStateId;
|
|
172
|
+
this.setCurrentStateForInteraction(interaction.id, targetStateId ?? activeStateId);
|
|
173
|
+
const transitioningItems = this.stateItemsIdsMap[activeStateId] ?? [];
|
|
174
|
+
const state = interaction.states.find((state) => state.id === targetStateId);
|
|
175
|
+
const actions = state?.actions ?? [];
|
|
176
|
+
for (const action of actions) {
|
|
177
|
+
const ctrl = this.ctrls.get(action.itemId);
|
|
178
|
+
if (!ctrl) continue;
|
|
179
|
+
ctrl.receiveAction(action.type);
|
|
180
|
+
}
|
|
181
|
+
const itemsStages = this.itemsStages.map((stage) => {
|
|
182
|
+
if (stage.interactionId !== interaction.id) return stage;
|
|
183
|
+
return {
|
|
184
|
+
itemId: stage.itemId,
|
|
185
|
+
interactionId: stage.interactionId,
|
|
186
|
+
type: 'transitioning' as const,
|
|
187
|
+
from: stage.type === 'transitioning' ? stage.to : stage.stateId!,
|
|
188
|
+
to: targetStateId,
|
|
189
|
+
direction: isNewStateActive ? 'in' as const : 'out' as const,
|
|
190
|
+
updated: timestamp
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
this.itemsStages = itemsStages;
|
|
194
|
+
const itemsToNotify = new Set<ItemId>(transitioningItems);
|
|
195
|
+
for (const trigger of interaction.triggers) {
|
|
196
|
+
if (!('itemId' in trigger)) continue;
|
|
197
|
+
itemsToNotify.add(trigger.itemId);
|
|
198
|
+
}
|
|
199
|
+
this.notifyItemCtrlsChange(Array.from(itemsToNotify));
|
|
200
|
+
this.notifyTransitionStartForItems(transitioningItems, activeStateId);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
notifyItemTrigger(itemId: string, triggerType: TriggerType): void {
|
|
115
205
|
const timestamp = Date.now();
|
|
116
206
|
for (const interaction of this.interactions) {
|
|
117
207
|
const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
|
|
118
208
|
const matchingTrigger = interaction.triggers.find((trigger) =>
|
|
119
|
-
|
|
209
|
+
'itemId' in trigger
|
|
210
|
+
&& trigger.itemId === itemId
|
|
120
211
|
&& trigger.from === currentStateId
|
|
121
212
|
&& trigger.type === triggerType
|
|
122
213
|
);
|
|
@@ -146,6 +237,7 @@ export class InteractionsRegistry implements InteractionsRegistryPort {
|
|
|
146
237
|
});
|
|
147
238
|
const itemsToNotify = new Set<ItemId>(transitioningItems);
|
|
148
239
|
for (const trigger of interaction.triggers) {
|
|
240
|
+
if (!('itemId' in trigger)) continue;
|
|
149
241
|
itemsToNotify.add(trigger.itemId);
|
|
150
242
|
}
|
|
151
243
|
this.notifyItemCtrlsChange(Array.from(itemsToNotify));
|
|
@@ -269,7 +361,7 @@ type TransitioningStage = {
|
|
|
269
361
|
type ActiveStage = { type: 'active'; itemId: string; interactionId: string; stateId?: string; isStartState: boolean; updated: number; };
|
|
270
362
|
type InteractionStateMap = Record<InteractionId, StateId>;
|
|
271
363
|
type StateItemsIdsMap = Record<StateId, ItemId[]>;
|
|
272
|
-
type TriggerType =
|
|
364
|
+
type TriggerType = InteractionItemTrigger['type'];
|
|
273
365
|
type InteractionId = string;
|
|
274
366
|
type StateId = string;
|
|
275
367
|
type ItemId = string;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { InteractionsRegistryPort, ItemInteractionCtrl } from './types';
|
|
2
2
|
import { getTransition } from './getTransition';
|
|
3
3
|
import { getStyleKeysFromCSSProperty } from './CSSPropertyNameMap';
|
|
4
|
-
import {
|
|
4
|
+
import { InteractionItemTrigger } from '@cntrl-site/sdk';
|
|
5
5
|
|
|
6
6
|
export class ItemInteractionController implements ItemInteractionCtrl {
|
|
7
7
|
private transitionsInProgress: Set<string> = new Set();
|
|
8
8
|
private actionReceiver: ((type: 'play' | 'pause') => void) | undefined;
|
|
9
|
+
|
|
9
10
|
constructor(
|
|
10
11
|
private itemId: string,
|
|
11
12
|
private registry: InteractionsRegistryPort,
|
|
@@ -29,13 +30,13 @@ export class ItemInteractionController implements ItemInteractionCtrl {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
getHasTrigger(itemId: string, triggerType:
|
|
33
|
+
getHasTrigger(itemId: string, triggerType: InteractionItemTrigger['type']): boolean {
|
|
33
34
|
const triggers = this.registry.getItemAvailableTriggers(itemId);
|
|
34
35
|
return triggers.has(triggerType);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
sendTrigger(type: 'click' | 'hover-in' | 'hover-out') {
|
|
38
|
-
this.registry.
|
|
39
|
+
this.registry.notifyItemTrigger(this.itemId, type);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
receiveAction(type: 'play' | 'pause') {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ArticleItemType,
|
|
1
|
+
import { ArticleItemType, InteractionItemTrigger, ItemState } from '@cntrl-site/sdk';
|
|
2
2
|
|
|
3
3
|
export interface ItemInteractionCtrl {
|
|
4
4
|
getState(keys: string[]): StateCSSInfo;
|
|
5
|
-
getHasTrigger(itemId: string, triggerType:
|
|
5
|
+
getHasTrigger(itemId: string, triggerType: InteractionItemTrigger['type']): boolean;
|
|
6
6
|
sendTrigger(type: 'click' | 'hover-in' | 'hover-out'): void;
|
|
7
7
|
handleTransitionEnd?: (styleKey: string) => void;
|
|
8
8
|
handleTransitionStart?: (styleKeys: string[]) => void;
|
|
@@ -14,8 +14,8 @@ export interface ItemInteractionCtrl {
|
|
|
14
14
|
export interface InteractionsRegistryPort {
|
|
15
15
|
register(itemId: string, ctrl: ItemInteractionCtrl): void;
|
|
16
16
|
getStatePropsForItem(itemId: string): StateProps;
|
|
17
|
-
getItemAvailableTriggers(itemId: string): Set<
|
|
18
|
-
|
|
17
|
+
getItemAvailableTriggers(itemId: string): Set<InteractionItemTrigger['type']>;
|
|
18
|
+
notifyItemTrigger(itemId: string, type: TriggerType): void;
|
|
19
19
|
notifyTransitionEnd(itemId: string): void;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createContext, FC, PropsWithChildren, useContext, useMemo } from 'react';
|
|
1
|
+
import { createContext, FC, PropsWithChildren, useContext, useEffect, useMemo } from 'react';
|
|
2
2
|
import { InteractionsRegistry } from '../interactions/InteractionsRegistry';
|
|
3
3
|
import { Article } from '@cntrl-site/sdk';
|
|
4
4
|
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
5
|
+
import { ArticleRectContext } from './ArticleRectContext';
|
|
5
6
|
|
|
6
7
|
export const InteractionsContext = createContext<InteractionsRegistry | undefined>(undefined);
|
|
7
8
|
|
|
@@ -11,10 +12,32 @@ interface Props {
|
|
|
11
12
|
|
|
12
13
|
export const InteractionsProvider: FC<PropsWithChildren<Props>> = ({ article, children }) => {
|
|
13
14
|
const { layoutId } = useCurrentLayout();
|
|
15
|
+
const articleRectObserver = useContext(ArticleRectContext);
|
|
14
16
|
const registry = useMemo(() => {
|
|
15
17
|
if (!layoutId) return;
|
|
16
18
|
return new InteractionsRegistry(article, layoutId);
|
|
17
19
|
}, [layoutId]);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!registry || !articleRectObserver) return;
|
|
23
|
+
const handleScroll = () => {
|
|
24
|
+
const scrollY = window.scrollY;
|
|
25
|
+
registry.notifyScrollTrigger(scrollY);
|
|
26
|
+
};
|
|
27
|
+
return articleRectObserver.on('scroll', handleScroll);
|
|
28
|
+
}, [registry, articleRectObserver]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!registry) return;
|
|
32
|
+
registry.notifyLoad();
|
|
33
|
+
|
|
34
|
+
const handleLoad = () => {
|
|
35
|
+
registry.notifyLoad();
|
|
36
|
+
};
|
|
37
|
+
window.addEventListener('load', handleLoad);
|
|
38
|
+
return () => window.removeEventListener('load', handleLoad);
|
|
39
|
+
}, [registry]);
|
|
40
|
+
|
|
18
41
|
return (
|
|
19
42
|
<InteractionsContext.Provider value={registry}>
|
|
20
43
|
{children}
|