@cntrl-site/sdk-nextjs 1.9.71-9 → 1.9.72

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 (143) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +4 -4
  3. package/TODO.md +1 -1
  4. package/eslint.config.mjs +35 -35
  5. package/jest.config.js +6 -6
  6. package/lib/common/useItemFXData.js +5 -5
  7. package/lib/components/Article.js +7 -6
  8. package/lib/components/LayoutStyle.js +4 -4
  9. package/lib/components/Section/Section.js +30 -48
  10. package/lib/components/items/CodeEmbedItem/CodeEmbedItem.js +36 -36
  11. package/lib/components/items/ComponentItem/ComponentItem.js +13 -13
  12. package/lib/components/items/CompoundItem/CompoundChild.js +20 -20
  13. package/lib/components/items/CompoundItem/CompoundItem.js +14 -14
  14. package/lib/components/items/CustomItem/CustomItem.js +9 -9
  15. package/lib/components/items/EmbedVideoItem/VimeoEmbed.js +24 -24
  16. package/lib/components/items/EmbedVideoItem/YoutubeEmbed.js +27 -27
  17. package/lib/components/items/FileItem/ImageItem.js +40 -40
  18. package/lib/components/items/FileItem/VideoItem.js +64 -64
  19. package/lib/components/items/GroupItem/GroupItem.js +14 -14
  20. package/lib/components/items/Item.js +33 -32
  21. package/lib/components/items/RectangleItem/RectangleItem.js +27 -27
  22. package/lib/components/items/RichTextItem/RichTextItem.js +22 -22
  23. package/lib/provider/CntrlSdkContext.js +1 -1
  24. package/lib/utils/RichTextConverter/RichTextConverter.js +21 -21
  25. package/package.json +62 -62
  26. package/src/ItemGeometry/CSSPropertyNameMap.ts +37 -37
  27. package/src/ItemGeometry/GenericGeometryController.ts +59 -59
  28. package/src/ItemGeometry/ItemGeometryContext.ts +4 -4
  29. package/src/ItemGeometry/ItemGeometryController.ts +22 -22
  30. package/src/ItemGeometry/ItemGeometryService.ts +34 -34
  31. package/src/ItemGeometry/RichTextGeometryController.ts +166 -166
  32. package/src/ItemGeometry/useItemGeometry.ts +25 -25
  33. package/src/common/useCurrentLayout.ts +52 -52
  34. package/src/common/useExemplary.ts +9 -9
  35. package/src/common/useItemFXData.ts +63 -63
  36. package/src/common/useKeyframeValue.ts +85 -85
  37. package/src/common/useRegisterResize.ts +16 -16
  38. package/src/components/Article.tsx +75 -69
  39. package/src/components/ArticleWrapper.tsx +21 -21
  40. package/src/components/Head.tsx +95 -95
  41. package/src/components/LayoutStyle.tsx +21 -21
  42. package/src/components/Page.tsx +47 -47
  43. package/src/components/ScrollPlaybackVideo.tsx +75 -75
  44. package/src/components/Section/Section.tsx +130 -162
  45. package/src/components/Section/SectionImage.tsx +48 -48
  46. package/src/components/Section/SectionVideo.tsx +144 -144
  47. package/src/components/Section/useSectionColor.ts +19 -19
  48. package/src/components/Section/useSectionHeightMap.ts +19 -19
  49. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +148 -148
  50. package/src/components/items/CodeEmbedItem/useCodeEmbedItem.ts +36 -36
  51. package/src/components/items/ComponentItem/ComponentItem.tsx +85 -85
  52. package/src/components/items/ComponentItem/useComponentItem.ts +35 -35
  53. package/src/components/items/CompoundItem/CompoundChild.tsx +151 -151
  54. package/src/components/items/CompoundItem/CompoundItem.tsx +88 -88
  55. package/src/components/items/CompoundItem/useCompoundItem.ts +21 -21
  56. package/src/components/items/CustomItem/CustomItem.tsx +50 -50
  57. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +217 -217
  58. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +201 -201
  59. package/src/components/items/EmbedVideoItem/useEmbedVideoItem.ts +46 -46
  60. package/src/components/items/FileItem/ImageItem.tsx +166 -166
  61. package/src/components/items/FileItem/VideoItem.tsx +287 -287
  62. package/src/components/items/FileItem/useFileItem.ts +81 -81
  63. package/src/components/items/GroupItem/GroupItem.tsx +99 -99
  64. package/src/components/items/GroupItem/useGroupItem.ts +34 -34
  65. package/src/components/items/Item.tsx +268 -268
  66. package/src/components/items/LinkWrapper.tsx +39 -39
  67. package/src/components/items/RectangleItem/RectangleItem.tsx +177 -177
  68. package/src/components/items/RectangleItem/useRectangleItem.ts +89 -89
  69. package/src/components/items/RichTextItem/RichTextItem.tsx +113 -113
  70. package/src/components/items/RichTextItem/useRichTextItem.ts +12 -12
  71. package/src/components/items/RichTextWrapper.tsx +15 -15
  72. package/src/components/items/itemsMap.ts +28 -28
  73. package/src/components/items/useDraggable.ts +252 -252
  74. package/src/components/items/useItemAngle.ts +13 -13
  75. package/src/components/items/useItemArea.ts +35 -35
  76. package/src/components/items/useItemPointerEvents.ts +23 -23
  77. package/src/components/items/useItemScale.ts +17 -17
  78. package/src/components/items/useItemTriggers.ts +15 -15
  79. package/src/components/items/useRichTextItemValues.ts +66 -66
  80. package/src/components/items/useSizing.ts +25 -25
  81. package/src/components/items/useStickyItemTop.ts +21 -21
  82. package/src/components/useLayoutContext.ts +7 -7
  83. package/src/index.ts +25 -25
  84. package/src/interactions/InteractionsRegistry.ts +512 -513
  85. package/src/interactions/ItemInteractionCtrl.ts +78 -78
  86. package/src/interactions/getTransition.ts +27 -27
  87. package/src/interactions/types.ts +36 -36
  88. package/src/interactions/useItemInteractionCtrl.ts +18 -18
  89. package/src/provider/ArticleRectContext.ts +4 -4
  90. package/src/provider/CntrlContext.ts +5 -5
  91. package/src/provider/CntrlProvider.tsx +11 -11
  92. package/src/provider/CntrlSdkContext.ts +113 -113
  93. package/src/provider/CustomItemRegistry.ts +14 -14
  94. package/src/provider/CustomItemTypes.ts +3 -3
  95. package/src/provider/CustomSectionRegistry.ts +25 -25
  96. package/src/provider/InteractionsContext.tsx +73 -73
  97. package/src/provider/Keyframes.ts +11 -11
  98. package/src/provider/KeyframesContext.ts +5 -5
  99. package/src/provider/LayoutContext.ts +3 -3
  100. package/src/provider/WebGLContextManagerContext.tsx +4 -4
  101. package/src/provider/defaultContext.ts +8 -8
  102. package/src/provider/useCntrlContext.ts +8 -8
  103. package/src/styled-jsx.d.ts +1 -1
  104. package/src/types/components-utils.d.ts +7 -7
  105. package/src/utils/Animator/Animator.ts +570 -570
  106. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +93 -93
  107. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -17
  108. package/src/utils/ArticleRectManager/useSectionRegistry.ts +11 -11
  109. package/src/utils/CSSNumeric.ts +19 -19
  110. package/src/utils/EventEmitter.ts +41 -41
  111. package/src/utils/RichTextConverter/RichTextConverter.tsx +342 -342
  112. package/src/utils/ScaleAnchorMap.ts +13 -13
  113. package/src/utils/StickyManager/StickyManager.ts +23 -23
  114. package/src/utils/Youtube/YouTubeIframeApiLoader.ts +58 -58
  115. package/src/utils/Youtube/YoutubeIframeApi.ts +101 -101
  116. package/src/utils/Youtube/useYouTubeIframeApi.ts +12 -12
  117. package/src/utils/areFillsVisible/areFillsVisible.test.ts +63 -63
  118. package/src/utils/areFillsVisible/areFillsVisible.ts +37 -37
  119. package/src/utils/binSearchInsertAt.ts +33 -33
  120. package/src/utils/castObject.ts +10 -10
  121. package/src/utils/checkOverflowClipSupport.ts +13 -13
  122. package/src/utils/domDfs.ts +11 -11
  123. package/src/utils/effects/useImageFx.ts +154 -154
  124. package/src/utils/effects/useVideoFx.ts +156 -156
  125. package/src/utils/evaluateComponentBundle.ts +83 -83
  126. package/src/utils/getAnchoredItemTop.ts +12 -12
  127. package/src/utils/getCompoundBoundaryStyles.ts +60 -60
  128. package/src/utils/getFill.ts +46 -46
  129. package/src/utils/getFontFamilyValue.ts +3 -3
  130. package/src/utils/getInvertedRanges.ts +43 -43
  131. package/src/utils/getItemTopStyle.ts +39 -39
  132. package/src/utils/getStyleFromItemStateAndParams.ts +8 -8
  133. package/src/utils/getValidYoutubeUrl.ts +29 -29
  134. package/src/utils/isItemType.ts +5 -5
  135. package/src/utils/rangeMap.ts +13 -13
  136. package/src/utils/useElementRect.ts +23 -23
  137. package/cntrl-site-sdk-nextjs-1.9.71-3.tgz +0 -0
  138. package/lib/components/StructuredBlockItem/StructuredBlockItem.js +0 -58
  139. package/lib/components/StructuredBlockItem/StructuredComponent/StructuredComponent.js +0 -48
  140. package/lib/components/StructuredContent/StructuredContent.js +0 -37
  141. package/src/components/StructuredBlockItem/StructuredBlockItem.tsx +0 -69
  142. package/src/components/StructuredBlockItem/StructuredComponent/StructuredComponent.tsx +0 -66
  143. package/src/components/StructuredContent/StructuredContent.tsx +0 -42
@@ -1,47 +1,47 @@
1
- import React, { FC, useMemo } from 'react';
2
- import HTMLReactParser from 'html-react-parser';
3
- import { Article as TArticle, Project, Meta, KeyframeAny } from '@cntrl-site/sdk';
4
- import { Article } from './Article';
5
- import { KeyframesContext } from '../provider/KeyframesContext';
6
- import { CNTRLHead } from './Head';
7
- import { Keyframes } from '../provider/Keyframes';
8
- import { ItemGeometryContext } from '../ItemGeometry/ItemGeometryContext';
9
- import { ItemGeometryService } from '../ItemGeometry/ItemGeometryService';
10
-
11
- export interface CustomComponentBundlesData {
12
- bundles: Record<string, string>;
13
- schemas: Record<string, Record<string, unknown>>;
14
- }
15
-
16
- export interface PageProps {
17
- article: TArticle;
18
- project: Project;
19
- meta: Meta;
20
- keyframes: KeyframeAny[];
21
- sectionData: Record<SectionName, any>;
22
- publicApiBase?: string;
23
- customComponentBundles?: CustomComponentBundlesData;
24
- siteUrl?: string;
25
- }
26
-
27
- export const Page: FC<PageProps> = ({ article, project, meta, keyframes, sectionData, siteUrl }) => {
28
- const afterBodyOpen = HTMLReactParser(project.html.afterBodyOpen);
29
- const beforeBodyClose = HTMLReactParser(project.html.beforeBodyClose);
30
- const keyframesRepo = useMemo(() => new Keyframes(keyframes), [keyframes]);
31
- const itemGeometryService = useMemo(() => new ItemGeometryService(), []);
32
- const currentSlug = project.pages.find(p => p.articleId === article.id)?.slug;
33
- return (
34
- <>
35
- <CNTRLHead project={project} meta={meta} slug={currentSlug} siteUrl={siteUrl} />
36
- {afterBodyOpen}
37
- <ItemGeometryContext.Provider value={itemGeometryService}>
38
- <KeyframesContext.Provider value={keyframesRepo}>
39
- <Article article={article} sectionData={sectionData} />
40
- </KeyframesContext.Provider>
41
- </ItemGeometryContext.Provider>
42
- {beforeBodyClose}
43
- </>
44
- );
45
- };
46
-
47
- type SectionName = string;
1
+ import React, { FC, useMemo } from 'react';
2
+ import HTMLReactParser from 'html-react-parser';
3
+ import { Article as TArticle, Project, Meta, KeyframeAny } from '@cntrl-site/sdk';
4
+ import { Article } from './Article';
5
+ import { KeyframesContext } from '../provider/KeyframesContext';
6
+ import { CNTRLHead } from './Head';
7
+ import { Keyframes } from '../provider/Keyframes';
8
+ import { ItemGeometryContext } from '../ItemGeometry/ItemGeometryContext';
9
+ import { ItemGeometryService } from '../ItemGeometry/ItemGeometryService';
10
+
11
+ export interface CustomComponentBundlesData {
12
+ bundles: Record<string, string>;
13
+ schemas: Record<string, Record<string, unknown>>;
14
+ }
15
+
16
+ export interface PageProps {
17
+ article: TArticle;
18
+ project: Project;
19
+ meta: Meta;
20
+ keyframes: KeyframeAny[];
21
+ sectionData: Record<SectionName, any>;
22
+ publicApiBase?: string;
23
+ customComponentBundles?: CustomComponentBundlesData;
24
+ siteUrl?: string;
25
+ }
26
+
27
+ export const Page: FC<PageProps> = ({ article, project, meta, keyframes, sectionData, siteUrl }) => {
28
+ const afterBodyOpen = HTMLReactParser(project.html.afterBodyOpen);
29
+ const beforeBodyClose = HTMLReactParser(project.html.beforeBodyClose);
30
+ const keyframesRepo = useMemo(() => new Keyframes(keyframes), [keyframes]);
31
+ const itemGeometryService = useMemo(() => new ItemGeometryService(), []);
32
+ const currentSlug = project.pages.find(p => p.articleId === article.id)?.slug;
33
+ return (
34
+ <>
35
+ <CNTRLHead project={project} meta={meta} slug={currentSlug} siteUrl={siteUrl} />
36
+ {afterBodyOpen}
37
+ <ItemGeometryContext.Provider value={itemGeometryService}>
38
+ <KeyframesContext.Provider value={keyframesRepo}>
39
+ <Article article={article} sectionData={sectionData} />
40
+ </KeyframesContext.Provider>
41
+ </ItemGeometryContext.Provider>
42
+ {beforeBodyClose}
43
+ </>
44
+ );
45
+ };
46
+
47
+ type SectionName = string;
@@ -1,75 +1,75 @@
1
- import React, { FC, useContext, useEffect, useMemo, useState } from 'react';
2
- import { rangeMap } from '../utils/rangeMap';
3
- import { ArticleRectContext } from '../provider/ArticleRectContext';
4
- import { ScrollPlaybackVideoManager, ScrollPlaybackFrameData } from '@cntrl-site/sdk';
5
-
6
- type PlaybackParams = { from: number, to: number };
7
-
8
- interface Props {
9
- sectionId: string;
10
- src: string;
11
- playbackParams: PlaybackParams | null;
12
- style?: React.CSSProperties;
13
- className: string;
14
- scrollPlaybackFrameData?: ScrollPlaybackFrameData | null;
15
- frameBaseUrl?: string;
16
- }
17
-
18
- export const ScrollPlaybackVideo: FC<Props> = ({ sectionId, src, playbackParams, style, className, scrollPlaybackFrameData, frameBaseUrl }) => {
19
- const [containerElement, setContainerElement] = useState<HTMLDivElement | null>(null);
20
- const [time, setTime] = useState(0);
21
- const articleRectObserver = useContext(ArticleRectContext);
22
- const hasServerFrames = scrollPlaybackFrameData?.status === 'ready'
23
- && scrollPlaybackFrameData.batchId
24
- && scrollPlaybackFrameData.frameCount
25
- && scrollPlaybackFrameData.frameRate
26
- && scrollPlaybackFrameData.frameFormat
27
- && frameBaseUrl;
28
-
29
- useEffect(() => {
30
- if (!playbackParams || !articleRectObserver) return;
31
- const from = playbackParams.from;
32
- const to = playbackParams.to;
33
- return articleRectObserver.on('scroll', () => {
34
- const scrollPos = articleRectObserver.getSectionScroll(sectionId);
35
- const nextTime = rangeMap(scrollPos, from, to, 0, 1, true);
36
- setTime(toFixed(nextTime));
37
- });
38
- }, [sectionId, playbackParams?.from, playbackParams?.to, articleRectObserver]);
39
-
40
- const scrollVideoManager = useMemo<ScrollPlaybackVideoManager | null>(() => {
41
- if (!containerElement) return null;
42
- const manager = new ScrollPlaybackVideoManager({
43
- src,
44
- videoContainer: containerElement,
45
- ...(hasServerFrames ? {
46
- frameData: {
47
- batchId: scrollPlaybackFrameData.batchId!,
48
- frameCount: scrollPlaybackFrameData.frameCount!,
49
- frameRate: scrollPlaybackFrameData.frameRate!,
50
- frameFormat: scrollPlaybackFrameData.frameFormat!
51
- },
52
- frameBaseUrl
53
- } : {})
54
- });
55
- return manager;
56
- }, [containerElement, src, hasServerFrames, frameBaseUrl]);
57
-
58
- useEffect(() => {
59
- return () => {
60
- scrollVideoManager?.destroy();
61
- };
62
- }, [scrollVideoManager]);
63
-
64
- useEffect(() => {
65
- if (scrollVideoManager && time >= 0 && time <= 1) {
66
- scrollVideoManager.setTargetTimePercent(time);
67
- }
68
- }, [time, scrollVideoManager]);
69
-
70
- return <div className={className} style={style} ref={setContainerElement} />;
71
- };
72
-
73
- function toFixed(num: number) {
74
- return Number(num.toFixed(3));
75
- }
1
+ import React, { FC, useContext, useEffect, useMemo, useState } from 'react';
2
+ import { rangeMap } from '../utils/rangeMap';
3
+ import { ArticleRectContext } from '../provider/ArticleRectContext';
4
+ import { ScrollPlaybackVideoManager, ScrollPlaybackFrameData } from '@cntrl-site/sdk';
5
+
6
+ type PlaybackParams = { from: number, to: number };
7
+
8
+ interface Props {
9
+ sectionId: string;
10
+ src: string;
11
+ playbackParams: PlaybackParams | null;
12
+ style?: React.CSSProperties;
13
+ className: string;
14
+ scrollPlaybackFrameData?: ScrollPlaybackFrameData | null;
15
+ frameBaseUrl?: string;
16
+ }
17
+
18
+ export const ScrollPlaybackVideo: FC<Props> = ({ sectionId, src, playbackParams, style, className, scrollPlaybackFrameData, frameBaseUrl }) => {
19
+ const [containerElement, setContainerElement] = useState<HTMLDivElement | null>(null);
20
+ const [time, setTime] = useState(0);
21
+ const articleRectObserver = useContext(ArticleRectContext);
22
+ const hasServerFrames = scrollPlaybackFrameData?.status === 'ready'
23
+ && scrollPlaybackFrameData.batchId
24
+ && scrollPlaybackFrameData.frameCount
25
+ && scrollPlaybackFrameData.frameRate
26
+ && scrollPlaybackFrameData.frameFormat
27
+ && frameBaseUrl;
28
+
29
+ useEffect(() => {
30
+ if (!playbackParams || !articleRectObserver) return;
31
+ const from = playbackParams.from;
32
+ const to = playbackParams.to;
33
+ return articleRectObserver.on('scroll', () => {
34
+ const scrollPos = articleRectObserver.getSectionScroll(sectionId);
35
+ const nextTime = rangeMap(scrollPos, from, to, 0, 1, true);
36
+ setTime(toFixed(nextTime));
37
+ });
38
+ }, [sectionId, playbackParams?.from, playbackParams?.to, articleRectObserver]);
39
+
40
+ const scrollVideoManager = useMemo<ScrollPlaybackVideoManager | null>(() => {
41
+ if (!containerElement) return null;
42
+ const manager = new ScrollPlaybackVideoManager({
43
+ src,
44
+ videoContainer: containerElement,
45
+ ...(hasServerFrames ? {
46
+ frameData: {
47
+ batchId: scrollPlaybackFrameData.batchId!,
48
+ frameCount: scrollPlaybackFrameData.frameCount!,
49
+ frameRate: scrollPlaybackFrameData.frameRate!,
50
+ frameFormat: scrollPlaybackFrameData.frameFormat!
51
+ },
52
+ frameBaseUrl
53
+ } : {})
54
+ });
55
+ return manager;
56
+ }, [containerElement, src, hasServerFrames, frameBaseUrl]);
57
+
58
+ useEffect(() => {
59
+ return () => {
60
+ scrollVideoManager?.destroy();
61
+ };
62
+ }, [scrollVideoManager]);
63
+
64
+ useEffect(() => {
65
+ if (scrollVideoManager && time >= 0 && time <= 1) {
66
+ scrollVideoManager.setTargetTimePercent(time);
67
+ }
68
+ }, [time, scrollVideoManager]);
69
+
70
+ return <div className={className} style={style} ref={setContainerElement} />;
71
+ };
72
+
73
+ function toFixed(num: number) {
74
+ return Number(num.toFixed(3));
75
+ }
@@ -1,162 +1,130 @@
1
- import React, { FC, ReactElement, useId, useRef, useMemo, useState, useEffect, PropsWithChildren } from 'react';
2
- import JSXStyle from 'styled-jsx/style';
3
- import {
4
- getLayoutMediaQuery,
5
- getLayoutStyles,
6
- Section as TSection,
7
- SectionHeight,
8
- SectionHeightMode
9
- } from '@cntrl-site/sdk';
10
- import { useCntrlContext } from '../../provider/useCntrlContext';
11
- import { useSectionRegistry } from '../../utils/ArticleRectManager/useSectionRegistry';
12
- import { CntrlColor } from '@cntrl-site/color';
13
- import { useLayoutContext } from '../useLayoutContext';
14
- import { SectionVideo } from './SectionVideo';
15
- import { SectionImage } from './SectionImage';
16
- import { isOverflowClipSupported } from '../../utils/checkOverflowClipSupport';
17
- import { StructuredContent } from '../StructuredContent/StructuredContent';
18
- import { Item } from '../items/Item';
19
-
20
- const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
21
-
22
- interface Props {
23
- section: TSection;
24
- zIndex: number;
25
- data?: any;
26
- articleHeight: number;
27
- }
28
-
29
- export const Section: FC<PropsWithChildren<Props>> = ({ section, data, zIndex, articleHeight }) => {
30
- const reactId = useId();
31
- const id = `${reactId}-section-${section.id}`;
32
- const sectionRef = useRef<HTMLDivElement | null>(null);
33
- const { layouts, customSections } = useCntrlContext();
34
- const layout = useLayoutContext();
35
- const [sectionHeight, setSectionHeight] = useState(0);
36
- const layoutValues: Record<string, any>[] = [
37
- section.minHeight,
38
- section.color,
39
- section.media ?? {}
40
- ];
41
-
42
- const SectionComponent = section.name ? customSections.getComponent(section.name) : undefined;
43
- useSectionRegistry(section.id, sectionRef.current);
44
- const layoutMedia = layout && section.media && section.media[layout] ? section.media[layout] : undefined;
45
-
46
- const media = useMemo(() => {
47
- if (layoutMedia && !isOverflowClipSupported()) {
48
- return {
49
- ...layoutMedia,
50
- position: 'local'
51
- };
52
- }
53
- return layoutMedia;
54
- }, [layoutMedia]);
55
-
56
- const getSectionVisibilityStyles = () => {
57
- return layouts
58
- .sort((a, b) => a.startsWith - b.startsWith)
59
- .reduce((acc, layout) => {
60
- const isHidden = section.hidden[layout.id];
61
- return `
62
- ${acc}
63
- ${getLayoutMediaQuery(layout.id, layouts)} {
64
- .section-${section.id} {
65
- display: ${isHidden ? 'none' : 'block'};
66
- }
67
- }`;
68
- }, '');
69
- };
70
-
71
- useEffect(() => {
72
- if (!sectionRef.current) return;
73
- const observer = new ResizeObserver((entries) => {
74
- const [entry] = entries;
75
- const height = entry.contentRect.height;
76
- setSectionHeight(height);
77
- });
78
- observer.observe(sectionRef.current);
79
- return () => observer.disconnect();
80
- }, []);
81
-
82
- const children = (
83
- <>
84
- <StructuredContent section={section} />
85
- {section.items.map(item => (
86
- <Item
87
- sectionHeight={sectionHeight}
88
- item={item}
89
- key={item.id}
90
- sectionId={section.id}
91
- articleHeight={articleHeight}
92
- />
93
- ))}
94
- </>
95
- );
96
-
97
- if (SectionComponent) return <div ref={sectionRef}><SectionComponent data={data}>{children}</SectionComponent></div>;
98
-
99
- return (
100
- <>
101
- <div
102
- className={`section-${section.id}`}
103
- id={section.name}
104
- ref={sectionRef}
105
- style={{ zIndex }}
106
- >
107
- {media && media.size !== 'none' && sectionRef.current && (
108
- <div className={`section-background-overlay-${section.id}`}>
109
- <div
110
- key={`section-background-wrapper-${section.id}`}
111
- className={`section-background-wrapper-${section.id}`}
112
- style={{
113
- transform: media.position === 'fixed' ? 'translateY(-100vh)' : 'unset',
114
- ...(sectionHeight && { height: media.position === 'fixed' ? `calc(${sectionHeight}px + 200vh)` : `${sectionHeight}px` })
115
- }}
116
- >
117
- {media.type === 'video' && (
118
- <SectionVideo container={sectionRef.current} sectionId={section.id} media={media} />
119
- )}
120
- {media.type === 'image' && (
121
- <SectionImage media={media} sectionId={section.id} />
122
- )}
123
- </div>
124
- </div>
125
- )}
126
- {children}
127
- </div>
128
- <JSXStyle id={id}>{`
129
- ${
130
- getLayoutStyles(layouts, layoutValues, ([height, color, media]) => (`
131
- .section-${section.id} {
132
- min-height: ${getSectionHeight(height)};
133
- position: relative;
134
- background-color: ${CntrlColor.parse(color ?? DEFAULT_COLOR).fmt('rgba')};
135
- }
136
- .section-background-overlay-${section.id} {
137
- height: 100%;
138
- width: 100%;
139
- position: absolute;
140
- overflow: clip;
141
- }
142
- .section-background-wrapper-${section.id} {
143
- transform: ${media?.position === 'fixed' ? 'translateY(-100vh)' : 'unset'};
144
- position: relative;
145
- height: ${media?.position === 'fixed' ? `calc(${getSectionHeight(height)} + 200vh)` : getSectionHeight(height)};
146
- width: 100%;
147
- }
148
- `
149
- ))
150
- }
151
- ${getSectionVisibilityStyles()}
152
- `}</JSXStyle>
153
- </>
154
- );
155
- };
156
-
157
- export function getSectionHeight(heightData: SectionHeight): string {
158
- const { units, vhUnits, mode } = heightData;
159
- if (mode === SectionHeightMode.ViewportHeightUnits) return `${vhUnits}vh`;
160
- if (mode === SectionHeightMode.ControlUnits) return `${units * 100}vw`;
161
- return '0';
162
- }
1
+ import React, { FC, ReactElement, useId, useRef, useMemo } from 'react';
2
+ import JSXStyle from 'styled-jsx/style';
3
+ import {
4
+ getLayoutMediaQuery,
5
+ getLayoutStyles,
6
+ Section as TSection,
7
+ SectionHeight,
8
+ SectionHeightMode
9
+ } from '@cntrl-site/sdk';
10
+ import { useCntrlContext } from '../../provider/useCntrlContext';
11
+ import { useSectionRegistry } from '../../utils/ArticleRectManager/useSectionRegistry';
12
+ import { CntrlColor } from '@cntrl-site/color';
13
+ import { useLayoutContext } from '../useLayoutContext';
14
+ import { SectionVideo } from './SectionVideo';
15
+ import { SectionImage } from './SectionImage';
16
+ import { isOverflowClipSupported } from '../../utils/checkOverflowClipSupport';
17
+
18
+ type SectionChild = ReactElement<any, any>;
19
+ const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
20
+
21
+ interface Props {
22
+ section: TSection;
23
+ children: SectionChild[];
24
+ zIndex: number;
25
+ data?: any;
26
+ }
27
+
28
+ export const Section: FC<Props> = ({ section, data, children, zIndex }) => {
29
+ const reactId = useId();
30
+ const id = `${reactId}-section-${section.id}`;
31
+ const sectionRef = useRef<HTMLDivElement | null>(null);
32
+ const { layouts, customSections } = useCntrlContext();
33
+ const layout = useLayoutContext();
34
+ const layoutValues: Record<string, any>[] = [section.height, section.color, section.media ?? {}];
35
+ const SectionComponent = section.name ? customSections.getComponent(section.name) : undefined;
36
+ useSectionRegistry(section.id, sectionRef.current);
37
+ const sectionHeight = layout && section.height[layout] ? section.height[layout] : undefined;
38
+ const layoutMedia = layout && section.media && section.media[layout] ? section.media[layout] : undefined;
39
+
40
+ const media = useMemo(() => {
41
+ if (layoutMedia && !isOverflowClipSupported()) {
42
+ return {
43
+ ...layoutMedia,
44
+ position: 'local'
45
+ };
46
+ }
47
+ return layoutMedia;
48
+ }, [layoutMedia]);
49
+
50
+ const getSectionVisibilityStyles = () => {
51
+ return layouts
52
+ .sort((a, b) => a.startsWith - b.startsWith)
53
+ .reduce((acc, layout) => {
54
+ const isHidden = section.hidden[layout.id];
55
+ return `
56
+ ${acc}
57
+ ${getLayoutMediaQuery(layout.id, layouts)} {
58
+ .section-${section.id} {
59
+ display: ${isHidden ? 'none' : 'block'};
60
+ }
61
+ }`;
62
+ }, '');
63
+ };
64
+
65
+ if (SectionComponent) return <div ref={sectionRef}><SectionComponent data={data}>{children}</SectionComponent></div>;
66
+
67
+ return (
68
+ <>
69
+ <div
70
+ className={`section-${section.id}`}
71
+ id={section.name}
72
+ ref={sectionRef}
73
+ style={{ zIndex }}
74
+ >
75
+ {media && media.size !== 'none' && sectionRef.current && (
76
+ <div className={`section-background-overlay-${section.id}`}>
77
+ <div
78
+ key={`section-background-wrapper-${section.id}`}
79
+ className={`section-background-wrapper-${section.id}`}
80
+ style={{
81
+ transform: media.position === 'fixed' ? 'translateY(-100vh)' : 'unset',
82
+ ...(sectionHeight && { height: media.position === 'fixed' ? `calc(${getSectionHeight(sectionHeight)} + 200vh)` : getSectionHeight(sectionHeight) })
83
+ }}
84
+ >
85
+ {media.type === 'video' && (
86
+ <SectionVideo container={sectionRef.current} sectionId={section.id} media={media} />
87
+ )}
88
+ {media.type === 'image' && (
89
+ <SectionImage media={media} sectionId={section.id} />
90
+ )}
91
+ </div>
92
+ </div>
93
+ )}
94
+ {children}
95
+ </div>
96
+ <JSXStyle id={id}>{`
97
+ ${
98
+ getLayoutStyles(layouts, layoutValues, ([height, color, media]) => (`
99
+ .section-${section.id} {
100
+ height: ${getSectionHeight(height)};
101
+ position: relative;
102
+ background-color: ${CntrlColor.parse(color ?? DEFAULT_COLOR).fmt('rgba')};
103
+ }
104
+ .section-background-overlay-${section.id} {
105
+ height: ${getSectionHeight(height)};
106
+ width: 100%;
107
+ position: relative;
108
+ overflow: clip;
109
+ }
110
+ .section-background-wrapper-${section.id} {
111
+ transform: ${media?.position === 'fixed' ? 'translateY(-100vh)' : 'unset'};
112
+ position: relative;
113
+ height: ${media?.position === 'fixed' ? `calc(${getSectionHeight(height)} + 200vh)` : getSectionHeight(height)};
114
+ width: 100%;
115
+ }
116
+ `
117
+ ))
118
+ }
119
+ ${getSectionVisibilityStyles()}
120
+ `}</JSXStyle>
121
+ </>
122
+ );
123
+ };
124
+
125
+ export function getSectionHeight(heightData: SectionHeight): string {
126
+ const { units, vhUnits, mode } = heightData;
127
+ if (mode === SectionHeightMode.ViewportHeightUnits) return `${vhUnits}vh`;
128
+ if (mode === SectionHeightMode.ControlUnits) return `${units * 100}vw`;
129
+ return '0';
130
+ }
@@ -1,48 +1,48 @@
1
- import { CSSProperties, FC, useId } from 'react';
2
- import JSXStyle from 'styled-jsx/style';
3
-
4
- export type TSectionImage = {
5
- url: string;
6
- type: 'image';
7
- size: string;
8
- position: string;
9
- offsetX: number | null;
10
- };
11
-
12
- interface Props {
13
- media: TSectionImage;
14
- sectionId: string;
15
- }
16
-
17
- export const SectionImage: FC<Props> = ({ media, sectionId }) => {
18
- const { url, size, position, offsetX } = media;
19
- const isContainHeight = size === 'contain-height';
20
- const hasOffsetX = offsetX !== null && size === 'contain';
21
- return (
22
- <>
23
- <div
24
- className={`section-image-wrapper-${sectionId}`}
25
- style={{
26
- position: position === 'fixed' ? 'sticky' : 'relative',
27
- height: position === 'fixed' ? '100vh' : '100%',
28
- top: position === 'fixed' ? '100vh' : '0',
29
- width: '100%',
30
- overflow: 'hidden'
31
- }}
32
- >
33
- <img
34
- src={url}
35
- className={`image-background-${sectionId}`}
36
- style={{
37
- objectFit: isContainHeight ? 'unset' : (size ?? 'cover') as CSSProperties['objectFit'],
38
- width: isContainHeight || hasOffsetX ? 'auto' : '100%',
39
- transform: isContainHeight ? 'translateX(-50%)' : 'none',
40
- position: 'relative',
41
- left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
42
- height: '100%'
43
- }}
44
- />
45
- </div>
46
- </>
47
- );
48
- };
1
+ import { CSSProperties, FC, useId } from 'react';
2
+ import JSXStyle from 'styled-jsx/style';
3
+
4
+ export type TSectionImage = {
5
+ url: string;
6
+ type: 'image';
7
+ size: string;
8
+ position: string;
9
+ offsetX: number | null;
10
+ };
11
+
12
+ interface Props {
13
+ media: TSectionImage;
14
+ sectionId: string;
15
+ }
16
+
17
+ export const SectionImage: FC<Props> = ({ media, sectionId }) => {
18
+ const { url, size, position, offsetX } = media;
19
+ const isContainHeight = size === 'contain-height';
20
+ const hasOffsetX = offsetX !== null && size === 'contain';
21
+ return (
22
+ <>
23
+ <div
24
+ className={`section-image-wrapper-${sectionId}`}
25
+ style={{
26
+ position: position === 'fixed' ? 'sticky' : 'relative',
27
+ height: position === 'fixed' ? '100vh' : '100%',
28
+ top: position === 'fixed' ? '100vh' : '0',
29
+ width: '100%',
30
+ overflow: 'hidden'
31
+ }}
32
+ >
33
+ <img
34
+ src={url}
35
+ className={`image-background-${sectionId}`}
36
+ style={{
37
+ objectFit: isContainHeight ? 'unset' : (size ?? 'cover') as CSSProperties['objectFit'],
38
+ width: isContainHeight || hasOffsetX ? 'auto' : '100%',
39
+ transform: isContainHeight ? 'translateX(-50%)' : 'none',
40
+ position: 'relative',
41
+ left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
42
+ height: '100%'
43
+ }}
44
+ />
45
+ </div>
46
+ </>
47
+ );
48
+ };