@cntrl-site/sdk-nextjs 1.10.9 → 1.11.0-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.
- package/cntrl-site-sdk-nextjs-1.9.125.tgz +0 -0
- package/lib/components/StructuredBlockItem/BlockCaption/BlockCaption.js +35 -0
- package/lib/components/StructuredBlockItem/StructuredBlockItem.js +17 -42
- package/lib/components/StructuredBlockItem/StructuredCode/StructuredCode.js +75 -0
- package/lib/components/StructuredBlockItem/StructuredDivider/StructuredDivider.js +29 -0
- package/lib/components/StructuredBlockItem/StructuredEmbed/StructuredVimeoEmbed.js +89 -0
- package/lib/components/StructuredBlockItem/StructuredEmbed/StructuredYoutubeEmbed.js +97 -0
- package/lib/components/StructuredBlockItem/StructuredEmbed/embedFrameStyles.js +36 -0
- package/lib/components/StructuredBlockItem/StructuredHeaderImage/StructuredHeaderImage.js +43 -0
- package/lib/components/StructuredBlockItem/StructuredImage/StructuredImage.js +65 -0
- package/lib/components/StructuredBlockItem/StructuredRichText/StructuredRichText.js +46 -0
- package/lib/components/StructuredBlockItem/StructuredVideo/StructuredVideo.js +74 -0
- package/lib/components/StructuredBlockItem/blockPlacement.js +41 -0
- package/lib/components/StructuredBlockItem/blocksMap.js +29 -0
- package/lib/components/StructuredBlockItem/textStylesRule.js +46 -0
- package/lib/components/StructuredContent/StructuredContent.js +24 -17
- package/lib/components/StructuredContent/StructuredHeader.js +95 -0
- package/lib/components/items/EmbedVideoItem/VimeoEmbed.js +2 -15
- package/lib/utils/RichTextConverter/RichTextConverter.js +52 -14
- package/lib/utils/getVimeoEmbedUrl.js +18 -0
- package/package.json +2 -2
- package/src/components/StructuredBlockItem/BlockCaption/BlockCaption.tsx +42 -0
- package/src/components/StructuredBlockItem/StructuredBlockItem.tsx +23 -50
- package/src/components/StructuredBlockItem/StructuredCode/StructuredCode.tsx +86 -0
- package/src/components/StructuredBlockItem/StructuredDivider/StructuredDivider.tsx +31 -0
- package/src/components/StructuredBlockItem/StructuredEmbed/StructuredVimeoEmbed.tsx +101 -0
- package/src/components/StructuredBlockItem/StructuredEmbed/StructuredYoutubeEmbed.tsx +106 -0
- package/src/components/StructuredBlockItem/StructuredEmbed/embedFrameStyles.ts +32 -0
- package/src/components/StructuredBlockItem/StructuredHeaderImage/StructuredHeaderImage.tsx +43 -0
- package/src/components/StructuredBlockItem/StructuredImage/StructuredImage.tsx +75 -0
- package/src/components/StructuredBlockItem/StructuredRichText/StructuredRichText.tsx +53 -0
- package/src/components/StructuredBlockItem/StructuredVideo/StructuredVideo.tsx +106 -0
- package/src/components/StructuredBlockItem/blockPlacement.ts +39 -0
- package/src/components/StructuredBlockItem/blocksMap.ts +30 -0
- package/src/components/StructuredBlockItem/textStylesRule.ts +44 -0
- package/src/components/StructuredContent/StructuredContent.test.tsx +217 -0
- package/src/components/StructuredContent/StructuredContent.tsx +25 -19
- package/src/components/StructuredContent/StructuredHeader.tsx +119 -0
- package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +2 -14
- package/src/utils/RichTextConverter/RichTextConverter.test.tsx +67 -0
- package/src/utils/RichTextConverter/RichTextConverter.tsx +77 -14
- package/src/utils/getVimeoEmbedUrl.ts +16 -0
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/modules.xml +0 -8
- package/.idea/sdk-nextjs.iml +0 -12
- package/.idea/vcs.xml +0 -6
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StructuredVideo = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const style_1 = __importDefault(require("styled-jsx/style"));
|
|
10
|
+
const useItemGeometry_1 = require("../../../ItemGeometry/useItemGeometry");
|
|
11
|
+
const useLayoutContext_1 = require("../../useLayoutContext");
|
|
12
|
+
/**
|
|
13
|
+
* A video at its own proportions, playing on its own, once pointed at, or once clicked, as the
|
|
14
|
+
* layout says; its cover shows until it first plays. It takes the 16:9 of a video until its own
|
|
15
|
+
* proportions are known.
|
|
16
|
+
*/
|
|
17
|
+
const StructuredVideo = ({ block }) => {
|
|
18
|
+
const reactId = (0, react_1.useId)();
|
|
19
|
+
const layoutId = (0, useLayoutContext_1.useLayoutContext)();
|
|
20
|
+
const [ref, setRef] = (0, react_1.useState)(null);
|
|
21
|
+
const [video, setVideo] = (0, react_1.useState)(null);
|
|
22
|
+
const [isLoaded, setIsLoaded] = (0, react_1.useState)(false);
|
|
23
|
+
const [isPlaying, setIsPlaying] = (0, react_1.useState)(false);
|
|
24
|
+
const [hasPlayed, setHasPlayed] = (0, react_1.useState)(false);
|
|
25
|
+
(0, useItemGeometry_1.useItemGeometry)(block.id, ref);
|
|
26
|
+
const { url, coverUrl } = block.commonParams;
|
|
27
|
+
const layoutParams = layoutId ? block.layoutParams[layoutId] : undefined;
|
|
28
|
+
const play = layoutParams === null || layoutParams === void 0 ? void 0 : layoutParams.play;
|
|
29
|
+
const start = () => {
|
|
30
|
+
if (!video)
|
|
31
|
+
return;
|
|
32
|
+
setHasPlayed(true);
|
|
33
|
+
video.play();
|
|
34
|
+
};
|
|
35
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: setRef, className: `structured-video-${block.id}${isLoaded ? '' : ` structured-video-loading-${block.id}`}`, children: [layoutParams && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("video", { ref: setVideo, className: `structured-video-el-${block.id}`, src: url, autoPlay: play === 'auto', muted: layoutParams.muted, controls: layoutParams.controls, loop: true, playsInline: true, preload: "auto", onLoadedMetadata: () => setIsLoaded(true), onPlay: () => {
|
|
36
|
+
setIsPlaying(true);
|
|
37
|
+
setHasPlayed(true);
|
|
38
|
+
}, onPause: () => setIsPlaying(false), onMouseEnter: () => {
|
|
39
|
+
if (play === 'on-hover')
|
|
40
|
+
start();
|
|
41
|
+
}, onMouseLeave: () => {
|
|
42
|
+
if (play === 'on-hover')
|
|
43
|
+
video === null || video === void 0 ? void 0 : video.pause();
|
|
44
|
+
} }, layoutId), coverUrl && play !== 'auto' && !hasPlayed && ((0, jsx_runtime_1.jsx)("img", { className: `structured-video-cover-${block.id}`, src: coverUrl, alt: "", onMouseEnter: () => {
|
|
45
|
+
if (play === 'on-hover')
|
|
46
|
+
start();
|
|
47
|
+
}, onClick: start })), play === 'on-click' && !layoutParams.controls && ((0, jsx_runtime_1.jsx)("div", { className: `structured-video-overlay-${block.id}`, onClick: () => (isPlaying ? video === null || video === void 0 ? void 0 : video.pause() : start()) }))] })), (0, jsx_runtime_1.jsx)(style_1.default, { id: `${reactId}-video-${block.id}`, children: `
|
|
48
|
+
.structured-video-${block.id} {
|
|
49
|
+
position: relative;
|
|
50
|
+
width: 100%;
|
|
51
|
+
}
|
|
52
|
+
.structured-video-loading-${block.id} {
|
|
53
|
+
aspect-ratio: 16 / 9;
|
|
54
|
+
}
|
|
55
|
+
.structured-video-el-${block.id} {
|
|
56
|
+
display: block;
|
|
57
|
+
width: 100%;
|
|
58
|
+
height: auto;
|
|
59
|
+
}
|
|
60
|
+
.structured-video-cover-${block.id},
|
|
61
|
+
.structured-video-overlay-${block.id} {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 0;
|
|
64
|
+
left: 0;
|
|
65
|
+
width: 100%;
|
|
66
|
+
height: 100%;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
}
|
|
69
|
+
.structured-video-cover-${block.id} {
|
|
70
|
+
object-fit: cover;
|
|
71
|
+
}
|
|
72
|
+
` })] }));
|
|
73
|
+
};
|
|
74
|
+
exports.StructuredVideo = StructuredVideo;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.blockPlacement = void 0;
|
|
4
|
+
const useSizing_1 = require("../items/useSizing");
|
|
5
|
+
/**
|
|
6
|
+
* Where a block sits in a column of blocks, as CSS declarations: on the content grid where it is
|
|
7
|
+
* drawn from a preset, the area then carrying its `left` and its inner paddings; anywhere else sized
|
|
8
|
+
* and aligned by itself.
|
|
9
|
+
*/
|
|
10
|
+
function blockPlacement(area, layoutParams) {
|
|
11
|
+
var _a, _b, _c, _d, _e;
|
|
12
|
+
const sizing = (0, useSizing_1.parseSizing)('sizing' in layoutParams ? layoutParams.sizing : undefined);
|
|
13
|
+
const height = sizing.y === 'manual' && area.height !== undefined ? `${area.height * 100}vw` : 'auto';
|
|
14
|
+
if (area.left !== undefined) {
|
|
15
|
+
return `
|
|
16
|
+
align-self: flex-start;
|
|
17
|
+
left: ${area.left * 100}vw;
|
|
18
|
+
width: ${((_a = area.width) !== null && _a !== void 0 ? _a : 1) * 100}vw;
|
|
19
|
+
height: ${height};
|
|
20
|
+
padding-top: ${((_b = area.innerPaddingTop) !== null && _b !== void 0 ? _b : 0) * 100}vw;
|
|
21
|
+
padding-bottom: ${((_c = area.innerPaddingBottom) !== null && _c !== void 0 ? _c : 0) * 100}vw;
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
return `
|
|
25
|
+
align-self: ${getAlignSelf((_d = area.alignment) !== null && _d !== void 0 ? _d : 'center')};
|
|
26
|
+
left: ${((_e = area.horizontalOffset) !== null && _e !== void 0 ? _e : 0) * 100}vw;
|
|
27
|
+
width: ${sizing.x === 'manual' && area.width ? `${area.width * 100}vw` : 'max-content'};
|
|
28
|
+
height: ${height};
|
|
29
|
+
`;
|
|
30
|
+
}
|
|
31
|
+
exports.blockPlacement = blockPlacement;
|
|
32
|
+
function getAlignSelf(alignment) {
|
|
33
|
+
switch (alignment) {
|
|
34
|
+
case 'left':
|
|
35
|
+
return 'flex-start';
|
|
36
|
+
case 'right':
|
|
37
|
+
return 'flex-end';
|
|
38
|
+
default:
|
|
39
|
+
return 'center';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.blocksMap = void 0;
|
|
4
|
+
const sdk_1 = require("@cntrl-site/sdk");
|
|
5
|
+
const StructuredComponent_1 = require("./StructuredComponent/StructuredComponent");
|
|
6
|
+
const StructuredRichText_1 = require("./StructuredRichText/StructuredRichText");
|
|
7
|
+
const StructuredImage_1 = require("./StructuredImage/StructuredImage");
|
|
8
|
+
const StructuredHeaderImage_1 = require("./StructuredHeaderImage/StructuredHeaderImage");
|
|
9
|
+
const StructuredVideo_1 = require("./StructuredVideo/StructuredVideo");
|
|
10
|
+
const StructuredDivider_1 = require("./StructuredDivider/StructuredDivider");
|
|
11
|
+
const StructuredCode_1 = require("./StructuredCode/StructuredCode");
|
|
12
|
+
const StructuredVimeoEmbed_1 = require("./StructuredEmbed/StructuredVimeoEmbed");
|
|
13
|
+
const StructuredYoutubeEmbed_1 = require("./StructuredEmbed/StructuredYoutubeEmbed");
|
|
14
|
+
const noop = () => null;
|
|
15
|
+
exports.blocksMap = {
|
|
16
|
+
[sdk_1.StructuredBlockType.Component]: StructuredComponent_1.StructuredComponent,
|
|
17
|
+
[sdk_1.StructuredBlockType.RichText]: StructuredRichText_1.StructuredRichText,
|
|
18
|
+
[sdk_1.StructuredBlockType.Quote]: StructuredRichText_1.StructuredRichText,
|
|
19
|
+
[sdk_1.StructuredBlockType.Date]: StructuredRichText_1.StructuredRichText,
|
|
20
|
+
[sdk_1.StructuredBlockType.Image]: StructuredImage_1.StructuredImage,
|
|
21
|
+
[sdk_1.StructuredBlockType.HeaderImage]: StructuredHeaderImage_1.StructuredHeaderImage,
|
|
22
|
+
[sdk_1.StructuredBlockType.Video]: StructuredVideo_1.StructuredVideo,
|
|
23
|
+
[sdk_1.StructuredBlockType.Divider]: StructuredDivider_1.StructuredDivider,
|
|
24
|
+
[sdk_1.StructuredBlockType.Code]: StructuredCode_1.StructuredCode,
|
|
25
|
+
[sdk_1.StructuredBlockType.VimeoEmbed]: StructuredVimeoEmbed_1.StructuredVimeoEmbed,
|
|
26
|
+
[sdk_1.StructuredBlockType.YoutubeEmbed]: StructuredYoutubeEmbed_1.StructuredYoutubeEmbed,
|
|
27
|
+
// a section's header is drawn by its section, never in the stack
|
|
28
|
+
[sdk_1.StructuredBlockType.Header]: noop
|
|
29
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.colorRule = exports.textStylesRule = void 0;
|
|
4
|
+
const color_1 = require("@cntrl-site/color");
|
|
5
|
+
const getFontFamilyValue_1 = require("../../utils/getFontFamilyValue");
|
|
6
|
+
/** A block's text styles as a CSS rule for `selector`, its lengths in viewport units as the block's own. */
|
|
7
|
+
function textStylesRule(selector, styles) {
|
|
8
|
+
const color = color_1.CntrlColor.parse(styles.color);
|
|
9
|
+
return `
|
|
10
|
+
${selector} {
|
|
11
|
+
font-family: ${(0, getFontFamilyValue_1.getFontFamilyValue)(styles.typeFace)};
|
|
12
|
+
font-size: ${styles.fontSize * 100}vw;
|
|
13
|
+
line-height: ${styles.lineHeight * 100}vw;
|
|
14
|
+
letter-spacing: ${styles.letterSpacing * 100}vw;
|
|
15
|
+
word-spacing: ${styles.wordSpacing * 100}vw;
|
|
16
|
+
font-weight: ${styles.fontWeight};
|
|
17
|
+
font-style: ${styles.fontStyle || 'normal'};
|
|
18
|
+
font-variant: ${styles.fontVariant};
|
|
19
|
+
text-transform: ${styles.textTransform};
|
|
20
|
+
vertical-align: ${styles.verticalAlign};
|
|
21
|
+
text-align: ${styles.textAlign};
|
|
22
|
+
color: ${color.fmt('oklch')};
|
|
23
|
+
}
|
|
24
|
+
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
25
|
+
${selector} {
|
|
26
|
+
color: ${color.fmt('rgba')};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
}
|
|
31
|
+
exports.textStylesRule = textStylesRule;
|
|
32
|
+
/** A colour of the palette as CSS for `property`, with a fallback where oklch is not supported. */
|
|
33
|
+
function colorRule(selector, property, value) {
|
|
34
|
+
const color = color_1.CntrlColor.parse(value);
|
|
35
|
+
return `
|
|
36
|
+
${selector} {
|
|
37
|
+
${property}: ${color.fmt('oklch')};
|
|
38
|
+
}
|
|
39
|
+
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
40
|
+
${selector} {
|
|
41
|
+
${property}: ${color.fmt('rgba')};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
exports.colorRule = colorRule;
|
|
@@ -10,28 +10,35 @@ const react_1 = require("react");
|
|
|
10
10
|
const style_1 = __importDefault(require("styled-jsx/style"));
|
|
11
11
|
const useCntrlContext_1 = require("../../provider/useCntrlContext");
|
|
12
12
|
const StructuredBlockItem_1 = require("../StructuredBlockItem/StructuredBlockItem");
|
|
13
|
+
const StructuredHeader_1 = require("./StructuredHeader");
|
|
14
|
+
/**
|
|
15
|
+
* A section's blocks, one under another across the section's whole width: a block drawn from a
|
|
16
|
+
* preset is placed on the content grid by its own area. A content-based section's header comes
|
|
17
|
+
* first, drawing the blocks it points at, which the stack leaves out.
|
|
18
|
+
*/
|
|
13
19
|
const StructuredContent = ({ section }) => {
|
|
14
20
|
const reactId = (0, react_1.useId)();
|
|
15
21
|
const id = `${reactId}-structured-content-${section.id}`;
|
|
16
22
|
const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
23
|
+
const header = section.type === 'content-based' ? section.header : undefined;
|
|
24
|
+
const headerElementIds = header ? Object.values(header.commonParams) : [];
|
|
25
|
+
const readingWidth = section.type === 'content-based' ? section.structuredContentSettings.defaultWidth : {};
|
|
26
|
+
const layoutValues = [section.structuredContentSettings.paddingBottom];
|
|
27
|
+
if (section.structuredContent.length === 0 && !header)
|
|
21
28
|
return null;
|
|
22
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: `structured-content-${section.id}`, children: [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `structured-content-${section.id}`, children: [header && (0, jsx_runtime_1.jsx)(StructuredHeader_1.StructuredHeader, { header: header, blocks: section.structuredContent, readingWidth: readingWidth }), section.structuredContent
|
|
30
|
+
.filter(block => !headerElementIds.includes(block.id))
|
|
31
|
+
.map(block => (0, jsx_runtime_1.jsx)(StructuredBlockItem_1.StructuredBlockItem, { block: block, readingWidth: readingWidth }, block.id)), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
|
|
32
|
+
.structured-content-${section.id} {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
width: 100%;
|
|
36
|
+
}
|
|
37
|
+
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([paddingBottom]) => (`
|
|
38
|
+
.structured-content-${section.id} {
|
|
39
|
+
padding-bottom: ${(paddingBottom !== null && paddingBottom !== void 0 ? paddingBottom : 0) * 100}vw;
|
|
40
|
+
}
|
|
41
|
+
`))}
|
|
35
42
|
` })] }));
|
|
36
43
|
};
|
|
37
44
|
exports.StructuredContent = StructuredContent;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StructuredHeader = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const sdk_1 = require("@cntrl-site/sdk");
|
|
9
|
+
const react_1 = require("react");
|
|
10
|
+
const style_1 = __importDefault(require("styled-jsx/style"));
|
|
11
|
+
const useCntrlContext_1 = require("../../provider/useCntrlContext");
|
|
12
|
+
const blockPlacement_1 = require("../StructuredBlockItem/blockPlacement");
|
|
13
|
+
const blocksMap_1 = require("../StructuredBlockItem/blocksMap");
|
|
14
|
+
const TEXT_KINDS = ['title', 'subtitle', 'date'];
|
|
15
|
+
/**
|
|
16
|
+
* A content-based section's header, drawn first. Variant A draws the key image full width with the
|
|
17
|
+
* other elements on its bottom edge, the image covering a share of the viewport's height if set to;
|
|
18
|
+
* variant B flows the shown elements in order, each placed as in the stack. The variant, the order
|
|
19
|
+
* and what is shown are a layout's, so every element is drawn once and arranged per layout.
|
|
20
|
+
*/
|
|
21
|
+
const StructuredHeader = ({ header, blocks, readingWidth }) => {
|
|
22
|
+
const reactId = (0, react_1.useId)();
|
|
23
|
+
const { layouts } = (0, useCntrlContext_1.useCntrlContext)();
|
|
24
|
+
const elementOf = (kind) => blocks.find(block => block.id === header.commonParams[kind]);
|
|
25
|
+
const image = elementOf('image');
|
|
26
|
+
const hasImage = (image === null || image === void 0 ? void 0 : image.type) === sdk_1.StructuredBlockType.HeaderImage && Boolean(image.commonParams.url);
|
|
27
|
+
const texts = TEXT_KINDS.flatMap((kind) => {
|
|
28
|
+
const block = elementOf(kind);
|
|
29
|
+
return block ? [{ kind, block }] : [];
|
|
30
|
+
});
|
|
31
|
+
const headerClass = `structured-header-${header.id}`;
|
|
32
|
+
const stackClass = `structured-header-stack-${header.id}`;
|
|
33
|
+
const elementClass = (block) => `structured-header-element-${block.id}`;
|
|
34
|
+
const renderElement = (block) => {
|
|
35
|
+
const BlockComponent = blocksMap_1.blocksMap[block.type];
|
|
36
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: elementClass(block), children: (0, jsx_runtime_1.jsx)(BlockComponent, { block: block, readingWidth: readingWidth }) }, block.id));
|
|
37
|
+
};
|
|
38
|
+
const elementStyles = (kind, block) => (0, sdk_1.getLayoutStyles)(layouts, [header.layoutParams, block.area, block.layoutParams], ([layoutParams, area, blockLayoutParams]) => {
|
|
39
|
+
const isBackdrop = kind === 'image' && layoutParams.variant === 'A';
|
|
40
|
+
const isHidden = !isBackdrop && layoutParams.hiddenElements.includes(kind);
|
|
41
|
+
const isCover = isBackdrop && layoutParams.imageFit === 'cover';
|
|
42
|
+
return (`
|
|
43
|
+
.${elementClass(block)} {
|
|
44
|
+
display: ${isHidden ? 'none' : 'block'};
|
|
45
|
+
position: relative;
|
|
46
|
+
order: ${layoutParams.order.indexOf(kind)};
|
|
47
|
+
${isBackdrop
|
|
48
|
+
? `width: 100%; left: 0; height: ${isCover ? '100%' : 'auto'}; padding: 0;`
|
|
49
|
+
: (0, blockPlacement_1.blockPlacement)(area, blockLayoutParams)}
|
|
50
|
+
}
|
|
51
|
+
${isBackdrop ? `
|
|
52
|
+
.${elementClass(block)} .structured-caption-${block.id} {
|
|
53
|
+
display: none;
|
|
54
|
+
}
|
|
55
|
+
` : ''}
|
|
56
|
+
${isCover ? `
|
|
57
|
+
.${elementClass(block)} .structured-header-image-${block.id},
|
|
58
|
+
.${elementClass(block)} .structured-header-picture-${block.id} {
|
|
59
|
+
height: 100%;
|
|
60
|
+
}
|
|
61
|
+
.${elementClass(block)} .structured-header-picture-${block.id} {
|
|
62
|
+
object-fit: cover;
|
|
63
|
+
}
|
|
64
|
+
` : ''}
|
|
65
|
+
`);
|
|
66
|
+
});
|
|
67
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: headerClass, children: [image && renderElement(image), (0, jsx_runtime_1.jsx)("div", { className: stackClass, children: texts.map(({ block }) => renderElement(block)) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: `${reactId}-header-${header.id}`, children: `
|
|
68
|
+
.${headerClass} {
|
|
69
|
+
position: relative;
|
|
70
|
+
width: 100%;
|
|
71
|
+
}
|
|
72
|
+
${(0, sdk_1.getLayoutStyles)(layouts, [header.layoutParams, header.area], ([layoutParams, area]) => {
|
|
73
|
+
var _a;
|
|
74
|
+
const isA = layoutParams.variant === 'A';
|
|
75
|
+
const isCover = isA && layoutParams.imageFit === 'cover';
|
|
76
|
+
return (`
|
|
77
|
+
.${headerClass} {
|
|
78
|
+
display: ${isA ? 'block' : 'flex'};
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
z-index: ${(_a = area === null || area === void 0 ? void 0 : area.zIndex) !== null && _a !== void 0 ? _a : 0};
|
|
81
|
+
height: ${isCover ? `${layoutParams.coverHeight * 100}vh` : 'auto'};
|
|
82
|
+
height: ${isCover ? `${layoutParams.coverHeight * 100}svh` : 'auto'};
|
|
83
|
+
}
|
|
84
|
+
.${stackClass} {
|
|
85
|
+
${isA
|
|
86
|
+
? `display: flex; flex-direction: column; position: ${hasImage ? 'absolute' : 'relative'}; left: 0; right: 0; bottom: 0;`
|
|
87
|
+
: 'display: contents;'}
|
|
88
|
+
}
|
|
89
|
+
`);
|
|
90
|
+
})}
|
|
91
|
+
${image ? elementStyles('image', image) : ''}
|
|
92
|
+
${texts.map(({ kind, block }) => elementStyles(kind, block)).join('\n')}
|
|
93
|
+
` })] }));
|
|
94
|
+
};
|
|
95
|
+
exports.StructuredHeader = StructuredHeader;
|
|
@@ -26,6 +26,7 @@ const useEmbedVideoItem_1 = require("./useEmbedVideoItem");
|
|
|
26
26
|
const LinkWrapper_1 = require("../LinkWrapper");
|
|
27
27
|
const useLayoutContext_1 = require("../../useLayoutContext");
|
|
28
28
|
const useItemGeometry_1 = require("../../../ItemGeometry/useItemGeometry");
|
|
29
|
+
const getVimeoEmbedUrl_1 = require("../../../utils/getVimeoEmbedUrl");
|
|
29
30
|
const VimeoEmbedItem = ({ item, sectionId, onResize, interactionCtrl, onVisibilityChange }) => {
|
|
30
31
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
31
32
|
const reactId = (0, react_1.useId)();
|
|
@@ -50,21 +51,7 @@ const VimeoEmbedItem = ({ item, sectionId, onResize, interactionCtrl, onVisibili
|
|
|
50
51
|
const radius = (0, getStyleFromItemStateAndParams_1.getStyleFromItemStateAndParams)((_d = frameStateParams === null || frameStateParams === void 0 ? void 0 : frameStateParams.styles) === null || _d === void 0 ? void 0 : _d.radius, itemRadius);
|
|
51
52
|
(0, useRegisterResize_1.useRegisterResize)(ref, onResize);
|
|
52
53
|
(0, useItemGeometry_1.useItemGeometry)(item.id, ref);
|
|
53
|
-
const validUrl = (0, react_1.useMemo)(() =>
|
|
54
|
-
if (!layoutParams)
|
|
55
|
-
return url;
|
|
56
|
-
const validURL = new URL(url);
|
|
57
|
-
validURL.searchParams.append('controls', String(layoutParams.controls));
|
|
58
|
-
validURL.searchParams.append('autoplay', String(layoutParams.play === 'auto'));
|
|
59
|
-
validURL.searchParams.append('muted', String(layoutParams.muted));
|
|
60
|
-
validURL.searchParams.append('loop', String(layoutParams.loop));
|
|
61
|
-
validURL.searchParams.append('pip', String(layoutParams.pictureInPicture));
|
|
62
|
-
validURL.searchParams.append('title', '0');
|
|
63
|
-
validURL.searchParams.append('byline', '0');
|
|
64
|
-
validURL.searchParams.append('portrait', '0');
|
|
65
|
-
validURL.searchParams.append('autopause', 'false');
|
|
66
|
-
return validURL.href;
|
|
67
|
-
}, [url, layoutParams]);
|
|
54
|
+
const validUrl = (0, react_1.useMemo)(() => (layoutParams ? (0, getVimeoEmbedUrl_1.getVimeoEmbedUrl)(url, layoutParams) : url), [url, layoutParams]);
|
|
68
55
|
(0, react_1.useEffect)(() => {
|
|
69
56
|
if (!vimeoPlayer || !imgRef || !layoutParams)
|
|
70
57
|
return;
|
|
@@ -13,10 +13,42 @@ exports.FontStyles = {
|
|
|
13
13
|
};
|
|
14
14
|
exports.RICH_TEXT_LAYOUT_PENDING_CLASS = 'rich-text-layout-pending';
|
|
15
15
|
const SCALING_STYLES = new Set(['FONTSIZE', 'LINEHEIGHT', 'LETTERSPACING', 'WORDSPACING']);
|
|
16
|
+
const LIST_ITEM_CLASSES = {
|
|
17
|
+
'unordered-list-item': 'rt-unordered-list-item',
|
|
18
|
+
'ordered-list-item': 'rt-ordered-list-item'
|
|
19
|
+
};
|
|
20
|
+
// a list item's marker hangs before its first line; a numbered run restarts after anything else
|
|
21
|
+
const LIST_STYLES = `
|
|
22
|
+
.rt-unordered-list-item,
|
|
23
|
+
.rt-ordered-list-item {
|
|
24
|
+
padding-left: 1.5em;
|
|
25
|
+
text-indent: -1.5em;
|
|
26
|
+
}
|
|
27
|
+
.rt-unordered-list-item::before,
|
|
28
|
+
.rt-ordered-list-item::before {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
width: 1.5em;
|
|
31
|
+
text-indent: 0;
|
|
32
|
+
}
|
|
33
|
+
.rt-unordered-list-item::before {
|
|
34
|
+
content: '•';
|
|
35
|
+
}
|
|
36
|
+
.rt-paragraph,
|
|
37
|
+
.rt-unordered-list-item {
|
|
38
|
+
counter-reset: cntrl-list;
|
|
39
|
+
}
|
|
40
|
+
.rt-ordered-list-item {
|
|
41
|
+
counter-increment: cntrl-list;
|
|
42
|
+
}
|
|
43
|
+
.rt-ordered-list-item::before {
|
|
44
|
+
content: counter(cntrl-list) '.';
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
16
47
|
class RichTextConverter {
|
|
17
|
-
toHtml(richText, layouts) {
|
|
18
|
-
var _a, _b, _c;
|
|
48
|
+
toHtml(richText, layouts, { isScaledToViewport = true } = {}) {
|
|
49
|
+
var _a, _b, _c, _d;
|
|
19
50
|
const { text, blocks = [] } = richText.commonParams;
|
|
51
|
+
const hasLists = blocks.some(block => block.type in LIST_ITEM_CLASSES);
|
|
20
52
|
const root = [];
|
|
21
53
|
const styleRules = layouts.reduce((rec, layout) => {
|
|
22
54
|
rec[layout.id] = [];
|
|
@@ -32,17 +64,20 @@ class RichTextConverter {
|
|
|
32
64
|
const block = blocks[blockIndex];
|
|
33
65
|
const content = text.slice(block.start, block.end + 1);
|
|
34
66
|
const entities = (_a = block.entities.sort((a, b) => a.start - b.start)) !== null && _a !== void 0 ? _a : [];
|
|
67
|
+
const typeClass = hasLists ? ` ${(_b = LIST_ITEM_CLASSES[block.type]) !== null && _b !== void 0 ? _b : 'rt-paragraph'}` : '';
|
|
35
68
|
if (content.length === 1) {
|
|
36
69
|
const id = `rt_${richText.id}_br_${blockIndex}`;
|
|
37
|
-
root.push((0, jsx_runtime_1.jsx)("div", { className: id
|
|
70
|
+
root.push((0, jsx_runtime_1.jsx)("div", { className: `${id}${typeClass}`, children: (0, jsx_runtime_1.jsx)("br", {}) }, id));
|
|
38
71
|
layouts.forEach(l => {
|
|
39
72
|
const lhForLayout = currentLineHeight[l.id];
|
|
40
73
|
if (lhForLayout === undefined)
|
|
41
74
|
return;
|
|
42
75
|
const lineHeightStyle = { name: 'LINEHEIGHT', value: lhForLayout };
|
|
43
|
-
const lh = RichTextConverter.fromRangeStylesToInline(lineHeightStyle, l.exemplary,
|
|
44
|
-
const lhPending = RichTextConverter.fromRangeStylesToInline(lineHeightStyle, l.exemplary, false);
|
|
76
|
+
const lh = RichTextConverter.fromRangeStylesToInline(lineHeightStyle, l.exemplary, isScaledToViewport);
|
|
45
77
|
styleRules[l.id].push(`.rt_${richText.id}_br_${blockIndex} {${lh}}`);
|
|
78
|
+
if (!isScaledToViewport)
|
|
79
|
+
return;
|
|
80
|
+
const lhPending = RichTextConverter.fromRangeStylesToInline(lineHeightStyle, l.exemplary, false);
|
|
46
81
|
styleRules[l.id].push(`.${exports.RICH_TEXT_LAYOUT_PENDING_CLASS} .rt_${richText.id}_br_${blockIndex} {${lhPending}}`);
|
|
47
82
|
});
|
|
48
83
|
continue;
|
|
@@ -73,7 +108,7 @@ class RichTextConverter {
|
|
|
73
108
|
`);
|
|
74
109
|
});
|
|
75
110
|
const item = group[0];
|
|
76
|
-
const entitiesGroups = (
|
|
111
|
+
const entitiesGroups = (_c = this.groupEntities(entities, item.styles)) !== null && _c !== void 0 ? _c : [];
|
|
77
112
|
let offset = 0;
|
|
78
113
|
for (const entity of entitiesGroups) {
|
|
79
114
|
const entityKids = [];
|
|
@@ -103,34 +138,36 @@ class RichTextConverter {
|
|
|
103
138
|
}
|
|
104
139
|
for (const item of group) {
|
|
105
140
|
const { exemplary } = layouts.find(l => l.id === item.layout);
|
|
106
|
-
const entitiesGroups = (
|
|
141
|
+
const entitiesGroups = (_d = this.groupEntities(entities, item.styles)) !== null && _d !== void 0 ? _d : [];
|
|
107
142
|
for (const entitiesGroup of entitiesGroups) {
|
|
108
143
|
if (!entitiesGroup.stylesGroup)
|
|
109
144
|
continue;
|
|
110
145
|
for (const styleGroup of entitiesGroup.stylesGroup) {
|
|
111
146
|
const lineHeight = styleGroup.styles.find(s => s.name === 'LINEHEIGHT');
|
|
112
147
|
const color = styleGroup.styles.find(s => s.name === 'COLOR');
|
|
148
|
+
const highlight = styleGroup.styles.find(s => s.name === 'HIGHLIGHT');
|
|
113
149
|
if (lineHeight === null || lineHeight === void 0 ? void 0 : lineHeight.value) {
|
|
114
150
|
currentLineHeight[item.layout] = lineHeight.value;
|
|
115
151
|
}
|
|
116
152
|
styleRules[item.layout].push(`
|
|
117
153
|
.${blockClass} .s-${styleGroup.start}-${styleGroup.end} {
|
|
118
|
-
${styleGroup.styles.map(s => RichTextConverter.fromRangeStylesToInline(s, exemplary,
|
|
154
|
+
${styleGroup.styles.map(s => RichTextConverter.fromRangeStylesToInline(s, exemplary, isScaledToViewport)).join('\n')}
|
|
119
155
|
}
|
|
120
156
|
`);
|
|
121
157
|
const lengthStyles = styleGroup.styles.filter(s => SCALING_STYLES.has(s.name));
|
|
122
|
-
if (lengthStyles.length !== 0) {
|
|
158
|
+
if (isScaledToViewport && lengthStyles.length !== 0) {
|
|
123
159
|
styleRules[item.layout].push(`
|
|
124
160
|
.${exports.RICH_TEXT_LAYOUT_PENDING_CLASS} .${blockClass} .s-${styleGroup.start}-${styleGroup.end} {
|
|
125
161
|
${lengthStyles.map(s => RichTextConverter.fromRangeStylesToInline(s, exemplary, false)).join('\n')}
|
|
126
162
|
}
|
|
127
163
|
`);
|
|
128
164
|
}
|
|
129
|
-
if (color) {
|
|
165
|
+
if ((color === null || color === void 0 ? void 0 : color.value) || (highlight === null || highlight === void 0 ? void 0 : highlight.value)) {
|
|
130
166
|
styleRules[item.layout].push(`
|
|
131
167
|
@supports not (color: oklch(42% 0.3 90 / 1)) {
|
|
132
168
|
.${blockClass} .s-${styleGroup.start}-${styleGroup.end} {
|
|
133
|
-
color: ${color_1.CntrlColor.parse(
|
|
169
|
+
${(color === null || color === void 0 ? void 0 : color.value) ? `color: ${color_1.CntrlColor.parse(color.value).fmt('rgba')};` : ''}
|
|
170
|
+
${(highlight === null || highlight === void 0 ? void 0 : highlight.value) ? `background-color: ${color_1.CntrlColor.parse(highlight.value).fmt('rgba')};` : ''}
|
|
134
171
|
}
|
|
135
172
|
}
|
|
136
173
|
`);
|
|
@@ -138,14 +175,14 @@ class RichTextConverter {
|
|
|
138
175
|
}
|
|
139
176
|
}
|
|
140
177
|
}
|
|
141
|
-
root.push((0, jsx_runtime_1.jsx)("div", { className: blockClass
|
|
178
|
+
root.push((0, jsx_runtime_1.jsx)("div", { className: `${blockClass}${typeClass}`, children: kids }, blockClass));
|
|
142
179
|
}
|
|
143
180
|
}
|
|
144
181
|
const styles = layouts.map(l => `
|
|
145
182
|
${(0, sdk_1.getLayoutMediaQuery)(l.id, layouts)} {
|
|
146
183
|
${styleRules[l.id].join('\n')}
|
|
147
184
|
}
|
|
148
|
-
`).join('\n');
|
|
185
|
+
`).join('\n') + (hasLists ? LIST_STYLES : '');
|
|
149
186
|
return [
|
|
150
187
|
root,
|
|
151
188
|
styles
|
|
@@ -234,6 +271,7 @@ class RichTextConverter {
|
|
|
234
271
|
const { value, name } = draftStyle;
|
|
235
272
|
const map = {
|
|
236
273
|
COLOR: { color: getResolvedValue(value, name) },
|
|
274
|
+
HIGHLIGHT: { 'background-color': getResolvedValue(value, name) },
|
|
237
275
|
TYPEFACE: { 'font-family': `${(0, getFontFamilyValue_1.getFontFamilyValue)(value)}` },
|
|
238
276
|
FONTSTYLE: value ? Object.assign({}, exports.FontStyles[value]) : {},
|
|
239
277
|
FONTWEIGHT: { 'font-weight': value },
|
|
@@ -270,7 +308,7 @@ function getScaledValue(value, exemplary, isLayoutDefined) {
|
|
|
270
308
|
return !isLayoutDefined ? `${ratio * 100}vw` : `${ratio * exemplary}px`;
|
|
271
309
|
}
|
|
272
310
|
function getResolvedValue(value, name) {
|
|
273
|
-
if (name !== 'COLOR')
|
|
311
|
+
if (name !== 'COLOR' && name !== 'HIGHLIGHT')
|
|
274
312
|
return value;
|
|
275
313
|
return value ? color_1.CntrlColor.parse(value).toCss() : value;
|
|
276
314
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getVimeoEmbedUrl = void 0;
|
|
4
|
+
/** The player's url with how the layout plays the video, and none of Vimeo's own title, byline or portrait. */
|
|
5
|
+
function getVimeoEmbedUrl(url, layoutParams) {
|
|
6
|
+
const validURL = new URL(url);
|
|
7
|
+
validURL.searchParams.append('controls', String(layoutParams.controls));
|
|
8
|
+
validURL.searchParams.append('autoplay', String(layoutParams.play === 'auto'));
|
|
9
|
+
validURL.searchParams.append('muted', String(layoutParams.muted));
|
|
10
|
+
validURL.searchParams.append('loop', String(layoutParams.loop));
|
|
11
|
+
validURL.searchParams.append('pip', String(layoutParams.pictureInPicture));
|
|
12
|
+
validURL.searchParams.append('title', '0');
|
|
13
|
+
validURL.searchParams.append('byline', '0');
|
|
14
|
+
validURL.searchParams.append('portrait', '0');
|
|
15
|
+
validURL.searchParams.append('autopause', 'false');
|
|
16
|
+
return validURL.href;
|
|
17
|
+
}
|
|
18
|
+
exports.getVimeoEmbedUrl = getVimeoEmbedUrl;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0-0",
|
|
4
4
|
"description": "SDK for Next.js",
|
|
5
5
|
"author": "arsen@momdesign.nyc",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@cntrl-site/color": "^1.0.0",
|
|
34
34
|
"@cntrl-site/components": "^1.0.95",
|
|
35
35
|
"@cntrl-site/effects": "^1.4.2",
|
|
36
|
-
"@cntrl-site/sdk": "^1.
|
|
36
|
+
"@cntrl-site/sdk": "^1.29.0-0",
|
|
37
37
|
"@types/vimeo__player": "^2.18.0",
|
|
38
38
|
"@vimeo/player": "^2.25.0",
|
|
39
39
|
"franc-min": "^5.0.0",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getLayoutStyles, ImageStructuredBlock } from '@cntrl-site/sdk';
|
|
2
|
+
import { FC, useId } from 'react';
|
|
3
|
+
import JSXStyle from 'styled-jsx/style';
|
|
4
|
+
import { useCntrlContext } from '../../../provider/useCntrlContext';
|
|
5
|
+
import { textStylesRule } from '../textStylesRule';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
blockId: string;
|
|
9
|
+
caption: string;
|
|
10
|
+
layoutParams: ImageStructuredBlock['layoutParams'];
|
|
11
|
+
readingWidth: Record<string, number>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const CAPTION_GAP_PX = 5;
|
|
15
|
+
|
|
16
|
+
/** The caption under an image: as wide as the reading column, or as the image where it is narrower, and centered under it. */
|
|
17
|
+
export const BlockCaption: FC<Props> = ({ blockId, caption, layoutParams, readingWidth }) => {
|
|
18
|
+
const reactId = useId();
|
|
19
|
+
const { layouts } = useCntrlContext();
|
|
20
|
+
const layoutValues: Record<string, any>[] = [layoutParams, readingWidth];
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<div className={`structured-caption-${blockId}`}>{caption}</div>
|
|
24
|
+
<JSXStyle id={`${reactId}-caption-${blockId}`}>{`
|
|
25
|
+
.structured-caption-${blockId} {
|
|
26
|
+
margin-left: auto;
|
|
27
|
+
margin-right: auto;
|
|
28
|
+
white-space: pre-wrap;
|
|
29
|
+
overflow-wrap: break-word;
|
|
30
|
+
}
|
|
31
|
+
${getLayoutStyles(layouts, layoutValues, ([params, width], exemplary) => (`
|
|
32
|
+
.structured-caption-${blockId} {
|
|
33
|
+
width: ${width !== undefined ? `min(${width * 100}vw, 100%)` : '100%'};
|
|
34
|
+
margin-top: ${CAPTION_GAP_PX / exemplary * 100}vw;
|
|
35
|
+
}
|
|
36
|
+
${params?.captionStyles ? textStylesRule(`.structured-caption-${blockId}`, params.captionStyles) : ''}
|
|
37
|
+
`))}
|
|
38
|
+
`}
|
|
39
|
+
</JSXStyle>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
};
|