@cntrl-site/sdk-nextjs 1.9.74-alpha.0 → 1.9.74

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