@cntrl-site/sdk-nextjs 0.2.13 → 0.3.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/.idea/inspectionProfiles/Project_Default.xml +15 -0
- package/lib/components/Article.js +2 -2
- package/lib/components/Item.js +4 -2
- package/lib/components/Page.js +4 -1
- package/lib/components/Section.js +3 -1
- package/lib/components/items/ImageItem.js +5 -1
- package/lib/components/items/RectangleItem.js +5 -1
- package/lib/components/items/RichTextItem.js +12 -4
- package/lib/components/items/VideoItem.js +26 -22
- package/lib/components/items/VimeoEmbed.js +3 -1
- package/lib/components/items/YoutubeEmbed.js +3 -1
- package/lib/index.js +4 -4
- package/lib/provider/CntrlSdkContext.js +7 -0
- package/lib/utils/{RichTextConverter.js → RichTextConverter/RichTextConverter.js} +3 -3
- package/lib/utils/generateTypePresetStyles/generateTypePresetStyles.js +27 -0
- package/package.json +5 -2
- package/src/components/Article.tsx +4 -5
- package/src/components/Item.tsx +4 -4
- package/src/components/Page.tsx +7 -1
- package/src/components/Section.tsx +4 -3
- package/src/components/items/ImageItem.tsx +15 -11
- package/src/components/items/RectangleItem.tsx +12 -8
- package/src/components/items/RichTextItem.tsx +22 -11
- package/src/components/items/VideoItem.tsx +31 -29
- package/src/components/items/VimeoEmbed.tsx +4 -2
- package/src/components/items/YoutubeEmbed.tsx +3 -1
- package/src/index.ts +2 -3
- package/src/provider/CntrlSdkContext.ts +13 -2
- package/src/utils/{RichTextConverter.tsx → RichTextConverter/RichTextConverter.tsx} +7 -6
- package/src/utils/generateTypePresetStyles/__mock__/layoutsMock.ts +37 -0
- package/src/utils/generateTypePresetStyles/__mock__/presetMock.ts +49 -0
- package/src/utils/generateTypePresetStyles/generateTypePresetStyles.test.ts +49 -0
- package/src/utils/generateTypePresetStyles/generateTypePresetStyles.ts +25 -0
- package/src/utils/getInvertedRanges.ts +43 -0
- package/.idea/workspace.xml +0 -293
- package/cntrl-site-sdk-nextjs-0.2.13.tgz +0 -0
|
@@ -3,39 +3,41 @@ import { TVideoItem } from '@cntrl-site/sdk';
|
|
|
3
3
|
import { ItemProps } from '../Item';
|
|
4
4
|
import { LinkWrapper } from '../LinkWrapper';
|
|
5
5
|
import { getLayoutStyles } from '@cntrl-site/sdk';
|
|
6
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
6
7
|
|
|
7
|
-
export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
export const VideoItem: FC<ItemProps<TVideoItem>> = ({ item }) => {
|
|
9
|
+
const { layouts } = useCntrlContext();
|
|
10
|
+
return (
|
|
11
|
+
<LinkWrapper url={item.link?.url}>
|
|
10
12
|
<div className={`video-wrapper-${item.id}`}>
|
|
11
13
|
<video autoPlay muted loop playsInline className="video">
|
|
12
14
|
<source src={item.commonParams.url} />
|
|
13
15
|
</video>
|
|
14
16
|
</div>
|
|
15
17
|
<style jsx>{`
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
${getLayoutStyles(layouts, [item.layoutParams], ([{ strokeColor, radius, strokeWidth, opacity }]) => (`
|
|
19
|
+
.video-wrapper-${item.id} {
|
|
20
|
+
position: absolute;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
width: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
border-style: solid;
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
border-color: ${strokeColor};
|
|
27
|
+
border-radius: ${radius * 100}vw;
|
|
28
|
+
border-width: ${strokeWidth * 100}vw;
|
|
29
|
+
opacity: ${opacity};
|
|
30
|
+
}`
|
|
31
|
+
))
|
|
32
|
+
}
|
|
33
|
+
.video {
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
opacity: 1;
|
|
37
|
+
object-fit: cover;
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
}
|
|
40
|
+
`}</style>
|
|
41
|
+
</LinkWrapper>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -3,8 +3,10 @@ import { getLayoutStyles } from '@cntrl-site/sdk';
|
|
|
3
3
|
import { TVimeoEmbedItem } from '@cntrl-site/core';
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
5
|
import { LinkWrapper } from '../LinkWrapper';
|
|
6
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
6
7
|
|
|
7
|
-
export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item
|
|
8
|
+
export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item }) => {
|
|
9
|
+
const { layouts } = useCntrlContext();
|
|
8
10
|
const { autoplay, controls, loop, muted, pictureInPicture, url } = item.commonParams;
|
|
9
11
|
const getValidVimeoUrl = (url: string): string => {
|
|
10
12
|
const validURL = new URL(url);
|
|
@@ -53,5 +55,5 @@ export const VimeoEmbedItem: FC<ItemProps<TVimeoEmbedItem>> = ({ item, layouts }
|
|
|
53
55
|
}
|
|
54
56
|
`}</style>
|
|
55
57
|
</LinkWrapper>
|
|
56
|
-
)
|
|
58
|
+
);
|
|
57
59
|
};
|
|
@@ -4,8 +4,10 @@ import { TYoutubeEmbedItem } from '@cntrl-site/core';
|
|
|
4
4
|
import { ItemProps } from '../Item';
|
|
5
5
|
import { LinkWrapper } from '../LinkWrapper';
|
|
6
6
|
import { getYoutubeId } from '../../utils/getValidYoutubeUrl';
|
|
7
|
+
import { useCntrlContext } from '../../provider/useCntrlContext';
|
|
7
8
|
|
|
8
|
-
export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item
|
|
9
|
+
export const YoutubeEmbedItem: FC<ItemProps<TYoutubeEmbedItem>> = ({ item }) => {
|
|
10
|
+
const { layouts } = useCntrlContext();
|
|
9
11
|
const { autoplay, controls, url } = item.commonParams;
|
|
10
12
|
|
|
11
13
|
const getValidYoutubeUrl = (url: string): string => {
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
export { cntrlSdkContext } from './provider/defaultContext';
|
|
2
2
|
|
|
3
3
|
export * from '@cntrl-site/sdk';
|
|
4
4
|
|
|
5
|
-
export { RichTextConverter } from './utils/RichTextConverter';
|
|
5
|
+
export { RichTextConverter } from './utils/RichTextConverter/RichTextConverter';
|
|
6
6
|
export { Page } from './components/Page';
|
|
7
7
|
export { CNTRLHead as Head } from './components/Head';
|
|
8
8
|
export { Article } from './components/Article';
|
|
@@ -19,4 +19,3 @@ export { YoutubeEmbedItem } from './components/items/YoutubeEmbed';
|
|
|
19
19
|
// custom items
|
|
20
20
|
export { CntrlProvider } from './provider/CntrlProvider';
|
|
21
21
|
export type { CustomItemComponent } from './provider/CustomItemTypes';
|
|
22
|
-
export const CustomItems = cntrlSdkContext.customItems;
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { CustomItemRegistry } from './CustomItemRegistry';
|
|
2
|
+
import { TLayout, TTypePresets } from '@cntrl-site/sdk';
|
|
2
3
|
|
|
3
4
|
export class CntrlSdkContext {
|
|
5
|
+
public typePresets?: TTypePresets;
|
|
6
|
+
public layouts: TLayout[] = [];
|
|
4
7
|
constructor(
|
|
5
|
-
public readonly customItems: CustomItemRegistry
|
|
6
|
-
) {
|
|
8
|
+
public readonly customItems: CustomItemRegistry,
|
|
9
|
+
) {}
|
|
10
|
+
|
|
11
|
+
setTypePresets(typePresets: TTypePresets) {
|
|
12
|
+
this.typePresets = typePresets;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
setLayouts(layouts: TLayout[]) {
|
|
16
|
+
this.layouts = layouts;
|
|
17
|
+
}
|
|
7
18
|
}
|
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
TRichTextItem,
|
|
8
8
|
VerticalAlign
|
|
9
9
|
} from '@cntrl-site/sdk';
|
|
10
|
-
import { LinkWrapper } from '
|
|
10
|
+
import { LinkWrapper } from '../../components/LinkWrapper';
|
|
11
11
|
|
|
12
12
|
interface StyleGroup {
|
|
13
13
|
start: number;
|
|
14
14
|
end: number;
|
|
15
|
-
styles:
|
|
15
|
+
styles: Style[];
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
interface EntitiesGroup {
|
|
@@ -22,7 +22,7 @@ interface EntitiesGroup {
|
|
|
22
22
|
end: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
interface
|
|
25
|
+
interface Style {
|
|
26
26
|
name: string;
|
|
27
27
|
value?: string;
|
|
28
28
|
}
|
|
@@ -36,7 +36,8 @@ export const FontStyles: Record<string, Record<string, string>> = {
|
|
|
36
36
|
export class RichTextConverter {
|
|
37
37
|
toHtml(
|
|
38
38
|
richText: TRichTextItem,
|
|
39
|
-
layouts: TLayout[]
|
|
39
|
+
layouts: TLayout[],
|
|
40
|
+
hasPreset: boolean
|
|
40
41
|
): [ReactNode[], string] {
|
|
41
42
|
const { text: rawText, blocks = [] } = richText.commonParams;
|
|
42
43
|
const text = Array.from(rawText); // because of emoji
|
|
@@ -86,7 +87,7 @@ export class RichTextConverter {
|
|
|
86
87
|
.${blockClass} {
|
|
87
88
|
display: ${group.some(g => g.layout === l.id) ? 'block' : 'none'};
|
|
88
89
|
text-align: ${ta};
|
|
89
|
-
line-height: 0;
|
|
90
|
+
${hasPreset ? '' : 'line-height: 0;'}
|
|
90
91
|
white-space: normal;
|
|
91
92
|
overflow-wrap: break-word;
|
|
92
93
|
}
|
|
@@ -215,7 +216,7 @@ export class RichTextConverter {
|
|
|
215
216
|
return entitiesGroups;
|
|
216
217
|
}
|
|
217
218
|
|
|
218
|
-
private static fromDraftToInline(draftStyle:
|
|
219
|
+
private static fromDraftToInline(draftStyle: Style): string {
|
|
219
220
|
const { value, name } = draftStyle;
|
|
220
221
|
const map: Record<string, Record<string, string | undefined>> = {
|
|
221
222
|
'COLOR': { 'color': value },
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { TLayout } from '@cntrl-site/sdk';
|
|
2
|
+
|
|
3
|
+
const sampleGrid = {
|
|
4
|
+
columnsAmount: 0,
|
|
5
|
+
beatHeight: 0,
|
|
6
|
+
beatMultiplier: 1,
|
|
7
|
+
maxWidth: 0,
|
|
8
|
+
columnWidth: 0.01,
|
|
9
|
+
gutterWidth: 0.001
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const layoutsMock: TLayout[] = [
|
|
13
|
+
{
|
|
14
|
+
id: 'tablet',
|
|
15
|
+
exemplary: 768,
|
|
16
|
+
startsWith: 768,
|
|
17
|
+
title: 'Tablet',
|
|
18
|
+
icon: 'any',
|
|
19
|
+
grid: sampleGrid
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'desktop',
|
|
23
|
+
exemplary: 1440,
|
|
24
|
+
startsWith: 1024,
|
|
25
|
+
title: 'Desktop',
|
|
26
|
+
icon: 'any',
|
|
27
|
+
grid: sampleGrid
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'mobile',
|
|
31
|
+
exemplary: 375,
|
|
32
|
+
startsWith: 0,
|
|
33
|
+
title: 'Mobile',
|
|
34
|
+
icon: 'any',
|
|
35
|
+
grid: sampleGrid
|
|
36
|
+
}
|
|
37
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AllowedTags, TTypePresets } from '@cntrl-site/sdk';
|
|
2
|
+
|
|
3
|
+
const layoutParams = {
|
|
4
|
+
'desktop': {
|
|
5
|
+
fontSize: 0.011111,
|
|
6
|
+
lineHeight: 0.013888,
|
|
7
|
+
wordSpacing: 0.0006944,
|
|
8
|
+
letterSpacing: 0.0006944,
|
|
9
|
+
color: 'rgba(0, 0, 0, 1)'
|
|
10
|
+
},
|
|
11
|
+
'tablet': {
|
|
12
|
+
fontSize: 0.02604,
|
|
13
|
+
lineHeight: 0.03125,
|
|
14
|
+
wordSpacing: 0,
|
|
15
|
+
letterSpacing: 0,
|
|
16
|
+
color: 'rgba(0, 0, 0, 1)'
|
|
17
|
+
},
|
|
18
|
+
'mobile': {
|
|
19
|
+
fontSize: 0.053333,
|
|
20
|
+
lineHeight: 0.064,
|
|
21
|
+
wordSpacing: 0.008,
|
|
22
|
+
letterSpacing: 0.008,
|
|
23
|
+
color: 'rgba(0, 0, 0, 1)'
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const presetMock: TTypePresets = {
|
|
28
|
+
id: 'presetId',
|
|
29
|
+
presets: [
|
|
30
|
+
{
|
|
31
|
+
id: 'heading01',
|
|
32
|
+
fontFamily: 'Aeonik',
|
|
33
|
+
fontWeight: '400',
|
|
34
|
+
fontStyle: '',
|
|
35
|
+
name: 'Heading',
|
|
36
|
+
tag: AllowedTags.h1,
|
|
37
|
+
layoutParams
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'heading02',
|
|
41
|
+
fontFamily: 'Aeonik',
|
|
42
|
+
fontWeight: '400',
|
|
43
|
+
fontStyle: 'italic',
|
|
44
|
+
name: 'Heading',
|
|
45
|
+
tag: AllowedTags.h1,
|
|
46
|
+
layoutParams
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { generateTypePresetStyles } from './generateTypePresetStyles';
|
|
2
|
+
import { presetMock } from './__mock__/presetMock';
|
|
3
|
+
import { layoutsMock } from './__mock__/layoutsMock';
|
|
4
|
+
|
|
5
|
+
describe('generateTypePresetStyles', () => {
|
|
6
|
+
it('generates blocks of styles', () => {
|
|
7
|
+
const styles = generateTypePresetStyles(presetMock, layoutsMock);
|
|
8
|
+
const expectedMedia = (id: string) =>
|
|
9
|
+
`@media (min-width: 0px) and (max-width: 767px) {
|
|
10
|
+
.cntrl-preset-${id} {
|
|
11
|
+
font-size: 5.3332999999999995vw;
|
|
12
|
+
line-height: 6.4vw;
|
|
13
|
+
letter-spacing: 0.8vw;
|
|
14
|
+
word-spacing: 0.8vw;
|
|
15
|
+
color: rgba(0, 0, 0, 1);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
@media (min-width: 768px) and (max-width: 1023px) {
|
|
19
|
+
.cntrl-preset-${id} {
|
|
20
|
+
font-size: 2.604vw;
|
|
21
|
+
line-height: 3.125vw;
|
|
22
|
+
letter-spacing: 0vw;
|
|
23
|
+
word-spacing: 0vw;
|
|
24
|
+
color: rgba(0, 0, 0, 1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
@media (min-width: 1024px) {
|
|
28
|
+
.cntrl-preset-${id} {
|
|
29
|
+
font-size: 1.1111vw;
|
|
30
|
+
line-height: 1.3888vw;
|
|
31
|
+
letter-spacing: 0.06944vw;
|
|
32
|
+
word-spacing: 0.06944vw;
|
|
33
|
+
color: rgba(0, 0, 0, 1);
|
|
34
|
+
}
|
|
35
|
+
}`;
|
|
36
|
+
expect(styles).toBe(
|
|
37
|
+
`.cntrl-preset-heading01 {
|
|
38
|
+
font-family: Aeonik;
|
|
39
|
+
font-weight: 400;
|
|
40
|
+
}
|
|
41
|
+
${expectedMedia('heading01')}
|
|
42
|
+
.cntrl-preset-heading02 {
|
|
43
|
+
font-family: Aeonik;
|
|
44
|
+
font-style: italic;
|
|
45
|
+
font-weight: 400;
|
|
46
|
+
}
|
|
47
|
+
${expectedMedia('heading02')}`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getLayoutMediaQuery, TLayout, TTypePresets } from '@cntrl-site/sdk';
|
|
2
|
+
|
|
3
|
+
const reEmptyLines = /^\s*\n/gm;
|
|
4
|
+
|
|
5
|
+
export function generateTypePresetStyles(preset: TTypePresets, layouts: TLayout[]): string {
|
|
6
|
+
const sorted = layouts.slice().sort((a, b) => a.startsWith - b.startsWith);
|
|
7
|
+
const stylesStr = (`
|
|
8
|
+
${preset.presets.map(p => (`
|
|
9
|
+
.cntrl-preset-${p.id} {
|
|
10
|
+
font-family: ${p.fontFamily};
|
|
11
|
+
${p.fontStyle.length > 0 ? `font-style: ${p.fontStyle};` : ''}
|
|
12
|
+
font-weight: ${p.fontWeight};
|
|
13
|
+
}
|
|
14
|
+
${sorted.map(l => (`
|
|
15
|
+
${getLayoutMediaQuery(l.id, sorted)} {
|
|
16
|
+
.cntrl-preset-${p.id} {
|
|
17
|
+
font-size: ${p.layoutParams[l.id].fontSize * 100}vw;
|
|
18
|
+
line-height: ${p.layoutParams[l.id].lineHeight * 100}vw;
|
|
19
|
+
letter-spacing: ${p.layoutParams[l.id].letterSpacing * 100}vw;
|
|
20
|
+
word-spacing: ${p.layoutParams[l.id].wordSpacing * 100}vw;
|
|
21
|
+
color: ${p.layoutParams[l.id].color};
|
|
22
|
+
}
|
|
23
|
+
}`)).join('\n')}`)).join('\n')}`);
|
|
24
|
+
return stylesStr.replace(reEmptyLines, '');
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
interface Range {
|
|
2
|
+
start: number;
|
|
3
|
+
end: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param length
|
|
8
|
+
* @param ranges - SORTED array of ranges [start, end)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export function getInvertedRanges(length: number, ranges: Range[]): Range[] {
|
|
12
|
+
if (ranges.length === 0) {
|
|
13
|
+
return [{
|
|
14
|
+
start: 0,
|
|
15
|
+
end: length
|
|
16
|
+
}];
|
|
17
|
+
}
|
|
18
|
+
const inverted: Range[] = [];
|
|
19
|
+
if (ranges[0].start > 0) {
|
|
20
|
+
inverted.push({
|
|
21
|
+
start: 0,
|
|
22
|
+
end: ranges[0].start
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
for (let i = 0; i < ranges.length - 1; i += 1) {
|
|
26
|
+
const left = ranges[i];
|
|
27
|
+
const right = ranges[i + 1];
|
|
28
|
+
if (right.start - left.end > 0) {
|
|
29
|
+
inverted.push({
|
|
30
|
+
start: left.end,
|
|
31
|
+
end: right.start
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const lastRange = ranges[ranges.length - 1];
|
|
36
|
+
if (lastRange.end < length) {
|
|
37
|
+
inverted.push({
|
|
38
|
+
start: lastRange.end,
|
|
39
|
+
end: length
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return inverted;
|
|
43
|
+
}
|