@cntrl-site/sdk-nextjs 1.6.11 → 1.6.13

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.
@@ -0,0 +1,5 @@
1
+ <component name="ProjectCodeStyleConfiguration">
2
+ <state>
3
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
4
+ </state>
5
+ </component>
package/Icon/r ADDED
File without changes
@@ -11,13 +11,22 @@ const sdk_1 = require("@cntrl-site/sdk");
11
11
  const useCntrlContext_1 = require("../../provider/useCntrlContext");
12
12
  const useSectionRegistry_1 = require("../../utils/ArticleRectManager/useSectionRegistry");
13
13
  const color_1 = require("@cntrl-site/color");
14
+ const useLayoutContext_1 = require("../useLayoutContext");
14
15
  const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
16
+ function isVideoUrl(url) {
17
+ const videoExtensions = ['.mp4', '.webm', '.ogg'];
18
+ return videoExtensions.some(ext => url.toLowerCase().endsWith(ext));
19
+ }
15
20
  const Section = ({ section, data, children }) => {
21
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
16
22
  const id = (0, react_1.useId)();
17
23
  const sectionRef = (0, react_1.useRef)(null);
18
24
  const { layouts, customSections } = (0, useCntrlContext_1.useCntrlContext)();
19
- const layoutValues = [section.height, section.color];
25
+ const layout = (0, useLayoutContext_1.useLayoutContext)();
26
+ const layoutValues = [section.height, section.color, (_a = section.media) !== null && _a !== void 0 ? _a : {}];
20
27
  const SectionComponent = section.name ? customSections.getComponent(section.name) : undefined;
28
+ const isVideo = layout && ((_c = (_b = section.media) === null || _b === void 0 ? void 0 : _b[layout]) === null || _c === void 0 ? void 0 : _c.url) ? isVideoUrl(String((_e = (_d = section.media) === null || _d === void 0 ? void 0 : _d[layout]) === null || _e === void 0 ? void 0 : _e.url)) : false;
29
+ const videoRef = (0, react_1.useRef)(null);
21
30
  (0, useSectionRegistry_1.useSectionRegistry)(section.id, sectionRef.current);
22
31
  const getSectionVisibilityStyles = () => {
23
32
  return layouts
@@ -33,15 +42,69 @@ const Section = ({ section, data, children }) => {
33
42
  }`;
34
43
  }, '');
35
44
  };
45
+ (0, react_1.useEffect)(() => {
46
+ if (!isVideo || !sectionRef.current || !videoRef.current)
47
+ return;
48
+ const video = videoRef.current;
49
+ const section = sectionRef.current;
50
+ const observer = new IntersectionObserver(([entry]) => {
51
+ if (entry.isIntersecting) {
52
+ video.style.display = 'block';
53
+ video.play().catch(() => { });
54
+ }
55
+ else {
56
+ video.style.display = 'none';
57
+ video.pause();
58
+ }
59
+ }, {
60
+ root: null,
61
+ rootMargin: '50px',
62
+ threshold: 0
63
+ });
64
+ observer.observe(section);
65
+ return () => observer.disconnect();
66
+ }, [isVideo]);
36
67
  if (SectionComponent)
37
68
  return (0, jsx_runtime_1.jsx)("div", { ref: sectionRef, children: (0, jsx_runtime_1.jsx)(SectionComponent, { data: data, children: children }) });
38
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: `section-${section.id}`, id: section.name, ref: sectionRef, children: children }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
39
- ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([height, color]) => (`
69
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: `section-${section.id}`, id: section.name, ref: sectionRef, children: [layout && ((_g = (_f = section.media) === null || _f === void 0 ? void 0 : _f[layout]) === null || _g === void 0 ? void 0 : _g.url) && ((_j = (_h = section.media) === null || _h === void 0 ? void 0 : _h[layout]) === null || _j === void 0 ? void 0 : _j.size) !== 'none' && sectionRef.current && ((0, jsx_runtime_1.jsx)("div", { className: `section-background-overlay-${section.id}`, children: (0, jsx_runtime_1.jsx)("div", { className: `section-background-wrapper-${section.id}`, children: isVideo ? ((0, jsx_runtime_1.jsx)("video", { ref: videoRef, autoPlay: true, loop: true, muted: true, playsInline: true, preload: "auto", className: `video-background-${section.id}`, children: (0, jsx_runtime_1.jsx)("source", { src: (_m = (_l = (_k = section.media) === null || _k === void 0 ? void 0 : _k[layout]) === null || _l === void 0 ? void 0 : _l.url) !== null && _m !== void 0 ? _m : '' }) })) : ((0, jsx_runtime_1.jsx)("img", { src: (_q = (_p = (_o = section.media) === null || _o === void 0 ? void 0 : _o[layout]) === null || _p === void 0 ? void 0 : _p.url) !== null && _q !== void 0 ? _q : '', className: `image-background-${section.id}` })) }, `section-background-wrapper-${section.id}`) })), children] }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
70
+ ${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([height, color, media]) => {
71
+ var _a;
72
+ return (`
40
73
  .section-${section.id} {
41
74
  height: ${getSectionHeight(height)};
42
75
  position: relative;
43
76
  background-color: ${color_1.CntrlColor.parse(color !== null && color !== void 0 ? color : DEFAULT_COLOR).fmt('rgba')};
44
- }`))}
77
+ }
78
+ .section-background-overlay-${section.id} {
79
+ height: ${getSectionHeight(height)};
80
+ width: 100%;
81
+ position: relative;
82
+ overflow: clip;
83
+ }
84
+ .section-background-wrapper-${section.id} {
85
+ transform: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? 'translateY(-100vh)' : 'unset'};
86
+ left: 0;
87
+ position: relative;
88
+ height: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? `calc(${getSectionHeight(height)} + 200vh)` : getSectionHeight(height)}};
89
+ width: 100%;
90
+ }
91
+ .video-background-${section.id} {
92
+ object-fit: ${(_a = media === null || media === void 0 ? void 0 : media.size) !== null && _a !== void 0 ? _a : 'cover'};
93
+ width: 100%;
94
+ height: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? '100vh' : '100%'};
95
+ position: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? 'sticky' : 'relative'};
96
+ top: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? '100vh' : 'unset'};
97
+ left: 0;
98
+ }
99
+ .image-background-${section.id} {
100
+ object-fit: ${media === null || media === void 0 ? void 0 : media.size};
101
+ width: 100%;
102
+ height: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? '100vh' : '100%'};
103
+ position: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? 'sticky' : 'relative'};
104
+ top: ${(media === null || media === void 0 ? void 0 : media.position) === 'fixed' ? '100vh' : 'unset'};
105
+ }
106
+ `);
107
+ })}
45
108
  ${getSectionVisibilityStyles()}
46
109
  ` })] }));
47
110
  };
@@ -25,9 +25,12 @@ const stylesMap = {
25
25
  [sdk_1.AreaAnchor.BottomRight]: { justifyContent: 'flex-end', alignItems: 'flex-end' }
26
26
  };
27
27
  const CodeEmbedItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
28
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
28
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
29
29
  const id = (0, react_1.useId)();
30
- const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
30
+ const { layouts, fonts } = (0, useCntrlContext_1.useCntrlContext)();
31
+ const fontGoogleTags = fonts === null || fonts === void 0 ? void 0 : fonts.google;
32
+ const fontAdobeTags = fonts === null || fonts === void 0 ? void 0 : fonts.adobe;
33
+ const fontCustomTags = new sdk_1.FontFaceGenerator((_a = fonts === null || fonts === void 0 ? void 0 : fonts.custom) !== null && _a !== void 0 ? _a : []).generate();
31
34
  const { anchor, blur: itemBlur, opacity: itemOpacity } = (0, useCodeEmbedItem_1.useCodeEmbedItem)(item, sectionId);
32
35
  const itemAngle = (0, useItemAngle_1.useItemAngle)(item, sectionId);
33
36
  const { html } = item.commonParams;
@@ -36,9 +39,9 @@ const CodeEmbedItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilit
36
39
  const pos = stylesMap[anchor];
37
40
  const layoutValues = [item.area, item.layoutParams];
38
41
  const stateParams = interactionCtrl === null || interactionCtrl === void 0 ? void 0 : interactionCtrl.getState(['angle', 'blur', 'opacity']);
39
- const blur = ((_b = (_a = stateParams === null || stateParams === void 0 ? void 0 : stateParams.styles) === null || _a === void 0 ? void 0 : _a.blur) !== null && _b !== void 0 ? _b : itemBlur);
40
- const opacity = (_d = (_c = stateParams === null || stateParams === void 0 ? void 0 : stateParams.styles) === null || _c === void 0 ? void 0 : _c.opacity) !== null && _d !== void 0 ? _d : itemOpacity;
41
- const angle = (_f = (_e = stateParams === null || stateParams === void 0 ? void 0 : stateParams.styles) === null || _e === void 0 ? void 0 : _e.angle) !== null && _f !== void 0 ? _f : itemAngle;
42
+ const blur = ((_c = (_b = stateParams === null || stateParams === void 0 ? void 0 : stateParams.styles) === null || _b === void 0 ? void 0 : _b.blur) !== null && _c !== void 0 ? _c : itemBlur);
43
+ const opacity = (_e = (_d = stateParams === null || stateParams === void 0 ? void 0 : stateParams.styles) === null || _d === void 0 ? void 0 : _d.opacity) !== null && _e !== void 0 ? _e : itemOpacity;
44
+ const angle = (_g = (_f = stateParams === null || stateParams === void 0 ? void 0 : stateParams.styles) === null || _f === void 0 ? void 0 : _f.angle) !== null && _g !== void 0 ? _g : itemAngle;
42
45
  (0, react_1.useEffect)(() => {
43
46
  if (!ref)
44
47
  return;
@@ -59,13 +62,21 @@ const CodeEmbedItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilit
59
62
  const iframe = ref.querySelector(`[data-embed="${item.id}"]`);
60
63
  if (!iframe)
61
64
  return;
62
- iframe.srcdoc = item.commonParams.html;
65
+ const htmlWithStyles = `
66
+ ${fontGoogleTags}
67
+ ${fontAdobeTags}
68
+ <style>
69
+ ${fontCustomTags}
70
+ </style>
71
+ ${item.commonParams.html}
72
+ `;
73
+ iframe.srcdoc = htmlWithStyles;
63
74
  }, [item.commonParams.html, item.commonParams.iframe, ref]);
64
75
  const isInteractive = opacity !== 0;
65
76
  (0, react_1.useEffect)(() => {
66
77
  onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(isInteractive);
67
78
  }, [isInteractive, onVisibilityChange]);
68
- return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_g = item.link) === null || _g === void 0 ? void 0 : _g.url, target: (_h = item.link) === null || _h === void 0 ? void 0 : _h.target, children: [(0, jsx_runtime_1.jsx)("div", { className: `embed-wrapper-${item.id}`, style: Object.assign(Object.assign(Object.assign(Object.assign({}, (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), (opacity !== undefined ? { opacity } : {})), { transition: (_j = stateParams === null || stateParams === void 0 ? void 0 : stateParams.transition) !== null && _j !== void 0 ? _j : 'none' }), ref: setRef, children: item.commonParams.iframe ? ((0, jsx_runtime_1.jsx)("iframe", { "data-embed": item.id, className: `embed-${item.id}`, style: Object.assign(Object.assign({}, pos), { border: 'unset' }) })) : ((0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id}`, style: Object.assign({}, pos), dangerouslySetInnerHTML: { __html: html } })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
79
+ return ((0, jsx_runtime_1.jsxs)(LinkWrapper_1.LinkWrapper, { url: (_h = item.link) === null || _h === void 0 ? void 0 : _h.url, target: (_j = item.link) === null || _j === void 0 ? void 0 : _j.target, children: [(0, jsx_runtime_1.jsx)("div", { className: `embed-wrapper-${item.id}`, style: Object.assign(Object.assign(Object.assign(Object.assign({}, (angle !== undefined ? { transform: `rotate(${angle}deg)` } : {})), (blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {})), (opacity !== undefined ? { opacity } : {})), { transition: (_k = stateParams === null || stateParams === void 0 ? void 0 : stateParams.transition) !== null && _k !== void 0 ? _k : 'none' }), ref: setRef, children: item.commonParams.iframe ? ((0, jsx_runtime_1.jsx)("iframe", { "data-embed": item.id, className: `embed-${item.id}`, style: Object.assign(Object.assign({}, pos), { border: 'unset' }) })) : ((0, jsx_runtime_1.jsx)("div", { className: `embed-${item.id}`, style: Object.assign({}, pos), dangerouslySetInnerHTML: { __html: html } })) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
69
80
  .embed-wrapper-${item.id} {
70
81
  position: absolute;
71
82
  width: 100%;
@@ -15,6 +15,7 @@ class CntrlSdkContext {
15
15
  this.customItems = customItems;
16
16
  this.customSections = customSections;
17
17
  this._layouts = [];
18
+ this._fonts = undefined;
18
19
  this.sectionHeightMap = new Map();
19
20
  }
20
21
  resolveSectionData(sections) {
@@ -33,11 +34,15 @@ class CntrlSdkContext {
33
34
  }
34
35
  init({ project, article }) {
35
36
  this.setLayouts(project.layouts);
37
+ this.setFonts(project.fonts);
36
38
  this.setSectionsHeight(article.sections);
37
39
  }
38
40
  setLayouts(layouts) {
39
41
  this._layouts = layouts;
40
42
  }
43
+ setFonts(fonts) {
44
+ this._fonts = fonts;
45
+ }
41
46
  setSectionsHeight(sections) {
42
47
  for (const section of sections) {
43
48
  this.sectionHeightMap.set(section.id, section.height);
@@ -50,6 +55,9 @@ class CntrlSdkContext {
50
55
  get layouts() {
51
56
  return this._layouts;
52
57
  }
58
+ get fonts() {
59
+ return this._fonts;
60
+ }
53
61
  }
54
62
  exports.CntrlSdkContext = CntrlSdkContext;
55
63
  function isDefined(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk-nextjs",
3
- "version": "1.6.11",
3
+ "version": "1.6.13",
4
4
  "description": "SDK for Next.js",
5
5
  "author": "arsen@momdesign.nyc",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  "@antfu/eslint-config": "^3.8.0",
32
32
  "@cntrl-site/color": "^1.0.0",
33
33
  "@cntrl-site/effects": "^1.3.2",
34
- "@cntrl-site/sdk": "^1.19.4",
34
+ "@cntrl-site/sdk": "^1.19.6",
35
35
  "@types/vimeo__player": "^2.18.0",
36
36
  "@vimeo/player": "^2.25.0",
37
37
  "html-react-parser": "^3.0.1",
@@ -1,4 +1,4 @@
1
- import { FC, ReactElement, useId, useRef } from 'react';
1
+ import { FC, ReactElement, useEffect, useId, useRef, useState, useContext } from 'react';
2
2
  import JSXStyle from 'styled-jsx/style';
3
3
  import {
4
4
  getLayoutMediaQuery,
@@ -10,6 +10,7 @@ import {
10
10
  import { useCntrlContext } from '../../provider/useCntrlContext';
11
11
  import { useSectionRegistry } from '../../utils/ArticleRectManager/useSectionRegistry';
12
12
  import { CntrlColor } from '@cntrl-site/color';
13
+ import { useLayoutContext } from '../useLayoutContext';
13
14
 
14
15
  type SectionChild = ReactElement<any, any>;
15
16
  const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
@@ -20,13 +21,22 @@ interface Props {
20
21
  data?: any;
21
22
  }
22
23
 
24
+ function isVideoUrl(url: string): boolean {
25
+ const videoExtensions = ['.mp4', '.webm', '.ogg'];
26
+ return videoExtensions.some(ext => url.toLowerCase().endsWith(ext));
27
+ }
28
+
23
29
  export const Section: FC<Props> = ({ section, data, children }) => {
24
30
  const id = useId();
25
31
  const sectionRef = useRef<HTMLDivElement | null>(null);
26
32
  const { layouts, customSections } = useCntrlContext();
27
- const layoutValues: Record<string, any>[] = [section.height, section.color];
33
+ const layout = useLayoutContext();
34
+ const layoutValues: Record<string, any>[] = [section.height, section.color, section.media ?? {}];
28
35
  const SectionComponent = section.name ? customSections.getComponent(section.name) : undefined;
36
+ const isVideo = layout && section.media?.[layout]?.url ? isVideoUrl(String(section.media?.[layout]?.url)) : false;
37
+ const videoRef = useRef<HTMLVideoElement | null>(null);
29
38
  useSectionRegistry(section.id, sectionRef.current);
39
+
30
40
  const getSectionVisibilityStyles = () => {
31
41
  return layouts
32
42
  .sort((a, b) => a.startsWith - b.startsWith)
@@ -36,33 +46,110 @@ export const Section: FC<Props> = ({ section, data, children }) => {
36
46
  ${acc}
37
47
  ${getLayoutMediaQuery(layout.id, layouts)} {
38
48
  .section-${section.id} {
39
- display: ${isHidden ? 'none': 'block'};
49
+ display: ${isHidden ? 'none' : 'block'};
40
50
  }
41
51
  }`;
42
52
  }, '');
43
53
  };
44
54
 
55
+ useEffect(() => {
56
+ if (!isVideo || !sectionRef.current || !videoRef.current) return;
57
+ const video = videoRef.current;
58
+ const section = sectionRef.current;
59
+ const observer = new IntersectionObserver(
60
+ ([entry]) => {
61
+ if (entry.isIntersecting) {
62
+ video.style.display = 'block';
63
+ video.play().catch(() => {});
64
+ } else {
65
+ video.style.display = 'none';
66
+ video.pause();
67
+ }
68
+ },
69
+ {
70
+ root: null,
71
+ rootMargin: '50px',
72
+ threshold: 0
73
+ }
74
+ );
75
+ observer.observe(section);
76
+ return () => observer.disconnect();
77
+ }, [isVideo]);
78
+
45
79
  if (SectionComponent) return <div ref={sectionRef}><SectionComponent data={data}>{children}</SectionComponent></div>;
46
80
 
47
- return (
81
+ return (
48
82
  <>
49
83
  <div
50
84
  className={`section-${section.id}`}
51
85
  id={section.name}
52
86
  ref={sectionRef}
53
87
  >
88
+ {layout && section.media?.[layout]?.url && section.media?.[layout]?.size !== 'none' && sectionRef.current && (
89
+ <div className={`section-background-overlay-${section.id}`}>
90
+ <div
91
+ key={`section-background-wrapper-${section.id}`}
92
+ className={`section-background-wrapper-${section.id}`}
93
+ >
94
+ {isVideo ? (
95
+ <video
96
+ ref={videoRef}
97
+ autoPlay
98
+ loop
99
+ muted
100
+ playsInline
101
+ preload="auto"
102
+ className={`video-background-${section.id}`}
103
+ >
104
+ <source src={section.media?.[layout]?.url ?? ''} />
105
+ </video>
106
+ ) : (
107
+ <img src={section.media?.[layout]?.url ?? ''} className={`image-background-${section.id}`} />
108
+ )}
109
+ </div>
110
+ </div>
111
+ )}
54
112
  {children}
55
113
  </div>
56
114
  <JSXStyle id={id}>{`
57
115
  ${
58
- getLayoutStyles(layouts, layoutValues, ([height, color]) => (`
116
+ getLayoutStyles(layouts, layoutValues, ([height, color, media]) => (`
59
117
  .section-${section.id} {
60
118
  height: ${getSectionHeight(height)};
61
119
  position: relative;
62
120
  background-color: ${CntrlColor.parse(color ?? DEFAULT_COLOR).fmt('rgba')};
63
- }`
64
- ))
65
- }
121
+ }
122
+ .section-background-overlay-${section.id} {
123
+ height: ${getSectionHeight(height)};
124
+ width: 100%;
125
+ position: relative;
126
+ overflow: clip;
127
+ }
128
+ .section-background-wrapper-${section.id} {
129
+ transform: ${media?.position === 'fixed' ? 'translateY(-100vh)' : 'unset'};
130
+ left: 0;
131
+ position: relative;
132
+ height: ${media?.position === 'fixed' ? `calc(${getSectionHeight(height)} + 200vh)` : getSectionHeight(height)}};
133
+ width: 100%;
134
+ }
135
+ .video-background-${section.id} {
136
+ object-fit: ${media?.size ?? 'cover'};
137
+ width: 100%;
138
+ height: ${media?.position === 'fixed' ? '100vh' : '100%'};
139
+ position: ${media?.position === 'fixed' ? 'sticky' : 'relative'};
140
+ top: ${media?.position === 'fixed' ? '100vh' : 'unset'};
141
+ left: 0;
142
+ }
143
+ .image-background-${section.id} {
144
+ object-fit: ${media?.size};
145
+ width: 100%;
146
+ height: ${media?.position === 'fixed' ? '100vh' : '100%'};
147
+ position: ${media?.position === 'fixed' ? 'sticky' : 'relative'};
148
+ top: ${media?.position === 'fixed' ? '100vh' : 'unset'};
149
+ }
150
+ `
151
+ ))
152
+ }
66
153
  ${getSectionVisibilityStyles()}
67
154
  `}</JSXStyle>
68
155
  </>
@@ -1,9 +1,9 @@
1
- import { getLayoutStyles, CodeEmbedItem as TCodeEmbedItem, AreaAnchor } from '@cntrl-site/sdk';
1
+ import { getLayoutStyles, CodeEmbedItem as TCodeEmbedItem, AreaAnchor, FontFaceGenerator } from '@cntrl-site/sdk';
2
2
  import { FC, useEffect, useId, useState } from 'react';
3
3
  import { useCntrlContext } from '../../../provider/useCntrlContext';
4
4
  import { ItemProps } from '../Item';
5
5
  import JSXStyle from 'styled-jsx/style';
6
- import { useRegisterResize } from "../../../common/useRegisterResize";
6
+ import { useRegisterResize } from '../../../common/useRegisterResize';
7
7
  import { useItemAngle } from '../useItemAngle';
8
8
  import { LinkWrapper } from '../LinkWrapper';
9
9
  import { useCodeEmbedItem } from './useCodeEmbedItem';
@@ -22,7 +22,10 @@ const stylesMap = {
22
22
 
23
23
  export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
24
24
  const id = useId();
25
- const { layouts } = useCntrlContext();
25
+ const { layouts, fonts } = useCntrlContext();
26
+ const fontGoogleTags = fonts?.google;
27
+ const fontAdobeTags = fonts?.adobe;
28
+ const fontCustomTags = new FontFaceGenerator(fonts?.custom ?? []).generate();
26
29
  const { anchor, blur: itemBlur, opacity: itemOpacity } = useCodeEmbedItem(item, sectionId);
27
30
  const itemAngle = useItemAngle(item, sectionId);
28
31
  const { html } = item.commonParams;
@@ -47,14 +50,23 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
47
50
  script.parentNode!.removeChild(script);
48
51
  ref.appendChild(newScript);
49
52
  }
50
- }, [item.commonParams.html])
53
+ }, [item.commonParams.html]);
51
54
 
52
55
  useEffect(() => {
53
56
  if (!ref) return;
54
57
  const iframe: HTMLIFrameElement | null = ref.querySelector(`[data-embed="${item.id}"]`);
55
58
  if (!iframe) return;
56
- iframe.srcdoc = item.commonParams.html;
59
+ const htmlWithStyles = `
60
+ ${fontGoogleTags}
61
+ ${fontAdobeTags}
62
+ <style>
63
+ ${fontCustomTags}
64
+ </style>
65
+ ${item.commonParams.html}
66
+ `;
67
+ iframe.srcdoc = htmlWithStyles;
57
68
  }, [item.commonParams.html, item.commonParams.iframe, ref]);
69
+
58
70
  const isInteractive = opacity !== 0;
59
71
  useEffect(() => {
60
72
  onVisibilityChange?.(isInteractive);
@@ -69,7 +81,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
69
81
  ...(blur !== undefined ? { filter: `blur(${blur * 100}vw)` } : {}),
70
82
  ...(opacity !== undefined ? { opacity } : {}),
71
83
  transition: stateParams?.transition ?? 'none'
72
- }}
84
+ }}
73
85
  ref={setRef}
74
86
  >
75
87
  {item.commonParams.iframe ? (
@@ -102,7 +114,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
102
114
  border: none;
103
115
  }
104
116
  ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams], exemplary) => {
105
- return (`
117
+ return (`
106
118
  .embed-wrapper-${item.id} {
107
119
  opacity: ${layoutParams.opacity};
108
120
  transform: rotate(${area.angle}deg);
@@ -113,7 +125,7 @@ export const CodeEmbedItem: FC<ItemProps<TCodeEmbedItem>> = ({ item, sectionId,
113
125
  height: ${item.commonParams.scale ? `${area.height * exemplary}px` : '100%'};
114
126
  }
115
127
  `);
116
- })}
128
+ })}
117
129
  `}</JSXStyle>
118
130
  </LinkWrapper>
119
131
  );
@@ -9,6 +9,7 @@ interface SdkContextInitProps {
9
9
 
10
10
  export class CntrlSdkContext {
11
11
  private _layouts: Layout[] = [];
12
+ private _fonts?: Project['fonts'] = undefined;
12
13
  private sectionHeightMap: Map<string, Record<string, SectionHeight>> = new Map();
13
14
  constructor(
14
15
  public readonly customItems: CustomItemRegistry,
@@ -31,6 +32,7 @@ export class CntrlSdkContext {
31
32
 
32
33
  init({ project, article }: SdkContextInitProps) {
33
34
  this.setLayouts(project.layouts);
35
+ this.setFonts(project.fonts);
34
36
  this.setSectionsHeight(article.sections);
35
37
  }
36
38
 
@@ -38,9 +40,13 @@ export class CntrlSdkContext {
38
40
  this._layouts = layouts;
39
41
  }
40
42
 
43
+ private setFonts(fonts: Project['fonts']) {
44
+ this._fonts = fonts;
45
+ }
46
+
41
47
  setSectionsHeight(sections: Section[]) {
42
48
  for (const section of sections) {
43
- this.sectionHeightMap.set(section.id, section.height)
49
+ this.sectionHeightMap.set(section.id, section.height);
44
50
  }
45
51
  }
46
52
 
@@ -52,6 +58,10 @@ export class CntrlSdkContext {
52
58
  get layouts(): Layout[] {
53
59
  return this._layouts;
54
60
  }
61
+
62
+ get fonts(): Project['fonts'] | undefined {
63
+ return this._fonts;
64
+ }
55
65
  }
56
66
 
57
67
  function isDefined<T>(value: T): value is Exclude<T, undefined> {
@@ -27,7 +27,7 @@ export class ArticleRectObserver extends EventEmitter<EventMap> {
27
27
  getSectionScroll(sectionId: string): number {
28
28
  const sectionTop = this.sectionsScrollMap.get(sectionId);
29
29
  if (sectionTop === undefined) return 0;
30
- return - (sectionTop / this.articleWidth - this.scrollPos);
30
+ return -(sectionTop / this.articleWidth - this.scrollPos);
31
31
  }
32
32
 
33
33
  getSectionTop(sectionId: string): number {
@@ -54,7 +54,6 @@ export function getCompoundTransform(compoundSettings: CompoundSettings | undefi
54
54
  return `translate(${kx * (-100)}%, ${ky * (-100)}%)`;
55
55
  }
56
56
 
57
-
58
57
  type CompoundSettings = {
59
58
  positionAnchor: AreaAnchor;
60
59
  widthMode: DimensionMode;