@cntrl-site/sdk-nextjs 0.5.1 → 0.7.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 (34) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +15 -0
  2. package/cntrl-site-sdk-nextjs-0.4.3.tgz +0 -0
  3. package/cntrl-site-sdk-nextjs-0.7.0.tgz +0 -0
  4. package/lib/common/useCurrentLayout.js +1 -5
  5. package/lib/common/useKeyframeValue.js +3 -0
  6. package/lib/components/Section.js +5 -5
  7. package/lib/components/items/ImageItem.js +4 -1
  8. package/lib/components/items/RectangleItem.js +6 -2
  9. package/lib/components/items/RichTextItem.js +4 -10
  10. package/lib/components/items/VideoItem.js +4 -1
  11. package/lib/components/items/useRectangleItem.js +2 -2
  12. package/lib/components/items/useRichTextItem.js +18 -0
  13. package/lib/components/useSectionColor.js +15 -0
  14. package/lib/utils/Animator/Animator.js +11 -21
  15. package/lib/utils/RichTextConverter/RichTextConverter.js +6 -1
  16. package/lib/utils/generateTypePresetStyles/generateTypePresetStyles.js +5 -5
  17. package/package.json +3 -3
  18. package/src/common/useCurrentLayout.ts +1 -6
  19. package/src/common/useKeyframeValue.ts +4 -0
  20. package/src/components/Section.tsx +16 -9
  21. package/src/components/items/ImageItem.tsx +4 -3
  22. package/src/components/items/RectangleItem.tsx +7 -4
  23. package/src/components/items/RichTextItem.tsx +10 -18
  24. package/src/components/items/VideoItem.tsx +4 -3
  25. package/src/components/items/useFileItem.ts +1 -1
  26. package/src/components/items/useRectangleItem.ts +5 -4
  27. package/src/components/items/useRichTextItem.ts +18 -0
  28. package/src/components/useSectionColor.ts +14 -0
  29. package/src/utils/Animator/Animator.ts +9 -19
  30. package/src/utils/RichTextConverter/RichTextConverter.tsx +7 -1
  31. package/src/utils/generateTypePresetStyles/__mock__/presetMock.ts +14 -30
  32. package/src/utils/generateTypePresetStyles/generateTypePresetStyles.test.ts +12 -12
  33. package/src/utils/generateTypePresetStyles/generateTypePresetStyles.ts +6 -6
  34. package/cntrl-site-sdk-nextjs-0.5.0.tgz +0 -0
@@ -0,0 +1,15 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <profile version="1.0">
3
+ <option name="myName" value="Project Default" />
4
+ <inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
5
+ <option name="myValues">
6
+ <value>
7
+ <list size="1">
8
+ <item index="0" class="java.lang.String" itemvalue="jsx" />
9
+ </list>
10
+ </value>
11
+ </option>
12
+ <option name="myCustomValuesEnabled" value="true" />
13
+ </inspection_tool>
14
+ </profile>
15
+ </component>
Binary file
Binary file
@@ -6,7 +6,6 @@ const react_1 = require("react");
6
6
  const ArticleRectContext_1 = require("../provider/ArticleRectContext");
7
7
  const useCurrentLayout = () => {
8
8
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
9
- const [initialLayout] = layouts;
10
9
  const articleRectObserver = (0, react_1.useContext)(ArticleRectContext_1.ArticleRectContext);
11
10
  const layoutRanges = (0, react_1.useMemo)(() => {
12
11
  const sorted = layouts.slice().sort((la, lb) => la.startsWith - lb.startsWith);
@@ -25,7 +24,7 @@ const useCurrentLayout = () => {
25
24
  const getCurrentLayout = (0, react_1.useCallback)((articleWidth) => {
26
25
  return layoutRanges.find(l => articleWidth >= l.start && articleWidth < l.end).layoutId;
27
26
  }, [layoutRanges]);
28
- const [layoutId, setLayoutId] = (0, react_1.useState)(getCurrentLayout(isServer() ? 0 : window.innerWidth));
27
+ const [layoutId, setLayoutId] = (0, react_1.useState)(getCurrentLayout(0));
29
28
  (0, react_1.useEffect)(() => {
30
29
  if (!articleRectObserver)
31
30
  return;
@@ -37,6 +36,3 @@ const useCurrentLayout = () => {
37
36
  return layoutId;
38
37
  };
39
38
  exports.useCurrentLayout = useCurrentLayout;
40
- function isServer() {
41
- return typeof window === 'undefined';
42
- }
@@ -38,6 +38,9 @@ const useKeyframeValue = (item, itemParamsGetter, animatorGetter, deps = emptyDe
38
38
  }));
39
39
  return new Animator_1.Animator(animationData);
40
40
  }, [keyframes, layoutId]);
41
+ (0, react_1.useEffect)(() => {
42
+ setAdjustedValue(paramValue);
43
+ }, [paramValue]);
41
44
  (0, react_1.useEffect)(() => {
42
45
  if (!animator || !articleRectObserver)
43
46
  return;
@@ -4,8 +4,10 @@ exports.Section = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const sdk_1 = require("@cntrl-site/sdk");
6
6
  const useCntrlContext_1 = require("../provider/useCntrlContext");
7
+ const useSectionColor_1 = require("./useSectionColor");
7
8
  const Section = ({ section, children }) => {
8
9
  const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
10
+ const backgroundColor = (0, useSectionColor_1.useSectionColor)(section.color);
9
11
  const getSectionVisibilityStyles = () => {
10
12
  return layouts
11
13
  .sort((a, b) => a.startsWith - b.startsWith)
@@ -20,16 +22,14 @@ const Section = ({ section, children }) => {
20
22
  }`;
21
23
  }, '');
22
24
  };
23
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `section-${section.id}`, children: children }), (0, jsx_runtime_1.jsx)("style", { jsx: true, children: `
25
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `section-${section.id}`, style: {
26
+ backgroundColor: backgroundColor
27
+ }, children: children }), (0, jsx_runtime_1.jsx)("style", { jsx: true, children: `
24
28
  ${(0, sdk_1.getLayoutStyles)(layouts, [section.height], ([height]) => (`
25
29
  .section-${section.id} {
26
30
  height: ${getSectionHeight(height)};
27
31
  position: relative;
28
32
  }`))}
29
- ${(0, sdk_1.getLayoutStyles)(layouts, [section.color], ([color]) => (`
30
- .section-${section.id} {
31
- background-color: ${color ? color : 'transparent'};
32
- }`))}
33
33
  ${getSectionVisibilityStyles()}
34
34
  ` })] }));
35
35
  };
@@ -2,15 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ImageItem = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const sdk_1 = require("@cntrl-site/sdk");
5
7
  const LinkWrapper_1 = require("../LinkWrapper");
6
8
  const useFileItem_1 = require("./useFileItem");
7
9
  const ImageItem = ({ item }) => {
8
10
  const { radius, strokeWidth, opacity, strokeColor } = (0, useFileItem_1.useFileItem)(item);
11
+ const borderColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(strokeColor).toCss(), [strokeColor]);
9
12
  return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `image-wrapper-${item.id}`, style: {
10
13
  borderRadius: `${radius * 100}vw`,
11
14
  borderWidth: `${strokeWidth * 100}vw`,
12
15
  opacity: `${opacity}`,
13
- borderColor: `${strokeColor}`
16
+ borderColor: `${borderColor}`
14
17
  }, children: (0, jsx_runtime_1.jsx)("img", { className: "image", src: item.commonParams.url }) }), (0, jsx_runtime_1.jsx)("style", { jsx: true, children: `
15
18
  .image-wrapper-${item.id} {
16
19
  position: absolute;
@@ -2,15 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RectangleItem = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const sdk_1 = require("@cntrl-site/sdk");
5
7
  const LinkWrapper_1 = require("../LinkWrapper");
6
8
  const useRectangleItem_1 = require("./useRectangleItem");
7
9
  const RectangleItem = ({ item }) => {
8
10
  const { fillColor, radius, strokeWidth, strokeColor } = (0, useRectangleItem_1.useRectangleItem)(item);
11
+ const backgroundColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(fillColor).toCss(), [fillColor]);
12
+ const borderColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(strokeColor).toCss(), [strokeColor]);
9
13
  return ((0, jsx_runtime_1.jsx)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `rectangle-${item.id}`, style: {
10
- backgroundColor: `${fillColor}`,
14
+ backgroundColor: `${backgroundColor}`,
11
15
  borderRadius: `${radius * 100}vw`,
12
16
  borderWidth: `${strokeWidth * 100}vw`,
13
- borderColor: `${strokeColor}`
17
+ borderColor: `${borderColor}`
14
18
  } }), (0, jsx_runtime_1.jsx)("style", { jsx: true, children: `
15
19
  .rectangle-${item.id} {
16
20
  position: absolute;
@@ -5,20 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.RichTextItem = void 0;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
- const react_1 = require("react");
9
8
  //@ts-ignore
10
9
  const style_1 = __importDefault(require("styled-jsx/style"));
11
10
  const RichTextConverter_1 = require("../../utils/RichTextConverter/RichTextConverter");
12
- const useCntrlContext_1 = require("../../provider/useCntrlContext");
11
+ const useRichTextItem_1 = require("./useRichTextItem");
13
12
  const richTextConv = new RichTextConverter_1.RichTextConverter();
14
13
  const RichTextItem = ({ item }) => {
15
- const { layouts, typePresets } = (0, useCntrlContext_1.useCntrlContext)();
16
- const preset = item.commonParams.preset
17
- ? typePresets?.presets.find(p => p.id === item.commonParams.preset)
18
- : null;
19
- const [content, styles] = richTextConv.toHtml(item, layouts, !!preset);
20
- return (0, react_1.createElement)(preset?.tag ?? 'div', {
21
- className: preset ? `cntrl-preset-${preset.id}` : undefined
22
- }, ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [content, (0, jsx_runtime_1.jsx)(style_1.default, { id: item.id, children: styles })] })));
14
+ const [content, styles, preset] = (0, useRichTextItem_1.useRichTextItem)(item);
15
+ const className = preset ? `cntrl-preset-${preset.id}` : undefined;
16
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: className, children: content }), (0, jsx_runtime_1.jsx)(style_1.default, { id: item.id, children: styles })] }));
23
17
  };
24
18
  exports.RichTextItem = RichTextItem;
@@ -2,15 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VideoItem = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const sdk_1 = require("@cntrl-site/sdk");
5
7
  const LinkWrapper_1 = require("../LinkWrapper");
6
8
  const useFileItem_1 = require("./useFileItem");
7
9
  const VideoItem = ({ item }) => {
8
10
  const { radius, strokeWidth, strokeColor, opacity } = (0, useFileItem_1.useFileItem)(item);
11
+ const borderColor = (0, react_1.useMemo)(() => sdk_1.CntrlColor.parse(strokeColor).toCss(), [strokeColor]);
9
12
  return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: item.link?.url, children: [(0, jsx_runtime_1.jsx)("div", { className: `video-wrapper-${item.id}`, style: {
10
13
  borderRadius: `${radius * 100}vw`,
11
14
  borderWidth: `${strokeWidth * 100}vw`,
12
15
  opacity: `${opacity}`,
13
- borderColor: `${strokeColor}`
16
+ borderColor: `${borderColor}`
14
17
  }, children: (0, jsx_runtime_1.jsx)("video", { autoPlay: true, muted: true, loop: true, playsInline: true, className: "video", children: (0, jsx_runtime_1.jsx)("source", { src: item.commonParams.url }) }) }), (0, jsx_runtime_1.jsx)("style", { jsx: true, children: `
15
18
  .video-wrapper-${item.id} {
16
19
  position: absolute;
@@ -23,13 +23,13 @@ const useRectangleItem = (item) => {
23
23
  return defaultColor;
24
24
  const layoutParams = item.layoutParams[layoutId];
25
25
  return 'fillColor' in layoutParams ? layoutParams.fillColor : defaultColor;
26
- }, (animator, scroll, value) => animator.getColor({ color: value }, scroll).color);
26
+ }, (animator, scroll, value) => animator.getColor({ color: value }, scroll).color, [layoutId]);
27
27
  const strokeColor = (0, useKeyframeValue_1.useKeyframeValue)(item, (item, layoutId) => {
28
28
  if (!layoutId)
29
29
  return defaultColor;
30
30
  const layoutParams = item.layoutParams[layoutId];
31
31
  return 'strokeColor' in layoutParams ? layoutParams.strokeColor : defaultColor;
32
- }, (animator, scroll, value) => animator.getBorderColor({ color: value }, scroll).color);
32
+ }, (animator, scroll, value) => animator.getBorderColor({ color: value }, scroll).color, [layoutId]);
33
33
  return { fillColor, strokeWidth, radius, strokeColor };
34
34
  };
35
35
  exports.useRectangleItem = useRectangleItem;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useRichTextItem = void 0;
4
+ const useCurrentLayout_1 = require("../../common/useCurrentLayout");
5
+ const RichTextConverter_1 = require("../../utils/RichTextConverter/RichTextConverter");
6
+ const useCntrlContext_1 = require("../../provider/useCntrlContext");
7
+ const richTextConv = new RichTextConverter_1.RichTextConverter();
8
+ const useRichTextItem = (item) => {
9
+ const layoutId = (0, useCurrentLayout_1.useCurrentLayout)();
10
+ const { layouts, typePresets } = (0, useCntrlContext_1.useCntrlContext)();
11
+ const presetId = item.layoutParams[layoutId].preset;
12
+ const preset = presetId
13
+ ? typePresets?.presets.find(p => p.id === presetId) ?? null
14
+ : null;
15
+ const [content, styles] = richTextConv.toHtml(item, layouts, !!preset);
16
+ return [content, styles, preset];
17
+ };
18
+ exports.useRichTextItem = useRichTextItem;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSectionColor = void 0;
4
+ const useCurrentLayout_1 = require("../common/useCurrentLayout");
5
+ const react_1 = require("react");
6
+ const sdk_1 = require("@cntrl-site/sdk");
7
+ const DEFAULT_COLOR = 'transparent';
8
+ const useSectionColor = (colorData) => {
9
+ const layoutId = (0, useCurrentLayout_1.useCurrentLayout)();
10
+ return (0, react_1.useMemo)(() => {
11
+ const layoutColor = colorData[layoutId];
12
+ return !layoutColor ? DEFAULT_COLOR : sdk_1.CntrlColor.parse(layoutColor).toCss();
13
+ }, []);
14
+ };
15
+ exports.useSectionColor = useSectionColor;
@@ -6,14 +6,14 @@ const binSearchInsertAt_1 = require("../binSearchInsertAt");
6
6
  const compare = (lhs, rhs) => lhs.position - rhs.position;
7
7
  const insertBin = (0, binSearchInsertAt_1.createInsert)(binSearchInsertAt_1.binSearchInsertAt, compare);
8
8
  class Animator {
9
+ static pushKeyframeToMap(keyframe, map) {
10
+ insertBin(map[keyframe.type], keyframe);
11
+ }
9
12
  constructor(keyframes) {
10
13
  this.keyframes = keyframes;
11
14
  this.keyframesMap = createKeyframesMap();
12
15
  this.fillKeyframesMap();
13
16
  }
14
- static pushKeyframeToMap(keyframe, map) {
15
- insertBin(map[keyframe.type], keyframe);
16
- }
17
17
  getDimensions(values, pos) {
18
18
  const keyframes = this.keyframesMap[sdk_1.KeyframeType.Dimensions];
19
19
  if (!keyframes || !keyframes.length)
@@ -60,7 +60,7 @@ class Animator {
60
60
  }
61
61
  const { start, end } = this.getStartEnd(pos, keyframes);
62
62
  return {
63
- color: this.getRgba(start, end, pos)
63
+ color: this.getRangeColor(start, end, pos)
64
64
  };
65
65
  }
66
66
  getBorderColor(values, pos) {
@@ -75,7 +75,7 @@ class Animator {
75
75
  }
76
76
  const { start, end } = this.getStartEnd(pos, keyframes);
77
77
  return {
78
- color: this.getRgba(start, end, pos)
78
+ color: this.getRangeColor(start, end, pos)
79
79
  };
80
80
  }
81
81
  getRadius(values, pos) {
@@ -150,25 +150,15 @@ class Animator {
150
150
  Animator.pushKeyframeToMap(keyframe, this.keyframesMap);
151
151
  }
152
152
  }
153
- getRgba(start, end, position) {
154
- const [startR, startG, startB, startA] = parseRgba(start.value.color);
155
- const [endR, endG, endB, endA] = parseRgba(end.value.color);
156
- const r = rangeMap(position, start.position, end.position, startR, endR, true);
157
- const g = rangeMap(position, start.position, end.position, startG, endG, true);
158
- const b = rangeMap(position, start.position, end.position, startB, endB, true);
159
- const a = rangeMap(position, start.position, end.position, startA, endA, true);
160
- return `rgba(${r},${g},${b},${a})`;
153
+ getRangeColor(start, end, position) {
154
+ const rangeAmount = rangeMap(position, start.position, end.position, 0, 1, true);
155
+ const startColor = sdk_1.CntrlColor.parse(start.value.color);
156
+ const endColor = sdk_1.CntrlColor.parse(end.value.color);
157
+ const mixedColor = startColor.mix(endColor, rangeAmount);
158
+ return mixedColor.fmt('oklch');
161
159
  }
162
160
  }
163
161
  exports.Animator = Animator;
164
- const reRgba = /^rgba?\( *(\d+) *, *(\d+) *, *(\d+)(?: *, *(\d+(?:\.\d+)?))? *\)$/i;
165
- function parseRgba(color) {
166
- const match = reRgba.exec(color);
167
- if (!match)
168
- return [0, 0, 0, 1];
169
- const [, r, g, b, a] = match;
170
- return [parseInt(r, 10), parseInt(g, 10), parseInt(b, 10), a ? parseFloat(a) : NaN];
171
- }
172
162
  function createKeyframesMap() {
173
163
  return {
174
164
  [sdk_1.KeyframeType.Dimensions]: [],
@@ -187,7 +187,7 @@ class RichTextConverter {
187
187
  static fromDraftToInline(draftStyle) {
188
188
  const { value, name } = draftStyle;
189
189
  const map = {
190
- 'COLOR': { 'color': value },
190
+ 'COLOR': { 'color': getResolvedValue(value, name) },
191
191
  'TYPEFACE': { 'font-family': `${value}` },
192
192
  'FONTSTYLE': value ? { ...exports.FontStyles[value] } : {},
193
193
  'FONTWEIGHT': { 'font-weight': value },
@@ -218,3 +218,8 @@ function groupBy(items, getKey) {
218
218
  }
219
219
  return groups;
220
220
  }
221
+ function getResolvedValue(value, name) {
222
+ if (name !== 'COLOR')
223
+ return value;
224
+ return value ? sdk_1.CntrlColor.parse(value).toCss() : value;
225
+ }
@@ -15,11 +15,11 @@ function generateTypePresetStyles(preset, layouts) {
15
15
  ${sorted.map(l => (`
16
16
  ${(0, sdk_1.getLayoutMediaQuery)(l.id, sorted)} {
17
17
  .cntrl-preset-${p.id} {
18
- font-size: ${p.layoutParams[l.id].fontSize * 100}vw;
19
- line-height: ${p.layoutParams[l.id].lineHeight * 100}vw;
20
- letter-spacing: ${p.layoutParams[l.id].letterSpacing * 100}vw;
21
- word-spacing: ${p.layoutParams[l.id].wordSpacing * 100}vw;
22
- color: ${p.layoutParams[l.id].color};
18
+ font-size: ${p.fontSize / l.exemplary * 100}vw;
19
+ line-height: ${p.lineHeight / l.exemplary * 100}vw;
20
+ letter-spacing: ${p.letterSpacing / l.exemplary * 100}vw;
21
+ word-spacing: ${p.wordSpacing / l.exemplary * 100}vw;
22
+ color: ${sdk_1.CntrlColor.parse(p.color).toCss()};
23
23
  }
24
24
  }`)).join('\n')}`)).join('\n')}`);
25
25
  return stylesStr.replace(reEmptyLines, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "description": "SDK for Next.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/index.js",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "homepage": "https://github.com/cntrl-site/sdk-nextjs#readme",
23
23
  "dependencies": {
24
- "@cntrl-site/sdk": "^0.4.3",
24
+ "@cntrl-site/sdk": "^0.6.2",
25
25
  "html-react-parser": "^3.0.1",
26
26
  "styled-jsx": "^5.0.2"
27
27
  },
@@ -40,7 +40,7 @@
40
40
  "@types/react": "^18.0.15",
41
41
  "jest": "^29.3.1",
42
42
  "ts-jest": "^29.0.3",
43
- "typescript": "^4.7.4"
43
+ "typescript": "^4.9.5"
44
44
  },
45
45
  "directories": {
46
46
  "lib": "lib"
@@ -10,7 +10,6 @@ interface LayoutData {
10
10
 
11
11
  export const useCurrentLayout = () => {
12
12
  const { layouts } = useCntrlContext();
13
- const [initialLayout] = layouts;
14
13
  const articleRectObserver = useContext(ArticleRectContext);
15
14
  const layoutRanges = useMemo(() => {
16
15
  const sorted = layouts.slice().sort((la, lb) => la.startsWith - lb.startsWith);
@@ -29,7 +28,7 @@ export const useCurrentLayout = () => {
29
28
  const getCurrentLayout = useCallback((articleWidth: number) => {
30
29
  return layoutRanges.find(l => articleWidth >= l.start && articleWidth < l.end)!.layoutId;
31
30
  }, [layoutRanges]);
32
- const [layoutId, setLayoutId] = useState<string>(getCurrentLayout(isServer() ? 0 : window.innerWidth));
31
+ const [layoutId, setLayoutId] = useState<string>(getCurrentLayout(0));
33
32
 
34
33
  useEffect(() => {
35
34
  if (!articleRectObserver) return;
@@ -41,7 +40,3 @@ export const useCurrentLayout = () => {
41
40
 
42
41
  return layoutId;
43
42
  };
44
-
45
- function isServer(): boolean {
46
- return typeof window === 'undefined';
47
- }
@@ -46,6 +46,10 @@ export const useKeyframeValue = <T>(
46
46
  return new Animator(animationData);
47
47
  }, [keyframes, layoutId]);
48
48
 
49
+ useEffect(() => {
50
+ setAdjustedValue(paramValue);
51
+ }, [paramValue]);
52
+
49
53
  useEffect(() => {
50
54
  if (!animator || !articleRectObserver) return;
51
55
  return articleRectObserver.on('resize', () => {
@@ -1,6 +1,14 @@
1
1
  import { FC, ReactElement } from 'react';
2
- import { getLayoutMediaQuery, getLayoutStyles, TArticleSection, TSectionHeight, SectionHeightMode } from '@cntrl-site/sdk';
2
+ import {
3
+ getLayoutMediaQuery,
4
+ getLayoutStyles,
5
+ TArticleSection,
6
+ TSectionHeight,
7
+ SectionHeightMode,
8
+ CntrlColor
9
+ } from '@cntrl-site/sdk';
3
10
  import { useCntrlContext } from '../provider/useCntrlContext';
11
+ import { useSectionColor } from './useSectionColor';
4
12
 
5
13
  type SectionChild = ReactElement<any, any>;
6
14
 
@@ -11,6 +19,7 @@ interface Props {
11
19
 
12
20
  export const Section: FC<Props> = ({ section, children }) => {
13
21
  const { layouts } = useCntrlContext();
22
+ const backgroundColor = useSectionColor(section.color);
14
23
  const getSectionVisibilityStyles = () => {
15
24
  return layouts
16
25
  .sort((a, b) => a.startsWith - b.startsWith)
@@ -28,7 +37,12 @@ export const Section: FC<Props> = ({ section, children }) => {
28
37
 
29
38
  return (
30
39
  <>
31
- <div className={`section-${section.id}`}>
40
+ <div
41
+ className={`section-${section.id}`}
42
+ style={{
43
+ backgroundColor: backgroundColor
44
+ }}
45
+ >
32
46
  {children}
33
47
  </div>
34
48
  <style jsx>{`
@@ -40,13 +54,6 @@ export const Section: FC<Props> = ({ section, children }) => {
40
54
  }`
41
55
  ))
42
56
  }
43
- ${
44
- getLayoutStyles(layouts, [section.color], ([color]) => (`
45
- .section-${section.id} {
46
- background-color: ${color ? color : 'transparent'};
47
- }`
48
- ))
49
- }
50
57
  ${getSectionVisibilityStyles()}
51
58
  `}</style>
52
59
  </>
@@ -1,11 +1,12 @@
1
- import { FC } from 'react';
2
- import { TImageItem } from '@cntrl-site/sdk';
1
+ import { FC, useMemo } from 'react';
2
+ import { CntrlColor, TImageItem } from '@cntrl-site/sdk';
3
3
  import { ItemProps } from '../Item';
4
4
  import { LinkWrapper } from '../LinkWrapper';
5
5
  import { useFileItem } from './useFileItem';
6
6
 
7
7
  export const ImageItem: FC<ItemProps<TImageItem>> = ({ item }) => {
8
8
  const { radius, strokeWidth, opacity, strokeColor } = useFileItem(item);
9
+ const borderColor = useMemo(() => CntrlColor.parse(strokeColor).toCss(), [strokeColor]);
9
10
  return (
10
11
  <LinkWrapper url={item.link?.url}>
11
12
  <>
@@ -14,7 +15,7 @@ export const ImageItem: FC<ItemProps<TImageItem>> = ({ item }) => {
14
15
  borderRadius: `${radius * 100}vw`,
15
16
  borderWidth: `${strokeWidth * 100}vw`,
16
17
  opacity: `${opacity}`,
17
- borderColor: `${strokeColor}`
18
+ borderColor: `${borderColor}`
18
19
  }}
19
20
  >
20
21
  <img className="image" src={item.commonParams.url} />
@@ -1,20 +1,23 @@
1
- import { FC } from 'react';
2
- import { TRectangleItem } from '@cntrl-site/sdk';
1
+ import { FC, useMemo } from 'react';
2
+ import { TRectangleItem, CntrlColor } from '@cntrl-site/sdk';
3
3
  import { ItemProps } from '../Item';
4
4
  import { LinkWrapper } from '../LinkWrapper';
5
5
  import { useRectangleItem } from './useRectangleItem';
6
6
 
7
7
  export const RectangleItem: FC<ItemProps<TRectangleItem>> = ({ item }) => {
8
8
  const { fillColor, radius, strokeWidth, strokeColor } = useRectangleItem(item);
9
+ const backgroundColor = useMemo(() => CntrlColor.parse(fillColor).toCss(), [fillColor]);
10
+ const borderColor = useMemo(() => CntrlColor.parse(strokeColor).toCss(), [strokeColor]);
11
+
9
12
  return (
10
13
  <LinkWrapper url={item.link?.url}>
11
14
  <>
12
15
  <div className={`rectangle-${item.id}`}
13
16
  style={{
14
- backgroundColor: `${fillColor}`,
17
+ backgroundColor: `${backgroundColor}`,
15
18
  borderRadius: `${radius * 100}vw`,
16
19
  borderWidth: `${strokeWidth * 100}vw`,
17
- borderColor: `${strokeColor}`
20
+ borderColor: `${borderColor}`
18
21
  }}
19
22
  />
20
23
  <style jsx>{`
@@ -5,27 +5,19 @@ import JSXStyle from 'styled-jsx/style';
5
5
  import { ItemProps } from '../Item';
6
6
  import { RichTextConverter } from '../../utils/RichTextConverter/RichTextConverter';
7
7
  import { useCntrlContext } from '../../provider/useCntrlContext';
8
+ import { useRichTextItem } from './useRichTextItem';
8
9
 
9
10
  const richTextConv = new RichTextConverter();
10
11
 
11
12
  export const RichTextItem: FC<ItemProps<TRichTextItem>> = ({ item }) => {
12
- const { layouts, typePresets } = useCntrlContext();
13
- const preset = item.commonParams.preset
14
- ? typePresets?.presets.find(p => p.id === item.commonParams.preset)
15
- : null;
16
- const [content, styles] = richTextConv.toHtml(item, layouts, !!preset);
17
- return createElement(
18
- preset?.tag ?? 'div',
19
- {
20
- className: preset ? `cntrl-preset-${preset.id}` : undefined
21
- },
22
- (
23
- <>
24
- {content}
25
- <JSXStyle id={item.id}>
26
- {styles}
27
- </JSXStyle>
28
- </>
29
- )
13
+ const [content, styles, preset] = useRichTextItem(item);
14
+ const className = preset ? `cntrl-preset-${preset.id}` : undefined;
15
+ return (
16
+ <>
17
+ <div className={className}>{content}</div>
18
+ <JSXStyle id={item.id}>
19
+ {styles}
20
+ </JSXStyle>
21
+ </>
30
22
  );
31
23
  };
@@ -1,11 +1,12 @@
1
- import { FC } from 'react';
2
- import { TVideoItem } from '@cntrl-site/sdk';
1
+ import { FC, useMemo } from 'react';
2
+ import { CntrlColor, TVideoItem } from '@cntrl-site/sdk';
3
3
  import { ItemProps } from '../Item';
4
4
  import { LinkWrapper } from '../LinkWrapper';
5
5
  import { useFileItem } from './useFileItem';
6
6
 
7
7
  export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item }) => {
8
8
  const { radius, strokeWidth, strokeColor, opacity } = useFileItem(item);
9
+ const borderColor = useMemo(() => CntrlColor.parse(strokeColor).toCss(), [strokeColor]);
9
10
  return (
10
11
  <LinkWrapper url={item.link?.url}>
11
12
  <div className={`video-wrapper-${item.id}`}
@@ -13,7 +14,7 @@ export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item }) => {
13
14
  borderRadius: `${radius * 100}vw`,
14
15
  borderWidth: `${strokeWidth * 100}vw`,
15
16
  opacity: `${opacity}`,
16
- borderColor: `${strokeColor}`
17
+ borderColor: `${borderColor}`
17
18
  }}
18
19
  >
19
20
  <video autoPlay muted loop playsInline className="video">
@@ -1,4 +1,4 @@
1
- import { TImageItem, TVideoItem } from '@cntrl-site/sdk';
1
+ import { CntrlColor, TImageItem, TVideoItem } from '@cntrl-site/sdk';
2
2
  import { useKeyframeValue } from '../../common/useKeyframeValue';
3
3
  import { useCurrentLayout } from '../../common/useCurrentLayout';
4
4
 
@@ -1,4 +1,4 @@
1
- import { TRectangleItem } from '@cntrl-site/sdk';
1
+ import { CntrlColor, TRectangleItem } from '@cntrl-site/sdk';
2
2
  import { useKeyframeValue } from '../../common/useKeyframeValue';
3
3
  import { useCurrentLayout } from '../../common/useCurrentLayout';
4
4
 
@@ -33,7 +33,8 @@ export const useRectangleItem = (item: TRectangleItem) => {
33
33
  const layoutParams = item.layoutParams[layoutId];
34
34
  return 'fillColor' in layoutParams ? layoutParams.fillColor : defaultColor;
35
35
  },
36
- (animator, scroll, value) => animator.getColor({ color: value }, scroll).color
36
+ (animator, scroll, value) => animator.getColor({ color: value }, scroll).color,
37
+ [layoutId]
37
38
  );
38
39
  const strokeColor = useKeyframeValue(
39
40
  item,
@@ -42,8 +43,8 @@ export const useRectangleItem = (item: TRectangleItem) => {
42
43
  const layoutParams = item.layoutParams[layoutId];
43
44
  return 'strokeColor' in layoutParams ? layoutParams.strokeColor : defaultColor;
44
45
  },
45
- (animator, scroll, value) => animator.getBorderColor({ color: value }, scroll).color
46
+ (animator, scroll, value) => animator.getBorderColor({ color: value }, scroll).color,
47
+ [layoutId]
46
48
  );
47
-
48
49
  return { fillColor, strokeWidth, radius, strokeColor };
49
50
  };
@@ -0,0 +1,18 @@
1
+ import { TRichTextItem, TTypePresetEntry } from '@cntrl-site/sdk';
2
+ import { useCurrentLayout } from '../../common/useCurrentLayout';
3
+ import { RichTextConverter } from '../../utils/RichTextConverter/RichTextConverter';
4
+ import { useCntrlContext } from '../../provider/useCntrlContext';
5
+ import { ReactNode } from 'react';
6
+
7
+ const richTextConv = new RichTextConverter();
8
+
9
+ export const useRichTextItem = (item: TRichTextItem): [ReactNode[], string, TTypePresetEntry | null] => {
10
+ const layoutId = useCurrentLayout();
11
+ const { layouts, typePresets } = useCntrlContext();
12
+ const presetId = item.layoutParams[layoutId].preset;
13
+ const preset = presetId
14
+ ? typePresets?.presets.find(p => p.id === presetId) ?? null
15
+ : null;
16
+ const [content, styles] = richTextConv.toHtml(item, layouts, !!preset);
17
+ return [content, styles, preset];
18
+ };
@@ -0,0 +1,14 @@
1
+ import { useCurrentLayout } from '../common/useCurrentLayout';
2
+ import { useMemo } from 'react';
3
+ import { CntrlColor } from '@cntrl-site/sdk';
4
+
5
+ type LayoutIdentifier = string;
6
+ const DEFAULT_COLOR = 'transparent';
7
+
8
+ export const useSectionColor = (colorData: Record<LayoutIdentifier, string | null>): string => {
9
+ const layoutId = useCurrentLayout();
10
+ return useMemo(() => {
11
+ const layoutColor = colorData[layoutId];
12
+ return !layoutColor ? DEFAULT_COLOR : CntrlColor.parse(layoutColor).toCss();
13
+ }, []);
14
+ };
@@ -1,4 +1,4 @@
1
- import { KeyframeType, TKeyframeValueMap } from '@cntrl-site/sdk';
1
+ import { KeyframeType, TKeyframeValueMap, CntrlColor } from '@cntrl-site/sdk';
2
2
  import { binSearchInsertAt, createInsert } from '../binSearchInsertAt';
3
3
 
4
4
  export interface AnimationData<T extends KeyframeType> {
@@ -84,7 +84,7 @@ export class Animator {
84
84
  }
85
85
  const { start, end } = this.getStartEnd<KeyframeType.Color>(pos, keyframes);
86
86
  return {
87
- color: this.getRgba(start, end, pos)
87
+ color: this.getRangeColor(start, end, pos)
88
88
  };
89
89
  }
90
90
 
@@ -102,7 +102,7 @@ export class Animator {
102
102
  }
103
103
  const { start, end } = this.getStartEnd<KeyframeType.BorderColor>(pos, keyframes);
104
104
  return {
105
- color: this.getRgba(start, end, pos)
105
+ color: this.getRangeColor(start, end, pos)
106
106
  };
107
107
  }
108
108
 
@@ -192,29 +192,19 @@ export class Animator {
192
192
  }
193
193
  }
194
194
 
195
- private getRgba(
195
+ private getRangeColor(
196
196
  start: AnimationData<KeyframeType.Color | KeyframeType.BorderColor>,
197
197
  end: AnimationData<KeyframeType.Color | KeyframeType.BorderColor>,
198
198
  position: number
199
199
  ): string {
200
- const [startR, startG, startB, startA] = parseRgba(start.value.color);
201
- const [endR, endG, endB, endA] = parseRgba(end.value.color);
202
- const r = rangeMap(position, start.position, end.position, startR, endR, true);
203
- const g = rangeMap(position, start.position, end.position, startG, endG, true);
204
- const b = rangeMap(position, start.position, end.position, startB, endB, true);
205
- const a = rangeMap(position, start.position, end.position, startA, endA, true);
206
- return `rgba(${r},${g},${b},${a})`;
200
+ const rangeAmount = rangeMap(position, start.position, end.position, 0, 1, true);
201
+ const startColor = CntrlColor.parse(start.value.color);
202
+ const endColor = CntrlColor.parse(end.value.color);
203
+ const mixedColor = startColor.mix(endColor, rangeAmount);
204
+ return mixedColor.fmt('oklch');
207
205
  }
208
206
  }
209
207
 
210
- const reRgba = /^rgba?\( *(\d+) *, *(\d+) *, *(\d+)(?: *, *(\d+(?:\.\d+)?))? *\)$/i;
211
-
212
- function parseRgba(color: string): [r: number, g: number, b: number, a: number] {
213
- const match = reRgba.exec(color);
214
- if (!match) return [0, 0, 0, 1];
215
- const [, r, g, b, a] = match;
216
- return [parseInt(r, 10), parseInt(g, 10), parseInt(b, 10), a ? parseFloat(a) : NaN];
217
- }
218
208
 
219
209
  function createKeyframesMap(): KeyframesMap {
220
210
  return {
@@ -1,5 +1,6 @@
1
1
  import { ReactElement, ReactNode } from 'react';
2
2
  import {
3
+ CntrlColor,
3
4
  getLayoutMediaQuery,
4
5
  RichText,
5
6
  TextTransform,
@@ -219,7 +220,7 @@ export class RichTextConverter {
219
220
  private static fromDraftToInline(draftStyle: Style): string {
220
221
  const { value, name } = draftStyle;
221
222
  const map: Record<string, Record<string, string | undefined>> = {
222
- 'COLOR': { 'color': value },
223
+ 'COLOR': { 'color': getResolvedValue(value, name) },
223
224
  'TYPEFACE': { 'font-family': `${value}` },
224
225
  'FONTSTYLE': value ? { ...FontStyles[value] } : {},
225
226
  'FONTWEIGHT': { 'font-weight': value },
@@ -250,3 +251,8 @@ function groupBy<I>(items: I[], getKey: (item: I) => PropertyKey): Record<Proper
250
251
  }
251
252
  return groups;
252
253
  }
254
+
255
+ function getResolvedValue(value: string | undefined, name: string) {
256
+ if (name !== 'COLOR') return value;
257
+ return value ? CntrlColor.parse(value).toCss() : value;
258
+ }
@@ -1,28 +1,4 @@
1
- import { AllowedTags, TTypePresets } from '@cntrl-site/sdk';
2
-
3
- const layoutParams = {
4
- 'desktop': {
5
- fontSize: 0.011111,
6
- lineHeight: 0.013888,
7
- wordSpacing: 0.0006944,
8
- letterSpacing: 0.0006944,
9
- color: 'rgba(0, 0, 0, 1)'
10
- },
11
- 'tablet': {
12
- fontSize: 0.02604,
13
- lineHeight: 0.03125,
14
- wordSpacing: 0,
15
- letterSpacing: 0,
16
- color: 'rgba(0, 0, 0, 1)'
17
- },
18
- 'mobile': {
19
- fontSize: 0.053333,
20
- lineHeight: 0.064,
21
- wordSpacing: 0.008,
22
- letterSpacing: 0.008,
23
- color: 'rgba(0, 0, 0, 1)'
24
- }
25
- };
1
+ import { TTypePresets, TypePresetStatus } from '@cntrl-site/sdk';
26
2
 
27
3
  export const presetMock: TTypePresets = {
28
4
  id: 'presetId',
@@ -33,17 +9,25 @@ export const presetMock: TTypePresets = {
33
9
  fontWeight: '400',
34
10
  fontStyle: '',
35
11
  name: 'Heading',
36
- tag: AllowedTags.h1,
37
- layoutParams
12
+ fontSize: 24,
13
+ lineHeight: 36,
14
+ wordSpacing: 1,
15
+ letterSpacing: 1,
16
+ color: 'rgba(0, 0, 0, 1)',
17
+ status: TypePresetStatus.Active
38
18
  },
39
19
  {
40
20
  id: 'heading02',
41
21
  fontFamily: 'Aeonik',
42
22
  fontWeight: '400',
43
23
  fontStyle: 'italic',
44
- name: 'Heading',
45
- tag: AllowedTags.h1,
46
- layoutParams
24
+ name: 'Heading 2',
25
+ fontSize: 24,
26
+ lineHeight: 36,
27
+ wordSpacing: 1,
28
+ letterSpacing: 1,
29
+ color: 'rgba(0, 0, 0, 1)',
30
+ status: TypePresetStatus.Active
47
31
  }
48
32
  ]
49
33
  };
@@ -8,28 +8,28 @@ describe('generateTypePresetStyles', () => {
8
8
  const expectedMedia = (id: string) =>
9
9
  `@media (min-width: 0px) and (max-width: 767px) {
10
10
  .cntrl-preset-${id} {
11
- font-size: 5.3332999999999995vw;
12
- line-height: 6.4vw;
13
- letter-spacing: 0.8vw;
14
- word-spacing: 0.8vw;
11
+ font-size: 6.4vw;
12
+ line-height: 9.6vw;
13
+ letter-spacing: 0.26666666666666666vw;
14
+ word-spacing: 0.26666666666666666vw;
15
15
  color: rgba(0, 0, 0, 1);
16
16
  }
17
17
  }
18
18
  @media (min-width: 768px) and (max-width: 1023px) {
19
19
  .cntrl-preset-${id} {
20
- font-size: 2.604vw;
21
- line-height: 3.125vw;
22
- letter-spacing: 0vw;
23
- word-spacing: 0vw;
20
+ font-size: 3.125vw;
21
+ line-height: 4.6875vw;
22
+ letter-spacing: 0.13020833333333331vw;
23
+ word-spacing: 0.13020833333333331vw;
24
24
  color: rgba(0, 0, 0, 1);
25
25
  }
26
26
  }
27
27
  @media (min-width: 1024px) {
28
28
  .cntrl-preset-${id} {
29
- font-size: 1.1111vw;
30
- line-height: 1.3888vw;
31
- letter-spacing: 0.06944vw;
32
- word-spacing: 0.06944vw;
29
+ font-size: 1.6666666666666667vw;
30
+ line-height: 2.5vw;
31
+ letter-spacing: 0.06944444444444445vw;
32
+ word-spacing: 0.06944444444444445vw;
33
33
  color: rgba(0, 0, 0, 1);
34
34
  }
35
35
  }`;
@@ -1,4 +1,4 @@
1
- import { getLayoutMediaQuery, TLayout, TTypePresets } from '@cntrl-site/sdk';
1
+ import { getLayoutMediaQuery, TLayout, TTypePresets, CntrlColor } from '@cntrl-site/sdk';
2
2
 
3
3
  const reEmptyLines = /^\s*\n/gm;
4
4
 
@@ -14,11 +14,11 @@ export function generateTypePresetStyles(preset: TTypePresets, layouts: TLayout[
14
14
  ${sorted.map(l => (`
15
15
  ${getLayoutMediaQuery(l.id, sorted)} {
16
16
  .cntrl-preset-${p.id} {
17
- font-size: ${p.layoutParams[l.id].fontSize * 100}vw;
18
- line-height: ${p.layoutParams[l.id].lineHeight * 100}vw;
19
- letter-spacing: ${p.layoutParams[l.id].letterSpacing * 100}vw;
20
- word-spacing: ${p.layoutParams[l.id].wordSpacing * 100}vw;
21
- color: ${p.layoutParams[l.id].color};
17
+ font-size: ${p.fontSize / l.exemplary * 100}vw;
18
+ line-height: ${p.lineHeight / l.exemplary * 100}vw;
19
+ letter-spacing: ${p.letterSpacing / l.exemplary * 100}vw;
20
+ word-spacing: ${p.wordSpacing / l.exemplary * 100}vw;
21
+ color: ${CntrlColor.parse(p.color).toCss()};
22
22
  }
23
23
  }`)).join('\n')}`)).join('\n')}`);
24
24
  return stylesStr.replace(reEmptyLines, '');
Binary file