@cntrl-site/sdk-nextjs 1.8.40 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/modules.xml +8 -0
- package/.idea/sdk-nextjs.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/lib/components/items/FileItem/ImageItem.js +49 -22
- package/lib/components/items/FileItem/VideoItem.js +48 -21
- package/lib/components/items/FileItem/useFileItem.js +12 -5
- package/lib/components/items/RectangleItem/RectangleItem.js +78 -22
- package/lib/components/items/RectangleItem/useRectangleItem.js +15 -8
- package/lib/interactions/CSSPropertyNameMap.js +18 -18
- package/lib/utils/Animator/Animator.js +171 -19
- package/lib/utils/areFillsVisible/areFillsVisible.js +23 -0
- package/lib/utils/getFill.js +43 -0
- package/package.json +2 -2
- package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +1 -1
- package/src/components/items/ComponentItem/ComponentItem.tsx +1 -1
- package/src/components/items/CompoundItem/CompoundChild.tsx +2 -2
- package/src/components/items/CompoundItem/CompoundItem.tsx +1 -1
- package/src/components/items/CustomItem/CustomItem.tsx +1 -1
- package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +2 -2
- package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +2 -2
- package/src/components/items/FileItem/ImageItem.tsx +63 -10
- package/src/components/items/FileItem/VideoItem.tsx +64 -11
- package/src/components/items/FileItem/useFileItem.ts +14 -6
- package/src/components/items/GroupItem/GroupItem.tsx +1 -1
- package/src/components/items/Item.tsx +2 -2
- package/src/components/items/RectangleItem/RectangleItem.tsx +126 -24
- package/src/components/items/RectangleItem/useRectangleItem.ts +17 -10
- package/src/components/items/RichTextItem/RichTextItem.tsx +5 -5
- package/src/interactions/CSSPropertyNameMap.ts +18 -19
- package/src/interactions/ItemInteractionCtrl.ts +4 -4
- package/src/interactions/types.ts +7 -7
- package/src/utils/Animator/Animator.ts +227 -27
- package/src/utils/areFillsVisible/areFillsVisible.test.ts +63 -0
- package/src/utils/areFillsVisible/areFillsVisible.ts +37 -0
- package/src/utils/getFill.ts +46 -0
- package/src/utils/getStyleFromItemStateAndParams.ts +2 -2
- package/src/provider/InteractionsContext.old.tsx +0 -66
- package/src/provider/InteractionsContext.test.tsx +0 -97
|
@@ -2,7 +2,15 @@ import { ImageItem, KeyframeType, VideoItem } from '@cntrl-site/sdk';
|
|
|
2
2
|
import { useKeyframeValue } from '../../../common/useKeyframeValue';
|
|
3
3
|
import { useLayoutContext } from '../../useLayoutContext';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const DEFAULT_FILL = [
|
|
6
|
+
{
|
|
7
|
+
id: 'default',
|
|
8
|
+
type: 'solid' as const,
|
|
9
|
+
value: 'rgba(0, 0, 0, 1)',
|
|
10
|
+
blendMode: 'normal'
|
|
11
|
+
}
|
|
12
|
+
];
|
|
13
|
+
|
|
6
14
|
export function useFileItem(item: ImageItem | VideoItem, sectionId: string) {
|
|
7
15
|
const layoutId = useLayoutContext();
|
|
8
16
|
const radius = useKeyframeValue(
|
|
@@ -43,15 +51,15 @@ export function useFileItem(item: ImageItem | VideoItem, sectionId: string) {
|
|
|
43
51
|
[layoutId]
|
|
44
52
|
);
|
|
45
53
|
|
|
46
|
-
const
|
|
54
|
+
const strokeFill = useKeyframeValue(
|
|
47
55
|
item,
|
|
48
|
-
KeyframeType.
|
|
56
|
+
KeyframeType.BorderFill,
|
|
49
57
|
(item, layoutId) => {
|
|
50
58
|
if (!layoutId) return;
|
|
51
59
|
const layoutParams = item.layoutParams[layoutId];
|
|
52
|
-
return '
|
|
60
|
+
return 'strokeFill' in layoutParams ? layoutParams.strokeFill : DEFAULT_FILL;
|
|
53
61
|
},
|
|
54
|
-
(animator, scroll, value) => value ? animator.
|
|
62
|
+
(animator, scroll, value) => value ? animator.getBorderFill(value, scroll) : undefined,
|
|
55
63
|
sectionId,
|
|
56
64
|
[layoutId]
|
|
57
65
|
);
|
|
@@ -69,5 +77,5 @@ export function useFileItem(item: ImageItem | VideoItem, sectionId: string) {
|
|
|
69
77
|
[layoutId]
|
|
70
78
|
);
|
|
71
79
|
|
|
72
|
-
return { radius, strokeWidth, opacity,
|
|
80
|
+
return { radius, strokeWidth, opacity, strokeFill, blur };
|
|
73
81
|
}
|
|
@@ -19,7 +19,7 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
|
|
|
19
19
|
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
20
20
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
21
21
|
useRegisterResize(ref, onResize);
|
|
22
|
-
const stateParams = interactionCtrl?.getState(['opacity', 'angle', 'blur']);
|
|
22
|
+
const stateParams = interactionCtrl?.getState<number>(['opacity', 'angle', 'blur']);
|
|
23
23
|
const angle = getStyleFromItemStateAndParams(stateParams?.styles?.angle, itemAngle);
|
|
24
24
|
const opacity = getStyleFromItemStateAndParams(stateParams?.styles?.opacity, itemOpacity);
|
|
25
25
|
const blur = getStyleFromItemStateAndParams(stateParams?.styles?.blur, itemBlur);
|
|
@@ -82,8 +82,8 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isP
|
|
|
82
82
|
const triggers = useItemTriggers(interactionCtrl);
|
|
83
83
|
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
84
84
|
const [isDraggingActive, setIsDraggingActive] = useState(false);
|
|
85
|
-
const wrapperStateProps = interactionCtrl?.getState(['top', 'left']);
|
|
86
|
-
const innerStateProps = interactionCtrl?.getState(['width', 'height', 'scale']);
|
|
85
|
+
const wrapperStateProps = interactionCtrl?.getState<number>(['top', 'left']);
|
|
86
|
+
const innerStateProps = interactionCtrl?.getState<number>(['width', 'height', 'scale']);
|
|
87
87
|
const { width, height, top, left } = useItemArea(item, sectionId, {
|
|
88
88
|
top: wrapperStateProps?.styles?.top as number,
|
|
89
89
|
left: wrapperStateProps?.styles?.left as number,
|
|
@@ -9,29 +9,30 @@ import { useItemAngle } from '../useItemAngle';
|
|
|
9
9
|
import { useCntrlContext } from '../../../provider/useCntrlContext';
|
|
10
10
|
import { useRegisterResize } from '../../../common/useRegisterResize';
|
|
11
11
|
import { getStyleFromItemStateAndParams } from '../../../utils/getStyleFromItemStateAndParams';
|
|
12
|
+
import { FillLayer } from '@cntrl-site/sdk/dist/types/article/Item';
|
|
13
|
+
import { getFill } from '../../../utils/getFill';
|
|
14
|
+
import { areFillsVisible } from '../../../utils/areFillsVisible/areFillsVisible';
|
|
12
15
|
|
|
13
16
|
export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
|
|
14
17
|
const id = useId();
|
|
15
18
|
const { layouts } = useCntrlContext();
|
|
16
19
|
const {
|
|
17
|
-
|
|
20
|
+
fill: itemFill,
|
|
18
21
|
radius: itemRadius,
|
|
19
22
|
strokeWidth: itemStrokeWidth,
|
|
20
|
-
|
|
23
|
+
strokeFill: itemStrokeFill,
|
|
21
24
|
blur: itemBlur,
|
|
22
25
|
backdropBlur: itemBackdropBlur
|
|
23
26
|
} = useRectangleItem(item, sectionId);
|
|
24
27
|
const itemAngle = useItemAngle(item, sectionId);
|
|
25
|
-
const stateParams = interactionCtrl?.getState(['angle', '
|
|
28
|
+
const stateParams = interactionCtrl?.getState<number>(['angle', 'strokeWidth', 'radius', 'blur', 'backdropBlur']);
|
|
29
|
+
const stateFillParams = interactionCtrl?.getState<FillLayer[]>(['fill']);
|
|
30
|
+
const stateFillLayers = stateFillParams?.styles?.fill;
|
|
31
|
+
const solidTransition = stateFillParams?.transition ?? 'none';
|
|
32
|
+
const stateStrokeFillParams = interactionCtrl?.getState<FillLayer[]>(['strokeFill']);
|
|
33
|
+
const stateStrokeFillLayers = stateStrokeFillParams?.styles?.strokeFill;
|
|
34
|
+
const strokeSolidTransition = stateStrokeFillParams?.transition ?? 'none';
|
|
26
35
|
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
36
|
const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
|
|
36
37
|
const [ref, setRef] = useState<HTMLDivElement | null>(null);
|
|
37
38
|
useRegisterResize(ref, onResize);
|
|
@@ -41,11 +42,18 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
|
|
|
41
42
|
const angle = getStyleFromItemStateAndParams(styles?.angle, itemAngle);
|
|
42
43
|
const blur = getStyleFromItemStateAndParams(styles?.blur, itemBlur);
|
|
43
44
|
const backdropFilterValue = backdropBlur ? `blur(${backdropBlur * 100}vw)` : undefined;
|
|
44
|
-
const isInteractive =
|
|
45
|
+
const isInteractive = areFillsVisible(stateFillLayers ?? itemFill ?? []) || (strokeWidth !== 0 && areFillsVisible(stateStrokeFillLayers ?? itemStrokeFill ?? []));
|
|
45
46
|
useEffect(() => {
|
|
46
47
|
onVisibilityChange?.(isInteractive);
|
|
47
48
|
}, [isInteractive, onVisibilityChange]);
|
|
48
49
|
|
|
50
|
+
const strokeValue = stateStrokeFillLayers
|
|
51
|
+
? getStyleFromItemStateAndParams<FillLayer>(stateStrokeFillLayers[0], itemStrokeFill?.[0])
|
|
52
|
+
: itemStrokeFill?.[0];
|
|
53
|
+
const stroke = strokeValue
|
|
54
|
+
? getFill(strokeValue) ?? 'transparent'
|
|
55
|
+
: 'transparent';
|
|
56
|
+
|
|
49
57
|
return (
|
|
50
58
|
<LinkWrapper url={item.link?.url} target={item.link?.target}>
|
|
51
59
|
<>
|
|
@@ -53,10 +61,15 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
|
|
|
53
61
|
className={`rectangle-${item.id}`}
|
|
54
62
|
ref={setRef}
|
|
55
63
|
style={{
|
|
56
|
-
...(
|
|
57
|
-
|
|
64
|
+
...(strokeValue ? {
|
|
65
|
+
'--stroke-background': stroke,
|
|
66
|
+
...(strokeValue.type === 'image' ? {
|
|
67
|
+
backgroundPosition: 'center',
|
|
68
|
+
backgroundSize: strokeValue.behavior === 'repeat' ? `${strokeValue.backgroundSize}%` : strokeValue.behavior,
|
|
69
|
+
backgroundRepeat: strokeValue.behavior === 'repeat' ? 'repeat' : 'no-repeat'
|
|
70
|
+
} : {})
|
|
71
|
+
} : {}),
|
|
58
72
|
...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
|
|
59
|
-
...(strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}),
|
|
60
73
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
61
74
|
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
62
75
|
willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset',
|
|
@@ -66,32 +79,121 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
|
|
|
66
79
|
),
|
|
67
80
|
transition: stateParams?.transition ?? 'none'
|
|
68
81
|
}}
|
|
69
|
-
|
|
82
|
+
>
|
|
83
|
+
<div
|
|
84
|
+
className={`rectangle-border-${item.id}`}
|
|
85
|
+
style={{
|
|
86
|
+
position: 'absolute',
|
|
87
|
+
inset: 0,
|
|
88
|
+
borderRadius: 'inherit',
|
|
89
|
+
pointerEvents: 'none',
|
|
90
|
+
zIndex: 2,
|
|
91
|
+
WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
|
|
92
|
+
WebkitMaskComposite: 'xor',
|
|
93
|
+
maskComposite: 'exclude',
|
|
94
|
+
...(strokeWidth !== 0 && strokeValue ? {
|
|
95
|
+
...(strokeWidth ? { padding: `${strokeWidth * 100}vw` } : {}),
|
|
96
|
+
...(strokeValue.type === 'solid' ? { transition: strokeSolidTransition, background: stroke } : {}),
|
|
97
|
+
...(strokeValue.type === 'image' ? {
|
|
98
|
+
backgroundPosition: 'center',
|
|
99
|
+
backgroundSize: strokeValue.behavior === 'repeat' ? `${strokeValue.backgroundSize}%` : strokeValue.behavior,
|
|
100
|
+
backgroundRepeat: strokeValue.behavior === 'repeat' ? 'repeat' : 'no-repeat'
|
|
101
|
+
} : {
|
|
102
|
+
background: stroke,
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
} : { background: stroke }),
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
{itemFill && itemFill.map((fill) => {
|
|
109
|
+
const stateFillLayer = stateFillLayers?.find((layer) => layer.id === fill.id);
|
|
110
|
+
const value = stateFillLayer
|
|
111
|
+
? (getStyleFromItemStateAndParams<FillLayer>(stateFillLayer, fill) ?? fill)
|
|
112
|
+
: fill;
|
|
113
|
+
const background = value
|
|
114
|
+
? getFill(value) ?? 'transparent'
|
|
115
|
+
: 'transparent';
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Fill fill={value} itemId={item.id} background={background} solidTransition={solidTransition} />
|
|
119
|
+
);
|
|
120
|
+
})}
|
|
121
|
+
</div>
|
|
70
122
|
<JSXStyle id={id}>{`
|
|
71
123
|
.rectangle-${item.id} {
|
|
72
124
|
position: absolute;
|
|
73
125
|
width: 100%;
|
|
74
126
|
height: 100%;
|
|
75
|
-
border-style: solid;
|
|
76
127
|
box-sizing: border-box;
|
|
77
|
-
}
|
|
128
|
+
},
|
|
129
|
+
.image-fill-${item.id} {
|
|
130
|
+
position: absolute;
|
|
131
|
+
top: 0;
|
|
132
|
+
left: 0;
|
|
133
|
+
width: 100%;
|
|
134
|
+
height: 100%;
|
|
135
|
+
transform-origin: center center;
|
|
136
|
+
z-index: 1;
|
|
137
|
+
background-position: center;
|
|
138
|
+
},
|
|
78
139
|
${getLayoutStyles(layouts, layoutValues, ([area, layoutParams]) => {
|
|
79
|
-
|
|
140
|
+
return (`
|
|
80
141
|
.rectangle-${item.id} {
|
|
81
|
-
|
|
82
|
-
border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
|
|
83
|
-
border-radius: ${layoutParams.radius * 100}vw;
|
|
84
|
-
border-width: ${layoutParams.strokeWidth * 100}vw;
|
|
142
|
+
border-radius: ${layoutParams.radius * 100}vw
|
|
85
143
|
transform: rotate(${area.angle}deg);
|
|
86
144
|
filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
|
|
87
145
|
${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
|
|
88
146
|
backdrop-filter: ${layoutParams.backdropBlur !== 0 ? `blur(${layoutParams.backdropBlur * 100}vw)` : 'unset'};
|
|
89
147
|
-webkit-backdrop-filter: ${layoutParams.backdropBlur !== 0 ? `blur(${layoutParams.backdropBlur * 100}vw)` : 'unset'};
|
|
148
|
+
},
|
|
149
|
+
.rectangle-border-${item.id} {
|
|
150
|
+
position: absolute;
|
|
151
|
+
inset: 0;
|
|
152
|
+
border-radius: inherit;
|
|
153
|
+
pointer-events: none;
|
|
154
|
+
z-index: 2;
|
|
155
|
+
-webkit-mask:
|
|
156
|
+
linear-gradient(#fff 0 0) content-box,
|
|
157
|
+
linear-gradient(#fff 0 0);
|
|
158
|
+
-webkit-mask-composite: xor;
|
|
159
|
+
mask-composite: exclude;
|
|
90
160
|
}
|
|
91
161
|
`);
|
|
92
|
-
|
|
162
|
+
})}
|
|
93
163
|
`}</JSXStyle>
|
|
94
164
|
</>
|
|
95
165
|
</LinkWrapper>
|
|
96
166
|
);
|
|
97
167
|
};
|
|
168
|
+
|
|
169
|
+
function Fill({ fill, itemId, background, solidTransition }: { fill: FillLayer; itemId: string; background: string; solidTransition: string; }) {
|
|
170
|
+
const isRotatedImage = fill.type === 'image' && fill.rotation && fill.rotation !== 0;
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div
|
|
174
|
+
key={fill.id}
|
|
175
|
+
className={fill.type === 'image' ? `image-fill-${itemId}` : `fill-${itemId}`}
|
|
176
|
+
style={{
|
|
177
|
+
...(fill.type === 'solid' ? { background, transition: solidTransition } : {}),
|
|
178
|
+
...(fill.type === 'image'
|
|
179
|
+
? {
|
|
180
|
+
transform: `rotate(${fill.rotation}deg)`,
|
|
181
|
+
backgroundImage: `url(${fill.src})`,
|
|
182
|
+
backgroundSize: fill.behavior === 'repeat' ? `${fill.backgroundSize}%` : fill.behavior,
|
|
183
|
+
backgroundRepeat: fill.behavior === 'repeat' ? 'repeat' : 'no-repeat',
|
|
184
|
+
opacity: fill.opacity
|
|
185
|
+
}
|
|
186
|
+
: { background }),
|
|
187
|
+
position: 'absolute',
|
|
188
|
+
mixBlendMode: fill.blendMode as any,
|
|
189
|
+
top: 0,
|
|
190
|
+
left: 0,
|
|
191
|
+
width: '100%',
|
|
192
|
+
height: '100%',
|
|
193
|
+
pointerEvents: 'none',
|
|
194
|
+
...(isRotatedImage ? { overflow: 'hidden' } : {})
|
|
195
|
+
}}
|
|
196
|
+
>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
@@ -2,7 +2,14 @@ import { KeyframeType, RectangleItem } from '@cntrl-site/sdk';
|
|
|
2
2
|
import { useKeyframeValue } from '../../../common/useKeyframeValue';
|
|
3
3
|
import { useLayoutContext } from '../../useLayoutContext';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const defaultFill = [
|
|
6
|
+
{
|
|
7
|
+
id: 'default',
|
|
8
|
+
type: 'solid' as const,
|
|
9
|
+
value: 'rgba(0, 0, 0, 1)',
|
|
10
|
+
blendMode: 'normal'
|
|
11
|
+
}
|
|
12
|
+
];
|
|
6
13
|
|
|
7
14
|
export function useRectangleItem(item: RectangleItem, sectionId: string) {
|
|
8
15
|
const layoutId = useLayoutContext();
|
|
@@ -30,27 +37,27 @@ export function useRectangleItem(item: RectangleItem, sectionId: string) {
|
|
|
30
37
|
sectionId,
|
|
31
38
|
[layoutId]
|
|
32
39
|
);
|
|
33
|
-
const
|
|
40
|
+
const fill = useKeyframeValue(
|
|
34
41
|
item,
|
|
35
|
-
KeyframeType.
|
|
42
|
+
KeyframeType.Fill,
|
|
36
43
|
(item, layoutId) => {
|
|
37
44
|
if (!layoutId) return;
|
|
38
45
|
const layoutParams = item.layoutParams[layoutId];
|
|
39
|
-
return '
|
|
46
|
+
return 'fill' in layoutParams ? layoutParams.fill : defaultFill;
|
|
40
47
|
},
|
|
41
|
-
(animator, scroll, value) => value ? animator.
|
|
48
|
+
(animator, scroll, value) => value ? animator.getFill(value, scroll) : undefined,
|
|
42
49
|
sectionId,
|
|
43
50
|
[layoutId]
|
|
44
51
|
);
|
|
45
|
-
const
|
|
52
|
+
const strokeFill = useKeyframeValue(
|
|
46
53
|
item,
|
|
47
|
-
KeyframeType.
|
|
54
|
+
KeyframeType.BorderFill,
|
|
48
55
|
(item, layoutId) => {
|
|
49
56
|
if (!layoutId) return;
|
|
50
57
|
const layoutParams = item.layoutParams[layoutId];
|
|
51
|
-
return '
|
|
58
|
+
return 'strokeFill' in layoutParams ? layoutParams.strokeFill : defaultFill;
|
|
52
59
|
},
|
|
53
|
-
(animator, scroll, value) => value ? animator.
|
|
60
|
+
(animator, scroll, value) => value ? animator.getBorderFill(value, scroll) : undefined,
|
|
54
61
|
sectionId,
|
|
55
62
|
[layoutId]
|
|
56
63
|
);
|
|
@@ -78,5 +85,5 @@ export function useRectangleItem(item: RectangleItem, sectionId: string) {
|
|
|
78
85
|
sectionId,
|
|
79
86
|
[layoutId]
|
|
80
87
|
);
|
|
81
|
-
return {
|
|
88
|
+
return { fill, strokeWidth, radius, strokeFill, blur, backdropBlur };
|
|
82
89
|
}
|
|
@@ -30,11 +30,11 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
30
30
|
const exemplary = useExemplary();
|
|
31
31
|
const { layoutId } = useCurrentLayout();
|
|
32
32
|
useRegisterResize(ref, onResize);
|
|
33
|
-
const stateParams = interactionCtrl?.getState(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
|
|
33
|
+
const stateParams = interactionCtrl?.getState<number | string>(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color']);
|
|
34
34
|
const stateStyles = stateParams?.styles ?? {};
|
|
35
35
|
const transition = stateParams?.transition ?? 'none';
|
|
36
36
|
const textColor = useMemo(() => {
|
|
37
|
-
const color = getStyleFromItemStateAndParams(stateParams?.styles?.color, itemColor);
|
|
37
|
+
const color = getStyleFromItemStateAndParams(stateParams?.styles?.color as string, itemColor);
|
|
38
38
|
return color ? CntrlColor.parse(color) : undefined;
|
|
39
39
|
}, [itemColor, stateStyles.color]);
|
|
40
40
|
const angle = getStyleFromItemStateAndParams(stateStyles.angle, itemAngle);
|
|
@@ -60,11 +60,11 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
|
|
|
60
60
|
ref={setRef}
|
|
61
61
|
className={`rich-text-wrapper-${item.id}`}
|
|
62
62
|
style={{
|
|
63
|
-
...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
|
|
63
|
+
...(blur !== undefined ? { filter: `blur(${blur as number * 100}vw)` } : {}),
|
|
64
64
|
...(textColor ? { color: `${textColor.fmt('rgba')}` } : {}),
|
|
65
65
|
...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
|
|
66
|
-
...(letterSpacing !== undefined ? { letterSpacing: `${letterSpacing * exemplary}px` } : {}),
|
|
67
|
-
...(wordSpacing !== undefined ? { wordSpacing: `${wordSpacing * exemplary}px` } : {}),
|
|
66
|
+
...(letterSpacing !== undefined ? { letterSpacing: `${letterSpacing as number * exemplary}px` } : {}),
|
|
67
|
+
...(wordSpacing !== undefined ? { wordSpacing: `${wordSpacing as number * exemplary}px` } : {}),
|
|
68
68
|
...(fontSize !== undefined ? { fontSize: `${Math.round(fontSize * exemplary)}px` } : {}),
|
|
69
69
|
...(lineHeight !== undefined ? { lineHeight: `${lineHeight * exemplary}px` } : {}),
|
|
70
70
|
willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset',
|
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
import { ArticleItemType, ItemState } from '@cntrl-site/sdk';
|
|
2
2
|
|
|
3
3
|
export const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
strokeFill: 'border-color',
|
|
14
|
+
fill: 'background',
|
|
15
|
+
blur: 'filter',
|
|
16
|
+
backdropBlur: 'backdrop-filter',
|
|
17
|
+
letterSpacing: 'letter-spacing',
|
|
18
|
+
wordSpacing: 'word-spacing',
|
|
19
|
+
color: 'color'
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const PropertyNameCSSMap: Record<string, string[]> = {
|
|
23
23
|
'transform': ['angle', 'scale'],
|
|
24
24
|
'border-radius': ['radius'],
|
|
25
25
|
'border-width': ['strokeWidth'],
|
|
26
|
-
'border-color': ['
|
|
27
|
-
'background
|
|
26
|
+
'border-color': ['strokeFill'],
|
|
27
|
+
'background': ['fill'],
|
|
28
28
|
'filter': ['blur'],
|
|
29
29
|
'backdrop-filter': ['backdrop-blur'],
|
|
30
30
|
'letter-spacing': ['letterSpacing'],
|
|
31
31
|
'word-spacing': ['wordSpacing'],
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
|
|
35
34
|
export function getStyleKeysFromCSSProperty(cssProp: string): string[] {
|
|
36
35
|
const key = PropertyNameCSSMap[cssProp] ?? [cssProp];
|
|
37
36
|
return key;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { InteractionsRegistryPort, ItemInteractionCtrl } from './types';
|
|
2
2
|
import { getTransition } from './getTransition';
|
|
3
3
|
import { getStyleKeysFromCSSProperty } from './CSSPropertyNameMap';
|
|
4
|
-
import { InteractionItemTrigger } from '@cntrl-site/sdk';
|
|
4
|
+
import { FillLayer, InteractionItemTrigger } from '@cntrl-site/sdk';
|
|
5
5
|
|
|
6
6
|
export class ItemInteractionController implements ItemInteractionCtrl {
|
|
7
7
|
private transitionsInProgress: Set<string> = new Set();
|
|
@@ -15,12 +15,12 @@ export class ItemInteractionController implements ItemInteractionCtrl {
|
|
|
15
15
|
this.registry.register(itemId, this);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
getState(keys: string[]) {
|
|
18
|
+
getState<T>(keys: string[]) {
|
|
19
19
|
const stateProps = this.registry.getStatePropsForItem(this.itemId);
|
|
20
|
-
const styles = keys.reduce<Record<string,
|
|
20
|
+
const styles = keys.reduce<Record<string, T>>((map, styleKey) => {
|
|
21
21
|
const prop = stateProps[styleKey];
|
|
22
22
|
if (prop?.value === undefined) return map;
|
|
23
|
-
map[styleKey] = prop.value;
|
|
23
|
+
map[styleKey] = prop.value as T;
|
|
24
24
|
return map;
|
|
25
25
|
}, {});
|
|
26
26
|
const transition = getTransition(stateProps, keys);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ArticleItemType, InteractionItemTrigger, ItemState } from '@cntrl-site/sdk';
|
|
1
|
+
import { ArticleItemType, FillLayer, InteractionItemTrigger, ItemState } from '@cntrl-site/sdk';
|
|
2
2
|
|
|
3
3
|
export interface ItemInteractionCtrl {
|
|
4
|
-
getState(keys: string[]): StateCSSInfo
|
|
4
|
+
getState<T>(keys: string[]): StateCSSInfo<T>;
|
|
5
5
|
getHasTrigger(itemId: string, triggerType: InteractionItemTrigger['type']): boolean;
|
|
6
6
|
sendTrigger(type: 'click' | 'hover-in' | 'hover-out'): void;
|
|
7
7
|
handleTransitionEnd?: (styleKey: string) => void;
|
|
@@ -13,19 +13,19 @@ export interface ItemInteractionCtrl {
|
|
|
13
13
|
|
|
14
14
|
export interface InteractionsRegistryPort {
|
|
15
15
|
register(itemId: string, ctrl: ItemInteractionCtrl): void;
|
|
16
|
-
getStatePropsForItem(itemId: string): StateProps
|
|
16
|
+
getStatePropsForItem(itemId: string): StateProps<unknown>;
|
|
17
17
|
getItemAvailableTriggers(itemId: string): Set<InteractionItemTrigger['type']>;
|
|
18
18
|
notifyItemTrigger(itemId: string, type: TriggerType): void;
|
|
19
19
|
notifyTransitionEnd(itemId: string): void;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
type StateCSSInfo = {
|
|
23
|
-
styles: Partial<Record<string,
|
|
22
|
+
type StateCSSInfo<T> = {
|
|
23
|
+
styles: Partial<Record<string, T>>;
|
|
24
24
|
transition?: string;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
type StateProps = Record<keyof ItemState<ArticleItemType>, {
|
|
28
|
-
value?:
|
|
27
|
+
type StateProps<T> = Record<keyof ItemState<ArticleItemType>, {
|
|
28
|
+
value?: T;
|
|
29
29
|
transition?: {
|
|
30
30
|
timing: string;
|
|
31
31
|
duration: number;
|