@cntrl-site/sdk-nextjs 1.8.40-alpha.0 → 1.8.40

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 (39) hide show
  1. package/lib/components/items/FileItem/ImageItem.js +22 -46
  2. package/lib/components/items/FileItem/VideoItem.js +21 -45
  3. package/lib/components/items/FileItem/useFileItem.js +5 -12
  4. package/lib/components/items/RectangleItem/RectangleItem.js +22 -78
  5. package/lib/components/items/RectangleItem/useRectangleItem.js +8 -15
  6. package/lib/interactions/CSSPropertyNameMap.js +18 -18
  7. package/lib/utils/Animator/Animator.js +19 -171
  8. package/package.json +2 -2
  9. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +1 -1
  10. package/src/components/items/ComponentItem/ComponentItem.tsx +1 -1
  11. package/src/components/items/CompoundItem/CompoundChild.tsx +2 -2
  12. package/src/components/items/CompoundItem/CompoundItem.tsx +1 -1
  13. package/src/components/items/CustomItem/CustomItem.tsx +1 -1
  14. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +2 -2
  15. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +2 -2
  16. package/src/components/items/FileItem/ImageItem.tsx +11 -60
  17. package/src/components/items/FileItem/VideoItem.tsx +12 -61
  18. package/src/components/items/FileItem/useFileItem.ts +6 -14
  19. package/src/components/items/GroupItem/GroupItem.tsx +1 -1
  20. package/src/components/items/Item.tsx +2 -2
  21. package/src/components/items/RectangleItem/RectangleItem.tsx +24 -126
  22. package/src/components/items/RectangleItem/useRectangleItem.ts +10 -17
  23. package/src/components/items/RichTextItem/RichTextItem.tsx +5 -5
  24. package/src/interactions/CSSPropertyNameMap.ts +19 -18
  25. package/src/interactions/ItemInteractionCtrl.ts +4 -4
  26. package/src/interactions/types.ts +7 -7
  27. package/src/provider/InteractionsContext.old.tsx +66 -0
  28. package/src/provider/InteractionsContext.test.tsx +97 -0
  29. package/src/utils/Animator/Animator.ts +27 -227
  30. package/src/utils/getStyleFromItemStateAndParams.ts +2 -2
  31. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  32. package/.idea/modules.xml +0 -8
  33. package/.idea/sdk-nextjs.iml +0 -12
  34. package/.idea/vcs.xml +0 -6
  35. package/lib/utils/areFillsVisible/areFillsVisible.js +0 -23
  36. package/lib/utils/getFill.js +0 -43
  37. package/src/utils/areFillsVisible/areFillsVisible.test.ts +0 -63
  38. package/src/utils/areFillsVisible/areFillsVisible.ts +0 -37
  39. package/src/utils/getFill.ts +0 -46
@@ -69,27 +69,35 @@ class Animator {
69
69
  top: (0, rangeMap_1.rangeMap)(pos, start.position, end.position, start.value.top, end.value.top, true)
70
70
  };
71
71
  }
72
- getBorderFill(values, pos) {
73
- const keyframes = this.keyframesMap[sdk_1.KeyframeType.BorderFill];
72
+ getColor(values, pos) {
73
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.Color];
74
74
  if (!keyframes || !keyframes.length)
75
75
  return values;
76
76
  if (keyframes.length === 1) {
77
77
  const [keyframe] = keyframes;
78
- return keyframe.value;
78
+ return {
79
+ color: keyframe.value.color
80
+ };
79
81
  }
80
82
  const { start, end } = this.getStartEnd(pos, keyframes);
81
- return this.getRangeGradient(start, end, pos);
83
+ return {
84
+ color: this.getRangeColor(start, end, pos)
85
+ };
82
86
  }
83
- getFill(values, pos) {
84
- const keyframes = this.keyframesMap[sdk_1.KeyframeType.Fill];
87
+ getBorderColor(values, pos) {
88
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.BorderColor];
85
89
  if (!keyframes || !keyframes.length)
86
90
  return values;
87
91
  if (keyframes.length === 1) {
88
92
  const [keyframe] = keyframes;
89
- return keyframe.value;
93
+ return {
94
+ color: keyframe.value.color
95
+ };
90
96
  }
91
97
  const { start, end } = this.getStartEnd(pos, keyframes);
92
- return this.getRangeGradient(start, end, pos);
98
+ return {
99
+ color: this.getRangeColor(start, end, pos)
100
+ };
93
101
  }
94
102
  getRadius(values, pos) {
95
103
  const keyframes = this.keyframesMap[sdk_1.KeyframeType.BorderRadius];
@@ -260,166 +268,6 @@ class Animator {
260
268
  const mixedColor = startColor.mix(endColor, rangeAmount);
261
269
  return mixedColor.fmt('oklch');
262
270
  }
263
- getRangeGradient(start, end, position) {
264
- const rangeAmount = (0, rangeMap_1.rangeMap)(position, start.position, end.position, 0, 1, true);
265
- const allLayerIds = new Set([
266
- ...start.value.map(layer => layer.id),
267
- ...end.value.map(layer => layer.id)
268
- ]);
269
- const result = [];
270
- for (const layerId of allLayerIds) {
271
- const startLayers = start.value.find(layer => layer.id === layerId);
272
- const endLayers = end.value.find(layer => layer.id === layerId);
273
- const startLayersArray = Array.isArray(startLayers) ? startLayers : startLayers ? [startLayers] : [];
274
- const endLayersArray = Array.isArray(endLayers) ? endLayers : endLayers ? [endLayers] : [];
275
- if (startLayersArray.length > 0 && endLayersArray.length === 0) {
276
- result.push(...startLayersArray);
277
- continue;
278
- }
279
- if (startLayersArray.length === 0 && endLayersArray.length > 0) {
280
- result.push(...endLayersArray);
281
- continue;
282
- }
283
- if (startLayersArray.length > 0 && endLayersArray.length > 0) {
284
- result.push(...this.interpolateFillLayerArray(startLayersArray, endLayersArray, rangeAmount));
285
- }
286
- }
287
- return result;
288
- }
289
- interpolateFillLayerArray(startLayers, endLayers, rangeAmount) {
290
- const startLayerMap = new Map(startLayers.map(layer => [layer.id, layer]));
291
- const endLayerMap = new Map(endLayers.map(layer => [layer.id, layer]));
292
- const allLayerIds = new Set([
293
- ...startLayers.map(layer => layer.id),
294
- ...endLayers.map(layer => layer.id)
295
- ]);
296
- const interpolatedLayers = [];
297
- for (const layerId of allLayerIds) {
298
- const startLayer = startLayerMap.get(layerId);
299
- const endLayer = endLayerMap.get(layerId);
300
- if (startLayer && !endLayer) {
301
- interpolatedLayers.push(startLayer);
302
- continue;
303
- }
304
- if (!startLayer && endLayer) {
305
- interpolatedLayers.push(endLayer);
306
- continue;
307
- }
308
- if (startLayer && endLayer) {
309
- interpolatedLayers.push(this.interpolateFillLayer(startLayer, endLayer, rangeAmount));
310
- }
311
- }
312
- const result = [];
313
- for (const startLayer of startLayers) {
314
- const interpolatedLayer = interpolatedLayers.find(layer => layer.id === startLayer.id);
315
- if (interpolatedLayer) {
316
- result.push(interpolatedLayer);
317
- }
318
- }
319
- for (const endLayer of endLayers) {
320
- if (!startLayerMap.has(endLayer.id)) {
321
- const interpolatedLayer = interpolatedLayers.find(layer => layer.id === endLayer.id);
322
- if (interpolatedLayer) {
323
- result.push(interpolatedLayer);
324
- }
325
- }
326
- }
327
- return result;
328
- }
329
- interpolateFillLayer(startLayer, endLayer, rangeAmount) {
330
- if (startLayer.type !== endLayer.type) {
331
- return startLayer;
332
- }
333
- switch (startLayer.type) {
334
- case 'solid': {
335
- const endSolidLayer = endLayer;
336
- const startColor = color_1.CntrlColor.parse(startLayer.value);
337
- const endColor = color_1.CntrlColor.parse(endSolidLayer.value);
338
- const mixedColor = startColor.mix(endColor, rangeAmount);
339
- return Object.assign(Object.assign({}, startLayer), { value: mixedColor.fmt('oklch') });
340
- }
341
- case 'linear-gradient': {
342
- const endLinearLayer = endLayer;
343
- const interpolatedColors = this.interpolateColorStops(startLayer.colors, endLinearLayer.colors, rangeAmount);
344
- const angle = startLayer.angle + (endLinearLayer.angle - startLayer.angle) * rangeAmount;
345
- const startPoint = [
346
- startLayer.start[0] + (endLinearLayer.start[0] - startLayer.start[0]) * rangeAmount,
347
- startLayer.start[1] + (endLinearLayer.start[1] - startLayer.start[1]) * rangeAmount
348
- ];
349
- const endPoint = [
350
- startLayer.end[0] + (endLinearLayer.end[0] - startLayer.end[0]) * rangeAmount,
351
- startLayer.end[1] + (endLinearLayer.end[1] - startLayer.end[1]) * rangeAmount
352
- ];
353
- return Object.assign(Object.assign({}, startLayer), { colors: interpolatedColors, angle, start: startPoint, end: endPoint });
354
- }
355
- case 'radial-gradient': {
356
- const endRadialLayer = endLayer;
357
- const interpolatedColors = this.interpolateColorStops(startLayer.colors, endRadialLayer.colors, rangeAmount);
358
- const angle = startLayer.angle + (endRadialLayer.angle - startLayer.angle) * rangeAmount;
359
- const center = [
360
- startLayer.center[0] + (endRadialLayer.center[0] - startLayer.center[0]) * rangeAmount,
361
- startLayer.center[1] + (endRadialLayer.center[1] - startLayer.center[1]) * rangeAmount
362
- ];
363
- const diameter = startLayer.diameter + (endRadialLayer.diameter - startLayer.diameter) * rangeAmount;
364
- return Object.assign(Object.assign({}, startLayer), { colors: interpolatedColors, angle,
365
- center,
366
- diameter });
367
- }
368
- case 'conic-gradient': {
369
- const endConicLayer = endLayer;
370
- const interpolatedColors = this.interpolateColorStops(startLayer.colors, endConicLayer.colors, rangeAmount);
371
- const angle = startLayer.angle + (endConicLayer.angle - startLayer.angle) * rangeAmount;
372
- const center = [
373
- startLayer.center[0] + (endConicLayer.center[0] - startLayer.center[0]) * rangeAmount,
374
- startLayer.center[1] + (endConicLayer.center[1] - startLayer.center[1]) * rangeAmount
375
- ];
376
- return Object.assign(Object.assign({}, startLayer), { colors: interpolatedColors, angle,
377
- center });
378
- }
379
- case 'image': {
380
- const endImageLayer = endLayer;
381
- const opacity = startLayer.opacity + (endImageLayer.opacity - startLayer.opacity) * rangeAmount;
382
- const backgroundSize = startLayer.backgroundSize + (endImageLayer.backgroundSize - startLayer.backgroundSize) * rangeAmount;
383
- return Object.assign(Object.assign({}, startLayer), { opacity,
384
- backgroundSize });
385
- }
386
- default:
387
- return startLayer;
388
- }
389
- }
390
- interpolateColorStops(startColors, endColors, rangeAmount) {
391
- const startColorMap = new Map(startColors.map(stop => [stop.id, stop]));
392
- const endColorMap = new Map(endColors.map(stop => [stop.id, stop]));
393
- const allStopIds = new Set([
394
- ...startColors.map(stop => stop.id),
395
- ...endColors.map(stop => stop.id)
396
- ]);
397
- const interpolatedStops = [];
398
- for (const stopId of allStopIds) {
399
- const startStop = startColorMap.get(stopId);
400
- const endStop = endColorMap.get(stopId);
401
- if (startStop && !endStop) {
402
- interpolatedStops.push(startStop);
403
- continue;
404
- }
405
- if (!startStop && endStop) {
406
- interpolatedStops.push(endStop);
407
- continue;
408
- }
409
- if (startStop && endStop) {
410
- const startColor = color_1.CntrlColor.parse(startStop.value);
411
- const endColor = color_1.CntrlColor.parse(endStop.value);
412
- const mixedColor = startColor.mix(endColor, rangeAmount);
413
- const position = startStop.position + (endStop.position - startStop.position) * rangeAmount;
414
- interpolatedStops.push({
415
- id: stopId,
416
- value: mixedColor.fmt('oklch'),
417
- position
418
- });
419
- }
420
- }
421
- return interpolatedStops.sort((a, b) => a.position - b.position);
422
- }
423
271
  }
424
272
  exports.Animator = Animator;
425
273
  function createKeyframesMap() {
@@ -428,8 +276,9 @@ function createKeyframesMap() {
428
276
  [sdk_1.KeyframeType.Position]: [],
429
277
  [sdk_1.KeyframeType.BorderWidth]: [],
430
278
  [sdk_1.KeyframeType.BorderRadius]: [],
279
+ [sdk_1.KeyframeType.Color]: [],
431
280
  [sdk_1.KeyframeType.Rotation]: [],
432
- [sdk_1.KeyframeType.BorderFill]: [],
281
+ [sdk_1.KeyframeType.BorderColor]: [],
433
282
  [sdk_1.KeyframeType.Opacity]: [],
434
283
  [sdk_1.KeyframeType.Scale]: [],
435
284
  [sdk_1.KeyframeType.Blur]: [],
@@ -437,7 +286,6 @@ function createKeyframesMap() {
437
286
  [sdk_1.KeyframeType.LetterSpacing]: [],
438
287
  [sdk_1.KeyframeType.WordSpacing]: [],
439
288
  [sdk_1.KeyframeType.TextColor]: [],
440
- [sdk_1.KeyframeType.FXParams]: [],
441
- [sdk_1.KeyframeType.Fill]: []
289
+ [sdk_1.KeyframeType.FXParams]: []
442
290
  };
443
291
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "1.8.40-alpha.0",
3
+ "version": "1.8.40",
4
4
  "description": "SDK for Next.js",
5
5
  "author": "arsen@momdesign.nyc",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  "@antfu/eslint-config": "^3.8.0",
32
32
  "@cntrl-site/color": "^1.0.0",
33
33
  "@cntrl-site/effects": "^1.4.0",
34
- "@cntrl-site/sdk": "^1.23.1-alpha.1",
34
+ "@cntrl-site/sdk": "^1.23.2",
35
35
  "@types/vimeo__player": "^2.18.0",
36
36
  "@vimeo/player": "^2.25.0",
37
37
  "html-react-parser": "^3.0.1",
@@ -33,7 +33,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
33
33
  useRegisterResize(ref, onResize);
34
34
  const pos = stylesMap[anchor];
35
35
  const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
36
- const stateParams = interactionCtrl?.getState<number>(['angle', 'blur', 'opacity']);
36
+ const stateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
37
37
  const blur = (stateParams?.styles?.blur ?? itemBlur) as number;
38
38
  const opacity = stateParams?.styles?.opacity ?? itemOpacity;
39
39
  const angle = stateParams?.styles?.angle ?? itemAngle;
@@ -19,7 +19,7 @@ export const ComponentItem: FC<ItemProps<TComponentItem>> = ({ item, sectionId,
19
19
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
20
20
  const { opacity: itemOpacity, blur: itemBlur } = useComponentItem(item, sectionId);
21
21
  useRegisterResize(ref, onResize);
22
- const stateParams = interactionCtrl?.getState<number>(['opacity', 'angle', 'blur']);
22
+ const stateParams = interactionCtrl?.getState(['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);
@@ -47,8 +47,8 @@ export const CompoundChild: FC<ChildItemProps> = ({ item, sectionId, isParentVis
47
47
  const itemScale = useItemScale(item, sectionId);
48
48
  const interactionCtrl = useItemInteractionCtrl(item.id);
49
49
  const triggers = useItemTriggers(interactionCtrl);
50
- const wrapperStateProps = interactionCtrl?.getState<number>(['top', 'left', 'width', 'height']);
51
- const innerStateProps = interactionCtrl?.getState<number>(['scale']);
50
+ const wrapperStateProps = interactionCtrl?.getState(['top', 'left', 'width', 'height']);
51
+ const innerStateProps = interactionCtrl?.getState(['scale']);
52
52
  const compoundSettings = layout && item.compoundSettings ? item.compoundSettings[layout] : undefined;
53
53
  const { width, height, top, left } = useItemArea(item, sectionId, {
54
54
  top: wrapperStateProps?.styles?.top as number,
@@ -19,7 +19,7 @@ export const CompoundItem: FC<ItemProps<TCompoundItem>> = ({ item, sectionId, on
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<number>(['opacity', 'angle']);
22
+ const stateParams = interactionCtrl?.getState(['opacity', 'angle']);
23
23
  const angle = getStyleFromItemStateAndParams(stateParams?.styles?.angle, itemAngle);
24
24
  const opacity = getStyleFromItemStateAndParams(stateParams?.styles?.opacity, itemOpacity);
25
25
  const isInteractive = opacity !== 0 && opacity !== undefined;
@@ -14,7 +14,7 @@ export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, section
14
14
  const layoutValues: Record<string, any>[] = [item.area];
15
15
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
16
16
  useRegisterResize(ref, onResize);
17
- const stateParams = interactionCtrl?.getState<number>(['angle']);
17
+ const stateParams = interactionCtrl?.getState(['angle']);
18
18
  const angle = stateParams?.styles?.angle ?? itemAngle;
19
19
  if (!component) return null;
20
20
  return (
@@ -31,8 +31,8 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, sectionId
31
31
  const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
32
32
  const [isCoverVisible, setIsCoverVisible] = useState(false);
33
33
  const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
34
- const wrapperStateParams = interactionCtrl?.getState<number>(['angle', 'blur', 'opacity']);
35
- const frameStateParams = interactionCtrl?.getState<number>(['radius']);
34
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
35
+ const frameStateParams = interactionCtrl?.getState(['radius']);
36
36
  const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
37
37
  const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
38
38
  const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
@@ -27,8 +27,8 @@ export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item, secti
27
27
  const [isCoverVisible, setIsCoverVisible] = useState(false);
28
28
  const [imgRef, setImgRef] = useState<HTMLImageElement | null>(null);
29
29
  const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
30
- const wrapperStateParams = interactionCtrl?.getState<number>(['angle', 'blur', 'opacity']);
31
- const frameStateParams = interactionCtrl?.getState<number>(['radius']);
30
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'blur', 'opacity']);
31
+ const frameStateParams = interactionCtrl?.getState(['radius']);
32
32
  const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
33
33
  const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
34
34
  const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
@@ -13,8 +13,6 @@ import { useElementRect } from '../../../utils/useElementRect';
13
13
  import { useLayoutContext } from '../../useLayoutContext';
14
14
  import { getStyleFromItemStateAndParams } from '../../../utils/getStyleFromItemStateAndParams';
15
15
  import { useItemFXData } from '../../../common/useItemFXData';
16
- import { getFill } from '../../../utils/getFill';
17
- import { FillLayer } from '@cntrl-site/sdk/dist/types/article/Item';
18
16
 
19
17
  export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
20
18
  const id = useId();
@@ -24,7 +22,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
24
22
  radius: itemRadius,
25
23
  strokeWidth: itemStrokeWidth,
26
24
  opacity: itemOpacity,
27
- strokeFill: itemStrokeFill,
25
+ strokeColor: itemStrokeColor,
28
26
  blur: itemBlur
29
27
  } = useFileItem(item, sectionId);
30
28
  const itemAngle = useItemAngle(item, sectionId);
@@ -40,11 +38,8 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
40
38
  const exemplary = layouts?.find(l => l.id === layoutId)?.exemplary;
41
39
  const width = area && exemplary ? area.width * exemplary : 0;
42
40
  const height = area && exemplary ? area.height * exemplary : 0;
43
- const wrapperStateParams = interactionCtrl?.getState<number>(['angle', 'opacity', 'blur']);
44
- const imgStateParams = interactionCtrl?.getState<number>(['strokeWidth', 'radius']);
45
- const stateStrokeFillParams = interactionCtrl?.getState<FillLayer[]>(['strokeFill']);
46
- const stateStrokeFillLayers = stateStrokeFillParams?.styles?.strokeFill;
47
- const strokeSolidTransition = stateStrokeFillParams?.transition ?? 'none';
41
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'opacity', 'blur']);
42
+ const imgStateParams = interactionCtrl?.getState(['strokeWidth', 'radius', 'strokeColor']);
48
43
 
49
44
  useEffect(() => {
50
45
  isInitialRef.current = false;
@@ -63,20 +58,19 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
63
58
  const rect = useElementRect(wrapperRef);
64
59
  const rectWidth = Math.floor(rect?.width ?? 0);
65
60
  const rectHeight = Math.floor(rect?.height ?? 0);
61
+ const borderColor = useMemo(() => {
62
+ const borderColor = getStyleFromItemStateAndParams(imgStateParams?.styles?.strokeColor, itemStrokeColor);
63
+ return borderColor ? CntrlColor.parse(borderColor) : undefined;
64
+ }, [itemStrokeColor, imgStateParams?.styles?.strokeColor]);
66
65
  const radius = getStyleFromItemStateAndParams(imgStateParams?.styles?.radius, itemRadius);
67
66
  const strokeWidth = getStyleFromItemStateAndParams(imgStateParams?.styles?.strokeWidth, itemStrokeWidth);
68
67
  const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
69
68
  const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
70
69
  const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
71
-
72
- const strokeValue = stateStrokeFillLayers
73
- ? getStyleFromItemStateAndParams<FillLayer>(stateStrokeFillLayers[0], itemStrokeFill?.[0])
74
- : itemStrokeFill?.[0];
75
- const stroke = strokeValue
76
- ? getFill(strokeValue) ?? 'transparent'
77
- : 'transparent';
78
70
  const inlineStyles = {
71
+ ...(borderColor ? { borderColor: `${borderColor.fmt('rgba')}` } : {}),
79
72
  ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
73
+ ...(strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}),
80
74
  transition: imgStateParams?.transition ?? 'none'
81
75
  };
82
76
  const isInteractive = opacity !== 0;
@@ -93,14 +87,6 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
93
87
  ...(opacity !== undefined ? { opacity } : {}),
94
88
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
95
89
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
96
- ...(strokeValue ? {
97
- '--stroke-background': stroke,
98
- ...(strokeValue.type === 'image' ? {
99
- '--stroke-background-position': 'center',
100
- '--stroke-background-size': strokeValue.behavior === 'repeat' ? `${strokeValue.backgroundSize}%` : strokeValue.behavior,
101
- '--stroke-background-repeat': strokeValue.behavior === 'repeat' ? 'repeat' : 'no-repeat'
102
- } : {})
103
- } : {}),
104
90
  willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset',
105
91
  transition: wrapperStateParams?.transition ?? 'none'
106
92
  }}
@@ -123,31 +109,6 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
123
109
  src={item.commonParams.url}
124
110
  />
125
111
  )}
126
- <div
127
- className={`image-border-${item.id}`}
128
- style={{
129
- position: 'absolute',
130
- inset: 0,
131
- borderRadius: 'inherit',
132
- pointerEvents: 'none',
133
- zIndex: 2,
134
- WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
135
- WebkitMaskComposite: 'xor',
136
- maskComposite: 'exclude',
137
- ...(strokeWidth !== 0 && strokeValue ? {
138
- ...(strokeWidth ? { padding: `${strokeWidth * 100}vw` } : {}),
139
- ...(strokeValue.type === 'solid' ? { transition: strokeSolidTransition, background: stroke } : {}),
140
- ...(strokeValue.type === 'image' ? {
141
- backgroundPosition: 'center',
142
- backgroundSize: strokeValue.behavior === 'repeat' ? `${strokeValue.backgroundSize}%` : strokeValue.behavior,
143
- backgroundRepeat: strokeValue.behavior === 'repeat' ? 'repeat' : 'no-repeat'
144
- } : {
145
- background: stroke,
146
- }
147
- )
148
- } : { background: stroke }),
149
- }}
150
- />
151
112
  </div>
152
113
  <JSXStyle id={id}>{`
153
114
  .image-wrapper-${item.id} {
@@ -184,19 +145,9 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
184
145
  ${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
185
146
  }
186
147
  .image-${item.id} {
148
+ border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
187
149
  border-radius: ${layoutParams.radius * 100}vw;
188
- }
189
- .image-border-${item.id} {
190
- position: absolute;
191
- inset: 0;
192
- border-radius: inherit;
193
- pointer-events: none;
194
- z-index: 2;
195
- -webkit-mask:
196
- linear-gradient(#fff 0 0) content-box,
197
- linear-gradient(#fff 0 0);
198
- -webkit-mask-composite: xor;
199
- mask-composite: exclude;
150
+ border-width: ${layoutParams.strokeWidth * 100}vw;
200
151
  }
201
152
  `);
202
153
  })}
@@ -14,8 +14,6 @@ import { getStyleFromItemStateAndParams } from '../../../utils/getStyleFromItemS
14
14
  import { useVideoFx } from '../../../utils/effects/useVideoFx';
15
15
  import { useElementRect } from '../../../utils/useElementRect';
16
16
  import { useItemFXData } from '../../../common/useItemFXData';
17
- import { getFill } from '../../../utils/getFill';
18
- import { FillLayer } from '@cntrl-site/sdk/dist/types/article/Item';
19
17
 
20
18
  export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
21
19
  const id = useId();
@@ -24,7 +22,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
24
22
  const {
25
23
  radius: itemRadius,
26
24
  strokeWidth: itemStrokeWidth,
27
- strokeFill: itemStrokeFill,
25
+ strokeColor: itemStrokeColor,
28
26
  opacity: itemOpacity,
29
27
  blur: itemBlur
30
28
  } = useFileItem(item, sectionId);
@@ -50,23 +48,17 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
50
48
  const scrollPlayback = layoutParams ? layoutParams.scrollPlayback : null;
51
49
  const layoutValues: Record<string, any>[] = [item.area, item.layoutParams];
52
50
  const hasScrollPlayback = scrollPlayback !== null;
53
- const wrapperStateParams = interactionCtrl?.getState<number>(['angle', 'opacity', 'blur']);
54
- const videoStateParams = interactionCtrl?.getState<number>(['strokeWidth', 'radius']);
55
- const stateStrokeFillParams = interactionCtrl?.getState<FillLayer[]>(['strokeFill']);
56
- const stateStrokeFillLayers = stateStrokeFillParams?.styles?.strokeFill;
57
- const strokeSolidTransition = stateStrokeFillParams?.transition ?? 'none';
51
+ const wrapperStateParams = interactionCtrl?.getState(['angle', 'opacity', 'blur']);
52
+ const videoStateParams = interactionCtrl?.getState(['strokeWidth', 'radius', 'strokeColor']);
53
+ const borderColor = useMemo(() => {
54
+ const strokeColor = getStyleFromItemStateAndParams(videoStateParams?.styles?.color, itemStrokeColor);
55
+ return strokeColor ? CntrlColor.parse(strokeColor) : undefined;
56
+ }, [videoStateParams?.styles?.color, itemStrokeColor]);
58
57
  const angle = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.angle, itemAngle);
59
58
  const opacity = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.opacity, itemOpacity);
60
59
  const blur = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.blur, itemBlur);
61
60
  const strokeWidth = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.strokeWidth, itemStrokeWidth);
62
61
  const radius = getStyleFromItemStateAndParams(wrapperStateParams?.styles?.radius, itemRadius);
63
-
64
- const strokeValue = stateStrokeFillLayers
65
- ? getStyleFromItemStateAndParams<FillLayer>(stateStrokeFillLayers[0], itemStrokeFill?.[0])
66
- : itemStrokeFill?.[0];
67
- const stroke = strokeValue
68
- ? getFill(strokeValue) ?? 'transparent'
69
- : 'transparent';
70
62
  useEffect(() => {
71
63
  isInitialRef.current = false;
72
64
  }, []);
@@ -84,6 +76,8 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
84
76
  useRegisterResize(ref, onResize);
85
77
  const inlineStyles = {
86
78
  ...(radius !== undefined ? { borderRadius: `${radius * 100}vw` } : {}),
79
+ ...(strokeWidth !== undefined ? { borderWidth: `${strokeWidth * 100}vw` } : {}),
80
+ ...(borderColor ? { borderColor: `${borderColor.toCss()}` } : {}),
87
81
  transition: videoStateParams?.transition ?? 'none'
88
82
  };
89
83
  const isInteractive = opacity !== 0;
@@ -118,14 +112,6 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
118
112
  ...(opacity !== undefined ? { opacity } : {}),
119
113
  ...(angle !== undefined ? { transform: `rotate(${angle}deg)` } : {}),
120
114
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
121
- ...(strokeValue ? {
122
- '--stroke-background': stroke,
123
- ...(strokeValue.type === 'image' ? {
124
- '--stroke-background-position': 'center',
125
- '--stroke-background-size': strokeValue.behavior === 'repeat' ? `${strokeValue.backgroundSize}%` : strokeValue.behavior,
126
- '--stroke-background-repeat': strokeValue.behavior === 'repeat' ? 'repeat' : 'no-repeat'
127
- } : {})
128
- } : {}),
129
115
  willChange: blur !== 0 && blur !== undefined ? 'transform' : 'unset',
130
116
  transition: wrapperStateParams?.transition ?? 'none'
131
117
  }}
@@ -217,31 +203,6 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
217
203
  ))}
218
204
  </>
219
205
  )}
220
- <div
221
- className={`video-border-${item.id}`}
222
- style={{
223
- position: 'absolute',
224
- inset: 0,
225
- borderRadius: 'inherit',
226
- pointerEvents: 'none',
227
- zIndex: 2,
228
- WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
229
- WebkitMaskComposite: 'xor',
230
- maskComposite: 'exclude',
231
- ...(strokeWidth !== 0 && strokeValue ? {
232
- ...(strokeWidth ? { padding: `${strokeWidth * 100}vw` } : {}),
233
- ...(strokeValue.type === 'solid' ? { transition: strokeSolidTransition, background: stroke } : {}),
234
- ...(strokeValue.type === 'image' ? {
235
- backgroundPosition: 'center',
236
- backgroundSize: strokeValue.behavior === 'repeat' ? `${strokeValue.backgroundSize}%` : strokeValue.behavior,
237
- backgroundRepeat: strokeValue.behavior === 'repeat' ? 'repeat' : 'no-repeat'
238
- } : {
239
- background: stroke,
240
- }
241
- )
242
- } : { background: stroke }),
243
- }}
244
- />
245
206
  </div>
246
207
  <JSXStyle id={id}>{`
247
208
  .video-wrapper-${item.id} {
@@ -279,7 +240,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
279
240
  border-style: solid;
280
241
  }
281
242
  .video-${item.id} {
282
- border-radius: ${radius !== undefined ? `${radius * 100}vw` : '0'};
243
+ border-color: ${itemStrokeColor};
283
244
  }
284
245
  .video-playback-wrapper {
285
246
  display: flex;
@@ -302,19 +263,9 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item, sectionId, onResize
302
263
  ${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
303
264
  }
304
265
  .video-${item.id} {
266
+ border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
305
267
  border-radius: ${layoutParams.radius * 100}vw;
306
- }
307
- .video-border-${item.id} {
308
- position: absolute;
309
- inset: 0;
310
- border-radius: inherit;
311
- pointer-events: none;
312
- z-index: 2;
313
- -webkit-mask:
314
- linear-gradient(#fff 0 0) content-box,
315
- linear-gradient(#fff 0 0);
316
- -webkit-mask-composite: xor;
317
- mask-composite: exclude;
268
+ border-width: ${layoutParams.strokeWidth * 100}vw;
318
269
  }
319
270
  `);
320
271
  })}
@@ -2,15 +2,7 @@ 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 DEFAULT_FILL = [
6
- {
7
- id: 'default',
8
- type: 'solid' as const,
9
- value: 'rgba(0, 0, 0, 1)',
10
- blendMode: 'normal'
11
- }
12
- ];
13
-
5
+ const DEFAULT_COLOR = 'rgba(0, 0, 0, 1)';
14
6
  export function useFileItem(item: ImageItem | VideoItem, sectionId: string) {
15
7
  const layoutId = useLayoutContext();
16
8
  const radius = useKeyframeValue(
@@ -51,15 +43,15 @@ export function useFileItem(item: ImageItem | VideoItem, sectionId: string) {
51
43
  [layoutId]
52
44
  );
53
45
 
54
- const strokeFill = useKeyframeValue(
46
+ const strokeColor = useKeyframeValue(
55
47
  item,
56
- KeyframeType.BorderFill,
48
+ KeyframeType.BorderColor,
57
49
  (item, layoutId) => {
58
50
  if (!layoutId) return;
59
51
  const layoutParams = item.layoutParams[layoutId];
60
- return 'strokeFill' in layoutParams ? layoutParams.strokeFill : DEFAULT_FILL;
52
+ return 'strokeColor' in layoutParams ? layoutParams.strokeColor : DEFAULT_COLOR;
61
53
  },
62
- (animator, scroll, value) => value ? animator.getBorderFill(value, scroll) : undefined,
54
+ (animator, scroll, value) => value ? animator.getBorderColor({ color: value }, scroll).color : undefined,
63
55
  sectionId,
64
56
  [layoutId]
65
57
  );
@@ -77,5 +69,5 @@ export function useFileItem(item: ImageItem | VideoItem, sectionId: string) {
77
69
  [layoutId]
78
70
  );
79
71
 
80
- return { radius, strokeWidth, opacity, strokeFill, blur };
72
+ return { radius, strokeWidth, opacity, strokeColor, blur };
81
73
  }
@@ -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<number>(['opacity', 'angle', 'blur']);
22
+ const stateParams = interactionCtrl?.getState(['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<number>(['top', 'left']);
86
- const innerStateProps = interactionCtrl?.getState<number>(['width', 'height', 'scale']);
85
+ const wrapperStateProps = interactionCtrl?.getState(['top', 'left']);
86
+ const innerStateProps = interactionCtrl?.getState(['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,