@cntrl-site/sdk-nextjs 1.0.20 → 1.1.1
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 +9 -8
- package/lib/components/Item.js +39 -21
- package/lib/components/items/CodeEmbedItem.js +15 -12
- package/lib/components/items/CustomItem.js +7 -9
- package/lib/components/items/GroupItem.js +15 -12
- package/lib/components/items/ImageItem.js +28 -21
- package/lib/components/items/RectangleItem.js +28 -15
- package/lib/components/items/RichTextItem.js +33 -12
- package/lib/components/items/VideoItem.js +26 -19
- package/lib/components/items/VimeoEmbed.js +22 -20
- package/lib/components/items/YoutubeEmbed.js +20 -18
- package/lib/components/useItemPosition.js +10 -5
- package/lib/interactions/CSSPropertyNameMap.js +38 -0
- package/lib/interactions/InteractionsRegistry.js +220 -0
- package/lib/interactions/ItemInteractionCtrl.js +61 -0
- package/lib/interactions/getTransition.js +21 -0
- package/lib/interactions/types.js +2 -0
- package/lib/interactions/useItemInteractionCtrl.js +17 -0
- package/lib/provider/InteractionsContext.js +23 -0
- package/lib/utils/getStyleFromItemStateAndParams.js +9 -0
- package/package.json +7 -4
- package/src/components/Article.tsx +30 -27
- package/src/components/ArticleWrapper.tsx +1 -2
- package/src/components/Item.tsx +75 -29
- package/src/components/items/CodeEmbedItem.tsx +15 -11
- package/src/components/items/CustomItem.tsx +9 -11
- package/src/components/items/GroupItem.tsx +17 -13
- package/src/components/items/ImageItem.tsx +35 -20
- package/src/components/items/RectangleItem.tsx +35 -14
- package/src/components/items/RichTextItem.tsx +41 -14
- package/src/components/items/VideoItem.tsx +34 -20
- package/src/components/items/VimeoEmbed.tsx +27 -19
- package/src/components/items/YoutubeEmbed.tsx +20 -18
- package/src/components/useItemPosition.ts +12 -5
- package/src/interactions/CSSPropertyNameMap.ts +38 -0
- package/src/interactions/InteractionsRegistry.ts +244 -0
- package/src/interactions/ItemInteractionCtrl.ts +62 -0
- package/src/interactions/getTransition.ts +27 -0
- package/src/interactions/types.ts +32 -0
- package/src/interactions/useItemInteractionCtrl.ts +18 -0
- package/src/provider/InteractionsContext.old.tsx +65 -0
- package/src/provider/InteractionsContext.test.tsx +97 -0
- package/src/provider/InteractionsContext.tsx +28 -0
- package/src/utils/getStyleFromItemStateAndParams.ts +8 -0
- package/lib/utils/HoverStyles/HoverStyles.js +0 -77
- package/src/utils/HoverStyles/HoverStyles.ts +0 -85
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTransition = void 0;
|
|
4
|
+
const CSSPropertyNameMap_1 = require("./CSSPropertyNameMap");
|
|
5
|
+
function getTransition(state, keys) {
|
|
6
|
+
if (Object.keys(state).length === 0)
|
|
7
|
+
return 'none';
|
|
8
|
+
const transitions = [];
|
|
9
|
+
for (const [key, params] of Object.entries(state)) {
|
|
10
|
+
if (!keys.includes(key) || !params.transition)
|
|
11
|
+
continue;
|
|
12
|
+
const { transition: { duration, delay, timing } } = params;
|
|
13
|
+
const cssKey = CSSPropertyNameMap_1.CSSPropertyNameMap[key];
|
|
14
|
+
const nonZeroDuration = Math.max(duration, 0.01);
|
|
15
|
+
transitions.push(`${cssKey} ${nonZeroDuration}ms ${timing} ${delay}ms`);
|
|
16
|
+
}
|
|
17
|
+
return transitions.length > 0
|
|
18
|
+
? transitions.join(', ')
|
|
19
|
+
: 'none';
|
|
20
|
+
}
|
|
21
|
+
exports.getTransition = getTransition;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useItemInteractionCtrl = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ItemInteractionCtrl_1 = require("./ItemInteractionCtrl");
|
|
6
|
+
const InteractionsContext_1 = require("../provider/InteractionsContext");
|
|
7
|
+
function useItemInteractionCtrl(itemId) {
|
|
8
|
+
const [_, triggerRender] = (0, react_1.useState)(0);
|
|
9
|
+
const registry = (0, InteractionsContext_1.useInteractionsRegistry)();
|
|
10
|
+
const ctrl = (0, react_1.useMemo)(() => {
|
|
11
|
+
if (!registry)
|
|
12
|
+
return;
|
|
13
|
+
return new ItemInteractionCtrl_1.ItemInteractionController(itemId, registry, () => triggerRender(prev => prev + 1));
|
|
14
|
+
}, [itemId, registry]);
|
|
15
|
+
return ctrl;
|
|
16
|
+
}
|
|
17
|
+
exports.useItemInteractionCtrl = useItemInteractionCtrl;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useInteractionsRegistry = exports.InteractionsProvider = exports.InteractionsContext = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const InteractionsRegistry_1 = require("../interactions/InteractionsRegistry");
|
|
7
|
+
const useCurrentLayout_1 = require("../common/useCurrentLayout");
|
|
8
|
+
exports.InteractionsContext = (0, react_1.createContext)(undefined);
|
|
9
|
+
const InteractionsProvider = ({ article, children }) => {
|
|
10
|
+
const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
|
|
11
|
+
const registry = (0, react_1.useMemo)(() => {
|
|
12
|
+
if (!layoutId)
|
|
13
|
+
return;
|
|
14
|
+
return new InteractionsRegistry_1.InteractionsRegistry(article, layoutId);
|
|
15
|
+
}, [layoutId]);
|
|
16
|
+
return ((0, jsx_runtime_1.jsx)(exports.InteractionsContext.Provider, { value: registry, children: children }));
|
|
17
|
+
};
|
|
18
|
+
exports.InteractionsProvider = InteractionsProvider;
|
|
19
|
+
function useInteractionsRegistry() {
|
|
20
|
+
const registry = (0, react_1.useContext)(exports.InteractionsContext);
|
|
21
|
+
return registry;
|
|
22
|
+
}
|
|
23
|
+
exports.useInteractionsRegistry = useInteractionsRegistry;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStyleFromItemStateAndParams = void 0;
|
|
4
|
+
function getStyleFromItemStateAndParams(stateValue, paramsValue) {
|
|
5
|
+
return stateValue !== undefined
|
|
6
|
+
? stateValue
|
|
7
|
+
: paramsValue;
|
|
8
|
+
}
|
|
9
|
+
exports.getStyleFromItemStateAndParams = getStyleFromItemStateAndParams;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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.
|
|
26
|
+
"@cntrl-site/sdk": "^1.13.0",
|
|
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
|
},
|
|
@@ -6,6 +6,7 @@ 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 article={article}>
|
|
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
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ComponentType,
|
|
3
|
+
FC,
|
|
4
|
+
PropsWithChildren,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useId,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState
|
|
12
|
+
} from 'react';
|
|
2
13
|
import JSXStyle from 'styled-jsx/style';
|
|
3
14
|
import {
|
|
4
15
|
AnchorSide,
|
|
@@ -21,7 +32,6 @@ import { useItemDimensions } from './useItemDimensions';
|
|
|
21
32
|
import { useItemScale } from './useItemScale';
|
|
22
33
|
import { ScaleAnchorMap } from '../utils/ScaleAnchorMap';
|
|
23
34
|
import { useSectionHeightData } from './useSectionHeightMap';
|
|
24
|
-
import { getHoverStyles, getTransitions } from '../utils/HoverStyles/HoverStyles';
|
|
25
35
|
import { getItemTopStyle } from '../utils/getItemTopStyle';
|
|
26
36
|
import { useStickyItemTop } from './items/useStickyItemTop';
|
|
27
37
|
import { getAnchoredItemTop } from '../utils/getAnchoredItemTop';
|
|
@@ -32,17 +42,23 @@ import { GroupItem } from './items/GroupItem';
|
|
|
32
42
|
import { CodeEmbedItem } from './items/CodeEmbedItem';
|
|
33
43
|
import { AreaAnchor } from '@cntrl-site/sdk/src/types/article/ItemArea';
|
|
34
44
|
import { KeyframesContext } from '../provider/KeyframesContext';
|
|
45
|
+
import { useItemInteractionCtrl } from '../interactions/useItemInteractionCtrl';
|
|
35
46
|
|
|
36
47
|
export interface ItemProps<I extends ItemAny> {
|
|
37
48
|
item: I;
|
|
38
49
|
sectionId: string;
|
|
39
|
-
onResize?: (height: number) => void;
|
|
40
50
|
articleHeight: number;
|
|
51
|
+
onResize?: (height: number) => void;
|
|
52
|
+
interactionCtrl?: ReturnType<typeof useItemInteractionCtrl>;
|
|
53
|
+
onVisibilityChange: (isVisible: boolean) => void;
|
|
41
54
|
}
|
|
42
55
|
|
|
43
|
-
export interface ItemWrapperProps
|
|
56
|
+
export interface ItemWrapperProps {
|
|
57
|
+
item: ItemAny;
|
|
58
|
+
sectionId: string;
|
|
44
59
|
articleHeight: number;
|
|
45
60
|
isInGroup?: boolean;
|
|
61
|
+
isParentVisible?: boolean;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
const itemsMap: Record<ArticleItemType, ComponentType<ItemProps<any>>> = {
|
|
@@ -77,8 +93,9 @@ const RichTextWrapper: FC<PropsWithChildren<RTWrapperProps>> = ({ isRichText, ch
|
|
|
77
93
|
|
|
78
94
|
const noop = () => null;
|
|
79
95
|
|
|
80
|
-
export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isInGroup = false }) => {
|
|
96
|
+
export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isParentVisible = true, isInGroup = false }) => {
|
|
81
97
|
const itemWrapperRef = useRef<HTMLDivElement | null>(null);
|
|
98
|
+
const itemInnerRef = useRef<HTMLDivElement | null>(null);
|
|
82
99
|
const rectObserver = useContext(ArticleRectContext);
|
|
83
100
|
const keyframesRepo = useContext(KeyframesContext);
|
|
84
101
|
const id = useId();
|
|
@@ -86,21 +103,27 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
86
103
|
const { layouts } = useCntrlContext();
|
|
87
104
|
const layout = useLayoutContext();
|
|
88
105
|
const exemplary = useExemplary();
|
|
106
|
+
const [allowPointerEvents, setAllowPointerEvents] = useState<boolean>(isParentVisible);
|
|
89
107
|
const [wrapperHeight, setWrapperHeight] = useState<undefined | number>(undefined);
|
|
90
108
|
const [itemHeight, setItemHeight] = useState<undefined | number>(undefined);
|
|
91
|
-
const
|
|
92
|
-
const
|
|
109
|
+
const itemScale = useItemScale(item, sectionId);
|
|
110
|
+
const interactionCtrl = useItemInteractionCtrl(item.id);
|
|
111
|
+
const wrapperStateProps = interactionCtrl?.getState(['top', 'left']);
|
|
112
|
+
const innerStateProps = interactionCtrl?.getState(['width', 'height', 'scale']);
|
|
113
|
+
const position = useItemPosition(item, sectionId, {
|
|
114
|
+
top: wrapperStateProps?.styles?.top as number,
|
|
115
|
+
left: wrapperStateProps?.styles?.left as number,
|
|
116
|
+
});
|
|
93
117
|
const sectionHeight = useSectionHeightData(sectionId);
|
|
94
118
|
const stickyTop = useStickyItemTop(item, sectionHeight, sectionId);
|
|
95
119
|
const dimensions = useItemDimensions(item, sectionId);
|
|
96
|
-
const layoutValues: Record<string, any>[] = [item.area, item.hidden
|
|
120
|
+
const layoutValues: Record<string, any>[] = [item.area, item.hidden];
|
|
97
121
|
const isInitialRef = useRef(true);
|
|
98
122
|
layoutValues.push(item.sticky);
|
|
99
123
|
layoutValues.push(sectionHeight);
|
|
100
124
|
if (item.layoutParams) {
|
|
101
125
|
layoutValues.push(item.layoutParams);
|
|
102
126
|
}
|
|
103
|
-
|
|
104
127
|
const sizing = layout && isItemType(item, ArticleItemType.RichText)
|
|
105
128
|
? item.layoutParams[layout].sizing
|
|
106
129
|
: undefined;
|
|
@@ -125,20 +148,33 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
125
148
|
setWrapperHeight(wrapperHeight);
|
|
126
149
|
};
|
|
127
150
|
|
|
151
|
+
const handleVisibilityChange = useCallback((isVisible: boolean) => {
|
|
152
|
+
if (!isParentVisible) return;
|
|
153
|
+
setAllowPointerEvents(isVisible);
|
|
154
|
+
}, [isParentVisible]);
|
|
155
|
+
|
|
128
156
|
useEffect(() => {
|
|
129
157
|
isInitialRef.current = false;
|
|
130
158
|
}, []);
|
|
131
159
|
|
|
132
160
|
const isRichText = isItemType(item, ArticleItemType.RichText);
|
|
161
|
+
const width = (innerStateProps?.styles?.width ?? dimensions?.width) as number | undefined;
|
|
162
|
+
const height = (innerStateProps?.styles?.height ?? dimensions?.height) as number | undefined;
|
|
163
|
+
const scale = innerStateProps?.styles?.scale ?? itemScale;
|
|
133
164
|
return (
|
|
134
165
|
<div
|
|
135
166
|
className={`item-wrapper-${item.id}`}
|
|
136
167
|
ref={itemWrapperRef}
|
|
168
|
+
onTransitionEnd={(e) => {
|
|
169
|
+
e.stopPropagation();
|
|
170
|
+
interactionCtrl?.handleTransitionEnd?.(e.propertyName);
|
|
171
|
+
}}
|
|
137
172
|
style={{
|
|
138
173
|
...(position ? { top: position.top } : {}),
|
|
139
174
|
...(position ? { left: position.left } : {}),
|
|
140
175
|
...(position ? { bottom: position.bottom } : {}),
|
|
141
|
-
...(wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {})
|
|
176
|
+
...(wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {}),
|
|
177
|
+
transition: wrapperStateProps?.transition ?? 'none'
|
|
142
178
|
}}
|
|
143
179
|
>
|
|
144
180
|
<div
|
|
@@ -153,23 +189,42 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
153
189
|
<RichTextWrapper isRichText={isRichText}>
|
|
154
190
|
<div
|
|
155
191
|
className={`item-${item.id}-inner`}
|
|
192
|
+
ref={itemInnerRef}
|
|
193
|
+
onClick={() => {
|
|
194
|
+
interactionCtrl?.sendTrigger('click');
|
|
195
|
+
}}
|
|
196
|
+
onMouseEnter={() => {
|
|
197
|
+
interactionCtrl?.sendTrigger('hover-in');
|
|
198
|
+
}}
|
|
199
|
+
onMouseLeave={() => {
|
|
200
|
+
interactionCtrl?.sendTrigger('hover-out');
|
|
201
|
+
}}
|
|
156
202
|
style={{
|
|
157
|
-
...(
|
|
203
|
+
...((width && height) ? {
|
|
158
204
|
width: `${sizingAxis.x === 'manual'
|
|
159
205
|
? isRichText
|
|
160
|
-
? `${
|
|
161
|
-
: `${
|
|
206
|
+
? `${width * exemplary}px`
|
|
207
|
+
: `${width * 100}vw`
|
|
162
208
|
: 'max-content'}`,
|
|
163
|
-
height: `${sizingAxis.y === 'manual' ? `${
|
|
209
|
+
height: `${sizingAxis.y === 'manual' ? `${height * 100}vw` : 'unset'}` } : {}),
|
|
164
210
|
...(scale !== undefined ? { transform: `scale(${scale})`, 'WebkitTransform': `scale(${scale})` } : {}),
|
|
211
|
+
transition: innerStateProps?.transition ?? 'none',
|
|
212
|
+
pointerEvents: allowPointerEvents ? 'auto' : 'none'
|
|
165
213
|
}}
|
|
166
214
|
>
|
|
167
|
-
<ItemComponent
|
|
215
|
+
<ItemComponent
|
|
216
|
+
item={item}
|
|
217
|
+
sectionId={sectionId}
|
|
218
|
+
onResize={handleItemResize}
|
|
219
|
+
articleHeight={articleHeight}
|
|
220
|
+
interactionCtrl={interactionCtrl}
|
|
221
|
+
onVisibilityChange={handleVisibilityChange}
|
|
222
|
+
/>
|
|
168
223
|
</div>
|
|
169
224
|
</RichTextWrapper>
|
|
170
225
|
</div>
|
|
171
226
|
<JSXStyle id={id}>{`
|
|
172
|
-
${getLayoutStyles(layouts, layoutValues, ([area, hidden,
|
|
227
|
+
${getLayoutStyles(layouts, layoutValues, ([area, hidden, sticky, sectionHeight, layoutParams], exemplary) => {
|
|
173
228
|
const sizingAxis = parseSizing(layoutParams.sizing);
|
|
174
229
|
const isScreenBasedBottom = area.positionType === PositionType.ScreenBased && area.anchorSide === AnchorSide.Bottom;
|
|
175
230
|
const scaleAnchor = area.scaleAnchor as AreaAnchor;
|
|
@@ -183,8 +238,6 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
183
238
|
height: fit-content;
|
|
184
239
|
}
|
|
185
240
|
.item-${item.id}-inner {
|
|
186
|
-
transition: ${getTransitions(['width', 'height', 'scale'], hoverParams)};
|
|
187
|
-
pointer-events: auto;
|
|
188
241
|
width: ${sizingAxis.x === 'manual'
|
|
189
242
|
? `${area.width * 100}vw`
|
|
190
243
|
: 'max-content'};
|
|
@@ -200,13 +253,6 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
|
|
|
200
253
|
bottom: ${isScreenBasedBottom ? `${-area.top * 100}vw` : 'unset'};
|
|
201
254
|
top: ${isScreenBasedBottom ? 'unset' : getItemTopStyle(area.top, area.anchorSide)};
|
|
202
255
|
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)};
|
|
210
256
|
}
|
|
211
257
|
`);
|
|
212
258
|
})}
|
|
@@ -223,11 +269,11 @@ function parseSizing(sizing: string = 'manual'): Axis {
|
|
|
223
269
|
} as Axis;
|
|
224
270
|
}
|
|
225
271
|
|
|
272
|
+
export function isItemType<T extends ArticleItemType>(item: ItemAny, itemType: T): item is TItem<T> {
|
|
273
|
+
return item.type === itemType;
|
|
274
|
+
}
|
|
275
|
+
|
|
226
276
|
interface Axis {
|
|
227
277
|
x: 'manual' | 'auto';
|
|
228
278
|
y: 'manual' | 'auto';
|
|
229
279
|
}
|
|
230
|
-
|
|
231
|
-
export function isItemType<T extends ArticleItemType>(item: ItemAny, itemType: T): item is TItem<T> {
|
|
232
|
-
return item.type === itemType;
|
|
233
|
-
}
|
|
@@ -1,8 +1,7 @@
|
|
|
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';
|
|
@@ -21,16 +20,20 @@ const stylesMap = {
|
|
|
21
20
|
[AreaAnchor.BottomRight]: { justifyContent: 'flex-end', alignItems: 'flex-end' }
|
|
22
21
|
};
|
|
23
22
|
|
|
24
|
-
export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId, onResize }) => {
|
|
23
|
+
export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
|
|
25
24
|
const id = useId();
|
|
26
25
|
const { layouts } = useCntrlContext();
|
|
27
|
-
const { anchor, blur, opacity } = useCodeEmbedItem(item, sectionId);
|
|
28
|
-
const
|
|
26
|
+
const { anchor, blur: itemBlur, opacity: itemOpacity } = useCodeEmbedItem(item, sectionId);
|
|
27
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
29
28
|
const { html } = item.commonParams;
|
|
30
29
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
31
30
|
useRegisterResize(ref, onResize);
|
|
32
31
|
const pos = stylesMap[anchor];
|
|
33
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams
|
|
32
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
33
|
+
const stateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
|
|
34
|
+
const blur = (stateParams?.styles?.blur ?? itemBlur) as number;
|
|
35
|
+
const opacity = stateParams?.styles?.opacity ?? itemOpacity;
|
|
36
|
+
const angle = stateParams?.styles?.angle ?? itemAngle;
|
|
34
37
|
|
|
35
38
|
useEffect(() => {
|
|
36
39
|
if (!ref) return;
|
|
@@ -52,6 +55,10 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
52
55
|
if (!iframe) return;
|
|
53
56
|
iframe.srcdoc = item.commonParams.html;
|
|
54
57
|
}, [item.commonParams.html, item.commonParams.iframe, ref]);
|
|
58
|
+
const isInteractive = opacity !== 0;
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
onVisibilityChange?.(isInteractive);
|
|
61
|
+
}, [isInteractive, onVisibilityChange]);
|
|
55
62
|
|
|
56
63
|
return (
|
|
57
64
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
@@ -61,6 +68,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
61
68
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
62
69
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
63
70
|
...(opacity !== undefined ? { opacity } : {}),
|
|
71
|
+
transition: stateParams?.transition ?? 'none'
|
|
64
72
|
}}
|
|
65
73
|
ref={setRef}
|
|
66
74
|
>
|
|
@@ -93,21 +101,17 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
|
|
|
93
101
|
z-index: 1;
|
|
94
102
|
border: none;
|
|
95
103
|
}
|
|
96
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
104
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams], exemplary) => {
|
|
97
105
|
return (`
|
|
98
106
|
.embed-wrapper-${item.id} {
|
|
99
107
|
opacity: ${layoutParams.opacity};
|
|
100
108
|
transform: rotate(${area.angle}deg);
|
|
101
109
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
102
|
-
transition: ${getTransitions<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)};
|
|
103
110
|
}
|
|
104
111
|
.embed-${item.id} {
|
|
105
112
|
width: ${item.commonParams.scale ? `${area.width * exemplary}px` : '100%'};
|
|
106
113
|
height: ${item.commonParams.scale ? `${area.height * exemplary}px` : '100%'};
|
|
107
114
|
}
|
|
108
|
-
.embed-wrapper-${item.id}:hover {
|
|
109
|
-
${getHoverStyles<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)}
|
|
110
|
-
}
|
|
111
115
|
`);
|
|
112
116
|
})}
|
|
113
117
|
`}</JSXStyle>
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { 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';
|
|
6
5
|
import JSXStyle from 'styled-jsx/style';
|
|
7
|
-
import { useRegisterResize } from
|
|
6
|
+
import { useRegisterResize } from '../../common/useRegisterResize';
|
|
8
7
|
import { useItemAngle } from '../useItemAngle';
|
|
9
8
|
|
|
10
|
-
export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId }) => {
|
|
9
|
+
export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId, interactionCtrl }) => {
|
|
11
10
|
const sdk = useCntrlContext();
|
|
12
11
|
const { layouts } = useCntrlContext();
|
|
13
|
-
const
|
|
12
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
14
13
|
const component = sdk.customItems.get(item.commonParams.name);
|
|
15
|
-
const layoutValues: Record<string, any>[] = [item.area
|
|
14
|
+
const layoutValues: Record<string, any>[] = [item.area];
|
|
16
15
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
17
16
|
useRegisterResize(ref, onResize);
|
|
17
|
+
const stateParams = interactionCtrl?.getState(['angle']);
|
|
18
|
+
const angle = stateParams?.styles?.angle ?? itemAngle;
|
|
18
19
|
if (!component) return null;
|
|
19
20
|
return (
|
|
20
21
|
<>
|
|
@@ -23,25 +24,22 @@ export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, section
|
|
|
23
24
|
ref={setRef}
|
|
24
25
|
style={{
|
|
25
26
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
27
|
+
transition: stateParams?.transition ?? 'none'
|
|
26
28
|
}}
|
|
27
29
|
>
|
|
28
30
|
{component({})}
|
|
29
31
|
</div>
|
|
30
32
|
<JSXStyle id={item.id}>
|
|
31
|
-
{`${getLayoutStyles(layouts, layoutValues, ([area
|
|
33
|
+
{`${getLayoutStyles(layouts, layoutValues, ([area]) => {
|
|
32
34
|
return (`
|
|
33
35
|
.custom-component-${item.id} {
|
|
34
36
|
transform: rotate(${area.angle}deg);
|
|
35
|
-
transition: ${getTransitions<ArticleItemType.Custom>(['angle'], hoverParams)};
|
|
36
37
|
height: 100%;
|
|
37
38
|
width: 100%;
|
|
38
39
|
position: absolute;
|
|
39
40
|
left: 0;
|
|
40
41
|
top: 0;
|
|
41
42
|
}
|
|
42
|
-
.custom-component-${item.id}:hover {
|
|
43
|
-
${getHoverStyles<ArticleItemType.Custom>(['angle'], hoverParams)};
|
|
44
|
-
}
|
|
45
43
|
`);
|
|
46
44
|
})}`}
|
|
47
45
|
</JSXStyle>
|
|
@@ -1,24 +1,30 @@
|
|
|
1
|
-
import React, { FC, useId, useState } from 'react';
|
|
1
|
+
import React, { FC, useEffect, useId, useState } from 'react';
|
|
2
2
|
import { Item, ItemProps } from '../Item';
|
|
3
3
|
import JSXStyle from 'styled-jsx/style';
|
|
4
|
-
import {
|
|
5
|
-
import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
|
|
4
|
+
import { getLayoutStyles, GroupItem as TGroupItem } from '@cntrl-site/sdk';
|
|
6
5
|
import { LinkWrapper } from '../LinkWrapper';
|
|
7
6
|
import { useRegisterResize } from '../../common/useRegisterResize';
|
|
8
7
|
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
9
8
|
import { useItemAngle } from '../useItemAngle';
|
|
10
9
|
import { useGroupItem } from './useGroupItem';
|
|
10
|
+
import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
|
|
11
11
|
|
|
12
|
-
export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize, articleHeight }) => {
|
|
12
|
+
export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize, articleHeight, interactionCtrl, onVisibilityChange }) => {
|
|
13
13
|
const id = useId();
|
|
14
14
|
const { items } = item;
|
|
15
|
-
const
|
|
15
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
16
16
|
const { layouts } = useCntrlContext();
|
|
17
|
-
const { opacity } = useGroupItem(item, sectionId);
|
|
18
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams
|
|
17
|
+
const { opacity: itemOpacity } = useGroupItem(item, sectionId);
|
|
18
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
19
19
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
20
20
|
useRegisterResize(ref, onResize);
|
|
21
|
-
|
|
21
|
+
const stateParams = interactionCtrl?.getState(['opacity', 'angle']);
|
|
22
|
+
const angle = getStyleFromItemStateAndParams(stateParams?.styles?.angle, itemAngle);
|
|
23
|
+
const opacity = getStyleFromItemStateAndParams(stateParams?.styles?.opacity, itemOpacity);
|
|
24
|
+
const isInteractive = opacity !== 0 && opacity !== undefined;
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
onVisibilityChange?.(isInteractive);
|
|
27
|
+
}, [isInteractive, onVisibilityChange]);
|
|
22
28
|
return (
|
|
23
29
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
24
30
|
<>
|
|
@@ -28,6 +34,7 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
|
|
|
28
34
|
style={{
|
|
29
35
|
...(opacity !== undefined ? { opacity } : {}),
|
|
30
36
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
37
|
+
transition: stateParams?.transition ?? 'none'
|
|
31
38
|
}}
|
|
32
39
|
>
|
|
33
40
|
{items && items.map(item => (
|
|
@@ -36,6 +43,7 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
|
|
|
36
43
|
key={item.id}
|
|
37
44
|
sectionId={sectionId}
|
|
38
45
|
articleHeight={articleHeight}
|
|
46
|
+
isParentVisible={isInteractive}
|
|
39
47
|
isInGroup
|
|
40
48
|
/>
|
|
41
49
|
))}
|
|
@@ -47,15 +55,11 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
|
|
|
47
55
|
height: 100%;
|
|
48
56
|
box-sizing: border-box;
|
|
49
57
|
}
|
|
50
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
58
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
51
59
|
return (`
|
|
52
60
|
.group-${item.id} {
|
|
53
61
|
opacity: ${layoutParams.opacity};
|
|
54
62
|
transform: rotate(${area.angle}deg);
|
|
55
|
-
transition: ${getTransitions<ArticleItemType.Group>(['opacity', 'angle'], hoverParams)};
|
|
56
|
-
}
|
|
57
|
-
.group-${item.id}:hover {
|
|
58
|
-
${getHoverStyles<ArticleItemType.Group>(['opacity', 'angle'], hoverParams)};
|
|
59
63
|
}
|
|
60
64
|
`);
|
|
61
65
|
})}
|