@cntrl-site/sdk-nextjs 0.29.6 → 0.30.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.
Files changed (55) hide show
  1. package/lib/common/useKeyframeValue.js +2 -2
  2. package/lib/components/ArticleWrapper.js +1 -25
  3. package/lib/components/Item.js +30 -23
  4. package/lib/components/Section.js +6 -12
  5. package/lib/components/items/CodeEmbedItem.js +67 -0
  6. package/lib/components/items/CustomItem.js +8 -2
  7. package/lib/components/items/GroupItem.js +6 -3
  8. package/lib/components/items/ImageItem.js +18 -12
  9. package/lib/components/items/RectangleItem.js +15 -12
  10. package/lib/components/items/RichTextItem.js +22 -22
  11. package/lib/components/items/VideoItem.js +17 -26
  12. package/lib/components/items/VimeoEmbed.js +7 -2
  13. package/lib/components/items/YoutubeEmbed.js +9 -4
  14. package/lib/components/items/useCodeEmbedItem.js +24 -0
  15. package/lib/components/items/useEmbedVideoItem.js +4 -3
  16. package/lib/components/items/useFileItem.js +11 -10
  17. package/lib/components/items/useGroupItem.js +4 -3
  18. package/lib/components/items/useRectangleItem.js +7 -6
  19. package/lib/components/items/useRichTextItemValues.js +6 -6
  20. package/lib/components/items/useStickyItemTop.js +5 -3
  21. package/lib/components/useItemAngle.js +2 -1
  22. package/lib/components/useItemDimensions.js +3 -10
  23. package/lib/components/useItemPosition.js +8 -8
  24. package/lib/components/useItemScale.js +2 -3
  25. package/lib/utils/ScaleAnchorMap.js +9 -9
  26. package/lib/utils/effects/useImageFx.js +2 -2
  27. package/package.json +2 -2
  28. package/src/common/useKeyframeValue.ts +3 -2
  29. package/src/components/ArticleWrapper.tsx +0 -21
  30. package/src/components/Item.tsx +43 -29
  31. package/src/components/Section.tsx +5 -12
  32. package/src/components/items/CodeEmbedItem.tsx +73 -0
  33. package/src/components/items/CustomItem.tsx +15 -3
  34. package/src/components/items/GroupItem.tsx +6 -3
  35. package/src/components/items/ImageItem.tsx +19 -13
  36. package/src/components/items/RectangleItem.tsx +17 -13
  37. package/src/components/items/RichTextItem.tsx +22 -23
  38. package/src/components/items/VideoItem.tsx +33 -45
  39. package/src/components/items/VimeoEmbed.tsx +7 -2
  40. package/src/components/items/YoutubeEmbed.tsx +11 -4
  41. package/src/components/items/useCodeEmbedItem.ts +36 -0
  42. package/src/components/items/useEmbedVideoItem.ts +4 -1
  43. package/src/components/items/useFileItem.ts +11 -7
  44. package/src/components/items/useGroupItem.ts +4 -3
  45. package/src/components/items/useRectangleItem.ts +7 -1
  46. package/src/components/items/useRichTextItemValues.ts +6 -9
  47. package/src/components/items/useStickyItemTop.ts +7 -5
  48. package/src/components/useItemAngle.ts +5 -4
  49. package/src/components/useItemDimensions.ts +6 -14
  50. package/src/components/useItemPosition.ts +10 -9
  51. package/src/components/useItemScale.ts +6 -6
  52. package/src/utils/ScaleAnchorMap.ts +1 -1
  53. package/src/utils/effects/useImageFx.ts +2 -2
  54. package/.idea/inspectionProfiles/Project_Default.xml +0 -15
  55. package/lib/components/useSectionColor.js +0 -19
@@ -1,4 +1,4 @@
1
- import { ComponentType, FC, PropsWithChildren, useContext, useEffect, useId, useRef, useState } from 'react';
1
+ import { ComponentType, FC, PropsWithChildren, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
3
  import {
4
4
  AnchorSide,
@@ -29,6 +29,9 @@ import { useLayoutContext } from './useLayoutContext';
29
29
  import { ArticleRectContext } from "../provider/ArticleRectContext";
30
30
  import { useExemplary } from "../common/useExemplary";
31
31
  import { GroupItem } from './items/GroupItem';
32
+ import { CodeEmbedItem } from './items/CodeEmbedItem';
33
+ import { AreaAnchor } from '@cntrl-site/sdk/src/types/article/ItemArea';
34
+ import { KeyframesContext } from '../provider/KeyframesContext';
32
35
 
33
36
  export interface ItemProps<I extends ItemAny> {
34
37
  item: I;
@@ -50,7 +53,8 @@ const itemsMap: Record<ArticleItemType, ComponentType<ItemProps<any>>> = {
50
53
  [ArticleItemType.YoutubeEmbed]: YoutubeEmbedItem,
51
54
  [ArticleItemType.VimeoEmbed]: VimeoEmbedItem,
52
55
  [ArticleItemType.Custom]: CustomItem,
53
- [ArticleItemType.Group]: GroupItem
56
+ [ArticleItemType.Group]: GroupItem,
57
+ [ArticleItemType.CodeEmbed]: CodeEmbedItem
54
58
  };
55
59
 
56
60
  interface RTWrapperProps {
@@ -76,17 +80,19 @@ const noop = () => null;
76
80
  export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isInGroup = false }) => {
77
81
  const itemWrapperRef = useRef<HTMLDivElement | null>(null);
78
82
  const rectObserver = useContext(ArticleRectContext);
83
+ const keyframesRepo = useContext(KeyframesContext);
79
84
  const id = useId();
85
+ const keyframes = useMemo(() => keyframesRepo.getItemKeyframes(item.id), [keyframesRepo, item.id]);
80
86
  const { layouts } = useCntrlContext();
81
87
  const layout = useLayoutContext();
82
88
  const exemplary = useExemplary();
83
89
  const [wrapperHeight, setWrapperHeight] = useState<undefined | number>(undefined);
84
90
  const [itemHeight, setItemHeight] = useState<undefined | number>(undefined);
85
- const { scale, scaleAnchor } = useItemScale(item, sectionId);
86
- const { top, left, bottom } = useItemPosition(item, sectionId);
91
+ const scale = useItemScale(item, sectionId);
92
+ const position = useItemPosition(item, sectionId);
87
93
  const sectionHeight = useSectionHeightData(sectionId);
88
94
  const stickyTop = useStickyItemTop(item, sectionHeight, sectionId);
89
- const { width, height } = useItemDimensions(item, sectionId);
95
+ const dimensions = useItemDimensions(item, sectionId);
90
96
  const layoutValues: Record<string, any>[] = [item.area, item.hidden, item.state.hover];
91
97
  const isInitialRef = useRef(true);
92
98
  layoutValues.push(item.sticky);
@@ -105,7 +111,7 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
105
111
  const handleItemResize = (height: number) => {
106
112
  if (!layout) return;
107
113
  const sticky = item.sticky[layout];
108
- if (!sticky) {
114
+ if (!sticky || stickyTop === undefined) {
109
115
  setWrapperHeight(undefined);
110
116
  return;
111
117
  }
@@ -124,36 +130,38 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
124
130
  }, []);
125
131
 
126
132
  const isRichText = isItemType(item, ArticleItemType.RichText);
127
-
128
- if (!layout) return null;
129
- const styles = {
130
- top: `${stickyTop * 100}vw`,
131
- height: isRichText && itemHeight ? `${itemHeight * 100}vw` : 'unset'
132
- };
133
-
134
133
  return (
135
134
  <div
136
135
  className={`item-wrapper-${item.id}`}
137
136
  ref={itemWrapperRef}
138
- style={isInitialRef.current ? {} : { top, left, bottom, ...(wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {}) }}
137
+ style={{
138
+ ...(position ? { top: position.top } : {}),
139
+ ...(position ? { left: position.left } : {}),
140
+ ...(position ? { bottom: position.bottom } : {}),
141
+ ...(wrapperHeight !== undefined ? { height: `${wrapperHeight * 100}vw` } : {})
142
+ }}
139
143
  >
140
144
  <div
141
145
  suppressHydrationWarning={true}
142
146
  className={`item-${item.id}`}
143
- style={isInitialRef.current ? {} : styles }
147
+ style={{
148
+ opacity: (keyframes.length !== 0 && !layout) ? 0 : 1,
149
+ top: `${stickyTop * 100}vw`,
150
+ height: isRichText && itemHeight ? `${itemHeight * 100}vw` : 'unset'
151
+ }}
144
152
  >
145
153
  <RichTextWrapper isRichText={isRichText}>
146
154
  <div
147
155
  className={`item-${item.id}-inner`}
148
156
  style={{
149
- width: `${sizingAxis.x === 'manual'
150
- ? isRichText
151
- ? `${width * exemplary}px`
152
- : `${width * 100}vw`
153
- : 'max-content'}`,
154
- height: `${sizingAxis.y === 'manual' ? `${height * 100}vw` : 'unset'}`,
155
- transform: `scale(${scale})`,
156
- transformOrigin: ScaleAnchorMap[scaleAnchor]
157
+ ...(dimensions ? {
158
+ width: `${sizingAxis.x === 'manual'
159
+ ? isRichText
160
+ ? `${dimensions.width * exemplary}px`
161
+ : `${dimensions.width * 100}vw`
162
+ : 'max-content'}`,
163
+ height: `${sizingAxis.y === 'manual' ? `${dimensions.height * 100}vw` : 'unset'}` } : {}),
164
+ ...(scale !== undefined ? { transform: `scale(${scale})` } : {}),
157
165
  }}
158
166
  >
159
167
  <ItemComponent item={item} sectionId={sectionId} onResize={handleItemResize} articleHeight={articleHeight} />
@@ -161,22 +169,28 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
161
169
  </RichTextWrapper>
162
170
  </div>
163
171
  <JSXStyle id={id}>{`
164
- ${getLayoutStyles(layouts, layoutValues, ([area, hidden, hoverParams, sticky, sectionHeight, layoutParams]) => {
172
+ ${getLayoutStyles(layouts, layoutValues, ([area, hidden, hoverParams, sticky, sectionHeight, layoutParams], exemplary) => {
165
173
  const sizingAxis = parseSizing(layoutParams.sizing);
166
- const isScreenBasedBottom = area.positionType === PositionType.ScreenBased && area.anchorSide === AnchorSide.Bottom;
174
+ const isScreenBasedBottom = area.positionType === PositionType.ScreenBased && area.anchorSide === AnchorSide.Bottom;
175
+ const scaleAnchor = area.scaleAnchor as AreaAnchor;
167
176
  return (`
168
177
  .item-${item.id} {
169
178
  position: ${sticky ? 'sticky' : 'absolute'};
170
179
  top: ${sticky ? `${getAnchoredItemTop(area.top - sticky.from, sectionHeight, area.anchorSide)}` : 0};
171
180
  pointer-events: auto;
181
+ transition: opacity 0.1s linear;
172
182
  display: ${hidden ? 'none' : 'block'};
173
183
  height: fit-content;
174
184
  }
175
185
  .item-${item.id}-inner {
176
186
  transition: ${getTransitions(['width', 'height', 'scale'], hoverParams)};
177
- width: ${sizingAxis.x === 'manual' ? `${area.width * 100}vw` : 'max-content'};
187
+ width: ${sizingAxis.x === 'manual'
188
+ ? isRichText
189
+ ? `${area.width * exemplary}px`
190
+ : `${area.width * 100}vw`
191
+ : 'max-content'};
178
192
  height: ${sizingAxis.y === 'manual' ? `${area.height * 100}vw` : 'unset'};
179
- transform: scale(${scale});
193
+ transform: scale(${area.scale});
180
194
  transform-origin: ${ScaleAnchorMap[scaleAnchor]};
181
195
  --webkit-backface-visibility: hidden;
182
196
  }
@@ -191,10 +205,10 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isI
191
205
  transition: ${getTransitions(['left', 'top'], hoverParams)};
192
206
  }
193
207
  .item-${item.id}-inner:hover {
194
- ${getHoverStyles(['width', 'height', 'scale'], hoverParams)}
208
+ ${getHoverStyles(['width', 'height', 'scale'], hoverParams)};
195
209
  }
196
210
  .item-wrapper-${item.id}:hover {
197
- ${getHoverStyles(['left', 'top'], hoverParams, area.anchorSide)}
211
+ ${getHoverStyles(['left', 'top'], hoverParams, area.anchorSide)};
198
212
  }
199
213
  `);
200
214
  })}
@@ -8,10 +8,11 @@ import {
8
8
  SectionHeightMode
9
9
  } from '@cntrl-site/sdk';
10
10
  import { useCntrlContext } from '../provider/useCntrlContext';
11
- import { useSectionColor } from './useSectionColor';
12
11
  import { useSectionRegistry } from '../utils/ArticleRectManager/useSectionRegistry';
12
+ import { CntrlColor } from '@cntrl-site/color';
13
13
 
14
14
  type SectionChild = ReactElement<any, any>;
15
+ const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
15
16
 
16
17
  interface Props {
17
18
  section: TSection;
@@ -23,7 +24,7 @@ export const Section: FC<Props> = ({ section, data, children }) => {
23
24
  const id = useId();
24
25
  const sectionRef = useRef<HTMLDivElement | null>(null);
25
26
  const { layouts, customSections } = useCntrlContext();
26
- const backgroundColor = useSectionColor(section.color);
27
+ const layoutValues: Record<string, any>[] = [section.height, section.color];
27
28
  const SectionComponent = section.name ? customSections.getComponent(section.name) : undefined;
28
29
  useSectionRegistry(section.id, sectionRef.current);
29
30
  const getSectionVisibilityStyles = () => {
@@ -48,29 +49,21 @@ export const Section: FC<Props> = ({ section, data, children }) => {
48
49
  <div
49
50
  className={`section-${section.id}`}
50
51
  id={section.name}
51
- style={{
52
- backgroundColor: backgroundColor.toCss()
53
- }}
54
52
  ref={sectionRef}
55
53
  >
56
54
  {children}
57
55
  </div>
58
56
  <JSXStyle id={id}>{`
59
57
  ${
60
- getLayoutStyles(layouts, [section.height], ([height]) => (`
58
+ getLayoutStyles(layouts, layoutValues, ([height, color]) => (`
61
59
  .section-${section.id} {
62
60
  height: ${getSectionHeight(height)};
63
61
  position: relative;
64
- --webkit-backface-visibility: hidden;
62
+ background-color: ${CntrlColor.parse(color ?? DEFAULT_COLOR).fmt('rgba')};
65
63
  }`
66
64
  ))
67
65
  }
68
66
  ${getSectionVisibilityStyles()}
69
- @supports not (color: oklch(42% 0.3 90 / 1)) {
70
- .section-${section.id} {
71
- background-color: ${backgroundColor.fmt('rgba')};
72
- }
73
- }
74
67
  `}</JSXStyle>
75
68
  </>
76
69
  );
@@ -0,0 +1,73 @@
1
+ import { ArticleItemType, getLayoutStyles, CodeEmbedItem as TCodeEmbedItem, AreaAnchor } from '@cntrl-site/sdk';
2
+ import { FC, useId, useState } from 'react';
3
+ import { useCntrlContext } from '../../provider/useCntrlContext';
4
+ import { ItemProps } from '../Item';
5
+ import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
6
+ import JSXStyle from 'styled-jsx/style';
7
+ import { useRegisterResize } from "../../common/useRegisterResize";
8
+ import { useItemAngle } from '../useItemAngle';
9
+ import { LinkWrapper } from '../LinkWrapper';
10
+ import { useCodeEmbedItem } from './useCodeEmbedItem';
11
+
12
+ const stylesMap = {
13
+ [AreaAnchor.TopLeft]: {},
14
+ [AreaAnchor.TopCenter]: { justifyContent: 'center' },
15
+ [AreaAnchor.TopRight]: { justifyContent: 'flex-end' },
16
+ [AreaAnchor.MiddleLeft]: { alignItems: 'center' },
17
+ [AreaAnchor.MiddleCenter]: { justifyContent: 'center', alignItems: 'center' },
18
+ [AreaAnchor.MiddleRight]: { justifyContent: 'flex-end', alignItems: 'center' },
19
+ [AreaAnchor.BottomLeft]: { alignItems: 'flex-end' },
20
+ [AreaAnchor.BottomCenter]: { justifyContent: 'center', alignItems: 'flex-end' },
21
+ [AreaAnchor.BottomRight]: { justifyContent: 'flex-end', alignItems: 'flex-end' }
22
+ };
23
+
24
+ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId, onResize }) => {
25
+ const id = useId();
26
+ const { layouts } = useCntrlContext();
27
+ const { anchor, blur, opacity } = useCodeEmbedItem(item, sectionId);
28
+ const angle = useItemAngle(item, sectionId);
29
+ const { html } = item.commonParams;
30
+ const [ref, setRef] = useState<HTMLDivElement | null>(null);
31
+ useRegisterResize(ref, onResize);
32
+ const pos = stylesMap[anchor];
33
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state.hover];
34
+
35
+ return (
36
+ <LinkWrapper url={item.link?.url} target={item.link?.target}>
37
+ <div
38
+ className={`embed-wrapper-${item.id}`}
39
+ style={{ opacity: `${opacity}`, transform: `rotate(${angle}deg)`, filter: `blur(${blur * 100}vw)`, ...pos}}
40
+ ref={setRef}
41
+ >
42
+ <div className="embed" dangerouslySetInnerHTML={{ __html: html }}></div>
43
+ </div>
44
+ <JSXStyle id={id}>{`
45
+ .embed -wrapper-${item.id} {
46
+ position: absolute;
47
+ width: 100%;
48
+ height: 100%;
49
+ }
50
+ .embed {
51
+ width: 100%;
52
+ height: 100%;
53
+ z-index: 1;
54
+ border: none;
55
+ overflow: hidden;
56
+ }
57
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
58
+ return (`
59
+ .embed-wrapper-${item.id} {
60
+ opacity: ${layoutParams.opacity};
61
+ transform: rotate(${area.angle}deg);
62
+ filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
63
+ transition: ${getTransitions<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)};
64
+ }
65
+ .embed-wrapper-${item.id}:hover {
66
+ ${getHoverStyles<ArticleItemType.CodeEmbed>(['angle', 'blur', 'opacity'], hoverParams)}
67
+ }
68
+ `);
69
+ })}
70
+ `}</JSXStyle>
71
+ </LinkWrapper>
72
+ );
73
+ };
@@ -5,21 +5,33 @@ import { ItemProps } from '../Item';
5
5
  import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
6
6
  import JSXStyle from 'styled-jsx/style';
7
7
  import { useRegisterResize } from "../../common/useRegisterResize";
8
+ import { useItemAngle } from '../useItemAngle';
8
9
 
9
- export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize }) => {
10
+ export const CustomItem: FC<ItemProps<TCustomItem>> = ({ item, onResize, sectionId }) => {
10
11
  const sdk = useCntrlContext();
11
12
  const { layouts } = useCntrlContext();
13
+ const angle = useItemAngle(item, sectionId);
12
14
  const component = sdk.customItems.get(item.commonParams.name);
15
+ const layoutValues: Record<string, any>[] = [item.area, item.state.hover];
13
16
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
14
17
  useRegisterResize(ref, onResize);
15
18
  if (!component) return null;
16
19
  return (
17
20
  <>
18
- <div className={`custom-component-${item.id}`} ref={setRef}>{component({})}</div>
21
+ <div
22
+ className={`custom-component-${item.id}`}
23
+ ref={setRef}
24
+ style={{
25
+ transform: `rotate(${angle}deg)`,
26
+ }}
27
+ >
28
+ {component({})}
29
+ </div>
19
30
  <JSXStyle id={item.id}>
20
- {`${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
31
+ {`${getLayoutStyles(layouts, layoutValues, ([area, hoverParams]) => {
21
32
  return (`
22
33
  .custom-component-${item.id} {
34
+ transform: rotate(${area.angle}deg);
23
35
  transition: ${getTransitions<ArticleItemType.Custom>(['angle'], hoverParams)};
24
36
  height: 100%;
25
37
  width: 100%;
@@ -15,6 +15,7 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
15
15
  const angle = useItemAngle(item, sectionId);
16
16
  const { layouts } = useCntrlContext();
17
17
  const { opacity } = useGroupItem(item, sectionId);
18
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state.hover];
18
19
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
19
20
  useRegisterResize(ref, onResize);
20
21
 
@@ -46,13 +47,15 @@ export const GroupItem: FC<ItemProps<TGroupItem>> = ({ item, sectionId, onResize
46
47
  height: 100%;
47
48
  box-sizing: border-box;
48
49
  }
49
- ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
50
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
50
51
  return (`
51
52
  .group-${item.id} {
52
- transition: ${getTransitions<ArticleItemType.Group>(['opacity'], hoverParams)};
53
+ opacity: ${layoutParams.opacity};
54
+ transform: rotate(${area.angle}deg);
55
+ transition: ${getTransitions<ArticleItemType.Group>(['opacity', 'angle'], hoverParams)};
53
56
  }
54
57
  .group-${item.id}:hover {
55
- ${getHoverStyles<ArticleItemType.Group>(['opacity'], hoverParams)}
58
+ ${getHoverStyles<ArticleItemType.Group>(['opacity', 'angle'], hoverParams)};
56
59
  }
57
60
  `);
58
61
  })}
@@ -1,4 +1,4 @@
1
- import { FC, useId, useMemo, useRef, useState } from 'react';
1
+ import { FC, useEffect, useId, useMemo, useRef, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
3
  import { CntrlColor } from '@cntrl-site/color';
4
4
  import { ArticleItemType, getLayoutStyles, ImageItem as TImageItem } from '@cntrl-site/sdk';
@@ -33,6 +33,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
33
33
  useRegisterResize(ref, onResize);
34
34
  const { url, hasGLEffect, fragmentShader } = item.commonParams;
35
35
  const fxCanvas = useRef<HTMLCanvasElement | null>(null);
36
+ const isInitialRef = useRef(true);
36
37
  const controls = item.commonParams.FXControls ?? [];
37
38
  const controlsVariables = controls.map((c) => `uniform ${c.type} ${c.shaderParam};`)
38
39
  .join('\n');
@@ -40,14 +41,18 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
40
41
  acc[control.shaderParam] = control.value;
41
42
  return acc;
42
43
  }, {});
44
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state.hover];
43
45
  const fullShaderCode = `${baseVariables}\n${controlsVariables}\n${fragmentShader}`;
44
46
  const area = layoutId ? item.area[layoutId] : null;
45
47
  const exemplary = layouts?.find(l => l.id === layoutId)?.exemplary;
46
48
  const width = area && exemplary ? area.width * exemplary : 0;
47
49
  const height = area && exemplary ? area.height * exemplary : 0;
50
+ useEffect(() => {
51
+ isInitialRef.current = false;
52
+ }, []);
48
53
  useImageFx(
49
54
  fxCanvas.current,
50
- hasGLEffect ?? false,
55
+ !!(hasGLEffect && !isInitialRef.current),
51
56
  {
52
57
  imageUrl: url,
53
58
  fragmentShader: fullShaderCode,
@@ -63,7 +68,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
63
68
  const inlineStyles = {
64
69
  borderRadius: `${radius * 100}vw`,
65
70
  borderWidth: `${strokeWidth * 100}vw`,
66
- borderColor: `${borderColor.toCss()}`
71
+ borderColor: `${borderColor.fmt('rgba')}`
67
72
  };
68
73
  return (
69
74
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
@@ -81,7 +86,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
81
86
  <canvas
82
87
  style={inlineStyles}
83
88
  ref={fxCanvas}
84
- className="img-canvas"
89
+ className={`img-canvas image-${item.id}`}
85
90
  width={rectWidth}
86
91
  height={rectHeight}
87
92
  />
@@ -95,11 +100,6 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
95
100
  )}
96
101
  </div>
97
102
  <JSXStyle id={id}>{`
98
- @supports not (color: oklch(42% 0.3 90 / 1)) {
99
- .image-${item.id} {
100
- border-color: ${borderColor.fmt('rgba')};
101
- }
102
- }
103
103
  .image-wrapper-${item.id} {
104
104
  position: absolute;
105
105
  width: 100%;
@@ -125,19 +125,25 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item, sectionId, onResize
125
125
  border-width: 0;
126
126
  box-sizing: border-box;
127
127
  }
128
- ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
128
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
129
129
  return (`
130
130
  .image-wrapper-${item.id} {
131
+ opacity: ${layoutParams.opacity};
132
+ transform: rotate(${area.angle}deg);
133
+ filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
131
134
  transition: ${getTransitions<ArticleItemType.Image>(['angle', 'opacity', 'blur'], hoverParams)};
132
135
  }
133
136
  .image-wrapper-${item.id}:hover {
134
- ${getHoverStyles<ArticleItemType.Image>(['angle', 'opacity', 'blur'], hoverParams)}
137
+ ${getHoverStyles<ArticleItemType.Image>(['angle', 'opacity', 'blur'], hoverParams)};
135
138
  }
136
139
  .image-${item.id} {
140
+ border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
141
+ border-radius: ${layoutParams.radius * 100}vw;
142
+ border-width: ${layoutParams.strokeWidth * 100}vw;
137
143
  transition: ${getTransitions<ArticleItemType.Image>(['strokeWidth', 'radius', 'strokeColor'], hoverParams)};
138
144
  }
139
- .image-wrapper-${item.id}:hover .image {
140
- ${getHoverStyles<ArticleItemType.Image>(['strokeWidth', 'radius', 'strokeColor'], hoverParams)}
145
+ .image-wrapper-${item.id}:hover .image, .image-wrapper-${item.id}:hover .img-canvas {
146
+ ${getHoverStyles<ArticleItemType.Image>(['strokeWidth', 'radius', 'strokeColor'], hoverParams)};
141
147
  }
142
148
  `);
143
149
  })}
@@ -1,4 +1,4 @@
1
- import { FC, useId, useMemo, useState } from 'react';
1
+ import { FC, useEffect, useId, useMemo, useRef, useState } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
3
  import { CntrlColor } from '@cntrl-site/color';
4
4
  import { RectangleItem as TRectangleItem, getLayoutStyles, ArticleItemType } from '@cntrl-site/sdk';
@@ -9,6 +9,7 @@ import { useItemAngle } from '../useItemAngle';
9
9
  import { useCntrlContext } from '../../provider/useCntrlContext';
10
10
  import { getHoverStyles, getTransitions } from '../../utils/HoverStyles/HoverStyles';
11
11
  import { useRegisterResize } from "../../common/useRegisterResize";
12
+ import { useLayoutContext } from '../useLayoutContext';
12
13
 
13
14
  export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId, onResize }) => {
14
15
  const id = useId();
@@ -17,9 +18,10 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
17
18
  const angle = useItemAngle(item, sectionId);
18
19
  const backgroundColor = useMemo(() => CntrlColor.parse(fillColor), [fillColor]);
19
20
  const borderColor = useMemo(() => CntrlColor.parse(strokeColor), [strokeColor]);
21
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state.hover];
20
22
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
21
23
  useRegisterResize(ref, onResize);
22
- const backdropFilterValue = backdropBlur !== 0 ? `blur(${backdropBlur * 100}vw)`: 'unset';
24
+ const backdropFilterValue = backdropBlur ? `blur(${backdropBlur * 100}vw)`: undefined;
23
25
 
24
26
  return (
25
27
  <LinkWrapper url={item.link?.url} target={item.link?.target}>
@@ -28,23 +30,17 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
28
30
  className={`rectangle-${item.id}`}
29
31
  ref={setRef}
30
32
  style={{
31
- backgroundColor: `${backgroundColor.toCss()}`,
33
+ backgroundColor: `${backgroundColor.fmt('rgba')}`,
32
34
  borderRadius: `${radius * 100}vw`,
33
35
  borderWidth: `${strokeWidth * 100}vw`,
34
36
  borderColor: `${borderColor.toCss()}`,
35
37
  transform: `rotate(${angle}deg)`,
36
- filter: blur !== 0 ? `blur(${blur * 100}vw)` : 'unset',
38
+ filter: `blur(${blur * 100}vw)`,
37
39
  backdropFilter: backdropFilterValue,
38
- WebkitBackdropFilter: backdropFilterValue,
40
+ WebkitBackdropFilter: backdropFilterValue
39
41
  }}
40
42
  />
41
43
  <JSXStyle id={id}>{`
42
- @supports not (color: oklch(42% 0.3 90 / 1)) {
43
- .rectangle-${item.id} {
44
- background-color: ${backgroundColor.fmt('rgba')};
45
- border-color: ${borderColor.fmt('rgba')};
46
- }
47
- }
48
44
  .rectangle-${item.id} {
49
45
  position: absolute;
50
46
  width: 100%;
@@ -52,13 +48,21 @@ export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item, sectionId,
52
48
  border-style: solid;
53
49
  box-sizing: border-box;
54
50
  }
55
- ${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
51
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hoverParams]) => {
56
52
  return (`
57
53
  .rectangle-${item.id} {
54
+ background-color: ${CntrlColor.parse(layoutParams.fillColor).fmt('rgba')};
55
+ border-color: ${CntrlColor.parse(layoutParams.strokeColor).fmt('rgba')};
56
+ border-radius: ${layoutParams.radius * 100}vw;
57
+ border-width: ${layoutParams.strokeWidth * 100}vw;
58
+ transform: rotate(${area.angle}deg);
59
+ filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
60
+ backdrop-filter: ${layoutParams.backdropFilter !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
61
+ -webkit-backdrop-filter: ${layoutParams.backdropFilter !== 0 ? `blur(${layoutParams.blur * 100}vw)` : 'unset'};
58
62
  transition: ${getTransitions<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)};
59
63
  }
60
64
  .rectangle-${item.id}:hover {
61
- ${getHoverStyles<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)}
65
+ ${getHoverStyles<ArticleItemType.Rectangle>(['angle', 'fillColor', 'strokeWidth', 'radius', 'strokeColor', 'blur', 'backdropBlur'], hoverParams)};
62
66
  }
63
67
  `);
64
68
  })}
@@ -10,14 +10,17 @@ import { useRichTextItemValues } from './useRichTextItemValues';
10
10
  import { useRegisterResize } from "../../common/useRegisterResize";
11
11
  import { getFontFamilyValue } from '../../utils/getFontFamilyValue';
12
12
  import { useExemplary } from '../../common/useExemplary';
13
+ import { useItemAngle } from '../useItemAngle';
13
14
 
14
15
  export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, onResize }) => {
15
16
  const [content, styles] = useRichTextItem(item);
16
17
  const id = useId();
17
18
  const [ref, setRef] = useState<HTMLDivElement | null>(null);
18
19
  const { layouts } = useCntrlContext();
19
- const { angle, blur, wordSpacing, letterSpacing, color } = useRichTextItemValues(item, sectionId);
20
+ const angle = useItemAngle(item, sectionId);
21
+ const { blur, wordSpacing, letterSpacing, color } = useRichTextItemValues(item, sectionId);
20
22
  const textColor = useMemo(() => CntrlColor.parse(color), [color]);
23
+ const layoutValues: Record<string, any>[] = [item.area, item.layoutParams, item.state.hover];
21
24
  const exemplary = useExemplary();
22
25
  useRegisterResize(ref, onResize);
23
26
 
@@ -31,45 +34,41 @@ export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item, sectionId, on
31
34
  filter: `blur(${blur * exemplary}px)`,
32
35
  letterSpacing: `${letterSpacing * exemplary}px`,
33
36
  wordSpacing: `${wordSpacing * exemplary}px`,
34
- color: `${textColor.toCss()}`
37
+ color: `${textColor.fmt('rgba')}`
35
38
  }}
36
39
  >
37
40
  {content}
38
41
  </div>
39
42
  <JSXStyle id={id}>
40
43
  {styles}
41
- {`${getLayoutStyles(layouts, [item.state.hover], ([hoverParams]) => {
44
+ {`${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hoverParams], exemplary) => {
45
+ const color = CntrlColor.parse(layoutParams.color);
42
46
  return (`
43
47
  .rich-text-wrapper-${item.id} {
48
+ font-size: ${Math.round(layoutParams.fontSize * exemplary)}px;
49
+ line-height: ${layoutParams.lineHeight * exemplary}px;
50
+ letter-spacing: ${layoutParams.letterSpacing * exemplary}px;
51
+ word-spacing: ${layoutParams.wordSpacing * exemplary}px;
52
+ font-family: ${getFontFamilyValue(layoutParams.typeFace)};
53
+ font-weight: ${layoutParams.fontWeight};
54
+ font-style: ${layoutParams.fontStyle ? layoutParams.fontStyle : 'normal'};
55
+ vertical-align: ${layoutParams.verticalAlign};
56
+ font-variant: ${layoutParams.fontVariant};
57
+ color: ${color.fmt('rgba')};
58
+ transform: rotate(${area.angle}deg);
59
+ filter: ${layoutParams.blur !== 0 ? `blur(${layoutParams.blur * exemplary}px)` : 'unset'};
60
+ text-transform: ${layoutParams.textTransform};
44
61
  transition: ${getTransitions<ArticleItemType.RichText>(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color'], hoverParams)};
45
62
  }
46
63
  .rich-text-wrapper-${item.id}:hover {
47
- ${getHoverStyles<ArticleItemType.RichText>(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color'], hoverParams)}
48
- }
49
- `);
50
- })}`}
51
- {`${getLayoutStyles(layouts, [item.layoutParams], ([layoutParams], exemplary) => {
52
- const color = CntrlColor.parse(layoutParams.color);
53
- return (`
54
- .rich-text-wrapper-${item.id} {
55
- font-size: ${Math.round(layoutParams.fontSize * exemplary)}px;
56
- line-height: ${layoutParams.lineHeight * exemplary}px;
57
- letter-spacing: ${layoutParams.letterSpacing * exemplary}px;
58
- word-spacing: ${layoutParams.wordSpacing * exemplary}px;
59
- font-family: ${getFontFamilyValue(layoutParams.typeFace)};
60
- font-weight: ${layoutParams.fontWeight};
61
- font-style: ${layoutParams.fontStyle ? layoutParams.fontStyle : 'normal'};
62
- vertical-align: ${layoutParams.verticalAlign};
63
- font-variant: ${layoutParams.fontVariant};
64
- color: ${color.toCss()};
65
- text-transform: ${layoutParams.textTransform};
64
+ ${getHoverStyles<ArticleItemType.RichText>(['angle', 'blur', 'letterSpacing', 'wordSpacing', 'color'], hoverParams)};
66
65
  }
67
66
  @supports not (color: oklch(42% 0.3 90 / 1)) {
68
67
  .rich-text-wrapper-${item.id} {
69
68
  color: ${color.fmt('rgba')};
70
69
  }
71
70
  }
72
- `);
71
+ `);
73
72
  })}`}
74
73
  </JSXStyle>
75
74
  </>