@cntrl-site/sdk-nextjs 1.0.19-alpha.2 → 1.0.19
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/jest.config.js +2 -2
- package/lib/components/Article.js +8 -9
- package/lib/components/Head.js +1 -1
- package/lib/components/Item.js +13 -33
- package/lib/components/items/CodeEmbedItem.js +8 -11
- package/lib/components/items/CustomItem.js +7 -10
- package/lib/components/items/GroupItem.js +8 -11
- package/lib/components/items/ImageItem.js +15 -21
- package/lib/components/items/RectangleItem.js +8 -11
- package/lib/components/items/RichTextItem.js +7 -10
- package/lib/components/items/VideoItem.js +12 -16
- package/lib/components/items/VimeoEmbed.js +15 -19
- package/lib/components/items/YoutubeEmbed.js +13 -18
- package/lib/utils/{StateStyles/StateStyles.js → HoverStyles/HoverStyles.js} +24 -36
- package/package.json +4 -7
- package/src/components/Article.tsx +28 -31
- package/src/components/ArticleWrapper.tsx +2 -1
- package/src/components/Head.tsx +1 -0
- package/src/components/Item.tsx +18 -38
- package/src/components/items/CodeEmbedItem.tsx +9 -12
- package/src/components/items/CustomItem.tsx +9 -12
- package/src/components/items/GroupItem.tsx +9 -12
- package/src/components/items/ImageItem.tsx +19 -26
- package/src/components/items/RectangleItem.tsx +10 -13
- package/src/components/items/RichTextItem.tsx +9 -17
- package/src/components/items/VideoItem.tsx +15 -19
- package/src/components/items/VimeoEmbed.tsx +16 -20
- package/src/components/items/YoutubeEmbed.tsx +16 -20
- package/src/utils/{StateStyles/StateStyles.ts → HoverStyles/HoverStyles.ts} +27 -41
- package/lib/components/useStatesClassNames.js +0 -18
- package/lib/components/useStatesTransitions.js +0 -89
- package/lib/provider/InteractionsContext.js +0 -45
- package/lib/utils/getStatesCSS.js +0 -16
- package/src/components/useStatesClassNames.ts +0 -23
- package/src/components/useStatesTransitions.ts +0 -95
- package/src/provider/InteractionsContext.test.tsx +0 -97
- package/src/provider/InteractionsContext.tsx +0 -65
- package/src/utils/getStatesCSS.ts +0 -24
|
@@ -13,10 +13,8 @@ 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");
|
|
16
17
|
const useRegisterResize_1 = require("../../common/useRegisterResize");
|
|
17
|
-
const useStatesClassNames_1 = require("../useStatesClassNames");
|
|
18
|
-
const getStatesCSS_1 = require("../../utils/getStatesCSS");
|
|
19
|
-
const useStatesTransitions_1 = require("../useStatesTransitions");
|
|
20
18
|
const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
21
19
|
var _a, _b, _c;
|
|
22
20
|
const id = (0, react_1.useId)();
|
|
@@ -29,12 +27,8 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
29
27
|
const [ref, setRef] = (0, react_1.useState)(null);
|
|
30
28
|
const [imgRef, setImgRef] = (0, react_1.useState)(null);
|
|
31
29
|
const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
|
|
32
|
-
const layoutValues = [item.area, item.layoutParams, item.state];
|
|
33
|
-
const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video-wrapper');
|
|
34
|
-
const videoClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-video');
|
|
30
|
+
const layoutValues = [item.area, item.layoutParams, item.state.hover];
|
|
35
31
|
(0, useRegisterResize_1.useRegisterResize)(ref, onResize);
|
|
36
|
-
(0, useStatesTransitions_1.useStatesTransitions)(ref, item.state, ['angle', 'blur', 'opacity']);
|
|
37
|
-
(0, useStatesTransitions_1.useStatesTransitions)(iframeRef, item.state, ['radius']);
|
|
38
32
|
const getValidVimeoUrl = (url) => {
|
|
39
33
|
const validURL = new URL(url);
|
|
40
34
|
validURL.searchParams.append('controls', String(controls));
|
|
@@ -67,7 +61,7 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
67
61
|
vimeoPlayer.play();
|
|
68
62
|
setIsCoverVisible(false);
|
|
69
63
|
};
|
|
70
|
-
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}
|
|
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: () => {
|
|
71
65
|
if (!vimeoPlayer || play !== 'on-hover')
|
|
72
66
|
return;
|
|
73
67
|
vimeoPlayer.play();
|
|
@@ -84,35 +78,37 @@ const VimeoEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
84
78
|
width: '100%',
|
|
85
79
|
top: '0',
|
|
86
80
|
left: '0'
|
|
87
|
-
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className:
|
|
81
|
+
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("iframe", { ref: setIframeRef, className: "embedVideo", 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: `
|
|
88
82
|
.embed-video-wrapper-${item.id} {
|
|
89
83
|
position: absolute;
|
|
90
84
|
width: 100%;
|
|
91
85
|
height: 100%;
|
|
92
86
|
}
|
|
93
|
-
.
|
|
87
|
+
.embedVideo {
|
|
94
88
|
width: 100%;
|
|
95
89
|
height: 100%;
|
|
96
90
|
z-index: 1;
|
|
97
91
|
border: none;
|
|
98
92
|
overflow: hidden;
|
|
99
93
|
}
|
|
100
|
-
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams,
|
|
101
|
-
const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
102
|
-
const videoStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-video', ['radius'], stateParams);
|
|
94
|
+
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
|
|
103
95
|
return (`
|
|
104
96
|
.embed-video-wrapper-${item.id} {
|
|
105
97
|
opacity: ${layoutParams.opacity};
|
|
106
98
|
transform: rotate(${area.angle}deg);
|
|
107
99
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
108
|
-
transition:
|
|
100
|
+
transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'blur', 'opacity'], hoverParams)};
|
|
109
101
|
}
|
|
110
|
-
.embed-video-wrapper-${item.id}
|
|
102
|
+
.embed-video-wrapper-${item.id}:hover {
|
|
103
|
+
${(0, HoverStyles_1.getHoverStyles)(['angle', 'blur', 'opacity'], hoverParams)}
|
|
104
|
+
}
|
|
105
|
+
.embed-video-wrapper-${item.id} .embedVideo {
|
|
111
106
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
112
|
-
transition:
|
|
107
|
+
transition: ${(0, HoverStyles_1.getTransitions)(['radius'], hoverParams)};
|
|
108
|
+
}
|
|
109
|
+
.embed-video-wrapper-${item.id}:hover .embedVideo {
|
|
110
|
+
${(0, HoverStyles_1.getHoverStyles)(['radius'], hoverParams)};
|
|
113
111
|
}
|
|
114
|
-
${wrapperStatesCSS}
|
|
115
|
-
${videoStatesCSS}
|
|
116
112
|
`);
|
|
117
113
|
})}
|
|
118
114
|
` })] }));
|
|
@@ -12,12 +12,10 @@ 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");
|
|
15
16
|
const useCntrlContext_1 = require("../../provider/useCntrlContext");
|
|
16
17
|
const useYouTubeIframeApi_1 = require("../../utils/Youtube/useYouTubeIframeApi");
|
|
17
18
|
const useRegisterResize_1 = require("../../common/useRegisterResize");
|
|
18
|
-
const useStatesClassNames_1 = require("../useStatesClassNames");
|
|
19
|
-
const getStatesCSS_1 = require("../../utils/getStatesCSS");
|
|
20
|
-
const useStatesTransitions_1 = require("../useStatesTransitions");
|
|
21
19
|
const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
22
20
|
var _a, _b, _c;
|
|
23
21
|
const id = (0, react_1.useId)();
|
|
@@ -27,16 +25,11 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
27
25
|
const angle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
|
|
28
26
|
const YT = (0, useYouTubeIframeApi_1.useYouTubeIframeApi)();
|
|
29
27
|
const [div, setDiv] = (0, react_1.useState)(null);
|
|
30
|
-
const [wrapperRef, setWrapperRef] = (0, react_1.useState)(null);
|
|
31
28
|
const [player, setPlayer] = (0, react_1.useState)(undefined);
|
|
32
29
|
const [isCoverVisible, setIsCoverVisible] = (0, react_1.useState)(false);
|
|
33
30
|
const [imgRef, setImgRef] = (0, react_1.useState)(null);
|
|
34
|
-
const layoutValues = [item.area, item.layoutParams, item.state];
|
|
35
|
-
const wrapperClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed-youtube-video-wrapper');
|
|
36
|
-
const embedClassNames = (0, useStatesClassNames_1.useStatesClassNames)(item.id, item.state, 'embed');
|
|
31
|
+
const layoutValues = [item.area, item.layoutParams, item.state.hover];
|
|
37
32
|
(0, useRegisterResize_1.useRegisterResize)(div, onResize);
|
|
38
|
-
(0, useStatesTransitions_1.useStatesTransitions)(wrapperRef, item.state, ['angle', 'blur', 'opacity']);
|
|
39
|
-
(0, useStatesTransitions_1.useStatesTransitions)(div, item.state, ['radius']);
|
|
40
33
|
(0, react_1.useEffect)(() => {
|
|
41
34
|
const newUrl = new URL(url);
|
|
42
35
|
const videoId = (0, getValidYoutubeUrl_1.getYoutubeId)(newUrl);
|
|
@@ -87,7 +80,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
87
80
|
setIsCoverVisible(true);
|
|
88
81
|
}
|
|
89
82
|
}, []);
|
|
90
|
-
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", {
|
|
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: () => {
|
|
91
84
|
if (!player || play !== 'on-hover')
|
|
92
85
|
return;
|
|
93
86
|
player.playVideo();
|
|
@@ -105,7 +98,7 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
105
98
|
top: '0',
|
|
106
99
|
left: '0',
|
|
107
100
|
zIndex: 1
|
|
108
|
-
}, alt: "Cover img" })), (0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id}
|
|
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: `
|
|
109
102
|
.embed-youtube-video-wrapper-${item.id},
|
|
110
103
|
.embed-${item.id} {
|
|
111
104
|
position: absolute;
|
|
@@ -121,22 +114,24 @@ const YoutubeEmbedItem = ({ item, sectionId, onResize }) => {
|
|
|
121
114
|
z-index: 1;
|
|
122
115
|
border: none;
|
|
123
116
|
}
|
|
124
|
-
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams,
|
|
125
|
-
const wrapperStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed-youtube-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
126
|
-
const embedStatesCSS = (0, getStatesCSS_1.getStatesCSS)(item.id, 'embed', ['radius'], stateParams);
|
|
117
|
+
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
|
|
127
118
|
return (`
|
|
128
119
|
.embed-youtube-video-wrapper-${item.id} {
|
|
129
120
|
opacity: ${layoutParams.opacity};
|
|
130
121
|
transform: rotate(${area.angle}deg);
|
|
131
122
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
132
|
-
transition:
|
|
123
|
+
transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'blur', 'opacity'], hoverParams)};
|
|
133
124
|
}
|
|
134
125
|
.embed-youtube-video-wrapper-${item.id} .embed-${item.id} {
|
|
135
126
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
136
|
-
transition:
|
|
127
|
+
transition: ${(0, HoverStyles_1.getTransitions)(['radius'], hoverParams)};
|
|
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)};
|
|
137
134
|
}
|
|
138
|
-
${wrapperStatesCSS}
|
|
139
|
-
${embedStatesCSS}
|
|
140
135
|
`);
|
|
141
136
|
})}
|
|
142
137
|
` })] }));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getHoverStyles = exports.getTransitions = void 0;
|
|
4
4
|
const color_1 = require("@cntrl-site/color");
|
|
5
5
|
const getItemTopStyle_1 = require("../getItemTopStyle");
|
|
6
|
-
const
|
|
6
|
+
const hoverTransformationMap = {
|
|
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,51 +39,39 @@ const CSSPropertyNameMap = {
|
|
|
39
39
|
'wordSpacing': 'word-spacing',
|
|
40
40
|
'color': 'color'
|
|
41
41
|
};
|
|
42
|
-
function
|
|
43
|
-
if (!
|
|
44
|
-
return '';
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// @ts-ignore
|
|
49
|
-
const stateProperties = state[valueName];
|
|
50
|
-
const getter = stateTransformationMap[valueName];
|
|
42
|
+
function getTransitions(values, hover) {
|
|
43
|
+
if (!hover)
|
|
44
|
+
return 'unset';
|
|
45
|
+
const transitionValues = values.reduce((acc, valueName) => {
|
|
46
|
+
if (valueName in hover && hover[valueName] !== undefined) {
|
|
47
|
+
const hoverProperties = hover[valueName];
|
|
51
48
|
return [
|
|
52
49
|
...acc,
|
|
53
|
-
|
|
50
|
+
`${CSSPropertyNameMap[valueName]} ${hoverProperties.duration}ms ${hoverProperties.timing} ${hoverProperties.delay}ms`
|
|
54
51
|
];
|
|
55
52
|
}
|
|
56
53
|
return acc;
|
|
57
54
|
}, []);
|
|
58
|
-
if (!
|
|
59
|
-
return '';
|
|
60
|
-
// @ts-ignore
|
|
61
|
-
const transitionStr = getTransitions(values, state);
|
|
62
|
-
stateValues.push(`transition: ${transitionStr};`);
|
|
63
|
-
return stateValues.join('\n');
|
|
64
|
-
}
|
|
65
|
-
exports.getStateStyles = getStateStyles;
|
|
66
|
-
function getTransitions(values, state) {
|
|
67
|
-
if (!state)
|
|
55
|
+
if (!transitionValues.length)
|
|
68
56
|
return 'unset';
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
return transitionValues.join(', ');
|
|
58
|
+
}
|
|
59
|
+
exports.getTransitions = getTransitions;
|
|
60
|
+
function getHoverStyles(values, hover, anchorSide) {
|
|
61
|
+
if (!hover)
|
|
62
|
+
return '';
|
|
63
|
+
const hoverValues = values.reduce((acc, valueName) => {
|
|
64
|
+
if (valueName in hover && hover[valueName] !== undefined) {
|
|
65
|
+
const hoverProperties = hover[valueName];
|
|
74
66
|
return [
|
|
75
67
|
...acc,
|
|
76
|
-
|
|
77
|
-
`${CSSPropertyNameMap[valueName]} ${stateProperties.duration}ms ${stateProperties.timing} ${stateProperties.delay}ms`
|
|
68
|
+
hoverTransformationMap[valueName](hoverProperties.value, anchorSide)
|
|
78
69
|
];
|
|
79
70
|
}
|
|
80
71
|
return acc;
|
|
81
72
|
}, []);
|
|
82
|
-
if (!
|
|
83
|
-
return '
|
|
84
|
-
return
|
|
73
|
+
if (!hoverValues.length)
|
|
74
|
+
return '';
|
|
75
|
+
return hoverValues.join('\n');
|
|
85
76
|
}
|
|
86
|
-
exports.
|
|
87
|
-
// export function getStateTransitions<T extends ArticleItemType>(
|
|
88
|
-
//
|
|
89
|
-
// )
|
|
77
|
+
exports.getHoverStyles = getHoverStyles;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.0.19
|
|
3
|
+
"version": "1.0.19",
|
|
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.3",
|
|
27
27
|
"@types/vimeo__player": "^2.18.0",
|
|
28
28
|
"@vimeo/player": "^2.20.1",
|
|
29
29
|
"html-react-parser": "^3.0.1",
|
|
@@ -38,15 +38,12 @@
|
|
|
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",
|
|
43
41
|
"@tsconfig/node16": "^1.0.3",
|
|
44
42
|
"@tsconfig/recommended": "^1.0.1",
|
|
45
|
-
"@types/jest": "^29.5
|
|
43
|
+
"@types/jest": "^29.2.5",
|
|
46
44
|
"@types/node": "^18.11.7",
|
|
47
45
|
"@types/react": "^18.0.15",
|
|
48
|
-
"jest": "^29.
|
|
49
|
-
"jest-environment-jsdom": "^29.7.0",
|
|
46
|
+
"jest": "^29.3.1",
|
|
50
47
|
"ts-jest": "^29.0.3",
|
|
51
48
|
"typescript": "^5.2.2"
|
|
52
49
|
},
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { FC, useEffect, useId, useRef, useState } from 'react';
|
|
2
2
|
import JSXStyle from 'styled-jsx/style';
|
|
3
|
-
import { Article as TArticle
|
|
3
|
+
import { Article as TArticle } 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';
|
|
10
9
|
|
|
11
10
|
interface Props {
|
|
12
11
|
article: TArticle;
|
|
@@ -28,37 +27,35 @@ export const Article: FC<Props> = ({ article, sectionData }) => {
|
|
|
28
27
|
|
|
29
28
|
return (
|
|
30
29
|
<ArticleRectContext.Provider value={articleRectObserver}>
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<JSXStyle id={id}>{`
|
|
30
|
+
<ArticleWrapper>
|
|
31
|
+
<div className="article" ref={articleRef}>
|
|
32
|
+
{article.sections.map((section, i) => {
|
|
33
|
+
const data = section.name ? sectionData[section.name] : {};
|
|
34
|
+
return (
|
|
35
|
+
<Section
|
|
36
|
+
section={section}
|
|
37
|
+
key={section.id}
|
|
38
|
+
data={data}
|
|
39
|
+
>
|
|
40
|
+
{article.sections[i].items.map(item => (
|
|
41
|
+
<Item
|
|
42
|
+
item={item}
|
|
43
|
+
key={item.id}
|
|
44
|
+
sectionId={section.id}
|
|
45
|
+
articleHeight={articleHeight}
|
|
46
|
+
/>
|
|
47
|
+
))}
|
|
48
|
+
</Section>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
51
|
+
</div>
|
|
52
|
+
</ArticleWrapper>
|
|
53
|
+
<JSXStyle id={id}>{`
|
|
56
54
|
.article {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
position: relative;
|
|
56
|
+
overflow: clip;
|
|
57
|
+
}
|
|
60
58
|
`}</JSXStyle>
|
|
61
|
-
</InteractionsProvider>
|
|
62
59
|
</ArticleRectContext.Provider>
|
|
63
60
|
);
|
|
64
61
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React, { CSSProperties, FC, PropsWithChildren } from 'react';
|
|
1
|
+
import React, { CSSProperties, FC, PropsWithChildren, useEffect, useId, useState } from 'react';
|
|
2
|
+
import JSXStyle from 'styled-jsx/style';
|
|
2
3
|
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
3
4
|
import { LayoutContext } from '../provider/LayoutContext';
|
|
4
5
|
|
package/src/components/Head.tsx
CHANGED
|
@@ -35,6 +35,7 @@ export const CNTRLHead: FC<Props> = ({ meta, project }) => {
|
|
|
35
35
|
<meta name="twitter:description" content={meta.description} />
|
|
36
36
|
<meta name="keywords" content={meta.keywords} />
|
|
37
37
|
<meta name="og:image" content={meta.opengraphThumbnail} />
|
|
38
|
+
<meta name="twitter:image" content={meta.opengraphThumbnail} />
|
|
38
39
|
<meta name="generator" content="https://cntrl.site" />
|
|
39
40
|
<link rel="icon" href={meta.favicon} />
|
|
40
41
|
{customFonts.length > 0 && (
|
package/src/components/Item.tsx
CHANGED
|
@@ -21,6 +21,7 @@ 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';
|
|
24
25
|
import { getItemTopStyle } from '../utils/getItemTopStyle';
|
|
25
26
|
import { useStickyItemTop } from './items/useStickyItemTop';
|
|
26
27
|
import { getAnchoredItemTop } from '../utils/getAnchoredItemTop';
|
|
@@ -31,10 +32,6 @@ import { GroupItem } from './items/GroupItem';
|
|
|
31
32
|
import { CodeEmbedItem } from './items/CodeEmbedItem';
|
|
32
33
|
import { AreaAnchor } from '@cntrl-site/sdk/src/types/article/ItemArea';
|
|
33
34
|
import { KeyframesContext } from '../provider/KeyframesContext';
|
|
34
|
-
import { InteractionsContext } from '../provider/InteractionsContext';
|
|
35
|
-
import { getStatesCSS } from '../utils/getStatesCSS';
|
|
36
|
-
import { useStatesClassNames } from './useStatesClassNames';
|
|
37
|
-
import { useStatesTransitions } from './useStatesTransitions';
|
|
38
35
|
|
|
39
36
|
export interface ItemProps<I extends ItemAny> {
|
|
40
37
|
item: I;
|
|
@@ -82,7 +79,6 @@ const noop = () => null;
|
|
|
82
79
|
|
|
83
80
|
export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isInGroup = false }) => {
|
|
84
81
|
const itemWrapperRef = useRef<HTMLDivElement | null>(null);
|
|
85
|
-
const itemInnerRef = useRef<HTMLDivElement | null>(null);
|
|
86
82
|
const rectObserver = useContext(ArticleRectContext);
|
|
87
83
|
const keyframesRepo = useContext(KeyframesContext);
|
|
88
84
|
const id = useId();
|
|
@@ -97,14 +93,14 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
97
93
|
const sectionHeight = useSectionHeightData(sectionId);
|
|
98
94
|
const stickyTop = useStickyItemTop(item, sectionHeight, sectionId);
|
|
99
95
|
const dimensions = useItemDimensions(item, sectionId);
|
|
100
|
-
const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state];
|
|
96
|
+
const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state.hover];
|
|
101
97
|
const isInitialRef = useRef(true);
|
|
102
|
-
const { transitionTo, getItemTrigger } = useContext(InteractionsContext);
|
|
103
98
|
layoutValues.push(item.sticky);
|
|
104
99
|
layoutValues.push(sectionHeight);
|
|
105
100
|
if (item.layoutParams) {
|
|
106
101
|
layoutValues.push(item.layoutParams);
|
|
107
102
|
}
|
|
103
|
+
|
|
108
104
|
const sizing = layout && isItemType(item, ArticleItemType.RichText)
|
|
109
105
|
? item.layoutParams[layout].sizing
|
|
110
106
|
: undefined;
|
|
@@ -134,13 +130,9 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
134
130
|
}, []);
|
|
135
131
|
|
|
136
132
|
const isRichText = isItemType(item, ArticleItemType.RichText);
|
|
137
|
-
const innerStatesClassNames = useStatesClassNames(item.id, item.state, 'item-inner');
|
|
138
|
-
const wrapperStatesClassNames = useStatesClassNames(item.id, item.state, 'item-wrapper');
|
|
139
|
-
useStatesTransitions(itemWrapperRef.current, item.state, ['top', 'left']);
|
|
140
|
-
useStatesTransitions(itemInnerRef.current, item.state, ['width', 'height', 'scale']);
|
|
141
133
|
return (
|
|
142
134
|
<div
|
|
143
|
-
className={`item-wrapper-${item.id}
|
|
135
|
+
className={`item-wrapper-${item.id}`}
|
|
144
136
|
ref={itemWrapperRef}
|
|
145
137
|
style={{
|
|
146
138
|
...(position ? { top: position.top } : {}),
|
|
@@ -160,23 +152,7 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
160
152
|
>
|
|
161
153
|
<RichTextWrapper isRichText={isRichText}>
|
|
162
154
|
<div
|
|
163
|
-
className={`item-${item.id}-inner
|
|
164
|
-
ref={itemInnerRef}
|
|
165
|
-
onClick={() => {
|
|
166
|
-
const trigger = getItemTrigger(item.id, 'click');
|
|
167
|
-
if (!trigger) return;
|
|
168
|
-
transitionTo(trigger.id, trigger.to);
|
|
169
|
-
}}
|
|
170
|
-
onMouseEnter={() => {
|
|
171
|
-
const trigger = getItemTrigger(item.id, 'hover-in');
|
|
172
|
-
if (!trigger) return;
|
|
173
|
-
transitionTo(trigger.id, trigger.to);
|
|
174
|
-
}}
|
|
175
|
-
onMouseLeave={() => {
|
|
176
|
-
const trigger = getItemTrigger(item.id, 'hover-out');
|
|
177
|
-
if (!trigger) return;
|
|
178
|
-
transitionTo(trigger.id, trigger.to);
|
|
179
|
-
}}
|
|
155
|
+
className={`item-${item.id}-inner`}
|
|
180
156
|
style={{
|
|
181
157
|
...(dimensions ? {
|
|
182
158
|
width: `${sizingAxis.x === 'manual'
|
|
@@ -193,12 +169,10 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
193
169
|
</RichTextWrapper>
|
|
194
170
|
</div>
|
|
195
171
|
<JSXStyle id={id}>{`
|
|
196
|
-
${getLayoutStyles(layouts, layoutValues, ([area, hidden,
|
|
172
|
+
${getLayoutStyles(layouts, layoutValues, ([area, hidden, hoverParams, sticky, sectionHeight, layoutParams], exemplary) => {
|
|
197
173
|
const sizingAxis = parseSizing(layoutParams.sizing);
|
|
198
174
|
const isScreenBasedBottom = area.positionType === PositionType.ScreenBased && area.anchorSide === AnchorSide.Bottom;
|
|
199
175
|
const scaleAnchor = area.scaleAnchor as AreaAnchor;
|
|
200
|
-
const statesInnerCSS = getStatesCSS(item.id, 'item-inner', ['width', 'height', 'scale'], states);
|
|
201
|
-
const statesWrapperCSS = getStatesCSS(item.id, 'item-wrapper', ['top', 'left'], states, area.anchorSide);
|
|
202
176
|
return (`
|
|
203
177
|
.item-${item.id} {
|
|
204
178
|
position: ${sticky ? 'sticky' : 'absolute'};
|
|
@@ -209,6 +183,7 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
209
183
|
height: fit-content;
|
|
210
184
|
}
|
|
211
185
|
.item-${item.id}-inner {
|
|
186
|
+
transition: ${getTransitions(['width', 'height', 'scale'], hoverParams)};
|
|
212
187
|
pointer-events: auto;
|
|
213
188
|
width: ${sizingAxis.x === 'manual'
|
|
214
189
|
? `${area.width * 100}vw`
|
|
@@ -225,9 +200,14 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
225
200
|
bottom: ${isScreenBasedBottom ? `${-area.top * 100}vw` : 'unset'};
|
|
226
201
|
top: ${isScreenBasedBottom ? 'unset' : getItemTopStyle(area.top, area.anchorSide)};
|
|
227
202
|
left: ${area.left * 100}vw;
|
|
203
|
+
transition: ${getTransitions(['left', 'top'], hoverParams)};
|
|
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)};
|
|
228
210
|
}
|
|
229
|
-
${statesWrapperCSS}
|
|
230
|
-
${statesInnerCSS}
|
|
231
211
|
`);
|
|
232
212
|
})}
|
|
233
213
|
`}</JSXStyle>
|
|
@@ -243,11 +223,11 @@ function parseSizing(sizing: string = 'manual'): Axis {
|
|
|
243
223
|
} as Axis;
|
|
244
224
|
}
|
|
245
225
|
|
|
246
|
-
export function isItemType<T extends ArticleItemType>(item: ItemAny, itemType: T): item is TItem<T> {
|
|
247
|
-
return item.type === itemType;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
226
|
interface Axis {
|
|
251
227
|
x: 'manual' | 'auto';
|
|
252
228
|
y: 'manual' | 'auto';
|
|
253
229
|
}
|
|
230
|
+
|
|
231
|
+
export function isItemType<T extends ArticleItemType>(item: ItemAny, itemType: T): item is TItem<T> {
|
|
232
|
+
return item.type === itemType;
|
|
233
|
+
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { getLayoutStyles, CodeEmbedItem as TCodeEmbedItem, AreaAnchor } from '@cntrl-site/sdk';
|
|
1
|
+
import { ArticleItemType, 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';
|
|
5
6
|
import JSXStyle from 'styled-jsx/style';
|
|
6
7
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
7
8
|
import { useItemAngle } from '../useItemAngle';
|
|
8
9
|
import { LinkWrapper } from '../LinkWrapper';
|
|
9
10
|
import { useCodeEmbedItem } from './useCodeEmbedItem';
|
|
10
|
-
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
11
|
-
import { useStatesClassNames } from '../useStatesClassNames';
|
|
12
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
13
11
|
|
|
14
12
|
const stylesMap = {
|
|
15
13
|
[AreaAnchor.TopLeft]: {},
|
|
@@ -32,9 +30,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
32
30
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
33
31
|
useRegisterResize(ref, onResize);
|
|
34
32
|
const pos = stylesMap[anchor];
|
|
35
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state];
|
|
36
|
-
const statesClassNames = useStatesClassNames(item.id, item.state, 'embed-wrapper');
|
|
37
|
-
useStatesTransitions(ref!, item.state, ['angle', 'blur', 'opacity']);
|
|
33
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state.hover];
|
|
38
34
|
|
|
39
35
|
useEffect(() => {
|
|
40
36
|
if (!ref) return;
|
|
@@ -60,7 +56,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
60
56
|
return (
|
|
61
57
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
62
58
|
<div
|
|
63
|
-
className={`embed-wrapper-${item.id}
|
|
59
|
+
className={`embed-wrapper-${item.id}`}
|
|
64
60
|
style={{
|
|
65
61
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
66
62
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
@@ -97,20 +93,21 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
97
93
|
z-index: 1;
|
|
98
94
|
border: none;
|
|
99
95
|
}
|
|
100
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams,
|
|
101
|
-
const statesCSS = getStatesCSS(item.id, 'embed-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
96
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hoverParams], exemplary) => {
|
|
102
97
|
return (`
|
|
103
98
|
.embed-wrapper-${item.id} {
|
|
104
99
|
opacity: ${layoutParams.opacity};
|
|
105
100
|
transform: rotate(${area.angle}deg);
|
|
106
101
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
107
|
-
transition:
|
|
102
|
+
transition: ${getTransitions<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)};
|
|
108
103
|
}
|
|
109
104
|
.embed-${item.id} {
|
|
110
105
|
width: ${item.commonParams.scale ? `${area.width * exemplary}px` : '100%'};
|
|
111
106
|
height: ${item.commonParams.scale ? `${area.height * exemplary}px` : '100%'};
|
|
112
107
|
}
|
|
113
|
-
|
|
108
|
+
.embed-wrapper-${item.id}:hover {
|
|
109
|
+
${getHoverStyles<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)}
|
|
110
|
+
}
|
|
114
111
|
`);
|
|
115
112
|
})}
|
|
116
113
|
`}</JSXStyle>
|
|
@@ -1,29 +1,25 @@
|
|
|
1
|
-
import { getLayoutStyles, CustomItem as TCustomItem } from '@cntrl-site/sdk';
|
|
1
|
+
import { ArticleItemType, getLayoutStyles, CustomItem as TCustomItem } from '@cntrl-site/sdk';
|
|
2
2
|
import { FC, useState } from 'react';
|
|
3
3
|
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
|
+
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
5
6
|
import JSXStyle from 'styled-jsx/style';
|
|
6
7
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
7
8
|
import { useItemAngle } from '../useItemAngle';
|
|
8
|
-
import { useStatesClassNames } from '../useStatesClassNames';
|
|
9
|
-
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
10
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
11
9
|
|
|
12
10
|
export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId }) => {
|
|
13
11
|
const sdk = useCntrlContext();
|
|
14
12
|
const { layouts } = useCntrlContext();
|
|
15
13
|
const angle = useItemAngle(item, sectionId);
|
|
16
14
|
const component = sdk.customItems.get(item.commonParams.name);
|
|
17
|
-
const layoutValues: Record<string, any>[] = [item.area, item.state];
|
|
15
|
+
const layoutValues: Record<string, any>[] = [item.area, item.state.hover];
|
|
18
16
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
19
17
|
useRegisterResize(ref, onResize);
|
|
20
|
-
const statesClassNames = useStatesClassNames(item.id, item.state, 'custom-component');
|
|
21
|
-
useStatesTransitions(ref, item.state, ['angle'])
|
|
22
18
|
if (!component) return null;
|
|
23
19
|
return (
|
|
24
20
|
<>
|
|
25
21
|
<div
|
|
26
|
-
className={`custom-component-${item.id}
|
|
22
|
+
className={`custom-component-${item.id}`}
|
|
27
23
|
ref={setRef}
|
|
28
24
|
style={{
|
|
29
25
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
@@ -32,19 +28,20 @@ export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, section
|
|
|
32
28
|
{component({})}
|
|
33
29
|
</div>
|
|
34
30
|
<JSXStyle id={item.id}>
|
|
35
|
-
{`${getLayoutStyles(layouts, layoutValues, ([area,
|
|
36
|
-
const statesCSS = getStatesCSS(item.id, 'custom-component', ['angle'], stateParams);
|
|
31
|
+
{`${getLayoutStyles(layouts, layoutValues, ([area, hoverParams]) => {
|
|
37
32
|
return (`
|
|
38
33
|
.custom-component-${item.id} {
|
|
39
34
|
transform: rotate(${area.angle}deg);
|
|
40
|
-
transition:
|
|
35
|
+
transition: ${getTransitions<ArticleItemType.Custom>(['angle'], hoverParams)};
|
|
41
36
|
height: 100%;
|
|
42
37
|
width: 100%;
|
|
43
38
|
position: absolute;
|
|
44
39
|
left: 0;
|
|
45
40
|
top: 0;
|
|
46
41
|
}
|
|
47
|
-
|
|
42
|
+
.custom-component-${item.id}:hover {
|
|
43
|
+
${getHoverStyles<ArticleItemType.Custom>(['angle'], hoverParams)};
|
|
44
|
+
}
|
|
48
45
|
`);
|
|
49
46
|
})}`}
|
|
50
47
|
</JSXStyle>
|