@cntrl-site/sdk-nextjs 1.9.78 → 1.9.80-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 (142) 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/useCurrentLayout.js +5 -3
  7. package/lib/common/useItemFXData.js +5 -5
  8. package/lib/components/Article.js +5 -5
  9. package/lib/components/LayoutStyle.js +4 -4
  10. package/lib/components/Section/Section.js +27 -27
  11. package/lib/components/StructuredBlockItem/StructuredBlockItem.js +13 -13
  12. package/lib/components/StructuredBlockItem/StructuredComponent/StructuredComponent.js +15 -13
  13. package/lib/components/StructuredContent/StructuredContent.js +11 -11
  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 +30 -30
  25. package/lib/components/items/RectangleItem/RectangleItem.js +27 -27
  26. package/lib/components/items/RichTextItem/RichTextItem.js +22 -22
  27. package/lib/utils/RichTextConverter/RichTextConverter.js +21 -21
  28. package/package.json +62 -62
  29. package/src/ItemGeometry/CSSPropertyNameMap.ts +37 -37
  30. package/src/ItemGeometry/GenericGeometryController.ts +59 -59
  31. package/src/ItemGeometry/ItemGeometryContext.ts +4 -4
  32. package/src/ItemGeometry/ItemGeometryController.ts +22 -22
  33. package/src/ItemGeometry/ItemGeometryService.ts +34 -34
  34. package/src/ItemGeometry/RichTextGeometryController.ts +166 -166
  35. package/src/ItemGeometry/useItemGeometry.ts +25 -25
  36. package/src/common/useCurrentLayout.ts +53 -52
  37. package/src/common/useExemplary.ts +9 -9
  38. package/src/common/useItemFXData.ts +63 -63
  39. package/src/common/useKeyframeValue.ts +85 -85
  40. package/src/common/useRegisterResize.ts +16 -16
  41. package/src/components/Article.tsx +69 -69
  42. package/src/components/ArticleWrapper.tsx +21 -21
  43. package/src/components/Head.tsx +95 -95
  44. package/src/components/LayoutStyle.tsx +21 -21
  45. package/src/components/Page.tsx +47 -47
  46. package/src/components/ScrollPlaybackVideo.tsx +75 -75
  47. package/src/components/Section/Section.tsx +162 -162
  48. package/src/components/Section/SectionImage.tsx +48 -48
  49. package/src/components/Section/SectionVideo.tsx +144 -144
  50. package/src/components/Section/useSectionColor.ts +19 -19
  51. package/src/components/Section/useSectionHeightMap.ts +19 -19
  52. package/src/components/StructuredBlockItem/StructuredBlockItem.tsx +69 -69
  53. package/src/components/StructuredBlockItem/StructuredComponent/StructuredComponent.tsx +68 -66
  54. package/src/components/StructuredContent/StructuredContent.tsx +42 -42
  55. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +148 -148
  56. package/src/components/items/CodeEmbedItem/useCodeEmbedItem.ts +36 -36
  57. package/src/components/items/ComponentItem/ComponentItem.tsx +85 -85
  58. package/src/components/items/ComponentItem/useComponentItem.ts +35 -35
  59. package/src/components/items/CompoundItem/CompoundChild.tsx +151 -151
  60. package/src/components/items/CompoundItem/CompoundItem.tsx +88 -88
  61. package/src/components/items/CompoundItem/useCompoundItem.ts +21 -21
  62. package/src/components/items/CustomItem/CustomItem.tsx +50 -50
  63. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +217 -217
  64. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +201 -201
  65. package/src/components/items/EmbedVideoItem/useEmbedVideoItem.ts +46 -46
  66. package/src/components/items/FileItem/ImageItem.tsx +166 -166
  67. package/src/components/items/FileItem/VideoItem.tsx +287 -287
  68. package/src/components/items/FileItem/useFileItem.ts +81 -81
  69. package/src/components/items/GroupItem/GroupItem.tsx +99 -99
  70. package/src/components/items/GroupItem/useGroupItem.ts +34 -34
  71. package/src/components/items/Item.tsx +268 -268
  72. package/src/components/items/LinkWrapper.tsx +39 -39
  73. package/src/components/items/RectangleItem/RectangleItem.tsx +177 -177
  74. package/src/components/items/RectangleItem/useRectangleItem.ts +89 -89
  75. package/src/components/items/RichTextItem/RichTextItem.tsx +113 -113
  76. package/src/components/items/RichTextItem/useRichTextItem.ts +12 -12
  77. package/src/components/items/RichTextWrapper.tsx +15 -15
  78. package/src/components/items/itemsMap.ts +28 -28
  79. package/src/components/items/useDraggable.ts +252 -252
  80. package/src/components/items/useItemAngle.ts +13 -13
  81. package/src/components/items/useItemArea.ts +35 -35
  82. package/src/components/items/useItemPointerEvents.ts +23 -23
  83. package/src/components/items/useItemScale.ts +17 -17
  84. package/src/components/items/useItemTriggers.ts +15 -15
  85. package/src/components/items/useRichTextItemValues.ts +66 -66
  86. package/src/components/items/useSizing.ts +25 -25
  87. package/src/components/items/useStickyItemTop.ts +21 -21
  88. package/src/components/useLayoutContext.ts +7 -7
  89. package/src/index.ts +25 -25
  90. package/src/interactions/InteractionsRegistry.ts +513 -513
  91. package/src/interactions/ItemInteractionCtrl.ts +78 -78
  92. package/src/interactions/getTransition.ts +27 -27
  93. package/src/interactions/types.ts +36 -36
  94. package/src/interactions/useItemInteractionCtrl.ts +18 -18
  95. package/src/provider/ArticleRectContext.ts +4 -4
  96. package/src/provider/CntrlContext.ts +5 -5
  97. package/src/provider/CntrlProvider.tsx +11 -11
  98. package/src/provider/CntrlSdkContext.ts +113 -113
  99. package/src/provider/CustomItemRegistry.ts +14 -14
  100. package/src/provider/CustomItemTypes.ts +3 -3
  101. package/src/provider/CustomSectionRegistry.ts +25 -25
  102. package/src/provider/InteractionsContext.tsx +73 -73
  103. package/src/provider/Keyframes.ts +11 -11
  104. package/src/provider/KeyframesContext.ts +5 -5
  105. package/src/provider/LayoutContext.ts +3 -3
  106. package/src/provider/WebGLContextManagerContext.tsx +4 -4
  107. package/src/provider/defaultContext.ts +8 -8
  108. package/src/provider/useCntrlContext.ts +8 -8
  109. package/src/styled-jsx.d.ts +1 -1
  110. package/src/types/components-utils.d.ts +7 -7
  111. package/src/utils/Animator/Animator.ts +570 -570
  112. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +93 -93
  113. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -17
  114. package/src/utils/ArticleRectManager/useSectionRegistry.ts +11 -11
  115. package/src/utils/CSSNumeric.ts +19 -19
  116. package/src/utils/EventEmitter.ts +41 -41
  117. package/src/utils/RichTextConverter/RichTextConverter.tsx +342 -342
  118. package/src/utils/ScaleAnchorMap.ts +13 -13
  119. package/src/utils/StickyManager/StickyManager.ts +23 -23
  120. package/src/utils/Youtube/YouTubeIframeApiLoader.ts +58 -58
  121. package/src/utils/Youtube/YoutubeIframeApi.ts +101 -101
  122. package/src/utils/Youtube/useYouTubeIframeApi.ts +12 -12
  123. package/src/utils/areFillsVisible/areFillsVisible.test.ts +63 -63
  124. package/src/utils/areFillsVisible/areFillsVisible.ts +37 -37
  125. package/src/utils/binSearchInsertAt.ts +33 -33
  126. package/src/utils/castObject.ts +10 -10
  127. package/src/utils/checkOverflowClipSupport.ts +13 -13
  128. package/src/utils/domDfs.ts +11 -11
  129. package/src/utils/effects/useImageFx.ts +154 -154
  130. package/src/utils/effects/useVideoFx.ts +156 -156
  131. package/src/utils/evaluateComponentBundle.ts +83 -83
  132. package/src/utils/getAnchoredItemTop.ts +12 -12
  133. package/src/utils/getCompoundBoundaryStyles.ts +60 -60
  134. package/src/utils/getFill.ts +46 -46
  135. package/src/utils/getFontFamilyValue.ts +3 -3
  136. package/src/utils/getInvertedRanges.ts +43 -43
  137. package/src/utils/getItemTopStyle.ts +39 -39
  138. package/src/utils/getStyleFromItemStateAndParams.ts +8 -8
  139. package/src/utils/getValidYoutubeUrl.ts +29 -29
  140. package/src/utils/isItemType.ts +5 -5
  141. package/src/utils/rangeMap.ts +13 -13
  142. package/src/utils/useElementRect.ts +23 -23
@@ -1,144 +1,144 @@
1
- import { CSSProperties, FC, useEffect, useId, useRef, useState } from 'react';
2
- import JSXStyle from 'styled-jsx/style';
3
-
4
- export type TSectionVideo = {
5
- url: string;
6
- size: string;
7
- type: 'video';
8
- play: 'on-click' | 'auto';
9
- position: string;
10
- coverUrl: string | null;
11
- offsetX: number | null;
12
- };
13
-
14
- interface Props {
15
- container: HTMLDivElement;
16
- sectionId: string;
17
- media: TSectionVideo;
18
- }
19
-
20
- export const SectionVideo: FC<Props> = ({ container, sectionId, media }) => {
21
- const [video, setVideo] = useState<HTMLVideoElement | null>(null);
22
- const [videoWrapper, setVideoWrapper] = useState<HTMLDivElement | null>(null);
23
- const [isVideoWidthOverflow, setIsVideoWidthOverflow] = useState(false);
24
- const { url, size, position, offsetX, coverUrl, play } = media;
25
- const [isPlaying, setIsPlaying] = useState(false);
26
- const [userPaused, setUserPaused] = useState(false);
27
- const [isClickedOnCover, setIsClickedOnCover] = useState(false);
28
-
29
- const handleCoverClick = () => {
30
- if (!video || play !== 'on-click') return;
31
- setIsClickedOnCover(true);
32
- if (isPlaying) {
33
- video.pause();
34
- setUserPaused(true);
35
- } else {
36
- video.play();
37
- setUserPaused(false);
38
- }
39
- };
40
-
41
- useEffect(() => {
42
- if (!video || play !== 'on-click') return;
43
- const observer = new IntersectionObserver(
44
- ([entry]) => {
45
- if (userPaused || !isClickedOnCover) return;
46
- if (entry.isIntersecting) {
47
- video.play();
48
- } else {
49
- video.pause();
50
- }
51
- }
52
- );
53
- observer.observe(container);
54
- return () => observer.disconnect();
55
- }, [container, play, userPaused, isClickedOnCover]);
56
-
57
- useEffect(() => {
58
- if (!video || !videoWrapper) return;
59
- video.addEventListener('loadedmetadata', () => {
60
- const h = video.videoHeight;
61
- const w = video.videoWidth;
62
- const width = (videoWrapper.clientHeight / h) * w;
63
- if (width > videoWrapper.clientWidth) {
64
- setIsVideoWidthOverflow(true);
65
- } else {
66
- setIsVideoWidthOverflow(false);
67
- }
68
- });
69
- }, [video, videoWrapper]);
70
-
71
- const isContainHeight = size === 'contain-height';
72
- const hasOffsetX = offsetX !== null && size === 'contain';
73
-
74
- return (
75
- <>
76
- <div
77
- ref={setVideoWrapper}
78
- className={`section-video-wrapper-${sectionId}`}
79
- style={{
80
- position: position === 'fixed' ? 'sticky' : 'relative',
81
- height: position === 'fixed' ? '100vh' : '100%',
82
- top: position === 'fixed' ? '100vh' : '0',
83
- overflow: 'hidden',
84
- width: '100%'
85
- }}
86
- >
87
- <video
88
- ref={setVideo}
89
- autoPlay={play === 'auto'}
90
- loop
91
- style={{
92
- opacity: !isClickedOnCover && play === 'on-click' && coverUrl ? 0 : 1,
93
- objectFit: isContainHeight ? 'cover' : (size ?? 'cover') as CSSProperties['objectFit'],
94
- width: isContainHeight && !isVideoWidthOverflow ? 'auto' : '100%',
95
- transform: isContainHeight ? 'translateX(-50%)' : 'none',
96
- left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
97
- height: '100%',
98
- position: 'relative'
99
- }}
100
- controls={play === 'on-click'}
101
- muted={play === 'auto'}
102
- playsInline
103
- preload="auto"
104
- className={`video-background-${sectionId}`}
105
- onPlay={() => setIsPlaying(true)}
106
- onPause={() => setIsPlaying(false)}
107
- >
108
- <source src={`${url}`} />
109
- </video>
110
- {play === 'on-click' && !isClickedOnCover && (
111
- <div
112
- className={`video-background-${sectionId}-cover-container`}
113
- style={{
114
- position: 'absolute',
115
- left: 0,
116
- width: '100%',
117
- height: '100%',
118
- top: 0
119
- }}
120
- onClick={handleCoverClick}
121
- >
122
- {coverUrl && play === 'on-click' && (
123
- <img
124
- src={coverUrl}
125
- alt="Video cover"
126
- className={`video-background-${sectionId}-cover`}
127
- style={{
128
- opacity: isPlaying ? 0 : 1,
129
- left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
130
- width: isContainHeight ? 'auto' : '100%',
131
- objectFit: isContainHeight ? 'unset' : (size ?? 'cover') as CSSProperties['objectFit'],
132
- transform: isContainHeight ? 'translateX(-50%)' : 'none',
133
- position: 'relative',
134
- height: '100%',
135
- transition: 'opacity 0.1s ease-in-out'
136
- }}
137
- />
138
- )}
139
- </div>
140
- )}
141
- </div>
142
- </>
143
- );
144
- };
1
+ import { CSSProperties, FC, useEffect, useId, useRef, useState } from 'react';
2
+ import JSXStyle from 'styled-jsx/style';
3
+
4
+ export type TSectionVideo = {
5
+ url: string;
6
+ size: string;
7
+ type: 'video';
8
+ play: 'on-click' | 'auto';
9
+ position: string;
10
+ coverUrl: string | null;
11
+ offsetX: number | null;
12
+ };
13
+
14
+ interface Props {
15
+ container: HTMLDivElement;
16
+ sectionId: string;
17
+ media: TSectionVideo;
18
+ }
19
+
20
+ export const SectionVideo: FC<Props> = ({ container, sectionId, media }) => {
21
+ const [video, setVideo] = useState<HTMLVideoElement | null>(null);
22
+ const [videoWrapper, setVideoWrapper] = useState<HTMLDivElement | null>(null);
23
+ const [isVideoWidthOverflow, setIsVideoWidthOverflow] = useState(false);
24
+ const { url, size, position, offsetX, coverUrl, play } = media;
25
+ const [isPlaying, setIsPlaying] = useState(false);
26
+ const [userPaused, setUserPaused] = useState(false);
27
+ const [isClickedOnCover, setIsClickedOnCover] = useState(false);
28
+
29
+ const handleCoverClick = () => {
30
+ if (!video || play !== 'on-click') return;
31
+ setIsClickedOnCover(true);
32
+ if (isPlaying) {
33
+ video.pause();
34
+ setUserPaused(true);
35
+ } else {
36
+ video.play();
37
+ setUserPaused(false);
38
+ }
39
+ };
40
+
41
+ useEffect(() => {
42
+ if (!video || play !== 'on-click') return;
43
+ const observer = new IntersectionObserver(
44
+ ([entry]) => {
45
+ if (userPaused || !isClickedOnCover) return;
46
+ if (entry.isIntersecting) {
47
+ video.play();
48
+ } else {
49
+ video.pause();
50
+ }
51
+ }
52
+ );
53
+ observer.observe(container);
54
+ return () => observer.disconnect();
55
+ }, [container, play, userPaused, isClickedOnCover]);
56
+
57
+ useEffect(() => {
58
+ if (!video || !videoWrapper) return;
59
+ video.addEventListener('loadedmetadata', () => {
60
+ const h = video.videoHeight;
61
+ const w = video.videoWidth;
62
+ const width = (videoWrapper.clientHeight / h) * w;
63
+ if (width > videoWrapper.clientWidth) {
64
+ setIsVideoWidthOverflow(true);
65
+ } else {
66
+ setIsVideoWidthOverflow(false);
67
+ }
68
+ });
69
+ }, [video, videoWrapper]);
70
+
71
+ const isContainHeight = size === 'contain-height';
72
+ const hasOffsetX = offsetX !== null && size === 'contain';
73
+
74
+ return (
75
+ <>
76
+ <div
77
+ ref={setVideoWrapper}
78
+ className={`section-video-wrapper-${sectionId}`}
79
+ style={{
80
+ position: position === 'fixed' ? 'sticky' : 'relative',
81
+ height: position === 'fixed' ? '100vh' : '100%',
82
+ top: position === 'fixed' ? '100vh' : '0',
83
+ overflow: 'hidden',
84
+ width: '100%'
85
+ }}
86
+ >
87
+ <video
88
+ ref={setVideo}
89
+ autoPlay={play === 'auto'}
90
+ loop
91
+ style={{
92
+ opacity: !isClickedOnCover && play === 'on-click' && coverUrl ? 0 : 1,
93
+ objectFit: isContainHeight ? 'cover' : (size ?? 'cover') as CSSProperties['objectFit'],
94
+ width: isContainHeight && !isVideoWidthOverflow ? 'auto' : '100%',
95
+ transform: isContainHeight ? 'translateX(-50%)' : 'none',
96
+ left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
97
+ height: '100%',
98
+ position: 'relative'
99
+ }}
100
+ controls={play === 'on-click'}
101
+ muted={play === 'auto'}
102
+ playsInline
103
+ preload="auto"
104
+ className={`video-background-${sectionId}`}
105
+ onPlay={() => setIsPlaying(true)}
106
+ onPause={() => setIsPlaying(false)}
107
+ >
108
+ <source src={`${url}`} />
109
+ </video>
110
+ {play === 'on-click' && !isClickedOnCover && (
111
+ <div
112
+ className={`video-background-${sectionId}-cover-container`}
113
+ style={{
114
+ position: 'absolute',
115
+ left: 0,
116
+ width: '100%',
117
+ height: '100%',
118
+ top: 0
119
+ }}
120
+ onClick={handleCoverClick}
121
+ >
122
+ {coverUrl && play === 'on-click' && (
123
+ <img
124
+ src={coverUrl}
125
+ alt="Video cover"
126
+ className={`video-background-${sectionId}-cover`}
127
+ style={{
128
+ opacity: isPlaying ? 0 : 1,
129
+ left: isContainHeight ? '50%' : (hasOffsetX ? `${offsetX * 100}vw` : '0'),
130
+ width: isContainHeight ? 'auto' : '100%',
131
+ objectFit: isContainHeight ? 'unset' : (size ?? 'cover') as CSSProperties['objectFit'],
132
+ transform: isContainHeight ? 'translateX(-50%)' : 'none',
133
+ position: 'relative',
134
+ height: '100%',
135
+ transition: 'opacity 0.1s ease-in-out'
136
+ }}
137
+ />
138
+ )}
139
+ </div>
140
+ )}
141
+ </div>
142
+ </>
143
+ );
144
+ };
@@ -1,19 +1,19 @@
1
- import { useMemo } from 'react';
2
- import { CntrlColor } from '@cntrl-site/color';
3
- import { useLayoutContext } from '../useLayoutContext';
4
-
5
- type LayoutIdentifier = string;
6
- const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
7
-
8
- export const useSectionColor = (colorData: Record<LayoutIdentifier, string | null>): CntrlColor => {
9
- const layoutId = useLayoutContext();
10
- return useMemo(() => {
11
- if (!layoutId) return CntrlColor.parse(DEFAULT_COLOR);
12
- const layoutColor = colorData[layoutId];
13
- return CntrlColor.parse(
14
- !layoutColor
15
- ? DEFAULT_COLOR
16
- : layoutColor
17
- );
18
- }, [layoutId]);
19
- };
1
+ import { useMemo } from 'react';
2
+ import { CntrlColor } from '@cntrl-site/color';
3
+ import { useLayoutContext } from '../useLayoutContext';
4
+
5
+ type LayoutIdentifier = string;
6
+ const DEFAULT_COLOR = 'rgba(0, 0, 0, 0)';
7
+
8
+ export const useSectionColor = (colorData: Record<LayoutIdentifier, string | null>): CntrlColor => {
9
+ const layoutId = useLayoutContext();
10
+ return useMemo(() => {
11
+ if (!layoutId) return CntrlColor.parse(DEFAULT_COLOR);
12
+ const layoutColor = colorData[layoutId];
13
+ return CntrlColor.parse(
14
+ !layoutColor
15
+ ? DEFAULT_COLOR
16
+ : layoutColor
17
+ );
18
+ }, [layoutId]);
19
+ };
@@ -1,19 +1,19 @@
1
- import { useContext } from 'react';
2
- import { CntrlContext } from '../../provider/CntrlContext';
3
- import { Layout, SectionHeight } from '@cntrl-site/sdk';
4
- import { getSectionHeight } from './Section';
5
-
6
- export function useSectionHeightData(sectionId: string): Record<string, string> {
7
- const sectionHeightContext = useContext(CntrlContext);
8
- const layouts = sectionHeightContext.layouts;
9
- const sectionHeightData = sectionHeightContext.getSectionHeightData(sectionId);
10
- return sectionHeightData ? getSectionHeightMap(sectionHeightData) : getDefaultHeightData(layouts);
11
- }
12
-
13
- export function getSectionHeightMap(sectionHeight: Record<string, SectionHeight>): Record<string, string> {
14
- return Object.fromEntries(Object.entries(sectionHeight).map(([sectionId, heightData]) => [sectionId, getSectionHeight(heightData)]));
15
- }
16
-
17
- function getDefaultHeightData(layouts: Layout[]) {
18
- return layouts.reduce((acc, layout) => ({ ...acc, [layout.id]: '0' }), {});
19
- }
1
+ import { useContext } from 'react';
2
+ import { CntrlContext } from '../../provider/CntrlContext';
3
+ import { Layout, SectionHeight } from '@cntrl-site/sdk';
4
+ import { getSectionHeight } from './Section';
5
+
6
+ export function useSectionHeightData(sectionId: string): Record<string, string> {
7
+ const sectionHeightContext = useContext(CntrlContext);
8
+ const layouts = sectionHeightContext.layouts;
9
+ const sectionHeightData = sectionHeightContext.getSectionHeightData(sectionId);
10
+ return sectionHeightData ? getSectionHeightMap(sectionHeightData) : getDefaultHeightData(layouts);
11
+ }
12
+
13
+ export function getSectionHeightMap(sectionHeight: Record<string, SectionHeight>): Record<string, string> {
14
+ return Object.fromEntries(Object.entries(sectionHeight).map(([sectionId, heightData]) => [sectionId, getSectionHeight(heightData)]));
15
+ }
16
+
17
+ function getDefaultHeightData(layouts: Layout[]) {
18
+ return layouts.reduce((acc, layout) => ({ ...acc, [layout.id]: '0' }), {});
19
+ }
@@ -1,69 +1,69 @@
1
- import { getLayoutStyles, StructuredBlockAny, StructuredBlockType } from '@cntrl-site/sdk';
2
- import { ComponentType, FC, useId } from 'react';
3
- import JSXStyle from 'styled-jsx/style';
4
- import { useCntrlContext } from '../../provider/useCntrlContext';
5
- import { parseSizing } from '../items/useSizing';
6
- import { StructuredComponent } from './StructuredComponent/StructuredComponent';
7
-
8
- export interface StructuredBlockItemProps<I extends StructuredBlockAny> {
9
- block: I;
10
- }
11
-
12
- interface Props {
13
- block: StructuredBlockAny;
14
- maxWidthMap: Record<string, number>;
15
- }
16
-
17
- const noop = () => null;
18
-
19
- const itemsMap: Record<StructuredBlockType, ComponentType<StructuredBlockItemProps<any>>> = {
20
- [StructuredBlockType.Component]: StructuredComponent,
21
- [StructuredBlockType.RichText]: noop,
22
- [StructuredBlockType.Image]: noop,
23
- [StructuredBlockType.VimeoEmbed]: noop,
24
- [StructuredBlockType.YoutubeEmbed]: noop,
25
- };
26
-
27
- export const StructuredBlockItem: FC<Props> = ({ block, maxWidthMap }) => {
28
- const reactId = useId();
29
- const id = `${reactId}-item-${block.id}`;
30
- const { layouts } = useCntrlContext();
31
- const layoutValues: Record<string, any>[] = [block.area, block.layoutParams, block.hidden ?? {}, maxWidthMap];
32
- const ItemComponent = itemsMap[block.type] || noop;
33
- return (
34
- <div
35
- className={`structured-block-item-${block.id}`}
36
- >
37
- <ItemComponent block={block} />
38
- <JSXStyle id={id}>{`
39
- ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hidden, maxWidth]) => {
40
- const sizingAxis = parseSizing(layoutParams.sizing);
41
- return (`
42
- .structured-block-item-${block.id} {
43
- display: ${hidden ? 'none' : 'block'};
44
- align-self: ${getAlignSelf(area.alignment ?? 'center')};
45
- width: ${sizingAxis.x === 'manual' && area.width ? `${area.width * 100}vw` : maxWidth ?? 'max-content'};
46
- height: ${sizingAxis.y === 'manual' && area.height ? `${area.height * 100}vw` : 'unset'};
47
- padding-top: ${area.paddingTop ? `${area.paddingTop * 100}vw` : 'unset'};
48
- outline: none;
49
- left: ${(area.horizontalOffset ?? 0) * 100}vw;
50
- position: relative;
51
- }
52
- `);
53
- })}
54
- `}
55
- </JSXStyle>
56
- </div>
57
- );
58
- };
59
-
60
- function getAlignSelf(alignment: 'left' | 'center' | 'right') {
61
- switch (alignment) {
62
- case 'left':
63
- return 'start';
64
- case 'right':
65
- return 'end';
66
- default:
67
- return 'center';
68
- }
69
- }
1
+ import { getLayoutStyles, StructuredBlockAny, StructuredBlockType } from '@cntrl-site/sdk';
2
+ import { ComponentType, FC, useId } from 'react';
3
+ import JSXStyle from 'styled-jsx/style';
4
+ import { useCntrlContext } from '../../provider/useCntrlContext';
5
+ import { parseSizing } from '../items/useSizing';
6
+ import { StructuredComponent } from './StructuredComponent/StructuredComponent';
7
+
8
+ export interface StructuredBlockItemProps<I extends StructuredBlockAny> {
9
+ block: I;
10
+ }
11
+
12
+ interface Props {
13
+ block: StructuredBlockAny;
14
+ maxWidthMap: Record<string, number>;
15
+ }
16
+
17
+ const noop = () => null;
18
+
19
+ const itemsMap: Record<StructuredBlockType, ComponentType<StructuredBlockItemProps<any>>> = {
20
+ [StructuredBlockType.Component]: StructuredComponent,
21
+ [StructuredBlockType.RichText]: noop,
22
+ [StructuredBlockType.Image]: noop,
23
+ [StructuredBlockType.VimeoEmbed]: noop,
24
+ [StructuredBlockType.YoutubeEmbed]: noop,
25
+ };
26
+
27
+ export const StructuredBlockItem: FC<Props> = ({ block, maxWidthMap }) => {
28
+ const reactId = useId();
29
+ const id = `${reactId}-item-${block.id}`;
30
+ const { layouts } = useCntrlContext();
31
+ const layoutValues: Record<string, any>[] = [block.area, block.layoutParams, block.hidden ?? {}, maxWidthMap];
32
+ const ItemComponent = itemsMap[block.type] || noop;
33
+ return (
34
+ <div
35
+ className={`structured-block-item-${block.id}`}
36
+ >
37
+ <ItemComponent block={block} />
38
+ <JSXStyle id={id}>{`
39
+ ${getLayoutStyles(layouts, layoutValues, ([area, layoutParams, hidden, maxWidth]) => {
40
+ const sizingAxis = parseSizing(layoutParams.sizing);
41
+ return (`
42
+ .structured-block-item-${block.id} {
43
+ display: ${hidden ? 'none' : 'block'};
44
+ align-self: ${getAlignSelf(area.alignment ?? 'center')};
45
+ width: ${sizingAxis.x === 'manual' && area.width ? `${area.width * 100}vw` : maxWidth ?? 'max-content'};
46
+ height: ${sizingAxis.y === 'manual' && area.height ? `${area.height * 100}vw` : 'unset'};
47
+ padding-top: ${area.paddingTop ? `${area.paddingTop * 100}vw` : 'unset'};
48
+ outline: none;
49
+ left: ${(area.horizontalOffset ?? 0) * 100}vw;
50
+ position: relative;
51
+ }
52
+ `);
53
+ })}
54
+ `}
55
+ </JSXStyle>
56
+ </div>
57
+ );
58
+ };
59
+
60
+ function getAlignSelf(alignment: 'left' | 'center' | 'right') {
61
+ switch (alignment) {
62
+ case 'left':
63
+ return 'start';
64
+ case 'right':
65
+ return 'end';
66
+ default:
67
+ return 'center';
68
+ }
69
+ }
@@ -1,66 +1,68 @@
1
- import JSXStyle from 'styled-jsx/style';
2
- import { StructuredBlock, StructuredBlockType, getLayoutStyles } from '@cntrl-site/sdk';
3
- import { FC, useState } from 'react';
4
- import { useCntrlContext } from '../../../provider/useCntrlContext';
5
- import { useLayoutContext } from '../../useLayoutContext';
6
- import { useItemGeometry } from '../../../ItemGeometry/useItemGeometry';
7
-
8
- interface Props {
9
- block: StructuredBlock<StructuredBlockType.Component>;
10
- }
11
-
12
- export const StructuredComponent: FC<Props> = ({ block }) => {
13
- const sdk = useCntrlContext();
14
- const { layouts } = sdk;
15
- const layout = useLayoutContext();
16
- const fallbackLayout = layouts[0]?.id;
17
- const effectiveLayout = layout ?? fallbackLayout;
18
- const layoutValues: Record<string, any>[] = [block.layoutParams];
19
- const component = sdk.getComponent(block.commonParams.componentId);
20
- const [ref, setRef] = useState<HTMLDivElement | null>(null);
21
- useItemGeometry(block.id, ref);
22
- const Element = component ? component.element : undefined;
23
- const layoutParameters = effectiveLayout ? block.layoutParams[effectiveLayout]?.parameters : undefined;
24
- const commonParameters = block.commonParams.parameters;
25
- const parameters = layoutParameters ? {
26
- ...layoutParameters,
27
- settings: { ...layoutParameters.settings, ...commonParameters?.settings }
28
- } : undefined;
29
-
30
- return (
31
- <>
32
- <div
33
- className={`custom-component-${block.id}`}
34
- ref={setRef}
35
- >
36
- {parameters && Element && (
37
- <Element
38
- metadata={{
39
- itemId: block.id,
40
- submitUrl: sdk.getSubmitUrl(commonParameters?.pluginConfigId)
41
- }}
42
- portalId="component-portal"
43
- content={block.commonParams.content}
44
- {...parameters}
45
- />
46
- )}
47
- </div>
48
- <JSXStyle id={block.id}>{`
49
- .custom-component-${block.id} {
50
- width: 100%;
51
- height: 100%;
52
- pointer-events: auto;
53
- }
54
- ${getLayoutStyles(layouts, layoutValues, ([layoutParams]) => {
55
- return (`
56
- .custom-component-${block.id} {
57
- opacity: ${layoutParams.opacity};
58
- filter: blur(${layoutParams.blur}vw);
59
- ${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
60
- }
61
- `);
62
- })}`}
63
- </JSXStyle>
64
- </>
65
- );
66
- };
1
+ import JSXStyle from 'styled-jsx/style';
2
+ import { StructuredBlock, StructuredBlockType, getLayoutStyles } from '@cntrl-site/sdk';
3
+ import { FC, useState } from 'react';
4
+ import { useCntrlContext } from '../../../provider/useCntrlContext';
5
+ import { useLayoutContext } from '../../useLayoutContext';
6
+ import { useItemGeometry } from '../../../ItemGeometry/useItemGeometry';
7
+
8
+ interface Props {
9
+ block: StructuredBlock<StructuredBlockType.Component>;
10
+ }
11
+
12
+ export const StructuredComponent: FC<Props> = ({ block }) => {
13
+ const sdk = useCntrlContext();
14
+ const { layouts } = sdk;
15
+ const layout = useLayoutContext();
16
+ const fallbackLayout = layouts[0]?.id;
17
+ const effectiveLayout = layout ?? fallbackLayout;
18
+ const layoutValues: Record<string, any>[] = [block.layoutParams];
19
+ const component = sdk.getComponent(block.commonParams.componentId);
20
+ const [ref, setRef] = useState<HTMLDivElement | null>(null);
21
+ useItemGeometry(block.id, ref);
22
+ const Element = component ? component.element : undefined;
23
+ const layoutParameters = effectiveLayout ? block.layoutParams[effectiveLayout]?.parameters : undefined;
24
+ const commonParameters = block.commonParams.parameters;
25
+ const parameters = layoutParameters ? {
26
+ ...layoutParameters,
27
+ settings: { ...layoutParameters.settings, ...commonParameters?.settings }
28
+ } : undefined;
29
+
30
+ return (
31
+ <>
32
+ <div
33
+ className={`custom-component-${block.id}`}
34
+ ref={setRef}
35
+ // preventing layout shift while supporting SSG for proper SEO
36
+ style={{ opacity: layout == null ? 0 : undefined }}
37
+ >
38
+ {parameters && Element && (
39
+ <Element
40
+ metadata={{
41
+ itemId: block.id,
42
+ submitUrl: sdk.getSubmitUrl(commonParameters?.pluginConfigId)
43
+ }}
44
+ portalId="component-portal"
45
+ content={block.commonParams.content}
46
+ {...parameters}
47
+ />
48
+ )}
49
+ </div>
50
+ <JSXStyle id={block.id}>{`
51
+ .custom-component-${block.id} {
52
+ width: 100%;
53
+ height: 100%;
54
+ pointer-events: auto;
55
+ }
56
+ ${getLayoutStyles(layouts, layoutValues, ([layoutParams]) => {
57
+ return (`
58
+ .custom-component-${block.id} {
59
+ opacity: ${layoutParams.opacity};
60
+ filter: blur(${layoutParams.blur}vw);
61
+ ${layoutParams.blur !== 0 ? 'will-change: transform;' : ''}
62
+ }
63
+ `);
64
+ })}`}
65
+ </JSXStyle>
66
+ </>
67
+ );
68
+ };