@cntrl-site/sdk-nextjs 1.9.77 → 1.9.79

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 (145) hide show
  1. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/sdk-nextjs.iml +12 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/LICENSE +21 -21
  6. package/README.md +4 -4
  7. package/TODO.md +1 -1
  8. package/eslint.config.mjs +35 -35
  9. package/jest.config.js +6 -6
  10. package/lib/common/useItemFXData.js +5 -5
  11. package/lib/components/Article.js +5 -5
  12. package/lib/components/LayoutStyle.js +4 -4
  13. package/lib/components/Section/Section.js +27 -27
  14. package/lib/components/StructuredBlockItem/StructuredBlockItem.js +13 -13
  15. package/lib/components/StructuredBlockItem/StructuredComponent/StructuredComponent.js +12 -12
  16. package/lib/components/StructuredContent/StructuredContent.js +11 -11
  17. package/lib/components/items/CodeEmbedItem/CodeEmbedItem.js +36 -36
  18. package/lib/components/items/ComponentItem/ComponentItem.js +13 -13
  19. package/lib/components/items/CompoundItem/CompoundChild.js +20 -20
  20. package/lib/components/items/CompoundItem/CompoundItem.js +14 -14
  21. package/lib/components/items/CustomItem/CustomItem.js +9 -9
  22. package/lib/components/items/EmbedVideoItem/VimeoEmbed.js +24 -24
  23. package/lib/components/items/EmbedVideoItem/YoutubeEmbed.js +27 -27
  24. package/lib/components/items/FileItem/ImageItem.js +40 -40
  25. package/lib/components/items/FileItem/VideoItem.js +64 -64
  26. package/lib/components/items/GroupItem/GroupItem.js +14 -14
  27. package/lib/components/items/Item.js +30 -30
  28. package/lib/components/items/RectangleItem/RectangleItem.js +27 -27
  29. package/lib/components/items/RichTextItem/RichTextItem.js +22 -22
  30. package/lib/utils/RichTextConverter/RichTextConverter.js +21 -21
  31. package/package.json +62 -62
  32. package/src/ItemGeometry/CSSPropertyNameMap.ts +37 -37
  33. package/src/ItemGeometry/GenericGeometryController.ts +59 -59
  34. package/src/ItemGeometry/ItemGeometryContext.ts +4 -4
  35. package/src/ItemGeometry/ItemGeometryController.ts +22 -22
  36. package/src/ItemGeometry/ItemGeometryService.ts +34 -34
  37. package/src/ItemGeometry/RichTextGeometryController.ts +166 -166
  38. package/src/ItemGeometry/useItemGeometry.ts +25 -25
  39. package/src/common/useCurrentLayout.ts +52 -52
  40. package/src/common/useExemplary.ts +9 -9
  41. package/src/common/useItemFXData.ts +63 -63
  42. package/src/common/useKeyframeValue.ts +85 -85
  43. package/src/common/useRegisterResize.ts +16 -16
  44. package/src/components/Article.tsx +69 -69
  45. package/src/components/ArticleWrapper.tsx +21 -21
  46. package/src/components/Head.tsx +95 -95
  47. package/src/components/LayoutStyle.tsx +21 -21
  48. package/src/components/Page.tsx +47 -47
  49. package/src/components/ScrollPlaybackVideo.tsx +75 -75
  50. package/src/components/Section/Section.tsx +162 -162
  51. package/src/components/Section/SectionImage.tsx +48 -48
  52. package/src/components/Section/SectionVideo.tsx +144 -144
  53. package/src/components/Section/useSectionColor.ts +19 -19
  54. package/src/components/Section/useSectionHeightMap.ts +19 -19
  55. package/src/components/StructuredBlockItem/StructuredBlockItem.tsx +69 -69
  56. package/src/components/StructuredBlockItem/StructuredComponent/StructuredComponent.tsx +66 -66
  57. package/src/components/StructuredContent/StructuredContent.tsx +42 -42
  58. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +148 -148
  59. package/src/components/items/CodeEmbedItem/useCodeEmbedItem.ts +36 -36
  60. package/src/components/items/ComponentItem/ComponentItem.tsx +85 -85
  61. package/src/components/items/ComponentItem/useComponentItem.ts +35 -35
  62. package/src/components/items/CompoundItem/CompoundChild.tsx +151 -151
  63. package/src/components/items/CompoundItem/CompoundItem.tsx +88 -88
  64. package/src/components/items/CompoundItem/useCompoundItem.ts +21 -21
  65. package/src/components/items/CustomItem/CustomItem.tsx +50 -50
  66. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +217 -217
  67. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +201 -201
  68. package/src/components/items/EmbedVideoItem/useEmbedVideoItem.ts +46 -46
  69. package/src/components/items/FileItem/ImageItem.tsx +166 -166
  70. package/src/components/items/FileItem/VideoItem.tsx +287 -287
  71. package/src/components/items/FileItem/useFileItem.ts +81 -81
  72. package/src/components/items/GroupItem/GroupItem.tsx +99 -99
  73. package/src/components/items/GroupItem/useGroupItem.ts +34 -34
  74. package/src/components/items/Item.tsx +268 -268
  75. package/src/components/items/LinkWrapper.tsx +39 -39
  76. package/src/components/items/RectangleItem/RectangleItem.tsx +177 -177
  77. package/src/components/items/RectangleItem/useRectangleItem.ts +89 -89
  78. package/src/components/items/RichTextItem/RichTextItem.tsx +113 -113
  79. package/src/components/items/RichTextItem/useRichTextItem.ts +12 -12
  80. package/src/components/items/RichTextWrapper.tsx +15 -15
  81. package/src/components/items/itemsMap.ts +28 -28
  82. package/src/components/items/useDraggable.ts +252 -252
  83. package/src/components/items/useItemAngle.ts +13 -13
  84. package/src/components/items/useItemArea.ts +35 -35
  85. package/src/components/items/useItemPointerEvents.ts +23 -23
  86. package/src/components/items/useItemScale.ts +17 -17
  87. package/src/components/items/useItemTriggers.ts +15 -15
  88. package/src/components/items/useRichTextItemValues.ts +66 -66
  89. package/src/components/items/useSizing.ts +25 -25
  90. package/src/components/items/useStickyItemTop.ts +21 -21
  91. package/src/components/useLayoutContext.ts +7 -7
  92. package/src/index.ts +25 -25
  93. package/src/interactions/InteractionsRegistry.ts +513 -513
  94. package/src/interactions/ItemInteractionCtrl.ts +78 -78
  95. package/src/interactions/getTransition.ts +27 -27
  96. package/src/interactions/types.ts +36 -36
  97. package/src/interactions/useItemInteractionCtrl.ts +18 -18
  98. package/src/provider/ArticleRectContext.ts +4 -4
  99. package/src/provider/CntrlContext.ts +5 -5
  100. package/src/provider/CntrlProvider.tsx +11 -11
  101. package/src/provider/CntrlSdkContext.ts +113 -113
  102. package/src/provider/CustomItemRegistry.ts +14 -14
  103. package/src/provider/CustomItemTypes.ts +3 -3
  104. package/src/provider/CustomSectionRegistry.ts +25 -25
  105. package/src/provider/InteractionsContext.tsx +73 -73
  106. package/src/provider/Keyframes.ts +11 -11
  107. package/src/provider/KeyframesContext.ts +5 -5
  108. package/src/provider/LayoutContext.ts +3 -3
  109. package/src/provider/WebGLContextManagerContext.tsx +4 -4
  110. package/src/provider/defaultContext.ts +8 -8
  111. package/src/provider/useCntrlContext.ts +8 -8
  112. package/src/styled-jsx.d.ts +1 -1
  113. package/src/types/components-utils.d.ts +7 -7
  114. package/src/utils/Animator/Animator.ts +570 -570
  115. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +93 -93
  116. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -17
  117. package/src/utils/ArticleRectManager/useSectionRegistry.ts +11 -11
  118. package/src/utils/CSSNumeric.ts +19 -19
  119. package/src/utils/EventEmitter.ts +41 -41
  120. package/src/utils/RichTextConverter/RichTextConverter.tsx +342 -342
  121. package/src/utils/ScaleAnchorMap.ts +13 -13
  122. package/src/utils/StickyManager/StickyManager.ts +23 -23
  123. package/src/utils/Youtube/YouTubeIframeApiLoader.ts +58 -58
  124. package/src/utils/Youtube/YoutubeIframeApi.ts +101 -101
  125. package/src/utils/Youtube/useYouTubeIframeApi.ts +12 -12
  126. package/src/utils/areFillsVisible/areFillsVisible.test.ts +63 -63
  127. package/src/utils/areFillsVisible/areFillsVisible.ts +37 -37
  128. package/src/utils/binSearchInsertAt.ts +33 -33
  129. package/src/utils/castObject.ts +10 -10
  130. package/src/utils/checkOverflowClipSupport.ts +13 -13
  131. package/src/utils/domDfs.ts +11 -11
  132. package/src/utils/effects/useImageFx.ts +154 -154
  133. package/src/utils/effects/useVideoFx.ts +156 -156
  134. package/src/utils/evaluateComponentBundle.ts +83 -83
  135. package/src/utils/getAnchoredItemTop.ts +12 -12
  136. package/src/utils/getCompoundBoundaryStyles.ts +60 -60
  137. package/src/utils/getFill.ts +46 -46
  138. package/src/utils/getFontFamilyValue.ts +3 -3
  139. package/src/utils/getInvertedRanges.ts +43 -43
  140. package/src/utils/getItemTopStyle.ts +39 -39
  141. package/src/utils/getStyleFromItemStateAndParams.ts +8 -8
  142. package/src/utils/getValidYoutubeUrl.ts +29 -29
  143. package/src/utils/isItemType.ts +5 -5
  144. package/src/utils/rangeMap.ts +13 -13
  145. package/src/utils/useElementRect.ts +23 -23
@@ -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,162 @@
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, 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,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
+ };