@cntrl-site/sdk-nextjs 0.2.13 → 0.4.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 (68) hide show
  1. package/cntrl-site-sdk-nextjs-0.4.0.tgz +0 -0
  2. package/lib/common/useCurrentLayout.js +42 -0
  3. package/lib/common/useKeyframeValue.js +66 -0
  4. package/lib/components/Article.js +7 -2
  5. package/lib/components/Item.js +24 -16
  6. package/lib/components/Page.js +9 -2
  7. package/lib/components/Section.js +3 -1
  8. package/lib/components/items/ImageItem.js +11 -4
  9. package/lib/components/items/RectangleItem.js +12 -5
  10. package/lib/components/items/RichTextItem.js +12 -4
  11. package/lib/components/items/VideoItem.js +29 -22
  12. package/lib/components/items/VimeoEmbed.js +15 -14
  13. package/lib/components/items/YoutubeEmbed.js +15 -14
  14. package/lib/components/items/useEmbedVideoItem.js +16 -0
  15. package/lib/components/items/useFileItem.js +22 -0
  16. package/lib/components/items/useRectangleItem.js +29 -0
  17. package/lib/components/useItemAngle.js +9 -0
  18. package/lib/components/useItemDimensions.js +19 -0
  19. package/lib/components/useItemPosition.js +34 -0
  20. package/lib/index.js +4 -4
  21. package/lib/provider/ArticleRectContext.js +5 -0
  22. package/lib/provider/CntrlSdkContext.js +13 -0
  23. package/lib/provider/Keyframes.js +12 -0
  24. package/lib/provider/KeyframesContext.js +6 -0
  25. package/lib/utils/Animator/Animator.js +159 -0
  26. package/lib/utils/ArticleRectManager/ArticleRectObserver.js +61 -0
  27. package/lib/utils/ArticleRectManager/useArticleRectObserver.js +18 -0
  28. package/lib/utils/EventEmitter.js +35 -0
  29. package/lib/utils/{RichTextConverter.js → RichTextConverter/RichTextConverter.js} +3 -3
  30. package/lib/utils/binSearchInsertAt.js +36 -0
  31. package/lib/utils/generateTypePresetStyles/generateTypePresetStyles.js +27 -0
  32. package/package.json +8 -3
  33. package/src/common/useCurrentLayout.ts +47 -0
  34. package/src/common/useKeyframeValue.ts +74 -0
  35. package/src/components/Article.tsx +13 -9
  36. package/src/components/Item.tsx +31 -21
  37. package/src/components/Page.tsx +16 -4
  38. package/src/components/Section.tsx +4 -3
  39. package/src/components/items/ImageItem.tsx +23 -14
  40. package/src/components/items/RectangleItem.tsx +21 -12
  41. package/src/components/items/RichTextItem.tsx +22 -11
  42. package/src/components/items/VideoItem.tsx +37 -30
  43. package/src/components/items/VimeoEmbed.tsx +19 -17
  44. package/src/components/items/YoutubeEmbed.tsx +17 -15
  45. package/src/components/items/useEmbedVideoItem.ts +19 -0
  46. package/src/components/items/useFileItem.ts +29 -0
  47. package/src/components/items/useRectangleItem.ts +40 -0
  48. package/src/components/useItemAngle.ts +12 -0
  49. package/src/components/useItemDimensions.ts +23 -0
  50. package/src/components/useItemPosition.ts +36 -0
  51. package/src/index.ts +2 -3
  52. package/src/provider/ArticleRectContext.ts +4 -0
  53. package/src/provider/CntrlSdkContext.ts +22 -2
  54. package/src/provider/Keyframes.ts +11 -0
  55. package/src/provider/KeyframesContext.ts +5 -0
  56. package/src/utils/Animator/Animator.ts +206 -0
  57. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +69 -0
  58. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -0
  59. package/src/utils/EventEmitter.ts +41 -0
  60. package/src/utils/{RichTextConverter.tsx → RichTextConverter/RichTextConverter.tsx} +7 -6
  61. package/src/utils/binSearchInsertAt.ts +33 -0
  62. package/src/utils/generateTypePresetStyles/__mock__/layoutsMock.ts +37 -0
  63. package/src/utils/generateTypePresetStyles/__mock__/presetMock.ts +49 -0
  64. package/src/utils/generateTypePresetStyles/generateTypePresetStyles.test.ts +49 -0
  65. package/src/utils/generateTypePresetStyles/generateTypePresetStyles.ts +25 -0
  66. package/src/utils/getInvertedRanges.ts +43 -0
  67. package/.idea/workspace.xml +0 -293
  68. package/cntrl-site-sdk-nextjs-0.2.13.tgz +0 -0
package/lib/index.js CHANGED
@@ -14,10 +14,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.CustomItems = exports.CntrlProvider = exports.YoutubeEmbedItem = exports.VimeoEmbedItem = exports.LayoutStyle = exports.VideoItem = exports.RichTextItem = exports.RectangleItem = exports.ImageItem = exports.Item = exports.Section = exports.Article = exports.Head = exports.Page = exports.RichTextConverter = void 0;
18
- const defaultContext_1 = require("./provider/defaultContext");
17
+ exports.CntrlProvider = exports.YoutubeEmbedItem = exports.VimeoEmbedItem = exports.LayoutStyle = exports.VideoItem = exports.RichTextItem = exports.RectangleItem = exports.ImageItem = exports.Item = exports.Section = exports.Article = exports.Head = exports.Page = exports.RichTextConverter = exports.cntrlSdkContext = void 0;
18
+ var defaultContext_1 = require("./provider/defaultContext");
19
+ Object.defineProperty(exports, "cntrlSdkContext", { enumerable: true, get: function () { return defaultContext_1.cntrlSdkContext; } });
19
20
  __exportStar(require("@cntrl-site/sdk"), exports);
20
- var RichTextConverter_1 = require("./utils/RichTextConverter");
21
+ var RichTextConverter_1 = require("./utils/RichTextConverter/RichTextConverter");
21
22
  Object.defineProperty(exports, "RichTextConverter", { enumerable: true, get: function () { return RichTextConverter_1.RichTextConverter; } });
22
23
  var Page_1 = require("./components/Page");
23
24
  Object.defineProperty(exports, "Page", { enumerable: true, get: function () { return Page_1.Page; } });
@@ -46,4 +47,3 @@ Object.defineProperty(exports, "YoutubeEmbedItem", { enumerable: true, get: func
46
47
  // custom items
47
48
  var CntrlProvider_1 = require("./provider/CntrlProvider");
48
49
  Object.defineProperty(exports, "CntrlProvider", { enumerable: true, get: function () { return CntrlProvider_1.CntrlProvider; } });
49
- exports.CustomItems = defaultContext_1.cntrlSdkContext.customItems;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArticleRectContext = void 0;
4
+ const react_1 = require("react");
5
+ exports.ArticleRectContext = (0, react_1.createContext)(null);
@@ -4,6 +4,19 @@ exports.CntrlSdkContext = void 0;
4
4
  class CntrlSdkContext {
5
5
  constructor(customItems) {
6
6
  this.customItems = customItems;
7
+ this._layouts = [];
8
+ }
9
+ setTypePresets(typePresets) {
10
+ this._typePresets = typePresets;
11
+ }
12
+ setLayouts(layouts) {
13
+ this._layouts = layouts;
14
+ }
15
+ get layouts() {
16
+ return this._layouts;
17
+ }
18
+ get typePresets() {
19
+ return this._typePresets;
7
20
  }
8
21
  }
9
22
  exports.CntrlSdkContext = CntrlSdkContext;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Keyframes = void 0;
4
+ class Keyframes {
5
+ constructor(keyframes = []) {
6
+ this.keyframes = keyframes;
7
+ }
8
+ getItemKeyframes(itemId) {
9
+ return this.keyframes.filter(kf => kf.itemId === itemId);
10
+ }
11
+ }
12
+ exports.Keyframes = Keyframes;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeyframesContext = void 0;
4
+ const react_1 = require("react");
5
+ const Keyframes_1 = require("./Keyframes");
6
+ exports.KeyframesContext = (0, react_1.createContext)(new Keyframes_1.Keyframes());
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Animator = void 0;
4
+ const sdk_1 = require("@cntrl-site/sdk");
5
+ const binSearchInsertAt_1 = require("../binSearchInsertAt");
6
+ const compare = (lhs, rhs) => lhs.position - rhs.position;
7
+ const insertBin = (0, binSearchInsertAt_1.createInsert)(binSearchInsertAt_1.binSearchInsertAt, compare);
8
+ class Animator {
9
+ constructor(keyframes) {
10
+ this.keyframes = keyframes;
11
+ this.keyframesMap = createKeyframesMap();
12
+ this.fillKeyframesMap();
13
+ }
14
+ static pushKeyframeToMap(keyframe, map) {
15
+ insertBin(map[keyframe.type], keyframe);
16
+ }
17
+ getDimensions(values, pos) {
18
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.Dimensions];
19
+ if (!keyframes || !keyframes.length)
20
+ return values;
21
+ if (keyframes.length === 1) {
22
+ const [keyframe] = keyframes;
23
+ return {
24
+ width: keyframe.value.width,
25
+ height: keyframe.value.height
26
+ };
27
+ }
28
+ const { start, end } = this.getStartEnd(pos, keyframes);
29
+ return {
30
+ width: rangeMap(pos, start.position, end.position, start.value.width, end.value.width, true),
31
+ height: rangeMap(pos, start.position, end.position, start.value.height, end.value.height, true)
32
+ };
33
+ }
34
+ getPositions(values, pos) {
35
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.Position];
36
+ if (!keyframes || !keyframes.length)
37
+ return values;
38
+ if (keyframes.length === 1) {
39
+ const [keyframe] = keyframes;
40
+ return {
41
+ left: keyframe.value.left,
42
+ top: keyframe.value.top
43
+ };
44
+ }
45
+ const { start, end } = this.getStartEnd(pos, keyframes);
46
+ return {
47
+ left: rangeMap(pos, start.position, end.position, start.value.left, end.value.left, true),
48
+ top: rangeMap(pos, start.position, end.position, start.value.top, end.value.top, true)
49
+ };
50
+ }
51
+ getColor(values, pos) {
52
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.Color];
53
+ if (!keyframes || !keyframes.length)
54
+ return values;
55
+ if (keyframes.length === 1) {
56
+ const [keyframe] = keyframes;
57
+ return {
58
+ color: keyframe.value.color
59
+ };
60
+ }
61
+ const { start, end } = this.getStartEnd(pos, keyframes);
62
+ return {
63
+ color: this.getRgba(start, end, pos)
64
+ };
65
+ }
66
+ getRadius(values, pos) {
67
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.BorderRadius];
68
+ if (!keyframes || !keyframes.length)
69
+ return values;
70
+ if (keyframes.length === 1) {
71
+ const [keyframe] = keyframes;
72
+ return {
73
+ radius: keyframe.value.radius
74
+ };
75
+ }
76
+ const { start, end } = this.getStartEnd(pos, keyframes);
77
+ return {
78
+ radius: rangeMap(pos, start.position, end.position, start.value.radius, end.value.radius, true)
79
+ };
80
+ }
81
+ getBorderWidth(values, pos) {
82
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.BorderWidth];
83
+ if (!keyframes || !keyframes.length)
84
+ return values;
85
+ if (keyframes.length === 1) {
86
+ const [keyframe] = keyframes;
87
+ return {
88
+ borderWidth: keyframe.value.borderWidth
89
+ };
90
+ }
91
+ const { start, end } = this.getStartEnd(pos, keyframes);
92
+ return {
93
+ borderWidth: rangeMap(pos, start.position, end.position, start.value.borderWidth, end.value.borderWidth, true)
94
+ };
95
+ }
96
+ getRotation(values, pos) {
97
+ const keyframes = this.keyframesMap[sdk_1.KeyframeType.Rotation];
98
+ if (!keyframes || !keyframes.length)
99
+ return values;
100
+ if (keyframes.length === 1) {
101
+ const [keyframe] = keyframes;
102
+ return {
103
+ angle: keyframe.value.angle
104
+ };
105
+ }
106
+ const { start, end } = this.getStartEnd(pos, keyframes);
107
+ return {
108
+ angle: rangeMap(pos, start.position, end.position, start.value.angle, end.value.angle, true)
109
+ };
110
+ }
111
+ getStartEnd(position, keyframes) {
112
+ const index = (0, binSearchInsertAt_1.binSearchInsertAt)(keyframes, { position }, compare);
113
+ const end = index === keyframes.length ? index - 1 : index;
114
+ const start = end === 0 ? end : end - 1;
115
+ return { start: keyframes[start], end: keyframes[end] };
116
+ }
117
+ fillKeyframesMap() {
118
+ this.keyframesMap = createKeyframesMap();
119
+ for (const keyframe of this.keyframes) {
120
+ Animator.pushKeyframeToMap(keyframe, this.keyframesMap);
121
+ }
122
+ }
123
+ getRgba(start, end, position) {
124
+ const [startR, startG, startB, startA] = parseRgba(start.value.color);
125
+ const [endR, endG, endB, endA] = parseRgba(end.value.color);
126
+ const r = rangeMap(position, start.position, end.position, startR, endR, true);
127
+ const g = rangeMap(position, start.position, end.position, startG, endG, true);
128
+ const b = rangeMap(position, start.position, end.position, startB, endB, true);
129
+ const a = rangeMap(position, start.position, end.position, startA, endA, true);
130
+ return `rgba(${r},${g},${b},${a})`;
131
+ }
132
+ }
133
+ exports.Animator = Animator;
134
+ const reRgba = /^rgba?\( *(\d+) *, *(\d+) *, *(\d+)(?: *, *(\d+(?:\.\d+)?))? *\)$/i;
135
+ function parseRgba(color) {
136
+ const match = reRgba.exec(color);
137
+ if (!match)
138
+ return [0, 0, 0, 1];
139
+ const [, r, g, b, a] = match;
140
+ return [parseInt(r, 10), parseInt(g, 10), parseInt(b, 10), a ? parseFloat(a) : NaN];
141
+ }
142
+ function createKeyframesMap() {
143
+ return {
144
+ [sdk_1.KeyframeType.Dimensions]: [],
145
+ [sdk_1.KeyframeType.Position]: [],
146
+ [sdk_1.KeyframeType.BorderWidth]: [],
147
+ [sdk_1.KeyframeType.BorderRadius]: [],
148
+ [sdk_1.KeyframeType.Color]: [],
149
+ [sdk_1.KeyframeType.Rotation]: []
150
+ };
151
+ }
152
+ function rangeMap(n, start1, stop1, start2, stop2, withinBounds = false) {
153
+ const mapped = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2;
154
+ if (withinBounds && n < start1)
155
+ return start2;
156
+ if (withinBounds && n > stop1)
157
+ return stop2;
158
+ return mapped;
159
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArticleRectObserver = void 0;
4
+ const EventEmitter_1 = require("../EventEmitter");
5
+ class ArticleRectObserver extends EventEmitter_1.EventEmitter {
6
+ constructor() {
7
+ super();
8
+ this.articleWidth = 0;
9
+ this.scrollPos = window.scrollY;
10
+ this.animationFrame = NaN;
11
+ this.handleScroll = (scroll) => {
12
+ this.setScroll(scroll / this.articleWidth);
13
+ };
14
+ this.handleResize = (entries) => {
15
+ const [entry] = entries;
16
+ if (!entry)
17
+ return;
18
+ const element = entry.target;
19
+ if (!(element instanceof HTMLElement))
20
+ return;
21
+ this.notify(element);
22
+ this.emit('resize', undefined);
23
+ };
24
+ this.resizeObserver = new ResizeObserver(this.handleResize);
25
+ }
26
+ get scroll() {
27
+ return this.scrollPos;
28
+ }
29
+ get width() {
30
+ return this.articleWidth;
31
+ }
32
+ setScroll(scroll) {
33
+ this.scrollPos = scroll;
34
+ }
35
+ start(el) {
36
+ this.resizeObserver.observe(el);
37
+ const onScroll = () => {
38
+ this.handleScroll(window.scrollY);
39
+ if (!isNaN(this.animationFrame))
40
+ return;
41
+ this.animationFrame = window.requestAnimationFrame(() => {
42
+ this.animationFrame = NaN;
43
+ this.emit('scroll', undefined);
44
+ });
45
+ };
46
+ window.addEventListener('scroll', onScroll);
47
+ return () => {
48
+ this.resizeObserver.unobserve(el);
49
+ window.removeEventListener('scroll', onScroll);
50
+ if (!isNaN(this.animationFrame)) {
51
+ window.cancelAnimationFrame(this.animationFrame);
52
+ this.animationFrame = NaN;
53
+ }
54
+ };
55
+ }
56
+ notify(el) {
57
+ const elBoundary = el.getBoundingClientRect();
58
+ this.articleWidth = elBoundary.width;
59
+ }
60
+ }
61
+ exports.ArticleRectObserver = ArticleRectObserver;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useArticleRectObserver = void 0;
4
+ const react_1 = require("react");
5
+ const ArticleRectObserver_1 = require("./ArticleRectObserver");
6
+ const useArticleRectObserver = (el) => {
7
+ const [articleRectObserver, setArticleRectObserver] = (0, react_1.useState)(null);
8
+ (0, react_1.useEffect)(() => {
9
+ setArticleRectObserver(new ArticleRectObserver_1.ArticleRectObserver());
10
+ }, []);
11
+ (0, react_1.useEffect)(() => {
12
+ if (!el || !articleRectObserver)
13
+ return;
14
+ return articleRectObserver.start(el);
15
+ }, [el, articleRectObserver]);
16
+ return articleRectObserver;
17
+ };
18
+ exports.useArticleRectObserver = useArticleRectObserver;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventEmitter = void 0;
4
+ class EventEmitter {
5
+ constructor() {
6
+ this.listeners = {};
7
+ }
8
+ on(event, listener) {
9
+ if (!Array.isArray(this.listeners[event])) {
10
+ this.listeners[event] = [];
11
+ }
12
+ this.listeners[event]?.push(listener);
13
+ return () => {
14
+ this.off(event, listener);
15
+ };
16
+ }
17
+ off(event, listener) {
18
+ const filtered = this.listeners[event]?.filter(l => l !== listener);
19
+ this.listeners[event] = filtered && filtered.length > 0 ? filtered : undefined;
20
+ }
21
+ emit(event, payload) {
22
+ const listeners = this.listeners[event];
23
+ if (!Array.isArray(listeners))
24
+ return;
25
+ for (const listener of listeners) {
26
+ try {
27
+ listener(payload);
28
+ }
29
+ catch (error) {
30
+ console.error(error instanceof Error ? error.stack : error);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ exports.EventEmitter = EventEmitter;
@@ -3,14 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RichTextConverter = exports.FontStyles = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const sdk_1 = require("@cntrl-site/sdk");
6
- const LinkWrapper_1 = require("../components/LinkWrapper");
6
+ const LinkWrapper_1 = require("../../components/LinkWrapper");
7
7
  exports.FontStyles = {
8
8
  'normal': {},
9
9
  'bold': { 'font-weight': 'bold' },
10
10
  'italic': { 'font-style': 'italic' }
11
11
  };
12
12
  class RichTextConverter {
13
- toHtml(richText, layouts) {
13
+ toHtml(richText, layouts, hasPreset) {
14
14
  const { text: rawText, blocks = [] } = richText.commonParams;
15
15
  const text = Array.from(rawText); // because of emoji
16
16
  const root = [];
@@ -58,7 +58,7 @@ class RichTextConverter {
58
58
  .${blockClass} {
59
59
  display: ${group.some(g => g.layout === l.id) ? 'block' : 'none'};
60
60
  text-align: ${ta};
61
- line-height: 0;
61
+ ${hasPreset ? '' : 'line-height: 0;'}
62
62
  white-space: normal;
63
63
  overflow-wrap: break-word;
64
64
  }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createInsert = exports.binSearchInsertAt = void 0;
4
+ function binSearchInsertAt(list, item, compare) {
5
+ switch (list.length) {
6
+ case 0: return 0;
7
+ case 1: return compare(item, list[0]) < 0 ? 0 : 1;
8
+ }
9
+ let start = 0;
10
+ let end = list.length;
11
+ let pivot;
12
+ let cmpResult;
13
+ while (start < end) {
14
+ pivot = start + Math.floor((end - start) / 2);
15
+ cmpResult = compare(item, list[pivot]);
16
+ if (cmpResult === 0)
17
+ return pivot + 1;
18
+ if (cmpResult < 0) {
19
+ end = pivot;
20
+ }
21
+ if (cmpResult > 0) {
22
+ start = pivot + 1;
23
+ }
24
+ }
25
+ if (start >= list.length)
26
+ return list.length;
27
+ return compare(item, list[start]) < 0 ? start : start + 1;
28
+ }
29
+ exports.binSearchInsertAt = binSearchInsertAt;
30
+ function createInsert(start, compare) {
31
+ return (list, item) => {
32
+ const pos = start(list, item, compare);
33
+ list.splice(pos, 0, item);
34
+ };
35
+ }
36
+ exports.createInsert = createInsert;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateTypePresetStyles = void 0;
4
+ const sdk_1 = require("@cntrl-site/sdk");
5
+ const reEmptyLines = /^\s*\n/gm;
6
+ function generateTypePresetStyles(preset, layouts) {
7
+ const sorted = layouts.slice().sort((a, b) => a.startsWith - b.startsWith);
8
+ const stylesStr = (`
9
+ ${preset.presets.map(p => (`
10
+ .cntrl-preset-${p.id} {
11
+ font-family: ${p.fontFamily};
12
+ ${p.fontStyle.length > 0 ? `font-style: ${p.fontStyle};` : ''}
13
+ font-weight: ${p.fontWeight};
14
+ }
15
+ ${sorted.map(l => (`
16
+ ${(0, sdk_1.getLayoutMediaQuery)(l.id, sorted)} {
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};
23
+ }
24
+ }`)).join('\n')}`)).join('\n')}`);
25
+ return stylesStr.replace(reEmptyLines, '');
26
+ }
27
+ exports.generateTypePresetStyles = generateTypePresetStyles;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "0.2.13",
3
+ "version": "0.4.0",
4
4
  "description": "SDK for Next.js",
5
5
  "main": "lib/index.js",
6
6
  "types": "src/index.js",
@@ -21,20 +21,25 @@
21
21
  },
22
22
  "homepage": "https://github.com/cntrl-site/sdk-nextjs#readme",
23
23
  "dependencies": {
24
- "@cntrl-site/sdk": "^0.2.13",
24
+ "@cntrl-site/sdk": "^0.4.1",
25
25
  "html-react-parser": "^3.0.1",
26
26
  "styled-jsx": "^5.0.2"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "next": "^12.2.3",
30
30
  "react": "^18.2.0",
31
- "react-dom": "^18.2.0"
31
+ "react-dom": "^18.2.0",
32
+ "lodash.isequal": "^4.5.0"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@tsconfig/node16": "^1.0.3",
35
36
  "@tsconfig/recommended": "^1.0.1",
37
+ "@types/lodash.isequal": "^4.5.6",
38
+ "@types/jest": "^29.2.5",
36
39
  "@types/node": "^18.11.7",
37
40
  "@types/react": "^18.0.15",
41
+ "jest": "^29.3.1",
42
+ "ts-jest": "^29.0.3",
38
43
  "typescript": "^4.7.4"
39
44
  },
40
45
  "directories": {
@@ -0,0 +1,47 @@
1
+ import { useCntrlContext } from '../provider/useCntrlContext';
2
+ import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
3
+ import { ArticleRectContext } from '../provider/ArticleRectContext';
4
+
5
+ interface LayoutData {
6
+ layoutId: string;
7
+ start: number;
8
+ end: number;
9
+ }
10
+
11
+ export const useCurrentLayout = () => {
12
+ const { layouts } = useCntrlContext();
13
+ const [initialLayout] = layouts;
14
+ const articleRectObserver = useContext(ArticleRectContext);
15
+ const layoutRanges = useMemo(() => {
16
+ const sorted = layouts.slice().sort((la, lb) => la.startsWith - lb.startsWith);
17
+ return sorted.reduce<LayoutData[]>((acc, layout, i, layouts) => {
18
+ const next = layouts[i + 1];
19
+ return [
20
+ ...acc,
21
+ {
22
+ layoutId: layout.id,
23
+ start: layout.startsWith,
24
+ end: next ? next.startsWith : Number.MAX_SAFE_INTEGER
25
+ }
26
+ ];
27
+ }, []);
28
+ }, [layouts]);
29
+ const getCurrentLayout = useCallback((articleWidth: number) => {
30
+ return layoutRanges.find(l => articleWidth >= l.start && articleWidth < l.end)!.layoutId;
31
+ }, [layoutRanges]);
32
+ const [layoutId, setLayoutId] = useState<string>(getCurrentLayout(isServer() ? 0 : window.innerWidth));
33
+
34
+ useEffect(() => {
35
+ if (!articleRectObserver) return;
36
+ return articleRectObserver.on('resize', () => {
37
+ const articleWidth = articleRectObserver.width;
38
+ setLayoutId(getCurrentLayout(articleWidth));
39
+ });
40
+ }, [articleRectObserver, getCurrentLayout]);
41
+
42
+ return layoutId;
43
+ };
44
+
45
+ function isServer(): boolean {
46
+ return typeof window === 'undefined';
47
+ }
@@ -0,0 +1,74 @@
1
+ import { KeyframeType, TArticleItemAny } from '@cntrl-site/sdk';
2
+ import isEqual from 'lodash.isequal';
3
+ import { DependencyList, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { ArticleRectContext } from '../provider/ArticleRectContext';
5
+ import { useCurrentLayout } from './useCurrentLayout';
6
+ import { KeyframesContext } from '../provider/KeyframesContext';
7
+ import { AnimationData, Animator } from '../utils/Animator/Animator';
8
+
9
+ export type AnimatorGetter<T> = (animator: Animator, scroll: number, value: T) => T;
10
+ type ItemParamGetter<T> = (item: TArticleItemAny, layoutId: string) => T;
11
+ const emptyDeps: DependencyList = [];
12
+
13
+ export const useKeyframeValue = <T>(
14
+ item: TArticleItemAny,
15
+ itemParamsGetter: ItemParamGetter<T>,
16
+ animatorGetter: AnimatorGetter<T>,
17
+ deps: DependencyList = emptyDeps
18
+ ) => {
19
+ const animatorGetterRef = useRef(animatorGetter);
20
+ const itemParamsGetterRef = useRef(itemParamsGetter);
21
+
22
+ animatorGetterRef.current = animatorGetter;
23
+ itemParamsGetterRef.current = itemParamsGetter;
24
+
25
+ const articleRectObserver = useContext(ArticleRectContext);
26
+ const layoutId = useCurrentLayout();
27
+ const keyframesRepo = useContext(KeyframesContext);
28
+ const keyframes = keyframesRepo.getItemKeyframes(item.id);
29
+ const paramValue = useMemo<T>(() => {
30
+ return itemParamsGetterRef.current(item, layoutId);
31
+ }, [item, layoutId, ...deps]);
32
+
33
+ const [adjustedValue, setAdjustedValue] = useState<T>(paramValue);
34
+ const adjustedValueRef = useRef<T>(adjustedValue);
35
+ adjustedValueRef.current = adjustedValue;
36
+
37
+ const animator = useMemo(() => {
38
+ if (!layoutId || !keyframes.length) return;
39
+ const animationData = keyframes
40
+ .filter(k => k.layoutId === layoutId)
41
+ .map<AnimationData<KeyframeType>>(({ position, type, value }) => ({
42
+ position,
43
+ type,
44
+ value
45
+ }));
46
+ return new Animator(animationData);
47
+ }, [keyframes, layoutId]);
48
+
49
+ useEffect(() => {
50
+ setAdjustedValue(paramValue);
51
+ }, [paramValue]);
52
+
53
+ useEffect(() => {
54
+ if (!animator || !articleRectObserver) return;
55
+ return articleRectObserver.on('resize', () => {
56
+ const scroll = articleRectObserver.scroll;
57
+ const newValue = animatorGetterRef.current(animator, scroll, paramValue);
58
+ setAdjustedValue(newValue);
59
+ });
60
+ }, [animator, articleRectObserver]);
61
+
62
+ useEffect(() => {
63
+ if (!animator || !articleRectObserver) return;
64
+ return articleRectObserver.on('scroll', () => {
65
+ const scroll = articleRectObserver.scroll;
66
+ const newValue = animatorGetterRef.current(animator, scroll, paramValue);
67
+ if (!isEqual(newValue, adjustedValueRef.current)) {
68
+ setAdjustedValue(newValue);
69
+ }
70
+ });
71
+ }, [animator, articleRectObserver]);
72
+ return adjustedValue;
73
+ };
74
+
@@ -1,21 +1,25 @@
1
- import { FC } from 'react';
2
- import { TArticle, TLayout, getLayoutStyles } from '@cntrl-site/sdk';
1
+ import { FC, useRef } from 'react';
2
+ import { TArticle } from '@cntrl-site/sdk';
3
3
  import { Section } from './Section';
4
4
  import { Item } from './Item';
5
+ import { useArticleRectObserver } from '../utils/ArticleRectManager/useArticleRectObserver';
6
+ import { ArticleRectContext } from '../provider/ArticleRectContext';
5
7
 
6
8
  interface Props {
7
9
  article: TArticle;
8
- layouts: TLayout[];
9
10
  }
10
11
 
11
- export const Article: FC<Props> = ({ article, layouts }) => {
12
+ export const Article: FC<Props> = ({ article }) => {
13
+ const articleRef = useRef<HTMLDivElement | null>(null);
14
+ const articleRectObserver = useArticleRectObserver(articleRef.current);
15
+
12
16
  return (
13
- <>
14
- <div className="article">
17
+ <ArticleRectContext.Provider value={articleRectObserver}>
18
+ <div className="article" ref={articleRef}>
15
19
  {article.sections.map((section, i) => (
16
- <Section section={section} key={section.id} layouts={layouts}>
20
+ <Section section={section} key={section.id}>
17
21
  {article.sections[i].items.map(item => (
18
- <Item layouts={layouts} item={item} key={item.id} />
22
+ <Item item={item} key={item.id} />
19
23
  ))}
20
24
  </Section>
21
25
  ))}
@@ -26,6 +30,6 @@ export const Article: FC<Props> = ({ article, layouts }) => {
26
30
  overflow: hidden;
27
31
  }
28
32
  `}</style>
29
- </>
33
+ </ArticleRectContext.Provider>
30
34
  );
31
35
  };