@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.
Files changed (38) hide show
  1. package/jest.config.js +2 -2
  2. package/lib/components/Article.js +8 -9
  3. package/lib/components/Head.js +1 -1
  4. package/lib/components/Item.js +13 -33
  5. package/lib/components/items/CodeEmbedItem.js +8 -11
  6. package/lib/components/items/CustomItem.js +7 -10
  7. package/lib/components/items/GroupItem.js +8 -11
  8. package/lib/components/items/ImageItem.js +15 -21
  9. package/lib/components/items/RectangleItem.js +8 -11
  10. package/lib/components/items/RichTextItem.js +7 -10
  11. package/lib/components/items/VideoItem.js +12 -16
  12. package/lib/components/items/VimeoEmbed.js +15 -19
  13. package/lib/components/items/YoutubeEmbed.js +13 -18
  14. package/lib/utils/{StateStyles/StateStyles.js → HoverStyles/HoverStyles.js} +24 -36
  15. package/package.json +4 -7
  16. package/src/components/Article.tsx +28 -31
  17. package/src/components/ArticleWrapper.tsx +2 -1
  18. package/src/components/Head.tsx +1 -0
  19. package/src/components/Item.tsx +18 -38
  20. package/src/components/items/CodeEmbedItem.tsx +9 -12
  21. package/src/components/items/CustomItem.tsx +9 -12
  22. package/src/components/items/GroupItem.tsx +9 -12
  23. package/src/components/items/ImageItem.tsx +19 -26
  24. package/src/components/items/RectangleItem.tsx +10 -13
  25. package/src/components/items/RichTextItem.tsx +9 -17
  26. package/src/components/items/VideoItem.tsx +15 -19
  27. package/src/components/items/VimeoEmbed.tsx +16 -20
  28. package/src/components/items/YoutubeEmbed.tsx +16 -20
  29. package/src/utils/{StateStyles/StateStyles.ts → HoverStyles/HoverStyles.ts} +27 -41
  30. package/lib/components/useStatesClassNames.js +0 -18
  31. package/lib/components/useStatesTransitions.js +0 -89
  32. package/lib/provider/InteractionsContext.js +0 -45
  33. package/lib/utils/getStatesCSS.js +0 -16
  34. package/src/components/useStatesClassNames.ts +0 -23
  35. package/src/components/useStatesTransitions.ts +0 -95
  36. package/src/provider/InteractionsContext.test.tsx +0 -97
  37. package/src/provider/InteractionsContext.tsx +0 -65
  38. 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} ${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: () => {
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: `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: `
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
- .embed-video {
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, stateParams]) => {
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: all 0.2s ease;
100
+ transition: ${(0, HoverStyles_1.getTransitions)(['angle', 'blur', 'opacity'], hoverParams)};
109
101
  }
110
- .embed-video-wrapper-${item.id} .embed-video {
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: all 0.2s ease;
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", { ref: setWrapperRef, className: `embed-youtube-video-wrapper-${item.id} ${wrapperClassNames}`, onMouseEnter: () => {
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} ${embedClassNames}`, ref: setDiv, style: Object.assign({}, (radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})) })] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
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, stateParams]) => {
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: all 0.2s ease;
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: all 0.2s ease;
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.getTransitions = exports.getStateStyles = void 0;
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 stateTransformationMap = {
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 getStateStyles(values, state, anchorSide) {
43
- if (!state)
44
- return '';
45
- const stateValues = values.reduce((acc, valueName) => {
46
- // @ts-ignore
47
- if (valueName in state && state[valueName] !== undefined) {
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
- getter(stateProperties.value, anchorSide)
50
+ `${CSSPropertyNameMap[valueName]} ${hoverProperties.duration}ms ${hoverProperties.timing} ${hoverProperties.delay}ms`
54
51
  ];
55
52
  }
56
53
  return acc;
57
54
  }, []);
58
- if (!stateValues.length)
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
- const transitionValues = values.reduce((acc, valueName) => {
70
- // @ts-ignore TODO
71
- if (valueName in state && state[valueName] !== undefined) {
72
- // @ts-ignore TODO
73
- const stateProperties = state[valueName];
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
- // @ts-ignore
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 (!transitionValues.length)
83
- return 'unset';
84
- return transitionValues.join(', ');
73
+ if (!hoverValues.length)
74
+ return '';
75
+ return hoverValues.join('\n');
85
76
  }
86
- exports.getTransitions = getTransitions;
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-alpha.2",
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.4-alpha.8",
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.12",
43
+ "@types/jest": "^29.2.5",
46
44
  "@types/node": "^18.11.7",
47
45
  "@types/react": "^18.0.15",
48
- "jest": "^29.7.0",
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, ArticleItemType, ItemState, ItemStateParams } from '@cntrl-site/sdk';
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
- <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}>{`
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
- position: relative;
58
- overflow: clip;
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
 
@@ -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 && (
@@ -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} ${wrapperStatesClassNames}`}
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 ${innerStatesClassNames}`}
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, states, sticky, sectionHeight, layoutParams], exemplary) => {
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} ${statesClassNames}`}
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, stateParams], exemplary) => {
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: all 0.2s ease;
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
- ${statesCSS}
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} ${statesClassNames}`}
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, stateParams]) => {
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: all 0.2s ease;
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
- ${statesCSS}
42
+ .custom-component-${item.id}:hover {
43
+ ${getHoverStyles<ArticleItemType.Custom>(['angle'], hoverParams)};
44
+ }
48
45
  `);
49
46
  })}`}
50
47
  </JSXStyle>