@cntrl-site/sdk-nextjs 1.0.18 → 1.0.19-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +15 -0
- package/jest.config.js +2 -2
- package/lib/components/Article.js +9 -8
- package/lib/components/Item.js +31 -13
- package/lib/components/items/CodeEmbedItem.js +9 -8
- package/lib/components/items/CustomItem.js +8 -7
- package/lib/components/items/GroupItem.js +9 -8
- package/lib/components/items/ImageItem.js +13 -12
- package/lib/components/items/RectangleItem.js +9 -8
- package/lib/components/items/RichTextItem.js +8 -7
- package/lib/components/items/VideoItem.js +13 -12
- package/lib/components/items/VimeoEmbed.js +16 -15
- package/lib/components/items/YoutubeEmbed.js +14 -13
- package/lib/components/useStatesClassNames.js +18 -0
- package/lib/provider/InteractionsContext.js +45 -0
- package/lib/utils/{HoverStyles/HoverStyles.js → StateStyles/StateStyles.js} +18 -16
- package/lib/utils/getStatesCSS.js +16 -0
- package/package.json +7 -4
- package/src/components/Article.tsx +31 -28
- package/src/components/ArticleWrapper.tsx +1 -2
- package/src/components/Item.tsx +35 -18
- package/src/components/items/CodeEmbedItem.tsx +10 -9
- package/src/components/items/CustomItem.tsx +10 -9
- package/src/components/items/GroupItem.tsx +10 -9
- package/src/components/items/ImageItem.tsx +16 -15
- package/src/components/items/RectangleItem.tsx +11 -10
- package/src/components/items/RichTextItem.tsx +15 -9
- package/src/components/items/VideoItem.tsx +16 -15
- package/src/components/items/VimeoEmbed.tsx +17 -16
- package/src/components/items/YoutubeEmbed.tsx +15 -15
- package/src/components/useStatesClassNames.ts +23 -0
- package/src/provider/InteractionsContext.test.tsx +97 -0
- package/src/provider/InteractionsContext.tsx +65 -0
- package/src/utils/{HoverStyles/HoverStyles.ts → StateStyles/StateStyles.ts} +22 -21
- package/src/utils/getStatesCSS.ts +21 -0
- package/cntrl-site-sdk-nextjs-1.0.17.tgz +0 -0
- package/cntrl-site-sdk-nextjs-1.0.18.tgz +0 -0
|
@@ -13,8 +13,9 @@ const useEmbedVideoItem_1 = require("./useEmbedVideoItem");
|
|
|
13
13
|
const useItemAngle_1 = require("../useItemAngle");
|
|
14
14
|
const sdk_1 = require("@cntrl-site/sdk");
|
|
15
15
|
const useCntrlContext_1 = require("../../provider/useCntrlContext");
|
|
16
|
-
const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
|
|
17
16
|
const useRegisterResize_1 = require("../../common/useRegisterResize");
|
|
17
|
+
const useStatesClassNames_1 = require("../useStatesClassNames");
|
|
18
|
+
const getStatesCSS_1 = require("../../utils/getStatesCSS");
|
|
18
19
|
const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
19
20
|
var _a, _b, _c;
|
|
20
21
|
const id = (0, react_1.useId)();
|
|
@@ -27,7 +28,9 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
27
28
|
const [ref, setRef] = (0, react_1.useState)(null);
|
|
28
29
|
const [imgRef, setImgRef] = (0, react_1.useState)(null);
|
|
29
30
|
const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
|
|
30
|
-
const layoutValues = [item.area, item.layoutParams, item.state
|
|
31
|
+
const layoutValues = [item.area, item.layoutParams, item.state];
|
|
32
|
+
const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video-wrapper');
|
|
33
|
+
const videoClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video');
|
|
31
34
|
(0, useRegisterResize_1.useRegisterResize)(ref, onResize);
|
|
32
35
|
const getValidVimeoUrl = (url) => {
|
|
33
36
|
const validURL = new URL(url);
|
|
@@ -61,7 +64,7 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
61
64
|
vimeoPlayer.play();
|
|
62
65
|
setIsCoverVisible(false);
|
|
63
66
|
};
|
|
64
|
-
return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-video-wrapper-${item.id}`, ref: setRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), onMouseEnter: () => {
|
|
67
|
+
return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-video-wrapper-${item.id} ${wrapperClassNames}`, ref: setRef, style: Object.assign(Object.assign(Object.assign({}, (opacity !== undefined ? { opacity } : {})), (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), onMouseEnter: () => {
|
|
65
68
|
if (!vimeoPlayer || play !== 'on-hover')
|
|
66
69
|
return;
|
|
67
70
|
vimeoPlayer.play();
|
|
@@ -78,37 +81,35 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
78
81
|
width: '100%',
|
|
79
82
|
top: '0',
|
|
80
83
|
left: '0'
|
|
81
|
-
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className:
|
|
84
|
+
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className: `embed-video ${videoClassNames}`, src: validUrl || '', allow: "autoplay; fullscreen; picture-in-picture;", allowFullScreen: true, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
|
|
82
85
|
.embed-video-wrapper-${item.id} {
|
|
83
86
|
position: absolute;
|
|
84
87
|
width: 100%;
|
|
85
88
|
height: 100%;
|
|
86
89
|
}
|
|
87
|
-
.
|
|
90
|
+
.embed-video {
|
|
88
91
|
width: 100%;
|
|
89
92
|
height: 100%;
|
|
90
93
|
z-index: 1;
|
|
91
94
|
border: none;
|
|
92
95
|
overflow: hidden;
|
|
93
96
|
}
|
|
94
|
-
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams,
|
|
97
|
+
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
|
|
98
|
+
const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
99
|
+
const videoStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-video', ['radius'], stateParams);
|
|
95
100
|
return (`
|
|
96
101
|
.embed-video-wrapper-${item.id} {
|
|
97
102
|
opacity: ${layoutParams.opacity};
|
|
98
103
|
transform: rotate(${area.angle}deg);
|
|
99
104
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
100
|
-
transition:
|
|
105
|
+
transition: all 0.2s ease;
|
|
101
106
|
}
|
|
102
|
-
.embed-video-wrapper-${item.id}
|
|
103
|
-
${(0, HoverStyles_1.getHoverStyles)(['angle', 'blur', 'opacity'], hoverParams)}
|
|
104
|
-
}
|
|
105
|
-
.embed-video-wrapper-${item.id} .embedVideo {
|
|
107
|
+
.embed-video-wrapper-${item.id} .embed-video {
|
|
106
108
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
107
|
-
transition:
|
|
108
|
-
}
|
|
109
|
-
.embed-video-wrapper-${item.id}:hover .embedVideo {
|
|
110
|
-
${(0, HoverStyles_1.getHoverStyles)(['radius'], hoverParams)};
|
|
109
|
+
transition: all 0.2s ease;
|
|
111
110
|
}
|
|
111
|
+
${wrapperStatesCSS}
|
|
112
|
+
${videoStatesCSS}
|
|
112
113
|
`);
|
|
113
114
|
})}
|
|
114
115
|
` })] }));
|
|
@@ -12,10 +12,11 @@ const getValidYoutubeUrl_1 = require("../../utils/getValidYoutubeUrl");
|
|
|
12
12
|
const useEmbedVideoItem_1 = require("./useEmbedVideoItem");
|
|
13
13
|
const useItemAngle_1 = require("../useItemAngle");
|
|
14
14
|
const sdk_1 = require("@cntrl-site/sdk");
|
|
15
|
-
const HoverStyles_1 = require("../../utils/HoverStyles/HoverStyles");
|
|
16
15
|
const useCntrlContext_1 = require("../../provider/useCntrlContext");
|
|
17
16
|
const useYouTubeIframeApi_1 = require("../../utils/Youtube/useYouTubeIframeApi");
|
|
18
17
|
const useRegisterResize_1 = require("../../common/useRegisterResize");
|
|
18
|
+
const useStatesClassNames_1 = require("../useStatesClassNames");
|
|
19
|
+
const getStatesCSS_1 = require("../../utils/getStatesCSS");
|
|
19
20
|
const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
20
21
|
var _a, _b, _c;
|
|
21
22
|
const id = (0, react_1.useId)();
|
|
@@ -28,7 +29,9 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
28
29
|
const [player, setPlayer] = (0, react_1.useState)(undefined);
|
|
29
30
|
const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
|
|
30
31
|
const [imgRef, setImgRef] = (0, react_1.useState)(null);
|
|
31
|
-
const layoutValues = [item.area, item.layoutParams, item.state
|
|
32
|
+
const layoutValues = [item.area, item.layoutParams, item.state];
|
|
33
|
+
const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-youtube-video-wrapper');
|
|
34
|
+
const embedClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed');
|
|
32
35
|
(0, useRegisterResize_1.useRegisterResize)(div, onResize);
|
|
33
36
|
(0, react_1.useEffect)(() => {
|
|
34
37
|
const newUrl = new URL(url);
|
|
@@ -80,7 +83,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
80
83
|
setIsCoverVisible(true);
|
|
81
84
|
}
|
|
82
85
|
}, []);
|
|
83
|
-
return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-youtube-video-wrapper-${item.id}`, onMouseEnter: () => {
|
|
86
|
+
return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_a = item.link) === null || _a === void 0 ? void 0 : _a.url, target: (_b = item.link) === null || _b === void 0 ? void 0 : _b.target, children: [(0, jsx_runtime_1.jsxs)("div", { className: `embed-youtube-video-wrapper-${item.id} ${wrapperClassNames}`, onMouseEnter: () => {
|
|
84
87
|
if (!player || play !== 'on-hover')
|
|
85
88
|
return;
|
|
86
89
|
player.playVideo();
|
|
@@ -98,7 +101,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
98
101
|
top: '0',
|
|
99
102
|
left: '0',
|
|
100
103
|
zIndex: 1
|
|
101
|
-
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id}`, ref: setDiv, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
|
|
104
|
+
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id} ${embedClassNames}`, ref: setDiv, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
|
|
102
105
|
.embed-youtube-video-wrapper-${item.id},
|
|
103
106
|
.embed-${item.id} {
|
|
104
107
|
position: absolute;
|
|
@@ -114,24 +117,22 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
114
117
|
z-index: 1;
|
|
115
118
|
border: none;
|
|
116
119
|
}
|
|
117
|
-
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams,
|
|
120
|
+
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, stateParams]) => {
|
|
121
|
+
const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-youtube-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
122
|
+
const embedStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed', ['radius'], stateParams);
|
|
118
123
|
return (`
|
|
119
124
|
.embed-youtube-video-wrapper-${item.id} {
|
|
120
125
|
opacity: ${layoutParams.opacity};
|
|
121
126
|
transform: rotate(${area.angle}deg);
|
|
122
127
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
123
|
-
transition:
|
|
128
|
+
transition: all 0.2s ease;
|
|
124
129
|
}
|
|
125
130
|
.embed-youtube-video-wrapper-${item.id} .embed-${item.id} {
|
|
126
131
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
127
|
-
transition:
|
|
128
|
-
}
|
|
129
|
-
.embed-youtube-video-wrapper-${item.id}:hover {
|
|
130
|
-
${(0, HoverStyles_1.getHoverStyles)(['angle', 'blur', 'opacity'], hoverParams)};
|
|
131
|
-
}
|
|
132
|
-
.embed-youtube-video-wrapper-${item.id}:hover .embed-${item.id} {
|
|
133
|
-
${(0, HoverStyles_1.getHoverStyles)(['radius'], hoverParams)};
|
|
132
|
+
transition: all 0.2s ease;
|
|
134
133
|
}
|
|
134
|
+
${wrapperStatesCSS}
|
|
135
|
+
${embedStatesCSS}
|
|
135
136
|
`);
|
|
136
137
|
})}
|
|
137
138
|
` })] }));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useStatesClassNames = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const InteractionsContext_1 = require("../provider/InteractionsContext");
|
|
6
|
+
const useCurrentLayout_1 = require("../common/useCurrentLayout");
|
|
7
|
+
function useStatesClassNames(itemId, itemStates, uniquePrefix) {
|
|
8
|
+
const { interactionsStatesMap } = (0, react_1.useContext)(InteractionsContext_1.InteractionsContext);
|
|
9
|
+
const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
|
|
10
|
+
const activeStates = Object.values(interactionsStatesMap);
|
|
11
|
+
const statesForLayout = layoutId ? itemStates[layoutId] : {};
|
|
12
|
+
const stateClassNames = Object.keys(statesForLayout !== null && statesForLayout !== void 0 ? statesForLayout : {})
|
|
13
|
+
.filter((stateId) => activeStates.includes(stateId))
|
|
14
|
+
.map((stateId) => `${uniquePrefix}-${itemId}-state-${stateId}`)
|
|
15
|
+
.join(' ');
|
|
16
|
+
return stateClassNames;
|
|
17
|
+
}
|
|
18
|
+
exports.useStatesClassNames = useStatesClassNames;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InteractionsProvider = exports.InteractionsContext = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const defaultState = {
|
|
7
|
+
interactionsStatesMap: {},
|
|
8
|
+
interactions: [],
|
|
9
|
+
transitionTo: () => { },
|
|
10
|
+
getItemTrigger: () => null
|
|
11
|
+
};
|
|
12
|
+
exports.InteractionsContext = (0, react_1.createContext)(defaultState);
|
|
13
|
+
const InteractionsProvider = ({ interactions, children }) => {
|
|
14
|
+
const defaultStatesMap = interactions.reduce((map, { id, startStateId }) => {
|
|
15
|
+
map[id] = startStateId;
|
|
16
|
+
return map;
|
|
17
|
+
}, {});
|
|
18
|
+
const [interactionsStatesMap, setInteractionsStatesMap] = (0, react_1.useState)(defaultStatesMap);
|
|
19
|
+
const transitionTo = (interactionId, stateId) => {
|
|
20
|
+
setInteractionsStatesMap((map) => (Object.assign(Object.assign({}, map), { [interactionId]: stateId })));
|
|
21
|
+
};
|
|
22
|
+
const getItemTrigger = (itemId, triggerType) => {
|
|
23
|
+
for (const interaction of interactions) {
|
|
24
|
+
const activeStateId = interactionsStatesMap[interaction.id];
|
|
25
|
+
const matchingTrigger = interaction.triggers.find((trigger) => trigger.itemId === itemId &&
|
|
26
|
+
trigger.from === activeStateId &&
|
|
27
|
+
trigger.type === triggerType);
|
|
28
|
+
if (matchingTrigger) {
|
|
29
|
+
return {
|
|
30
|
+
id: interaction.id,
|
|
31
|
+
from: matchingTrigger.from,
|
|
32
|
+
to: matchingTrigger.to,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
38
|
+
return ((0, jsx_runtime_1.jsx)(exports.InteractionsContext.Provider, { value: {
|
|
39
|
+
transitionTo,
|
|
40
|
+
interactionsStatesMap,
|
|
41
|
+
interactions,
|
|
42
|
+
getItemTrigger
|
|
43
|
+
}, children: children }));
|
|
44
|
+
};
|
|
45
|
+
exports.InteractionsProvider = InteractionsProvider;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getStateStyles = exports.getTransitions = void 0;
|
|
4
4
|
const color_1 = require("@cntrl-site/color");
|
|
5
5
|
const getItemTopStyle_1 = require("../getItemTopStyle");
|
|
6
|
-
const
|
|
6
|
+
const stateTransformationMap = {
|
|
7
7
|
'width': (width) => `width: ${width * 100}vw !important;`,
|
|
8
8
|
'height': (height) => `height: ${height * 100}vw !important;`,
|
|
9
9
|
'top': (top, anchorSide) => `top: ${(0, getItemTopStyle_1.getItemTopStyle)(top, anchorSide)} !important;`,
|
|
@@ -39,15 +39,16 @@ const CSSPropertyNameMap = {
|
|
|
39
39
|
'wordSpacing': 'word-spacing',
|
|
40
40
|
'color': 'color'
|
|
41
41
|
};
|
|
42
|
-
function getTransitions(values,
|
|
43
|
-
if (!
|
|
42
|
+
function getTransitions(values, state) {
|
|
43
|
+
if (!state)
|
|
44
44
|
return 'unset';
|
|
45
45
|
const transitionValues = values.reduce((acc, valueName) => {
|
|
46
|
-
if (valueName in
|
|
47
|
-
|
|
46
|
+
if (valueName in state && state[valueName] !== undefined) {
|
|
47
|
+
// @ts-ignore TODO
|
|
48
|
+
const stateProperties = state[valueName];
|
|
48
49
|
return [
|
|
49
50
|
...acc,
|
|
50
|
-
`${CSSPropertyNameMap[valueName]} ${
|
|
51
|
+
`${CSSPropertyNameMap[valueName]} ${stateProperties.duration}ms ${stateProperties.timing} ${stateProperties.delay}ms`
|
|
51
52
|
];
|
|
52
53
|
}
|
|
53
54
|
return acc;
|
|
@@ -57,21 +58,22 @@ function getTransitions(values, hover) {
|
|
|
57
58
|
return transitionValues.join(', ');
|
|
58
59
|
}
|
|
59
60
|
exports.getTransitions = getTransitions;
|
|
60
|
-
function
|
|
61
|
-
if (!
|
|
61
|
+
function getStateStyles(values, state, anchorSide) {
|
|
62
|
+
if (!state)
|
|
62
63
|
return '';
|
|
63
|
-
const
|
|
64
|
-
if (valueName in
|
|
65
|
-
|
|
64
|
+
const stateValues = values.reduce((acc, valueName) => {
|
|
65
|
+
if (valueName in state && state[valueName] !== undefined) {
|
|
66
|
+
// @ts-ignore TODO
|
|
67
|
+
const stateProperties = state[valueName];
|
|
66
68
|
return [
|
|
67
69
|
...acc,
|
|
68
|
-
|
|
70
|
+
stateTransformationMap[valueName](stateProperties.value, anchorSide)
|
|
69
71
|
];
|
|
70
72
|
}
|
|
71
73
|
return acc;
|
|
72
74
|
}, []);
|
|
73
|
-
if (!
|
|
75
|
+
if (!stateValues.length)
|
|
74
76
|
return '';
|
|
75
|
-
return
|
|
77
|
+
return stateValues.join('\n');
|
|
76
78
|
}
|
|
77
|
-
exports.
|
|
79
|
+
exports.getStateStyles = getStateStyles;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStatesCSS = void 0;
|
|
4
|
+
const StateStyles_1 = require("./StateStyles/StateStyles");
|
|
5
|
+
function getStatesCSS(itemId, classNamePrefix, keys, states, anchorSide) {
|
|
6
|
+
return states
|
|
7
|
+
? Object.entries(states).map(([stateId, params]) => {
|
|
8
|
+
return `
|
|
9
|
+
.${classNamePrefix}-${itemId}-state-${stateId} {
|
|
10
|
+
${(0, StateStyles_1.getStateStyles)(keys, params, anchorSide)};
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
}).join('\n')
|
|
14
|
+
: '';
|
|
15
|
+
}
|
|
16
|
+
exports.getStatesCSS = getStatesCSS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19-alpha.0",
|
|
4
4
|
"description": "SDK for Next.js",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@cntrl-site/color": "^1.0.0",
|
|
25
25
|
"@cntrl-site/effects": "^1.2.0",
|
|
26
|
-
"@cntrl-site/sdk": "^1.12.
|
|
26
|
+
"@cntrl-site/sdk": "^1.12.4-alpha.7",
|
|
27
27
|
"@types/vimeo__player": "^2.18.0",
|
|
28
28
|
"@vimeo/player": "^2.20.1",
|
|
29
29
|
"html-react-parser": "^3.0.1",
|
|
@@ -38,12 +38,15 @@
|
|
|
38
38
|
"react-dom": "^18.3.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@testing-library/jest-dom": "^6.4.8",
|
|
42
|
+
"@testing-library/react": "^16.0.0",
|
|
41
43
|
"@tsconfig/node16": "^1.0.3",
|
|
42
44
|
"@tsconfig/recommended": "^1.0.1",
|
|
43
|
-
"@types/jest": "^29.
|
|
45
|
+
"@types/jest": "^29.5.12",
|
|
44
46
|
"@types/node": "^18.11.7",
|
|
45
47
|
"@types/react": "^18.0.15",
|
|
46
|
-
"jest": "^29.
|
|
48
|
+
"jest": "^29.7.0",
|
|
49
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
47
50
|
"ts-jest": "^29.0.3",
|
|
48
51
|
"typescript": "^5.2.2"
|
|
49
52
|
},
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { FC, useEffect, useId, useRef, useState } from 'react';
|
|
2
2
|
import JSXStyle from 'styled-jsx/style';
|
|
3
|
-
import { Article as TArticle } from '@cntrl-site/sdk';
|
|
3
|
+
import { Article as TArticle, ArticleItemType, ItemState, ItemStateParams } from '@cntrl-site/sdk';
|
|
4
4
|
import { Section } from './Section';
|
|
5
5
|
import { Item } from './Item';
|
|
6
6
|
import { useArticleRectObserver } from '../utils/ArticleRectManager/useArticleRectObserver';
|
|
7
7
|
import { ArticleRectContext } from '../provider/ArticleRectContext';
|
|
8
8
|
import { ArticleWrapper } from './ArticleWrapper';
|
|
9
|
+
import { InteractionsProvider } from '../provider/InteractionsContext';
|
|
9
10
|
|
|
10
11
|
interface Props {
|
|
11
12
|
article: TArticle;
|
|
@@ -27,35 +28,37 @@ export const Article: FC<Props> = ({ article, sectionData }) => {
|
|
|
27
28
|
|
|
28
29
|
return (
|
|
29
30
|
<ArticleRectContext.Provider value={articleRectObserver}>
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
31
|
+
<InteractionsProvider interactions={article.interactions}>
|
|
32
|
+
<ArticleWrapper>
|
|
33
|
+
<div className="article" ref={articleRef}>
|
|
34
|
+
{article.sections.map((section, i) => {
|
|
35
|
+
const data = section.name ? sectionData[section.name] : {};
|
|
36
|
+
return (
|
|
37
|
+
<Section
|
|
38
|
+
section={section}
|
|
39
|
+
key={section.id}
|
|
40
|
+
data={data}
|
|
41
|
+
>
|
|
42
|
+
{article.sections[i].items.map(item => (
|
|
43
|
+
<Item
|
|
44
|
+
item={item}
|
|
45
|
+
key={item.id}
|
|
46
|
+
sectionId={section.id}
|
|
47
|
+
articleHeight={articleHeight}
|
|
48
|
+
/>
|
|
49
|
+
))}
|
|
50
|
+
</Section>
|
|
51
|
+
);
|
|
52
|
+
})}
|
|
53
|
+
</div>
|
|
54
|
+
</ArticleWrapper>
|
|
55
|
+
<JSXStyle id={id}>{`
|
|
54
56
|
.article {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
position: relative;
|
|
58
|
+
overflow: clip;
|
|
59
|
+
}
|
|
58
60
|
`}</JSXStyle>
|
|
61
|
+
</InteractionsProvider>
|
|
59
62
|
</ArticleRectContext.Provider>
|
|
60
63
|
);
|
|
61
64
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import React, { CSSProperties, FC, PropsWithChildren
|
|
2
|
-
import JSXStyle from 'styled-jsx/style';
|
|
1
|
+
import React, { CSSProperties, FC, PropsWithChildren } from 'react';
|
|
3
2
|
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
4
3
|
import { LayoutContext } from '../provider/LayoutContext';
|
|
5
4
|
|
package/src/components/Item.tsx
CHANGED
|
@@ -21,7 +21,6 @@ import { useItemDimensions } from './useItemDimensions';
|
|
|
21
21
|
import { useItemScale } from './useItemScale';
|
|
22
22
|
import { ScaleAnchorMap } from '../utils/ScaleAnchorMap';
|
|
23
23
|
import { useSectionHeightData } from './useSectionHeightMap';
|
|
24
|
-
import { getHoverStyles, getTransitions } from '../utils/HoverStyles/HoverStyles';
|
|
25
24
|
import { getItemTopStyle } from '../utils/getItemTopStyle';
|
|
26
25
|
import { useStickyItemTop } from './items/useStickyItemTop';
|
|
27
26
|
import { getAnchoredItemTop } from '../utils/getAnchoredItemTop';
|
|
@@ -32,6 +31,9 @@ import { GroupItem } from './items/GroupItem';
|
|
|
32
31
|
import { CodeEmbedItem } from './items/CodeEmbedItem';
|
|
33
32
|
import { AreaAnchor } from '@cntrl-site/sdk/src/types/article/ItemArea';
|
|
34
33
|
import { KeyframesContext } from '../provider/KeyframesContext';
|
|
34
|
+
import { InteractionsContext } from '../provider/InteractionsContext';
|
|
35
|
+
import { getStatesCSS } from '../utils/getStatesCSS';
|
|
36
|
+
import { useStatesClassNames } from './useStatesClassNames';
|
|
35
37
|
|
|
36
38
|
export interface ItemProps<I extends ItemAny> {
|
|
37
39
|
item: I;
|
|
@@ -93,14 +95,14 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
93
95
|
const sectionHeight = useSectionHeightData(sectionId);
|
|
94
96
|
const stickyTop = useStickyItemTop(item, sectionHeight, sectionId);
|
|
95
97
|
const dimensions = useItemDimensions(item, sectionId);
|
|
96
|
-
const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state
|
|
98
|
+
const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state];
|
|
97
99
|
const isInitialRef = useRef(true);
|
|
100
|
+
const { transitionTo, getItemTrigger } = useContext(InteractionsContext);
|
|
98
101
|
layoutValues.push(item.sticky);
|
|
99
102
|
layoutValues.push(sectionHeight);
|
|
100
103
|
if (item.layoutParams) {
|
|
101
104
|
layoutValues.push(item.layoutParams);
|
|
102
105
|
}
|
|
103
|
-
|
|
104
106
|
const sizing = layout && isItemType(item, ArticleItemType.RichText)
|
|
105
107
|
? item.layoutParams[layout].sizing
|
|
106
108
|
: undefined;
|
|
@@ -130,9 +132,11 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
130
132
|
}, []);
|
|
131
133
|
|
|
132
134
|
const isRichText = isItemType(item, ArticleItemType.RichText);
|
|
135
|
+
const innerStatesClassNames = useStatesClassNames(item.id, item.state, 'item-inner');
|
|
136
|
+
const wrapperStatesClassNames = useStatesClassNames(item.id, item.state, 'item-wrapper');
|
|
133
137
|
return (
|
|
134
138
|
<div
|
|
135
|
-
className={`item-wrapper-${item.id}`}
|
|
139
|
+
className={`item-wrapper-${item.id} ${wrapperStatesClassNames}`}
|
|
136
140
|
ref={itemWrapperRef}
|
|
137
141
|
style={{
|
|
138
142
|
...(position ? { top: position.top } : {}),
|
|
@@ -152,7 +156,22 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
152
156
|
>
|
|
153
157
|
<RichTextWrapper isRichText={isRichText}>
|
|
154
158
|
<div
|
|
155
|
-
className={`item-${item.id}-inner`}
|
|
159
|
+
className={`item-${item.id}-inner ${innerStatesClassNames}`}
|
|
160
|
+
onClick={() => {
|
|
161
|
+
const trigger = getItemTrigger(item.id, 'click');
|
|
162
|
+
if (!trigger) return;
|
|
163
|
+
transitionTo(trigger.id, trigger.to);
|
|
164
|
+
}}
|
|
165
|
+
onMouseEnter={() => {
|
|
166
|
+
const trigger = getItemTrigger(item.id, 'hover-in');
|
|
167
|
+
if (!trigger) return;
|
|
168
|
+
transitionTo(trigger.id, trigger.to);
|
|
169
|
+
}}
|
|
170
|
+
onMouseLeave={() => {
|
|
171
|
+
const trigger = getItemTrigger(item.id, 'hover-out');
|
|
172
|
+
if (!trigger) return;
|
|
173
|
+
transitionTo(trigger.id, trigger.to);
|
|
174
|
+
}}
|
|
156
175
|
style={{
|
|
157
176
|
...(dimensions ? {
|
|
158
177
|
width: `${sizingAxis.x === 'manual'
|
|
@@ -169,10 +188,12 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
169
188
|
</RichTextWrapper>
|
|
170
189
|
</div>
|
|
171
190
|
<JSXStyle id={id}>{`
|
|
172
|
-
${getLayoutStyles(layouts, layoutValues, ([area, hidden,
|
|
191
|
+
${getLayoutStyles(layouts, layoutValues, ([area, hidden, states, sticky, sectionHeight, layoutParams], exemplary) => {
|
|
173
192
|
const sizingAxis = parseSizing(layoutParams.sizing);
|
|
174
193
|
const isScreenBasedBottom = area.positionType === PositionType.ScreenBased && area.anchorSide === AnchorSide.Bottom;
|
|
175
194
|
const scaleAnchor = area.scaleAnchor as AreaAnchor;
|
|
195
|
+
const statesInnerCSS = getStatesCSS(item.id, 'item-inner', ['width', 'height', 'scale'], states);
|
|
196
|
+
const statesWrapperCSS = getStatesCSS(item.id, 'item-wrapper', ['top', 'left'], states, area.anchorSide);
|
|
176
197
|
return (`
|
|
177
198
|
.item-${item.id} {
|
|
178
199
|
position: ${sticky ? 'sticky' : 'absolute'};
|
|
@@ -183,7 +204,6 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
183
204
|
height: fit-content;
|
|
184
205
|
}
|
|
185
206
|
.item-${item.id}-inner {
|
|
186
|
-
transition: ${getTransitions(['width', 'height', 'scale'], hoverParams)};
|
|
187
207
|
pointer-events: auto;
|
|
188
208
|
width: ${sizingAxis.x === 'manual'
|
|
189
209
|
? `${area.width * 100}vw`
|
|
@@ -191,6 +211,7 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
191
211
|
height: ${sizingAxis.y === 'manual' ? `${area.height * 100}vw` : 'unset'};
|
|
192
212
|
transform-origin: ${ScaleAnchorMap[scaleAnchor]};
|
|
193
213
|
transform: scale(${area.scale});
|
|
214
|
+
transition: all 0.2s ease;
|
|
194
215
|
}
|
|
195
216
|
.item-wrapper-${item.id} {
|
|
196
217
|
position: ${area.positionType === PositionType.ScreenBased ? 'fixed': 'absolute'};
|
|
@@ -200,14 +221,10 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
200
221
|
bottom: ${isScreenBasedBottom ? `${-area.top * 100}vw` : 'unset'};
|
|
201
222
|
top: ${isScreenBasedBottom ? 'unset' : getItemTopStyle(area.top, area.anchorSide)};
|
|
202
223
|
left: ${area.left * 100}vw;
|
|
203
|
-
transition:
|
|
204
|
-
}
|
|
205
|
-
.item-${item.id}-inner:hover {
|
|
206
|
-
${getHoverStyles(['width', 'height', 'scale'], hoverParams)};
|
|
207
|
-
}
|
|
208
|
-
.item-wrapper-${item.id}:hover {
|
|
209
|
-
${getHoverStyles(['left', 'top'], hoverParams, area.anchorSide)};
|
|
224
|
+
transition: all 0.2s ease;
|
|
210
225
|
}
|
|
226
|
+
${statesWrapperCSS}
|
|
227
|
+
${statesInnerCSS}
|
|
211
228
|
`);
|
|
212
229
|
})}
|
|
213
230
|
`}</JSXStyle>
|
|
@@ -223,11 +240,11 @@ function parseSizing(sizing: string = 'manual'): Axis {
|
|
|
223
240
|
} as Axis;
|
|
224
241
|
}
|
|
225
242
|
|
|
243
|
+
export function isItemType<T extends ArticleItemType>(item: ItemAny, itemType: T): item is TItem<T> {
|
|
244
|
+
return item.type === itemType;
|
|
245
|
+
}
|
|
246
|
+
|
|
226
247
|
interface Axis {
|
|
227
248
|
x: 'manual' | 'auto';
|
|
228
249
|
y: 'manual' | 'auto';
|
|
229
250
|
}
|
|
230
|
-
|
|
231
|
-
export function isItemType<T extends ArticleItemType>(item: ItemAny, itemType: T): item is TItem<T> {
|
|
232
|
-
return item.type === itemType;
|
|
233
|
-
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getLayoutStyles, CodeEmbedItem as TCodeEmbedItem, AreaAnchor } from '@cntrl-site/sdk';
|
|
2
2
|
import { FC, useEffect, useId, useState } from 'react';
|
|
3
3
|
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
|
-
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
6
5
|
import JSXStyle from 'styled-jsx/style';
|
|
7
6
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
8
7
|
import { useItemAngle } from '../useItemAngle';
|
|
9
8
|
import { LinkWrapper } from '../LinkWrapper';
|
|
10
9
|
import { useCodeEmbedItem } from './useCodeEmbedItem';
|
|
10
|
+
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
11
|
+
import { useStatesClassNames } from '../useStatesClassNames';
|
|
11
12
|
|
|
12
13
|
const stylesMap = {
|
|
13
14
|
[AreaAnchor.TopLeft]: {},
|
|
@@ -30,7 +31,8 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
30
31
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
31
32
|
useRegisterResize(ref, onResize);
|
|
32
33
|
const pos = stylesMap[anchor];
|
|
33
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state
|
|
34
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
|
|
35
|
+
const statesClassNames = useStatesClassNames(item.id, item.state, 'embed-wrapper');
|
|
34
36
|
|
|
35
37
|
useEffect(() => {
|
|
36
38
|
if (!ref) return;
|
|
@@ -56,7 +58,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
56
58
|
return (
|
|
57
59
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
58
60
|
<div
|
|
59
|
-
className={`embed-wrapper-${item.id}`}
|
|
61
|
+
className={`embed-wrapper-${item.id} ${statesClassNames}`}
|
|
60
62
|
style={{
|
|
61
63
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
62
64
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
@@ -93,21 +95,20 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
93
95
|
z-index: 1;
|
|
94
96
|
border: none;
|
|
95
97
|
}
|
|
96
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams,
|
|
98
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, stateParams], exemplary) => {
|
|
99
|
+
const statesCSS = getStatesCSS(item.id, 'embed-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
97
100
|
return (`
|
|
98
101
|
.embed-wrapper-${item.id} {
|
|
99
102
|
opacity: ${layoutParams.opacity};
|
|
100
103
|
transform: rotate(${area.angle}deg);
|
|
101
104
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
102
|
-
transition:
|
|
105
|
+
transition: all 0.2s ease;
|
|
103
106
|
}
|
|
104
107
|
.embed-${item.id} {
|
|
105
108
|
width: ${item.commonParams.scale ? `${area.width * exemplary}px` : '100%'};
|
|
106
109
|
height: ${item.commonParams.scale ? `${area.height * exemplary}px` : '100%'};
|
|
107
110
|
}
|
|
108
|
-
|
|
109
|
-
${getHoverStyles<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)}
|
|
110
|
-
}
|
|
111
|
+
${statesCSS}
|
|
111
112
|
`);
|
|
112
113
|
})}
|
|
113
114
|
`}</JSXStyle>
|