@cntrl-site/sdk-nextjs 1.0.19-alpha.2 → 1.0.19-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Article.js +1 -1
- package/lib/components/Item.js +31 -36
- package/lib/components/items/CodeEmbedItem.js +11 -15
- package/lib/components/items/CustomItem.js +7 -12
- package/lib/components/items/GroupItem.js +11 -15
- package/lib/components/items/ImageItem.js +21 -24
- package/lib/components/items/RectangleItem.js +24 -18
- package/lib/components/items/RichTextItem.js +19 -14
- package/lib/components/items/VideoItem.js +22 -23
- package/lib/components/items/VimeoEmbed.js +16 -22
- package/lib/components/items/YoutubeEmbed.js +16 -23
- package/lib/components/useItemPosition.js +10 -5
- package/lib/interactions/CSSPropertyNameMap.js +38 -0
- package/lib/interactions/InteractionsRegistry.js +158 -0
- package/lib/interactions/ItemInteractionCtrl.js +55 -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 +17 -39
- package/lib/utils/getStyleFromItemStateAndParams.js +9 -0
- package/package.json +1 -1
- package/src/components/Article.tsx +2 -2
- package/src/components/Item.tsx +40 -34
- package/src/components/items/CodeEmbedItem.tsx +11 -14
- package/src/components/items/CustomItem.tsx +8 -13
- package/src/components/items/GroupItem.tsx +12 -14
- package/src/components/items/ImageItem.tsx +29 -25
- package/src/components/items/RectangleItem.tsx +30 -16
- package/src/components/items/RichTextItem.tsx +26 -20
- package/src/components/items/VideoItem.tsx +31 -25
- package/src/components/items/VimeoEmbed.tsx +21 -21
- package/src/components/items/YoutubeEmbed.tsx +17 -23
- package/src/components/useItemPosition.ts +12 -5
- package/src/interactions/CSSPropertyNameMap.ts +38 -0
- package/src/interactions/InteractionsRegistry.ts +193 -0
- package/src/interactions/ItemInteractionCtrl.ts +56 -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 +7 -7
- package/src/provider/InteractionsContext.tsx +17 -54
- package/src/utils/getStyleFromItemStateAndParams.ts +8 -0
- package/lib/components/useStatesClassNames.js +0 -18
- package/lib/components/useStatesTransitions.js +0 -89
- package/lib/utils/StateStyles/StateStyles.js +0 -89
- package/lib/utils/getStatesCSS.js +0 -16
- package/src/components/useStatesClassNames.ts +0 -23
- package/src/components/useStatesTransitions.ts +0 -95
- package/src/utils/StateStyles/StateStyles.ts +0 -99
- package/src/utils/getStatesCSS.ts +0 -24
|
@@ -8,29 +8,45 @@ import { useRectangleItem } from './useRectangleItem';
|
|
|
8
8
|
import { useItemAngle } from '../useItemAngle';
|
|
9
9
|
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
10
10
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
11
|
-
import {
|
|
12
|
-
import { useStatesClassNames } from '../useStatesClassNames';
|
|
13
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
11
|
+
import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
|
|
14
12
|
|
|
15
|
-
export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize }) => {
|
|
13
|
+
export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
|
|
16
14
|
const id = useId();
|
|
17
15
|
const { layouts } = useCntrlContext();
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const {
|
|
17
|
+
fillColor: itemFillColor,
|
|
18
|
+
radius: itemRadius,
|
|
19
|
+
strokeWidth: itemStrokeWidth,
|
|
20
|
+
strokeColor: itemStrokeColor,
|
|
21
|
+
blur: itemBlur,
|
|
22
|
+
backdropBlur: itemBackdropBlur
|
|
23
|
+
} = useRectangleItem(item, sectionId);
|
|
24
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
25
|
+
const stateParams = interactionCtrl?.getState(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur']);
|
|
26
|
+
const styles = stateParams?.styles ?? {};
|
|
27
|
+
const backgroundColor = useMemo(() => {
|
|
28
|
+
const fillColor = getStyleFromItemStateAndParams(styles?.fillColor, itemFillColor);
|
|
29
|
+
return fillColor ? CntrlColor.parse(fillColor) : undefined;
|
|
30
|
+
}, [itemFillColor, styles?.fillColor]);
|
|
31
|
+
const borderColor = useMemo(() => {
|
|
32
|
+
const strokeColor = getStyleFromItemStateAndParams(styles?.strokeColor, itemStrokeColor);
|
|
33
|
+
return strokeColor ? CntrlColor.parse(strokeColor) : undefined;
|
|
34
|
+
}, [itemStrokeColor, styles?.strokeColor]);
|
|
35
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
23
36
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
24
|
-
const statesClassNames = useStatesClassNames(item.id, item.state, 'rectangle');
|
|
25
37
|
useRegisterResize(ref, onResize);
|
|
38
|
+
const backdropBlur = getStyleFromItemStateAndParams(styles?.backdropBlur, itemBackdropBlur);
|
|
39
|
+
const radius = getStyleFromItemStateAndParams(styles?.radius, itemRadius);
|
|
40
|
+
const strokeWidth = getStyleFromItemStateAndParams(styles?.strokeWidth, itemStrokeWidth);
|
|
41
|
+
const angle = getStyleFromItemStateAndParams(styles?.angle, itemAngle);
|
|
42
|
+
const blur = getStyleFromItemStateAndParams(styles?.blur, itemBlur);
|
|
26
43
|
const backdropFilterValue = backdropBlur ? `blur(${backdropBlur * 100}vw)`: undefined;
|
|
27
|
-
useStatesTransitions(ref, item.state, ['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur']);
|
|
28
44
|
|
|
29
45
|
return (
|
|
30
46
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
31
47
|
<>
|
|
32
48
|
<div
|
|
33
|
-
className={`rectangle-${item.id}
|
|
49
|
+
className={`rectangle-${item.id}`}
|
|
34
50
|
ref={setRef}
|
|
35
51
|
style={{
|
|
36
52
|
...(backgroundColor ? { backgroundColor : `${backgroundColor.fmt('rgba')}` } : {}),
|
|
@@ -43,6 +59,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
|
|
|
43
59
|
? { backdropFilter: backdropFilterValue, WebkitBackdropFilter: backdropFilterValue }
|
|
44
60
|
: {}
|
|
45
61
|
),
|
|
62
|
+
transition: stateParams?.transition ?? 'none'
|
|
46
63
|
}}
|
|
47
64
|
/>
|
|
48
65
|
<JSXStyle id={id}>{`
|
|
@@ -53,8 +70,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
|
|
|
53
70
|
border-style: solid;
|
|
54
71
|
box-sizing: border-box;
|
|
55
72
|
}
|
|
56
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
57
|
-
const statesCSS = getStatesCSS(item.id, 'rectangle', ['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], states);
|
|
73
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
58
74
|
return (`
|
|
59
75
|
.rectangle-${item.id} {
|
|
60
76
|
background-color: ${CntrlColor.parse(layoutParams.fillColor).fmt('rgba')};
|
|
@@ -65,9 +81,7 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
|
|
|
65
81
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
66
82
|
backdrop-filter: ${layoutParams.backdropFilter !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
67
83
|
-webkit-backdrop-filter: ${layoutParams.backdropFilter !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
68
|
-
transition: all 0.2s ease;
|
|
69
84
|
}
|
|
70
|
-
${statesCSS}
|
|
71
85
|
`);
|
|
72
86
|
})}
|
|
73
87
|
`}</JSXStyle>
|
|
@@ -10,29 +10,42 @@ import { useRegisterResize } from "../../common/useRegisterResize";
|
|
|
10
10
|
import { getFontFamilyValue } from '../../utils/getFontFamilyValue';
|
|
11
11
|
import { useExemplary } from '../../common/useExemplary';
|
|
12
12
|
import { useItemAngle } from '../useItemAngle';
|
|
13
|
-
import {
|
|
14
|
-
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
15
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
13
|
+
import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
|
|
16
14
|
|
|
17
|
-
export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize }) => {
|
|
15
|
+
export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
|
|
18
16
|
const [content, styles] = useRichTextItem(item);
|
|
19
17
|
const id = useId();
|
|
20
18
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
21
19
|
const { layouts } = useCntrlContext();
|
|
22
|
-
const
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
21
|
+
const {
|
|
22
|
+
blur: itemBlur,
|
|
23
|
+
wordSpacing: itemWordSpacing,
|
|
24
|
+
letterSpacing: itemLetterSpacing,
|
|
25
|
+
color: itemColor,
|
|
26
|
+
fontSize,
|
|
27
|
+
lineHeight
|
|
28
|
+
} = useRichTextItemValues(item, sectionId);
|
|
29
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
26
30
|
const exemplary = useExemplary();
|
|
27
|
-
const stateClassNames = useStatesClassNames(item.id, item.state, 'rich-text-wrapper');
|
|
28
31
|
useRegisterResize(ref, onResize);
|
|
29
|
-
|
|
32
|
+
const stateParams = interactionCtrl?.getState(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
|
|
33
|
+
const stateStyles = stateParams?.styles ?? {};
|
|
34
|
+
const transition = stateParams?.transition ?? 'none';
|
|
35
|
+
const textColor = useMemo(() => {
|
|
36
|
+
const color = getStyleFromItemStateAndParams(stateParams?.styles?.color, itemColor);
|
|
37
|
+
return color ? CntrlColor.parse(color) : undefined;
|
|
38
|
+
}, [itemColor, stateStyles.color]);
|
|
39
|
+
const angle = getStyleFromItemStateAndParams(stateStyles.angle, itemAngle);
|
|
40
|
+
const blur = getStyleFromItemStateAndParams(stateStyles.blur, itemBlur);
|
|
41
|
+
const letterSpacing = getStyleFromItemStateAndParams(stateStyles.letterSpacing, itemLetterSpacing);
|
|
42
|
+
const wordSpacing = getStyleFromItemStateAndParams(stateStyles.wordSpacing, itemWordSpacing);
|
|
30
43
|
|
|
31
44
|
return (
|
|
32
45
|
<>
|
|
33
46
|
<div
|
|
34
47
|
ref={setRef}
|
|
35
|
-
className={`rich-text-wrapper-${item.id}
|
|
48
|
+
className={`rich-text-wrapper-${item.id}`}
|
|
36
49
|
style={{
|
|
37
50
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
38
51
|
...(textColor ? { color: `${textColor.fmt('rgba')}` } : {}),
|
|
@@ -41,20 +54,15 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
41
54
|
...(wordSpacing !== undefined ? { wordSpacing: `${wordSpacing * exemplary}px` } : {}),
|
|
42
55
|
...(fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {}),
|
|
43
56
|
...(lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {}),
|
|
57
|
+
transition
|
|
44
58
|
}}
|
|
45
59
|
>
|
|
46
60
|
{content}
|
|
47
61
|
</div>
|
|
48
62
|
<JSXStyle id={id}>
|
|
49
63
|
{styles}
|
|
50
|
-
{`${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
64
|
+
{`${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
51
65
|
const color = CntrlColor.parse(layoutParams.color);
|
|
52
|
-
const statesCSS = getStatesCSS(
|
|
53
|
-
item.id,
|
|
54
|
-
'rich-text-wrapper',
|
|
55
|
-
['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color'],
|
|
56
|
-
states
|
|
57
|
-
);
|
|
58
66
|
return (`
|
|
59
67
|
.rich-text-wrapper-${item.id} {
|
|
60
68
|
font-size: ${layoutParams.fontSize * 100}vw;
|
|
@@ -70,14 +78,12 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
70
78
|
transform: rotate(${area.angle}deg);
|
|
71
79
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
72
80
|
text-transform: ${layoutParams.textTransform};
|
|
73
|
-
transition: all 0.2s ease;
|
|
74
81
|
}
|
|
75
82
|
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
76
83
|
.rich-text-wrapper-${item.id} {
|
|
77
84
|
color: ${color.fmt('rgba')};
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
|
-
${statesCSS}
|
|
81
87
|
`);
|
|
82
88
|
})}`}
|
|
83
89
|
</JSXStyle>
|
|
@@ -10,42 +10,54 @@ import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
|
10
10
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
11
11
|
import { useLayoutContext } from '../useLayoutContext';
|
|
12
12
|
import { ScrollPlaybackVideo } from '../ScrollPlaybackVideo';
|
|
13
|
-
import {
|
|
14
|
-
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
15
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
13
|
+
import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
|
|
16
14
|
|
|
17
|
-
export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize }) => {
|
|
15
|
+
export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
|
|
18
16
|
const id = useId();
|
|
19
17
|
const { layouts } = useCntrlContext();
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
const {
|
|
19
|
+
radius: itemRadius,
|
|
20
|
+
strokeWidth: itemStrokeWidth,
|
|
21
|
+
strokeColor: itemStrokeColor,
|
|
22
|
+
opacity: itemOpacity,
|
|
23
|
+
blur: itemBlur
|
|
24
|
+
} = useFileItem(item, sectionId);
|
|
25
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
23
26
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
24
27
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
|
25
28
|
const layoutId = useLayoutContext();
|
|
26
29
|
const scrollPlayback = layoutId ? item.layoutParams[layoutId].scrollPlayback : null;
|
|
27
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams
|
|
28
|
-
const statesWrapperClassNames = useStatesClassNames(item.id, item.state, 'video-wrapper');
|
|
29
|
-
const statesVideoClassNames = useStatesClassNames(item.id, item.state, 'video');
|
|
30
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
30
31
|
const hasScrollPlayback = scrollPlayback !== null;
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
const wrapperStateParams = interactionCtrl?.getState(['angle', 'opacity', 'blur']);
|
|
33
|
+
const videoStateParams = interactionCtrl?.getState(['strokeWidth', 'radius', 'strokeColor']);
|
|
34
|
+
const borderColor = useMemo(() => {
|
|
35
|
+
const strokeColor = getStyleFromItemStateAndParams(videoStateParams?.styles?.color, itemStrokeColor);
|
|
36
|
+
return strokeColor ? CntrlColor.parse(strokeColor) : undefined;
|
|
37
|
+
}, [videoStateParams?.styles?.color, itemStrokeColor]);
|
|
38
|
+
const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
|
|
39
|
+
const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
|
|
40
|
+
const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
|
|
41
|
+
const strokeWidth = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.strokeWidth, itemStrokeWidth);
|
|
42
|
+
const radius = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.radius, itemRadius);
|
|
33
43
|
useRegisterResize(ref, onResize);
|
|
34
44
|
const inlineStyles = {
|
|
35
45
|
...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
|
|
36
46
|
...(strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}),
|
|
37
|
-
...(borderColor ? { borderColor: `${borderColor.toCss()}` } : {})
|
|
47
|
+
...(borderColor ? { borderColor: `${borderColor.toCss()}` } : {}),
|
|
48
|
+
transition: videoStateParams?.transition ?? 'none'
|
|
38
49
|
};
|
|
39
50
|
|
|
40
51
|
return (
|
|
41
52
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
42
53
|
<div
|
|
43
|
-
className={`video-wrapper-${item.id}
|
|
54
|
+
className={`video-wrapper-${item.id}`}
|
|
44
55
|
ref={setRef}
|
|
45
56
|
style={{
|
|
46
57
|
...(opacity !== undefined ? { opacity } : {}),
|
|
47
58
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
48
|
-
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})
|
|
59
|
+
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
60
|
+
transition: wrapperStateParams?.transition ?? 'none'
|
|
49
61
|
}}
|
|
50
62
|
>
|
|
51
63
|
{hasScrollPlayback ? (
|
|
@@ -54,7 +66,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
|
|
|
54
66
|
src={item.commonParams.url}
|
|
55
67
|
playbackParams={scrollPlayback}
|
|
56
68
|
style={inlineStyles}
|
|
57
|
-
className={`video video-playback-wrapper video-${item.id}
|
|
69
|
+
className={`video video-playback-wrapper video-${item.id}`}
|
|
58
70
|
/>
|
|
59
71
|
) : (
|
|
60
72
|
<video
|
|
@@ -64,7 +76,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
|
|
|
64
76
|
muted
|
|
65
77
|
loop
|
|
66
78
|
playsInline
|
|
67
|
-
className={`video video-${item.id}
|
|
79
|
+
className={`video video-${item.id}`}
|
|
68
80
|
style={inlineStyles}
|
|
69
81
|
>
|
|
70
82
|
<source src={item.commonParams.url} />
|
|
@@ -92,30 +104,24 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
|
|
|
92
104
|
pointer-events: auto;
|
|
93
105
|
}
|
|
94
106
|
.video-${item.id} {
|
|
95
|
-
border-color: ${
|
|
107
|
+
border-color: ${itemStrokeColor};
|
|
96
108
|
}
|
|
97
109
|
.video-playback-wrapper {
|
|
98
110
|
display: flex;
|
|
99
111
|
justify-content: center;
|
|
100
112
|
}
|
|
101
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
102
|
-
const wrapperStatesCSS = getStatesCSS(item.id, 'video-wrapper', ['angle', 'opacity', 'blur'], stateParams);
|
|
103
|
-
const videoStatesCSS = getStatesCSS(item.id, 'video', ['strokeWidth', 'radius', 'strokeColor'], stateParams);
|
|
113
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
104
114
|
return (`
|
|
105
115
|
.video-wrapper-${item.id} {
|
|
106
116
|
opacity: ${layoutParams.opacity};
|
|
107
117
|
transform: rotate(${area.angle}deg);
|
|
108
118
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
109
|
-
transition: all 0.2s ease;
|
|
110
119
|
}
|
|
111
120
|
.video-${item.id} {
|
|
112
121
|
border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
|
|
113
122
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
114
123
|
border-width: ${layoutParams.strokeWidth * 100}vw;
|
|
115
|
-
transition: all 0.2s ease;
|
|
116
124
|
}
|
|
117
|
-
${wrapperStatesCSS}
|
|
118
|
-
${videoStatesCSS}
|
|
119
125
|
`);
|
|
120
126
|
})}
|
|
121
127
|
`}</JSXStyle>
|
|
@@ -8,27 +8,31 @@ import { useItemAngle } from '../useItemAngle';
|
|
|
8
8
|
import { getLayoutStyles, VimeoEmbedItem as TVimeoEmbedItem } from '@cntrl-site/sdk';
|
|
9
9
|
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
10
10
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
11
|
-
import {
|
|
12
|
-
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
13
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
11
|
+
import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
|
|
14
12
|
|
|
15
|
-
export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId, onResize }) => {
|
|
13
|
+
export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
|
|
16
14
|
const id = useId();
|
|
17
15
|
const { layouts } = useCntrlContext();
|
|
18
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
radius: itemRadius,
|
|
18
|
+
blur: itemBlur,
|
|
19
|
+
opacity: itemOpacity
|
|
20
|
+
} = useEmbedVideoItem(item, sectionId);
|
|
19
21
|
const [iframeRef, setIframeRef] = useState<HTMLIFrameElement | null>(null);
|
|
20
22
|
const vimeoPlayer = useMemo(() => iframeRef ? new Player(iframeRef) : undefined, [iframeRef]);
|
|
21
|
-
const
|
|
23
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
22
24
|
const { play, controls, loop, muted, pictureInPicture, url } = item.commonParams;
|
|
23
25
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
24
26
|
const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
|
|
25
27
|
const [isCoverVisible, setIsCoverVisible] = useState(false);
|
|
26
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams
|
|
27
|
-
const
|
|
28
|
-
const
|
|
28
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
29
|
+
const wrapperStateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
|
|
30
|
+
const frameStateParams = interactionCtrl?.getState(['radius']);
|
|
31
|
+
const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
|
|
32
|
+
const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
|
|
33
|
+
const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
|
|
34
|
+
const radius = getStyleFromItemStateAndParams(frameStateParams?.styles?.radius, itemRadius);
|
|
29
35
|
useRegisterResize(ref, onResize);
|
|
30
|
-
useStatesTransitions(ref, item.state, ['angle', 'blur', 'opacity']);
|
|
31
|
-
useStatesTransitions(iframeRef, item.state, ['radius']);
|
|
32
36
|
const getValidVimeoUrl = (url: string): string => {
|
|
33
37
|
const validURL = new URL(url);
|
|
34
38
|
validURL.searchParams.append('controls', String(controls));
|
|
@@ -66,12 +70,13 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
|
|
|
66
70
|
return (
|
|
67
71
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
68
72
|
<div
|
|
69
|
-
className={`embed-video-wrapper-${item.id}
|
|
73
|
+
className={`embed-video-wrapper-${item.id}`}
|
|
70
74
|
ref={setRef}
|
|
71
75
|
style={{
|
|
72
76
|
...(opacity !== undefined ? { opacity } : {}),
|
|
73
77
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
74
78
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
79
|
+
transition: wrapperStateParams?.transition ?? 'none'
|
|
75
80
|
}}
|
|
76
81
|
onMouseEnter={() => {
|
|
77
82
|
if (!vimeoPlayer || play !== 'on-hover') return;
|
|
@@ -102,12 +107,13 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
|
|
|
102
107
|
)}
|
|
103
108
|
<iframe
|
|
104
109
|
ref={setIframeRef}
|
|
105
|
-
className={`embed-video
|
|
110
|
+
className={`embed-video`}
|
|
106
111
|
src={validUrl || ''}
|
|
107
112
|
allow="autoplay; fullscreen; picture-in-picture;"
|
|
108
113
|
allowFullScreen
|
|
109
114
|
style={{
|
|
110
|
-
...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {})
|
|
115
|
+
...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
|
|
116
|
+
transition: frameStateParams?.transition ?? 'none'
|
|
111
117
|
}}
|
|
112
118
|
/>
|
|
113
119
|
</div>
|
|
@@ -124,22 +130,16 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
|
|
|
124
130
|
border: none;
|
|
125
131
|
overflow: hidden;
|
|
126
132
|
}
|
|
127
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
128
|
-
const wrapperStatesCSS = getStatesCSS(item.id, 'embed-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
129
|
-
const videoStatesCSS = getStatesCSS(item.id, 'embed-video', ['radius'], stateParams);
|
|
133
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
130
134
|
return (`
|
|
131
135
|
.embed-video-wrapper-${item.id} {
|
|
132
136
|
opacity: ${layoutParams.opacity};
|
|
133
137
|
transform: rotate(${area.angle}deg);
|
|
134
138
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
135
|
-
transition: all 0.2s ease;
|
|
136
139
|
}
|
|
137
140
|
.embed-video-wrapper-${item.id} .embed-video {
|
|
138
141
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
139
|
-
transition: all 0.2s ease;
|
|
140
142
|
}
|
|
141
|
-
${wrapperStatesCSS}
|
|
142
|
-
${videoStatesCSS}
|
|
143
143
|
`);
|
|
144
144
|
})}
|
|
145
145
|
`}</JSXStyle>
|
|
@@ -10,28 +10,27 @@ import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
|
10
10
|
import { useYouTubeIframeApi } from '../../utils/Youtube/useYouTubeIframeApi';
|
|
11
11
|
import { YTPlayer } from '../../utils/Youtube/YoutubeIframeApi';
|
|
12
12
|
import { useRegisterResize } from "../../common/useRegisterResize";
|
|
13
|
-
import {
|
|
14
|
-
import { getStatesCSS } from '../../utils/getStatesCSS';
|
|
15
|
-
import { useStatesTransitions } from '../useStatesTransitions';
|
|
13
|
+
import { getStyleFromItemStateAndParams } from '../../utils/getStyleFromItemStateAndParams';
|
|
16
14
|
|
|
17
|
-
export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId, onResize }) => {
|
|
15
|
+
export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl }) => {
|
|
18
16
|
const id = useId();
|
|
19
17
|
const { layouts } = useCntrlContext();
|
|
20
18
|
const { play, controls, url } = item.commonParams;
|
|
21
|
-
const { radius, blur, opacity } = useEmbedVideoItem(item, sectionId);
|
|
22
|
-
const
|
|
19
|
+
const { radius: itemRadius, blur: itemBlur, opacity: itemOpacity } = useEmbedVideoItem(item, sectionId);
|
|
20
|
+
const itemAngle = useItemAngle(item, sectionId);
|
|
23
21
|
const YT = useYouTubeIframeApi();
|
|
24
22
|
const [div, setDiv] = useState<HTMLDivElement | null>(null);
|
|
25
|
-
const [wrapperRef, setWrapperRef] = useState<HTMLDivElement | null>(null);
|
|
26
23
|
const [player, setPlayer] = useState<YTPlayer | undefined>(undefined);
|
|
27
24
|
const [isCoverVisible, setIsCoverVisible] = useState(false);
|
|
28
25
|
const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
|
|
29
|
-
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams
|
|
30
|
-
const
|
|
31
|
-
const
|
|
26
|
+
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
27
|
+
const wrapperStateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
|
|
28
|
+
const frameStateParams = interactionCtrl?.getState(['radius']);
|
|
29
|
+
const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
|
|
30
|
+
const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
|
|
31
|
+
const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
|
|
32
|
+
const radius = getStyleFromItemStateAndParams(frameStateParams?.styles?.radius, itemRadius);
|
|
32
33
|
useRegisterResize(div, onResize);
|
|
33
|
-
useStatesTransitions(wrapperRef, item.state, ['angle', 'blur', 'opacity']);
|
|
34
|
-
useStatesTransitions(div, item.state, ['radius']);
|
|
35
34
|
useEffect(() => {
|
|
36
35
|
const newUrl = new URL(url);
|
|
37
36
|
const videoId = getYoutubeId(newUrl);
|
|
@@ -84,8 +83,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
|
|
|
84
83
|
return (
|
|
85
84
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
86
85
|
<div
|
|
87
|
-
|
|
88
|
-
className={`embed-youtube-video-wrapper-${item.id} ${wrapperClassNames}`}
|
|
86
|
+
className={`embed-youtube-video-wrapper-${item.id}`}
|
|
89
87
|
onMouseEnter={() => {
|
|
90
88
|
if (!player || play !== 'on-hover') return;
|
|
91
89
|
player.playVideo();
|
|
@@ -98,6 +96,7 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
|
|
|
98
96
|
...(opacity !== undefined ? { opacity } : {}),
|
|
99
97
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
100
98
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
99
|
+
transition: wrapperStateParams?.transition ?? 'none'
|
|
101
100
|
}}
|
|
102
101
|
>
|
|
103
102
|
{item.commonParams.coverUrl && (
|
|
@@ -120,10 +119,11 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
|
|
|
120
119
|
/>
|
|
121
120
|
)}
|
|
122
121
|
<div
|
|
123
|
-
className={`embed-${item.id}
|
|
122
|
+
className={`embed-${item.id}`}
|
|
124
123
|
ref={setDiv}
|
|
125
124
|
style={{
|
|
126
|
-
...(radius !== undefined
|
|
125
|
+
...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
|
|
126
|
+
transition: frameStateParams?.transition ?? 'none'
|
|
127
127
|
}}
|
|
128
128
|
/>
|
|
129
129
|
</div>
|
|
@@ -143,22 +143,16 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
|
|
|
143
143
|
z-index: 1;
|
|
144
144
|
border: none;
|
|
145
145
|
}
|
|
146
|
-
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams
|
|
147
|
-
const wrapperStatesCSS = getStatesCSS(item.id, 'embed-youtube-video-wrapper', ['angle', 'blur', 'opacity'], stateParams);
|
|
148
|
-
const embedStatesCSS = getStatesCSS(item.id, 'embed', ['radius'], stateParams);
|
|
146
|
+
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
149
147
|
return (`
|
|
150
148
|
.embed-youtube-video-wrapper-${item.id} {
|
|
151
149
|
opacity: ${layoutParams.opacity};
|
|
152
150
|
transform: rotate(${area.angle}deg);
|
|
153
151
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
154
|
-
transition: all 0.2s ease;
|
|
155
152
|
}
|
|
156
153
|
.embed-youtube-video-wrapper-${item.id} .embed-${item.id} {
|
|
157
154
|
border-radius: ${layoutParams.radius * 100}vw;
|
|
158
|
-
transition: all 0.2s ease;
|
|
159
155
|
}
|
|
160
|
-
${wrapperStatesCSS}
|
|
161
|
-
${embedStatesCSS}
|
|
162
156
|
`);
|
|
163
157
|
})}
|
|
164
158
|
`}</JSXStyle>
|
|
@@ -3,7 +3,11 @@ import { useKeyframeValue } from '../common/useKeyframeValue';
|
|
|
3
3
|
import { getItemTopStyle } from '../utils/getItemTopStyle';
|
|
4
4
|
import { useLayoutContext } from './useLayoutContext';
|
|
5
5
|
|
|
6
|
-
export const useItemPosition = (
|
|
6
|
+
export const useItemPosition = (
|
|
7
|
+
item: ItemAny,
|
|
8
|
+
sectionId: string,
|
|
9
|
+
stateValues: { top?: number; left?: number; }
|
|
10
|
+
) => {
|
|
7
11
|
const layoutId = useLayoutContext();
|
|
8
12
|
const position = useKeyframeValue<{ top: number; left: number } | undefined>(
|
|
9
13
|
item,
|
|
@@ -18,12 +22,15 @@ export const useItemPosition = (item: ItemAny, sectionId: string) => {
|
|
|
18
22
|
);
|
|
19
23
|
const anchorSide = layoutId ? item.area[layoutId].anchorSide : AnchorSide.Top;
|
|
20
24
|
const positionType = layoutId ? item.area[layoutId].positionType : PositionType.ScreenBased;
|
|
21
|
-
//
|
|
25
|
+
// to prevent fixed item (with anchor point bottom) to jump when scroll in safari on mobile
|
|
22
26
|
const isScreenBasedBottom = positionType === PositionType.ScreenBased && anchorSide === AnchorSide.Bottom;
|
|
27
|
+
if (!position) return undefined;
|
|
28
|
+
const top = stateValues?.top ?? position.top;
|
|
29
|
+
const left = stateValues?.left ?? position.left;
|
|
23
30
|
return position ? {
|
|
24
|
-
bottom: isScreenBasedBottom ? `${-
|
|
25
|
-
top: isScreenBasedBottom ? 'unset' : getItemTopStyle(
|
|
26
|
-
left: `${
|
|
31
|
+
bottom: isScreenBasedBottom ? `${-top * 100}vw` : 'unset',
|
|
32
|
+
top: isScreenBasedBottom ? 'unset' : getItemTopStyle(top, anchorSide),
|
|
33
|
+
left: `${left * 100}vw`
|
|
27
34
|
} : undefined;
|
|
28
35
|
};
|
|
29
36
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ArticleItemType, ItemState } from '@cntrl-site/sdk';
|
|
2
|
+
|
|
3
|
+
export const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
|
|
4
|
+
'width': 'width',
|
|
5
|
+
'height': 'height',
|
|
6
|
+
'top': 'top',
|
|
7
|
+
'left': 'left',
|
|
8
|
+
'scale': 'transform',
|
|
9
|
+
'angle': 'transform',
|
|
10
|
+
'opacity': 'opacity',
|
|
11
|
+
'radius': 'border-radius',
|
|
12
|
+
'strokeWidth': 'border-width',
|
|
13
|
+
'strokeColor': 'border-color',
|
|
14
|
+
'fillColor': 'background-color',
|
|
15
|
+
'blur': 'filter',
|
|
16
|
+
'backdropBlur': 'backdrop-filter',
|
|
17
|
+
'letterSpacing': 'letter-spacing',
|
|
18
|
+
'wordSpacing': 'word-spacing',
|
|
19
|
+
'color': 'color'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const PropertyNameCSSMap: Record<string, string[]> = {
|
|
23
|
+
'transform': ['angle', 'scale'],
|
|
24
|
+
'border-radius': ['radius'],
|
|
25
|
+
'border-width': ['strokeWidth'],
|
|
26
|
+
'border-color': ['strokeColor'],
|
|
27
|
+
'background-color': ['fillColor'],
|
|
28
|
+
'filter': ['blur'],
|
|
29
|
+
'backdrop-filter': ['backdrop-blur'],
|
|
30
|
+
'letter-spacing': ['letterSpacing'],
|
|
31
|
+
'word-spacing': ['wordSpacing'],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
export function getStyleKeysFromCSSProperty(cssProp: string): string[] {
|
|
36
|
+
const key = PropertyNameCSSMap[cssProp] ?? [cssProp];
|
|
37
|
+
return key;
|
|
38
|
+
}
|