@cntrl-site/sdk-nextjs 1.9.69 → 1.9.71-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +4 -4
  3. package/TODO.md +1 -1
  4. package/cntrl-site-sdk-nextjs-1.9.64-0.tgz +0 -0
  5. package/eslint.config.mjs +35 -35
  6. package/jest.config.js +6 -6
  7. package/lib/common/useItemFXData.js +5 -5
  8. package/lib/components/Article.js +6 -7
  9. package/lib/components/LayoutStyle.js +4 -4
  10. package/lib/components/Section/Section.js +49 -30
  11. package/lib/components/StructuredBlockItem/StructuredBlockItem.js +46 -0
  12. package/lib/components/StructuredBlockItem/StructuredComponent/StructuredComponent.js +48 -0
  13. package/lib/components/StructuredContent/StructuredContent.js +37 -0
  14. package/lib/components/items/CodeEmbedItem/CodeEmbedItem.js +36 -36
  15. package/lib/components/items/ComponentItem/ComponentItem.js +13 -13
  16. package/lib/components/items/CompoundItem/CompoundChild.js +20 -20
  17. package/lib/components/items/CompoundItem/CompoundItem.js +14 -14
  18. package/lib/components/items/CustomItem/CustomItem.js +9 -9
  19. package/lib/components/items/EmbedVideoItem/VimeoEmbed.js +24 -24
  20. package/lib/components/items/EmbedVideoItem/YoutubeEmbed.js +27 -27
  21. package/lib/components/items/FileItem/ImageItem.js +40 -40
  22. package/lib/components/items/FileItem/VideoItem.js +64 -64
  23. package/lib/components/items/GroupItem/GroupItem.js +14 -14
  24. package/lib/components/items/Item.js +32 -33
  25. package/lib/components/items/RectangleItem/RectangleItem.js +27 -27
  26. package/lib/components/items/RichTextItem/RichTextItem.js +22 -22
  27. package/lib/provider/CntrlSdkContext.js +1 -1
  28. package/lib/utils/RichTextConverter/RichTextConverter.js +21 -21
  29. package/package.json +62 -62
  30. package/src/ItemGeometry/CSSPropertyNameMap.ts +37 -37
  31. package/src/ItemGeometry/GenericGeometryController.ts +59 -59
  32. package/src/ItemGeometry/ItemGeometryContext.ts +4 -4
  33. package/src/ItemGeometry/ItemGeometryController.ts +22 -22
  34. package/src/ItemGeometry/ItemGeometryService.ts +34 -34
  35. package/src/ItemGeometry/RichTextGeometryController.ts +166 -166
  36. package/src/ItemGeometry/useItemGeometry.ts +25 -25
  37. package/src/common/useCurrentLayout.ts +52 -52
  38. package/src/common/useExemplary.ts +9 -9
  39. package/src/common/useItemFXData.ts +63 -63
  40. package/src/common/useKeyframeValue.ts +85 -85
  41. package/src/common/useRegisterResize.ts +16 -16
  42. package/src/components/Article.tsx +69 -75
  43. package/src/components/ArticleWrapper.tsx +21 -21
  44. package/src/components/Head.tsx +95 -95
  45. package/src/components/LayoutStyle.tsx +21 -21
  46. package/src/components/Page.tsx +47 -47
  47. package/src/components/ScrollPlaybackVideo.tsx +75 -75
  48. package/src/components/Section/Section.tsx +164 -130
  49. package/src/components/Section/SectionImage.tsx +48 -48
  50. package/src/components/Section/SectionVideo.tsx +144 -144
  51. package/src/components/Section/useSectionColor.ts +19 -19
  52. package/src/components/Section/useSectionHeightMap.ts +19 -19
  53. package/src/components/StructuredBlockItem/StructuredBlockItem.tsx +57 -0
  54. package/src/components/StructuredBlockItem/StructuredComponent/StructuredComponent.tsx +66 -0
  55. package/src/components/StructuredContent/StructuredContent.tsx +43 -0
  56. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +148 -148
  57. package/src/components/items/CodeEmbedItem/useCodeEmbedItem.ts +36 -36
  58. package/src/components/items/ComponentItem/ComponentItem.tsx +85 -85
  59. package/src/components/items/ComponentItem/useComponentItem.ts +35 -35
  60. package/src/components/items/CompoundItem/CompoundChild.tsx +151 -151
  61. package/src/components/items/CompoundItem/CompoundItem.tsx +88 -88
  62. package/src/components/items/CompoundItem/useCompoundItem.ts +21 -21
  63. package/src/components/items/CustomItem/CustomItem.tsx +50 -50
  64. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +217 -217
  65. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +201 -201
  66. package/src/components/items/EmbedVideoItem/useEmbedVideoItem.ts +46 -46
  67. package/src/components/items/FileItem/ImageItem.tsx +166 -166
  68. package/src/components/items/FileItem/VideoItem.tsx +287 -287
  69. package/src/components/items/FileItem/useFileItem.ts +81 -81
  70. package/src/components/items/GroupItem/GroupItem.tsx +99 -99
  71. package/src/components/items/GroupItem/useGroupItem.ts +34 -34
  72. package/src/components/items/Item.tsx +268 -268
  73. package/src/components/items/LinkWrapper.tsx +39 -39
  74. package/src/components/items/RectangleItem/RectangleItem.tsx +177 -177
  75. package/src/components/items/RectangleItem/useRectangleItem.ts +89 -89
  76. package/src/components/items/RichTextItem/RichTextItem.tsx +113 -113
  77. package/src/components/items/RichTextItem/useRichTextItem.ts +12 -12
  78. package/src/components/items/RichTextWrapper.tsx +15 -15
  79. package/src/components/items/itemsMap.ts +28 -28
  80. package/src/components/items/useDraggable.ts +252 -252
  81. package/src/components/items/useItemAngle.ts +13 -13
  82. package/src/components/items/useItemArea.ts +35 -35
  83. package/src/components/items/useItemPointerEvents.ts +23 -23
  84. package/src/components/items/useItemScale.ts +17 -17
  85. package/src/components/items/useItemTriggers.ts +15 -15
  86. package/src/components/items/useRichTextItemValues.ts +66 -66
  87. package/src/components/items/useSizing.ts +25 -25
  88. package/src/components/items/useStickyItemTop.ts +21 -21
  89. package/src/components/useLayoutContext.ts +7 -7
  90. package/src/index.ts +25 -25
  91. package/src/interactions/InteractionsRegistry.ts +513 -512
  92. package/src/interactions/ItemInteractionCtrl.ts +78 -78
  93. package/src/interactions/getTransition.ts +27 -27
  94. package/src/interactions/types.ts +36 -36
  95. package/src/interactions/useItemInteractionCtrl.ts +18 -18
  96. package/src/provider/ArticleRectContext.ts +4 -4
  97. package/src/provider/CntrlContext.ts +5 -5
  98. package/src/provider/CntrlProvider.tsx +11 -11
  99. package/src/provider/CntrlSdkContext.ts +113 -113
  100. package/src/provider/CustomItemRegistry.ts +14 -14
  101. package/src/provider/CustomItemTypes.ts +3 -3
  102. package/src/provider/CustomSectionRegistry.ts +25 -25
  103. package/src/provider/InteractionsContext.tsx +73 -73
  104. package/src/provider/Keyframes.ts +11 -11
  105. package/src/provider/KeyframesContext.ts +5 -5
  106. package/src/provider/LayoutContext.ts +3 -3
  107. package/src/provider/WebGLContextManagerContext.tsx +4 -4
  108. package/src/provider/defaultContext.ts +8 -8
  109. package/src/provider/useCntrlContext.ts +8 -8
  110. package/src/styled-jsx.d.ts +1 -1
  111. package/src/types/components-utils.d.ts +7 -7
  112. package/src/utils/Animator/Animator.ts +570 -570
  113. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +93 -93
  114. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -17
  115. package/src/utils/ArticleRectManager/useSectionRegistry.ts +11 -11
  116. package/src/utils/CSSNumeric.ts +19 -19
  117. package/src/utils/EventEmitter.ts +41 -41
  118. package/src/utils/RichTextConverter/RichTextConverter.tsx +342 -342
  119. package/src/utils/ScaleAnchorMap.ts +13 -13
  120. package/src/utils/StickyManager/StickyManager.ts +23 -23
  121. package/src/utils/Youtube/YouTubeIframeApiLoader.ts +58 -58
  122. package/src/utils/Youtube/YoutubeIframeApi.ts +101 -101
  123. package/src/utils/Youtube/useYouTubeIframeApi.ts +12 -12
  124. package/src/utils/areFillsVisible/areFillsVisible.test.ts +63 -63
  125. package/src/utils/areFillsVisible/areFillsVisible.ts +37 -37
  126. package/src/utils/binSearchInsertAt.ts +33 -33
  127. package/src/utils/castObject.ts +10 -10
  128. package/src/utils/checkOverflowClipSupport.ts +13 -13
  129. package/src/utils/domDfs.ts +11 -11
  130. package/src/utils/effects/useImageFx.ts +154 -154
  131. package/src/utils/effects/useVideoFx.ts +156 -156
  132. package/src/utils/evaluateComponentBundle.ts +83 -83
  133. package/src/utils/getAnchoredItemTop.ts +12 -12
  134. package/src/utils/getCompoundBoundaryStyles.ts +60 -60
  135. package/src/utils/getFill.ts +46 -46
  136. package/src/utils/getFontFamilyValue.ts +3 -3
  137. package/src/utils/getInvertedRanges.ts +43 -43
  138. package/src/utils/getItemTopStyle.ts +39 -39
  139. package/src/utils/getStyleFromItemStateAndParams.ts +8 -8
  140. package/src/utils/getValidYoutubeUrl.ts +29 -29
  141. package/src/utils/isItemType.ts +5 -5
  142. package/src/utils/rangeMap.ts +13 -13
  143. 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,130 +1,164 @@
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
+ 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.type === 'freehand' ? section.height : section.minHeight,
38
+ section.color,
39
+ section.media ?? {}
40
+ ];
41
+ const SectionComponent = section.name ? customSections.getComponent(section.name) : undefined;
42
+ useSectionRegistry(section.id, sectionRef.current);
43
+ const layoutMedia = layout && section.media && section.media[layout] ? section.media[layout] : undefined;
44
+
45
+ const media = useMemo(() => {
46
+ if (layoutMedia && !isOverflowClipSupported()) {
47
+ return {
48
+ ...layoutMedia,
49
+ position: 'local'
50
+ };
51
+ }
52
+ return layoutMedia;
53
+ }, [layoutMedia]);
54
+
55
+ const getSectionVisibilityStyles = () => {
56
+ return layouts
57
+ .sort((a, b) => a.startsWith - b.startsWith)
58
+ .reduce((acc, layout) => {
59
+ const isHidden = section.hidden[layout.id];
60
+ return `
61
+ ${acc}
62
+ ${getLayoutMediaQuery(layout.id, layouts)} {
63
+ .section-${section.id} {
64
+ display: ${isHidden ? 'none' : 'block'};
65
+ }
66
+ }`;
67
+ }, '');
68
+ };
69
+
70
+ useEffect(() => {
71
+ if (!sectionRef.current) return;
72
+ const observer = new ResizeObserver((entries) => {
73
+ const [entry] = entries;
74
+ const height = entry.contentRect.height;
75
+ setSectionHeight(height);
76
+ });
77
+ observer.observe(sectionRef.current);
78
+ return () => observer.disconnect();
79
+ }, []);
80
+
81
+ const children = (
82
+ <>
83
+ {section.type !== 'freehand' && (
84
+ <StructuredContent section={section} />
85
+ )}
86
+ {section.items.map(item => (
87
+ <Item
88
+ sectionHeight={sectionHeight}
89
+ item={item}
90
+ key={item.id}
91
+ sectionId={section.id}
92
+ articleHeight={articleHeight}
93
+ />
94
+ ))}
95
+ </>
96
+ );
97
+
98
+ if (SectionComponent) return <div ref={sectionRef}><SectionComponent data={data}>{children}</SectionComponent></div>;
99
+
100
+ return (
101
+ <>
102
+ <div
103
+ className={`section-${section.id}`}
104
+ id={section.name}
105
+ ref={sectionRef}
106
+ style={{ zIndex }}
107
+ >
108
+ {media && media.size !== 'none' && sectionRef.current && (
109
+ <div className={`section-background-overlay-${section.id}`}>
110
+ <div
111
+ key={`section-background-wrapper-${section.id}`}
112
+ className={`section-background-wrapper-${section.id}`}
113
+ style={{
114
+ transform: media.position === 'fixed' ? 'translateY(-100vh)' : 'unset',
115
+ ...(sectionHeight && { height: media.position === 'fixed' ? `calc(${sectionHeight}px + 200vh)` : `${sectionHeight}px` })
116
+ }}
117
+ >
118
+ {media.type === 'video' && (
119
+ <SectionVideo container={sectionRef.current} sectionId={section.id} media={media} />
120
+ )}
121
+ {media.type === 'image' && (
122
+ <SectionImage media={media} sectionId={section.id} />
123
+ )}
124
+ </div>
125
+ </div>
126
+ )}
127
+ {children}
128
+ </div>
129
+ <JSXStyle id={id}>{`
130
+ ${
131
+ getLayoutStyles(layouts, layoutValues, ([height, color, media]) => (`
132
+ .section-${section.id} {
133
+ height: ${section.type === 'freehand' ? getSectionHeight(height) : 'auto'};
134
+ min-height: ${section.type !== 'freehand' ? getSectionHeight(height) : 'unset'};
135
+ position: relative;
136
+ background-color: ${CntrlColor.parse(color ?? DEFAULT_COLOR).fmt('rgba')};
137
+ }
138
+ .section-background-overlay-${section.id} {
139
+ height: 100%;
140
+ width: 100%;
141
+ position: absolute;
142
+ overflow: clip;
143
+ }
144
+ .section-background-wrapper-${section.id} {
145
+ transform: ${media?.position === 'fixed' ? 'translateY(-100vh)' : 'unset'};
146
+ position: relative;
147
+ height: ${media?.position === 'fixed' ? `calc(${getSectionHeight(height)} + 200vh)` : getSectionHeight(height)};
148
+ width: 100%;
149
+ }
150
+ `
151
+ ))
152
+ }
153
+ ${getSectionVisibilityStyles()}
154
+ `}</JSXStyle>
155
+ </>
156
+ );
157
+ };
158
+
159
+ export function getSectionHeight(heightData: SectionHeight): string {
160
+ const { units, vhUnits, mode } = heightData;
161
+ if (mode === SectionHeightMode.ViewportHeightUnits) return `${vhUnits}vh`;
162
+ if (mode === SectionHeightMode.ControlUnits) return `${units * 100}vw`;
163
+ return '0';
164
+ }
@@ -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
+ };