@cntrl-site/sdk 1.2.2 → 1.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/lib/Client/Client.js +7 -32
- package/lib/index.js +18 -16
- package/lib/schemas/article/Article.schema.js +9 -0
- package/lib/schemas/article/Item.schema.js +137 -0
- package/lib/schemas/article/ItemArea.schema.js +16 -0
- package/lib/schemas/article/ItemBase.schema.js +20 -0
- package/lib/schemas/article/ItemState.schema.js +52 -0
- package/lib/schemas/article/RichTextItem.schema.js +60 -0
- package/lib/schemas/article/Section.schema.js +20 -0
- package/lib/schemas/keyframe/Keyframes.schema.js +114 -0
- package/lib/schemas/project/Layout.schema.js +10 -0
- package/lib/schemas/project/Project.schema.js +48 -0
- package/lib/types/article/Article.js +2 -0
- package/lib/types/article/ArticleItemType.js +13 -0
- package/lib/types/article/Item.js +3 -0
- package/lib/types/article/ItemArea.js +21 -0
- package/lib/types/article/ItemState.js +3 -0
- package/lib/types/article/RichText.js +27 -0
- package/lib/types/article/Section.js +8 -0
- package/lib/types/keyframe/Keyframe.js +20 -0
- package/lib/types/project/Fonts.js +11 -0
- package/lib/types/project/Layout.js +2 -0
- package/lib/types/project/Meta.js +2 -0
- package/lib/types/project/Page.js +2 -0
- package/lib/types/project/Project.js +2 -0
- package/lib/utils.js +15 -1
- package/package.json +3 -3
- package/src/Client/Client.test.ts +1 -5
- package/src/Client/Client.ts +18 -51
- package/src/Client/__mock__/articleMock.ts +2 -2
- package/src/Client/__mock__/keyframesMock.ts +2 -3
- package/src/Client/__mock__/projectMock.ts +2 -7
- package/src/FontFaceGenerator/FontFaceGenerator.test.ts +2 -4
- package/src/FontFaceGenerator/FontFaceGenerator.ts +2 -2
- package/src/cli.ts +2 -2
- package/src/index.ts +25 -2
- package/src/schemas/article/Article.schema.ts +7 -0
- package/src/schemas/article/Item.schema.ts +175 -0
- package/src/schemas/article/ItemArea.schema.ts +14 -0
- package/src/schemas/article/ItemBase.schema.ts +20 -0
- package/src/schemas/article/ItemState.schema.ts +62 -0
- package/src/schemas/article/RichTextItem.schema.ts +68 -0
- package/src/schemas/article/Section.schema.ts +19 -0
- package/src/schemas/keyframe/Keyframes.schema.ts +128 -0
- package/src/schemas/project/Layout.schema.ts +8 -0
- package/src/schemas/project/Project.schema.ts +48 -0
- package/src/types/article/Article.ts +6 -0
- package/src/types/article/ArticleItemType.ts +9 -0
- package/src/types/article/Item.ts +150 -0
- package/src/types/article/ItemArea.ts +29 -0
- package/src/types/article/ItemState.ts +64 -0
- package/src/types/article/RichText.ts +46 -0
- package/src/types/article/Section.ts +22 -0
- package/src/types/keyframe/Keyframe.ts +102 -0
- package/src/types/project/Fonts.ts +25 -0
- package/src/types/project/Layout.ts +6 -0
- package/src/types/project/Meta.ts +10 -0
- package/src/types/project/Page.ts +13 -0
- package/src/types/project/Project.ts +19 -0
- package/src/utils.ts +16 -2
- package/src/Client/__mock__/typePresetsMock.ts +0 -6
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TextDecoration = exports.VerticalAlign = exports.TextTransform = exports.TextAlign = void 0;
|
|
4
|
+
var TextAlign;
|
|
5
|
+
(function (TextAlign) {
|
|
6
|
+
TextAlign["Left"] = "left";
|
|
7
|
+
TextAlign["Right"] = "right";
|
|
8
|
+
TextAlign["Center"] = "center";
|
|
9
|
+
TextAlign["Justify"] = "justify";
|
|
10
|
+
})(TextAlign = exports.TextAlign || (exports.TextAlign = {}));
|
|
11
|
+
var TextTransform;
|
|
12
|
+
(function (TextTransform) {
|
|
13
|
+
TextTransform["None"] = "none";
|
|
14
|
+
TextTransform["Uppercase"] = "uppercase";
|
|
15
|
+
TextTransform["Lowercase"] = "lowercase";
|
|
16
|
+
})(TextTransform = exports.TextTransform || (exports.TextTransform = {}));
|
|
17
|
+
var VerticalAlign;
|
|
18
|
+
(function (VerticalAlign) {
|
|
19
|
+
VerticalAlign["Super"] = "super";
|
|
20
|
+
VerticalAlign["Sub"] = "sub";
|
|
21
|
+
VerticalAlign["Unset"] = "unset";
|
|
22
|
+
})(VerticalAlign = exports.VerticalAlign || (exports.VerticalAlign = {}));
|
|
23
|
+
var TextDecoration;
|
|
24
|
+
(function (TextDecoration) {
|
|
25
|
+
TextDecoration["Underline"] = "underline";
|
|
26
|
+
TextDecoration["None"] = "none";
|
|
27
|
+
})(TextDecoration = exports.TextDecoration || (exports.TextDecoration = {}));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SectionHeightMode = void 0;
|
|
4
|
+
var SectionHeightMode;
|
|
5
|
+
(function (SectionHeightMode) {
|
|
6
|
+
SectionHeightMode["ControlUnits"] = "control-units";
|
|
7
|
+
SectionHeightMode["ViewportHeightUnits"] = "viewport-height-units";
|
|
8
|
+
})(SectionHeightMode = exports.SectionHeightMode || (exports.SectionHeightMode = {}));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KeyframeType = void 0;
|
|
4
|
+
var KeyframeType;
|
|
5
|
+
(function (KeyframeType) {
|
|
6
|
+
KeyframeType["Dimensions"] = "dimensions";
|
|
7
|
+
KeyframeType["Position"] = "position";
|
|
8
|
+
KeyframeType["Rotation"] = "rotation";
|
|
9
|
+
KeyframeType["BorderRadius"] = "border-radius";
|
|
10
|
+
KeyframeType["BorderWidth"] = "border-width";
|
|
11
|
+
KeyframeType["Color"] = "color";
|
|
12
|
+
KeyframeType["BorderColor"] = "border-color";
|
|
13
|
+
KeyframeType["Opacity"] = "opacity";
|
|
14
|
+
KeyframeType["Scale"] = "scale";
|
|
15
|
+
KeyframeType["TextColor"] = "text-color";
|
|
16
|
+
KeyframeType["LetterSpacing"] = "letter-spacing";
|
|
17
|
+
KeyframeType["WordSpacing"] = "word-spacing";
|
|
18
|
+
KeyframeType["Blur"] = "blur";
|
|
19
|
+
KeyframeType["BackdropBlur"] = "backdrop-blur";
|
|
20
|
+
})(KeyframeType = exports.KeyframeType || (exports.KeyframeType = {}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FontFileTypes = void 0;
|
|
4
|
+
var FontFileTypes;
|
|
5
|
+
(function (FontFileTypes) {
|
|
6
|
+
FontFileTypes["OTF"] = "otf";
|
|
7
|
+
FontFileTypes["TTF"] = "ttf";
|
|
8
|
+
FontFileTypes["WOFF"] = "woff";
|
|
9
|
+
FontFileTypes["WOFF2"] = "woff2";
|
|
10
|
+
FontFileTypes["EOT"] = "eot";
|
|
11
|
+
})(FontFileTypes = exports.FontFileTypes || (exports.FontFileTypes = {}));
|
package/lib/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getLayoutStyles = void 0;
|
|
3
|
+
exports.getLayoutMediaQuery = exports.getLayoutStyles = void 0;
|
|
4
4
|
function getLayoutStyles(layouts, layoutValues, mapToStyles) {
|
|
5
5
|
const mediaQueries = layouts
|
|
6
6
|
.sort((a, b) => a.startsWith - b.startsWith)
|
|
@@ -15,3 +15,17 @@ function getLayoutStyles(layouts, layoutValues, mapToStyles) {
|
|
|
15
15
|
return mediaQueries;
|
|
16
16
|
}
|
|
17
17
|
exports.getLayoutStyles = getLayoutStyles;
|
|
18
|
+
function getLayoutMediaQuery(layoutId, layouts) {
|
|
19
|
+
const sorted = layouts.slice().sort((a, b) => a.startsWith - b.startsWith);
|
|
20
|
+
const layoutIndex = sorted.findIndex(l => l.id === layoutId);
|
|
21
|
+
if (layoutIndex === -1) {
|
|
22
|
+
throw new Error(`No layout was found by the given id #${layoutId}`);
|
|
23
|
+
}
|
|
24
|
+
const current = sorted[layoutIndex];
|
|
25
|
+
const next = sorted[layoutIndex + 1];
|
|
26
|
+
if (!next) {
|
|
27
|
+
return `@media (min-width: ${current.startsWith}px)`;
|
|
28
|
+
}
|
|
29
|
+
return `@media (min-width: ${current.startsWith}px) and (max-width: ${next.startsWith - 1}px)`;
|
|
30
|
+
}
|
|
31
|
+
exports.getLayoutMediaQuery = getLayoutMediaQuery;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Generic SDK for use in public websites.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"lib": "lib"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@cntrl-site/core": "^1.31.1",
|
|
31
30
|
"@types/ejs": "^3.1.2",
|
|
32
31
|
"@types/isomorphic-fetch": "^0.0.36",
|
|
33
32
|
"commander": "^10.0.1",
|
|
@@ -35,7 +34,8 @@
|
|
|
35
34
|
"ejs": "^3.1.9",
|
|
36
35
|
"isomorphic-fetch": "^3.0.0",
|
|
37
36
|
"ts-node": "^10.9.1",
|
|
38
|
-
"url": "^0.11.0"
|
|
37
|
+
"url": "^0.11.0",
|
|
38
|
+
"zod": "^3.22.4"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@tsconfig/node16": "^1.0.3",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Client } from './Client';
|
|
2
2
|
import { projectMock } from './__mock__/projectMock';
|
|
3
3
|
import { articleMock } from './__mock__/articleMock';
|
|
4
|
-
import { typePresetsMock } from './__mock__/typePresetsMock';
|
|
5
4
|
import { keyframesMock } from './__mock__/keyframesMock';
|
|
6
5
|
|
|
7
6
|
describe('Client', () => {
|
|
@@ -26,7 +25,6 @@ describe('Client', () => {
|
|
|
26
25
|
const API_BASE_URL = 'api-test.cntrl.site';
|
|
27
26
|
const fetchesMap = {
|
|
28
27
|
[`https://${API_BASE_URL}/projects/${projectId}`]: projectMock,
|
|
29
|
-
[`https://${API_BASE_URL}/projects/${projectId}/type-presets`]: typePresetsMock,
|
|
30
28
|
[`https://${API_BASE_URL}/projects/${projectId}/articles/articleId`]: {
|
|
31
29
|
article: articleMock,
|
|
32
30
|
keyframes: keyframesMock
|
|
@@ -45,10 +43,9 @@ describe('Client', () => {
|
|
|
45
43
|
};
|
|
46
44
|
const client = new Client(apiUrl, fetch);
|
|
47
45
|
const pageData = await client.getPageData('/');
|
|
48
|
-
expect(fetchCalledTimes).toBe(
|
|
46
|
+
expect(fetchCalledTimes).toBe(2);
|
|
49
47
|
expect(pageData.project).toEqual(projectMock);
|
|
50
48
|
expect(pageData.article).toEqual(articleMock);
|
|
51
|
-
expect(pageData.typePresets).toEqual(typePresetsMock);
|
|
52
49
|
expect(pageData.keyframes).toEqual(keyframesMock);
|
|
53
50
|
expect(pageData.meta).toEqual({
|
|
54
51
|
description: 'page description',
|
|
@@ -64,7 +61,6 @@ describe('Client', () => {
|
|
|
64
61
|
const API_BASE_URL = 'api-test.cntrl.site';
|
|
65
62
|
const fetchesMap = {
|
|
66
63
|
[`https://${API_BASE_URL}/projects/${projectId}`]: projectMock,
|
|
67
|
-
[`https://${API_BASE_URL}/projects/${projectId}/type-presets`]: typePresetsMock,
|
|
68
64
|
[`https://${API_BASE_URL}/projects/${projectId}/articles/articleId2`]: {
|
|
69
65
|
article: articleMock,
|
|
70
66
|
keyframes: keyframesMock
|
package/src/Client/Client.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TProject,
|
|
3
|
-
ProjectSchema,
|
|
4
|
-
TArticle,
|
|
5
|
-
ArticleSchema,
|
|
6
|
-
TMeta,
|
|
7
|
-
TPageMeta,
|
|
8
|
-
TTypePresets,
|
|
9
|
-
TypePresetsSchema,
|
|
10
|
-
TPage,
|
|
11
|
-
TKeyframeAny,
|
|
12
|
-
KeyframesSchema,
|
|
13
|
-
TLayout
|
|
14
|
-
} from '@cntrl-site/core';
|
|
15
1
|
import fetch from 'isomorphic-fetch';
|
|
16
2
|
import { URL } from 'url';
|
|
3
|
+
import { Meta } from '../types/project/Meta';
|
|
4
|
+
import { Page, PageMeta } from '../types/project/Page';
|
|
5
|
+
import { Project } from '../types/project/Project';
|
|
6
|
+
import { Layout } from '../types/project/Layout';
|
|
7
|
+
import { Article } from '../types/article/Article';
|
|
8
|
+
import { KeyframeAny } from '../types/keyframe/Keyframe';
|
|
9
|
+
import { ArticleSchema } from '../schemas/article/Article.schema';
|
|
10
|
+
import { ProjectSchema } from '../schemas/project/Project.schema';
|
|
11
|
+
import { KeyframesSchema } from '../schemas/keyframe/Keyframes.schema';
|
|
17
12
|
|
|
18
13
|
export class Client {
|
|
19
14
|
private url: URL;
|
|
@@ -30,7 +25,7 @@ export class Client {
|
|
|
30
25
|
}
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
private static getPageMeta(projectMeta:
|
|
28
|
+
private static getPageMeta(projectMeta: Meta, pageMeta: PageMeta): Meta {
|
|
34
29
|
return pageMeta.enabled ? {
|
|
35
30
|
title: pageMeta.title ? pageMeta.title : projectMeta.title ?? '',
|
|
36
31
|
description: pageMeta.description ? pageMeta.description : projectMeta.description ?? '',
|
|
@@ -44,15 +39,11 @@ export class Client {
|
|
|
44
39
|
try {
|
|
45
40
|
const project = await this.fetchProject();
|
|
46
41
|
const articleId = this.findArticleIdByPageSlug(pageSlug, project.pages);
|
|
47
|
-
const
|
|
48
|
-
this.fetchArticle(articleId),
|
|
49
|
-
this.fetchTypePresets()
|
|
50
|
-
]);
|
|
42
|
+
const { article, keyframes } = await this.fetchArticle(articleId);
|
|
51
43
|
const page = project.pages.find(page => page.slug === pageSlug)!;
|
|
52
44
|
const meta = Client.getPageMeta(project.meta, page?.meta!);
|
|
53
45
|
return {
|
|
54
46
|
project,
|
|
55
|
-
typePresets,
|
|
56
47
|
article,
|
|
57
48
|
keyframes,
|
|
58
49
|
meta
|
|
@@ -71,7 +62,7 @@ export class Client {
|
|
|
71
62
|
}
|
|
72
63
|
}
|
|
73
64
|
|
|
74
|
-
async getLayouts(): Promise<
|
|
65
|
+
async getLayouts(): Promise<Layout[]> {
|
|
75
66
|
try {
|
|
76
67
|
const { layouts } = await this.fetchProject();
|
|
77
68
|
return layouts;
|
|
@@ -80,12 +71,7 @@ export class Client {
|
|
|
80
71
|
}
|
|
81
72
|
}
|
|
82
73
|
|
|
83
|
-
async
|
|
84
|
-
const response = await this.fetchTypePresets();
|
|
85
|
-
return response;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private async fetchProject(): Promise<TProject> {
|
|
74
|
+
private async fetchProject(): Promise<Project> {
|
|
89
75
|
const { username: projectId, password: apiKey, origin } = this.url;
|
|
90
76
|
const url = new URL(`/projects/${projectId}`, origin);
|
|
91
77
|
const response = await this.fetchImpl(url.href, {
|
|
@@ -118,25 +104,7 @@ export class Client {
|
|
|
118
104
|
return { article, keyframes };
|
|
119
105
|
}
|
|
120
106
|
|
|
121
|
-
private
|
|
122
|
-
const { username: projectId, password: apiKey, origin } = this.url;
|
|
123
|
-
const url = new URL(`/projects/${projectId}/type-presets`, origin);
|
|
124
|
-
const response = await this.fetchImpl(url.href, {
|
|
125
|
-
headers: {
|
|
126
|
-
Authorization: `Bearer ${apiKey}`
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
if (!response.ok) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
`Failed to fetch type presets for the project with id #${projectId}: ${response.statusText}`
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
const data = await response.json();
|
|
135
|
-
const typePresets = TypePresetsSchema.parse(data);
|
|
136
|
-
return typePresets;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private findArticleIdByPageSlug(slug: string, pages: TPage[]): string {
|
|
107
|
+
private findArticleIdByPageSlug(slug: string, pages: Page[]): string {
|
|
140
108
|
const { username: projectId } = this.url;
|
|
141
109
|
const page = pages.find((page) => page.slug === slug);
|
|
142
110
|
if (!page) {
|
|
@@ -154,11 +122,10 @@ interface FetchImplResponse {
|
|
|
154
122
|
|
|
155
123
|
type FetchImpl = (url: string, init?: RequestInit) => Promise<FetchImplResponse>;
|
|
156
124
|
interface ArticleData {
|
|
157
|
-
article:
|
|
158
|
-
keyframes:
|
|
125
|
+
article: Article;
|
|
126
|
+
keyframes: KeyframeAny[];
|
|
159
127
|
}
|
|
160
128
|
interface CntrlPageData extends ArticleData {
|
|
161
|
-
project:
|
|
162
|
-
|
|
163
|
-
meta: TMeta;
|
|
129
|
+
project: Project;
|
|
130
|
+
meta: Meta;
|
|
164
131
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KeyframeAny, KeyframeType } from '../../types/keyframe/Keyframe';
|
|
2
2
|
|
|
3
|
-
export const keyframesMock:
|
|
3
|
+
export const keyframesMock: KeyframeAny[] = [
|
|
4
4
|
{
|
|
5
5
|
id: 'keyframeId',
|
|
6
|
-
articleId: 'articleId',
|
|
7
6
|
type: KeyframeType.Color,
|
|
8
7
|
position: 100,
|
|
9
8
|
value: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Project } from '../../types/project/Project';
|
|
2
2
|
|
|
3
|
-
export const projectMock:
|
|
3
|
+
export const projectMock: Project = {
|
|
4
4
|
id: 'projectId',
|
|
5
5
|
layouts: [],
|
|
6
6
|
fonts: {
|
|
@@ -20,15 +20,11 @@ export const projectMock: TProject = {
|
|
|
20
20
|
keywords: 'project keywords',
|
|
21
21
|
description: 'project description'
|
|
22
22
|
},
|
|
23
|
-
grid: {
|
|
24
|
-
color: 'rgba(0, 0, 0, 1)'
|
|
25
|
-
},
|
|
26
23
|
pages: [{
|
|
27
24
|
id: 'pageId',
|
|
28
25
|
title: 'Page',
|
|
29
26
|
articleId: 'articleId',
|
|
30
27
|
slug: '/',
|
|
31
|
-
isPublished: true,
|
|
32
28
|
meta: {
|
|
33
29
|
opengraphThumbnail: 'page thumbnail',
|
|
34
30
|
title: 'page title',
|
|
@@ -42,7 +38,6 @@ export const projectMock: TProject = {
|
|
|
42
38
|
title: 'Page 2',
|
|
43
39
|
articleId: 'articleId2',
|
|
44
40
|
slug: '/2',
|
|
45
|
-
isPublished: true,
|
|
46
41
|
meta: {
|
|
47
42
|
opengraphThumbnail: 'page thumbnail',
|
|
48
43
|
title: 'page title',
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { FontFaceGenerator } from './FontFaceGenerator';
|
|
2
|
-
import {
|
|
2
|
+
import { CustomFont, FontFileTypes } from '../types/project/Fonts';
|
|
3
3
|
|
|
4
4
|
describe('FontFaceGenerator', () => {
|
|
5
5
|
it('generates font face with eot', () => {
|
|
6
|
-
const fonts:
|
|
6
|
+
const fonts: CustomFont[] = [
|
|
7
7
|
{
|
|
8
8
|
name: 'Aeonik',
|
|
9
9
|
weight: 400,
|
|
10
10
|
style: 'normal',
|
|
11
|
-
readonly: false,
|
|
12
11
|
files: [
|
|
13
12
|
{
|
|
14
13
|
type: FontFileTypes.EOT,
|
|
@@ -24,7 +23,6 @@ describe('FontFaceGenerator', () => {
|
|
|
24
23
|
name: 'Anek Odia',
|
|
25
24
|
weight: 700,
|
|
26
25
|
style: 'italic',
|
|
27
|
-
readonly: false,
|
|
28
26
|
files: [
|
|
29
27
|
{
|
|
30
28
|
type: FontFileTypes.WOFF,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CustomFont } from '../types/project/Fonts';
|
|
2
2
|
|
|
3
3
|
const FILE_TYPES_MAP: Record<string, string> = {
|
|
4
4
|
ttf: 'truetype',
|
|
@@ -7,7 +7,7 @@ const FILE_TYPES_MAP: Record<string, string> = {
|
|
|
7
7
|
|
|
8
8
|
export class FontFaceGenerator {
|
|
9
9
|
constructor(
|
|
10
|
-
private fonts:
|
|
10
|
+
private fonts: CustomFont[]
|
|
11
11
|
) {}
|
|
12
12
|
|
|
13
13
|
generate(): string {
|
package/src/cli.ts
CHANGED
|
@@ -5,8 +5,8 @@ import path from 'path';
|
|
|
5
5
|
import ejs from 'ejs';
|
|
6
6
|
import { config } from 'dotenv';
|
|
7
7
|
import { program } from 'commander';
|
|
8
|
-
import { TLayout } from '@cntrl-site/core';
|
|
9
8
|
import { Client } from './Client/Client';
|
|
9
|
+
import { Layout } from './types/project/Layout';
|
|
10
10
|
|
|
11
11
|
program
|
|
12
12
|
.command('generate-layouts')
|
|
@@ -38,7 +38,7 @@ program
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
function convertLayouts(layouts:
|
|
41
|
+
function convertLayouts(layouts: Layout[], maxLayoutWidth: number = Number.MAX_SAFE_INTEGER): LayoutRange[] {
|
|
42
42
|
const sorted = layouts.slice().sort((la, lb) => la.startsWith - lb.startsWith);
|
|
43
43
|
const mapped = sorted.map<LayoutRange>((layout, i, ls) => {
|
|
44
44
|
const next = ls[i + 1];
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
+
// logic
|
|
1
2
|
export { Client as CntrlClient } from './Client/Client';
|
|
2
3
|
export { FontFaceGenerator } from './FontFaceGenerator/FontFaceGenerator';
|
|
3
|
-
export { getLayoutStyles } from './utils';
|
|
4
|
-
|
|
4
|
+
export { getLayoutStyles, getLayoutMediaQuery } from './utils';
|
|
5
|
+
|
|
6
|
+
// enums
|
|
7
|
+
export { SectionHeightMode } from './types/article/Section';
|
|
8
|
+
export { TextAlign, TextDecoration, TextTransform, VerticalAlign } from './types/article/RichText';
|
|
9
|
+
export { ArticleItemType } from './types/article/ArticleItemType';
|
|
10
|
+
export { ScaleAnchor, AnchorSide } from './types/article/ItemArea';
|
|
11
|
+
export { KeyframeType } from './types/keyframe/Keyframe';
|
|
12
|
+
|
|
13
|
+
// types
|
|
14
|
+
export type { Article } from './types/article/Article';
|
|
15
|
+
export type { Section, SectionHeight } from './types/article/Section';
|
|
16
|
+
export type {
|
|
17
|
+
Item, ImageItem, ItemAny, CustomItem, ItemCommonParamsMap,
|
|
18
|
+
ItemLayoutParamsMap, RectangleItem, StickyParams, VideoItem, RichTextItem,
|
|
19
|
+
Link, VimeoEmbedItem, YoutubeEmbedItem
|
|
20
|
+
} from './types/article/Item';
|
|
21
|
+
export type { RichTextBlock, RichTextEntity, RichTextStyle } from './types/article/RichText';
|
|
22
|
+
export type { ItemArea } from './types/article/ItemArea';
|
|
23
|
+
export type { ItemState, ItemHoverState, HoverParams } from './types/article/ItemState';
|
|
24
|
+
export type { Layout } from './types/project/Layout';
|
|
25
|
+
export type { Project } from './types/project/Project';
|
|
26
|
+
export type { Meta } from './types/project/Meta';
|
|
27
|
+
export type { KeyframeValueMap, KeyframeAny } from './types/keyframe/Keyframe';
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { z, ZodType } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
CustomItem,
|
|
4
|
+
ImageItem,
|
|
5
|
+
RectangleItem,
|
|
6
|
+
VideoItem,
|
|
7
|
+
VimeoEmbedItem,
|
|
8
|
+
YoutubeEmbedItem
|
|
9
|
+
} from '../../types/article/Item';
|
|
10
|
+
import {
|
|
11
|
+
CustomItemHoverStateParamsSchema,
|
|
12
|
+
EmbedHoverStateParamsSchema, MediaHoverStateParamsSchema,
|
|
13
|
+
RectangleHoverStateParamsSchema
|
|
14
|
+
} from './ItemState.schema';
|
|
15
|
+
import { RichTextItemSchema } from './RichTextItem.schema';
|
|
16
|
+
import { ItemBaseSchema } from './ItemBase.schema';
|
|
17
|
+
import { ArticleItemType } from '../../types/article/ArticleItemType';
|
|
18
|
+
|
|
19
|
+
const ImageItemSchema = ItemBaseSchema.extend({
|
|
20
|
+
type: z.literal(ArticleItemType.Image),
|
|
21
|
+
commonParams: z.object({
|
|
22
|
+
url: z.string().min(1)
|
|
23
|
+
}),
|
|
24
|
+
sticky: z.record(
|
|
25
|
+
z.object({
|
|
26
|
+
from: z.number(),
|
|
27
|
+
to: z.number().optional()
|
|
28
|
+
}).nullable(),
|
|
29
|
+
),
|
|
30
|
+
layoutParams: z.record(
|
|
31
|
+
z.object({
|
|
32
|
+
opacity: z.number().nonnegative(),
|
|
33
|
+
radius: z.number(),
|
|
34
|
+
strokeWidth: z.number(),
|
|
35
|
+
strokeColor: z.string(),
|
|
36
|
+
blur: z.number()
|
|
37
|
+
})
|
|
38
|
+
),
|
|
39
|
+
state: z.object({
|
|
40
|
+
hover: z.record(MediaHoverStateParamsSchema)
|
|
41
|
+
})
|
|
42
|
+
}) satisfies ZodType<ImageItem>;
|
|
43
|
+
|
|
44
|
+
const VideoItemSchema = ItemBaseSchema.extend({
|
|
45
|
+
type: z.literal(ArticleItemType.Video),
|
|
46
|
+
commonParams: z.object({
|
|
47
|
+
url: z.string().min(1)
|
|
48
|
+
}),
|
|
49
|
+
sticky: z.record(
|
|
50
|
+
z.object({
|
|
51
|
+
from: z.number(),
|
|
52
|
+
to: z.number().optional()
|
|
53
|
+
}).nullable(),
|
|
54
|
+
),
|
|
55
|
+
layoutParams: z.record(
|
|
56
|
+
z.object({
|
|
57
|
+
autoplay: z.boolean(),
|
|
58
|
+
opacity: z.number().nonnegative(),
|
|
59
|
+
radius: z.number(),
|
|
60
|
+
strokeWidth: z.number(),
|
|
61
|
+
strokeColor: z.string(),
|
|
62
|
+
blur: z.number()
|
|
63
|
+
})
|
|
64
|
+
),
|
|
65
|
+
state: z.object({
|
|
66
|
+
hover: z.record(MediaHoverStateParamsSchema)
|
|
67
|
+
})
|
|
68
|
+
}) satisfies ZodType<VideoItem>;
|
|
69
|
+
|
|
70
|
+
const RectangleItemSchema = ItemBaseSchema.extend({
|
|
71
|
+
type: z.literal(ArticleItemType.Rectangle),
|
|
72
|
+
commonParams: z.object({
|
|
73
|
+
ratioLock: z.boolean()
|
|
74
|
+
}),
|
|
75
|
+
sticky: z.record(
|
|
76
|
+
z.object({
|
|
77
|
+
from: z.number(),
|
|
78
|
+
to: z.number().optional()
|
|
79
|
+
}).nullable(),
|
|
80
|
+
),
|
|
81
|
+
layoutParams: z.record(
|
|
82
|
+
z.object({
|
|
83
|
+
radius: z.number(),
|
|
84
|
+
strokeWidth: z.number(),
|
|
85
|
+
fillColor: z.string().min(1),
|
|
86
|
+
strokeColor: z.string().min(1),
|
|
87
|
+
blur: z.number(),
|
|
88
|
+
backdropBlur: z.number(),
|
|
89
|
+
blurMode: z.enum(['default', 'backdrop'])
|
|
90
|
+
})
|
|
91
|
+
),
|
|
92
|
+
state: z.object({
|
|
93
|
+
hover: z.record(RectangleHoverStateParamsSchema)
|
|
94
|
+
})
|
|
95
|
+
}) satisfies ZodType<RectangleItem>;
|
|
96
|
+
|
|
97
|
+
const CustomItemSchema = ItemBaseSchema.extend({
|
|
98
|
+
type: z.literal(ArticleItemType.Custom),
|
|
99
|
+
commonParams: z.object({
|
|
100
|
+
name: z.string()
|
|
101
|
+
}),
|
|
102
|
+
sticky: z.record(
|
|
103
|
+
z.object({
|
|
104
|
+
from: z.number(),
|
|
105
|
+
to: z.number().optional()
|
|
106
|
+
}).nullable(),
|
|
107
|
+
),
|
|
108
|
+
layoutParams: z.record(z.object({})),
|
|
109
|
+
state: z.object({
|
|
110
|
+
hover: z.record(CustomItemHoverStateParamsSchema)
|
|
111
|
+
})
|
|
112
|
+
}) satisfies ZodType<CustomItem>;
|
|
113
|
+
|
|
114
|
+
const VimeoEmbedItemSchema = ItemBaseSchema.extend({
|
|
115
|
+
type: z.literal(ArticleItemType.VimeoEmbed),
|
|
116
|
+
commonParams: z.object({
|
|
117
|
+
play: z.union([z.literal('on-hover'), z.literal('on-click'), z.literal('auto')]),
|
|
118
|
+
controls: z.boolean(),
|
|
119
|
+
loop: z.boolean(),
|
|
120
|
+
muted: z.boolean(),
|
|
121
|
+
pictureInPicture: z.boolean(),
|
|
122
|
+
url: z.string().min(1),
|
|
123
|
+
ratioLock: z.boolean()
|
|
124
|
+
}),
|
|
125
|
+
sticky: z.record(
|
|
126
|
+
z.object({
|
|
127
|
+
from: z.number(),
|
|
128
|
+
to: z.number().optional()
|
|
129
|
+
}).nullable(),
|
|
130
|
+
),
|
|
131
|
+
layoutParams: z.record(
|
|
132
|
+
z.object({
|
|
133
|
+
radius: z.number(),
|
|
134
|
+
blur: z.number()
|
|
135
|
+
})
|
|
136
|
+
),
|
|
137
|
+
state: z.object({
|
|
138
|
+
hover: z.record(EmbedHoverStateParamsSchema)
|
|
139
|
+
})
|
|
140
|
+
}) satisfies ZodType<VimeoEmbedItem>;
|
|
141
|
+
|
|
142
|
+
const YoutubeEmbedItemSchema = ItemBaseSchema.extend({
|
|
143
|
+
type: z.literal(ArticleItemType.YoutubeEmbed),
|
|
144
|
+
commonParams: z.object({
|
|
145
|
+
play: z.enum(['on-hover', 'on-click', 'auto']),
|
|
146
|
+
controls: z.boolean(),
|
|
147
|
+
loop: z.boolean(),
|
|
148
|
+
url: z.string().min(1)
|
|
149
|
+
}),
|
|
150
|
+
sticky: z.record(
|
|
151
|
+
z.object({
|
|
152
|
+
from: z.number(),
|
|
153
|
+
to: z.number().optional()
|
|
154
|
+
}).nullable(),
|
|
155
|
+
),
|
|
156
|
+
layoutParams: z.record(
|
|
157
|
+
z.object({
|
|
158
|
+
radius: z.number(),
|
|
159
|
+
blur: z.number()
|
|
160
|
+
})
|
|
161
|
+
),
|
|
162
|
+
state: z.object({
|
|
163
|
+
hover: z.record(EmbedHoverStateParamsSchema)
|
|
164
|
+
})
|
|
165
|
+
}) satisfies ZodType<YoutubeEmbedItem>;
|
|
166
|
+
|
|
167
|
+
export const ItemSchema = z.discriminatedUnion('type', [
|
|
168
|
+
ImageItemSchema,
|
|
169
|
+
VideoItemSchema,
|
|
170
|
+
RectangleItemSchema,
|
|
171
|
+
CustomItemSchema,
|
|
172
|
+
RichTextItemSchema,
|
|
173
|
+
VimeoEmbedItemSchema,
|
|
174
|
+
YoutubeEmbedItemSchema
|
|
175
|
+
]);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AnchorSide, ScaleAnchor } from '../../types/article/ItemArea';
|
|
3
|
+
|
|
4
|
+
export const ItemAreaSchema = z.object({
|
|
5
|
+
top: z.number(),
|
|
6
|
+
left: z.number(),
|
|
7
|
+
width: z.number(),
|
|
8
|
+
height: z.number(),
|
|
9
|
+
zIndex: z.number(),
|
|
10
|
+
angle: z.number(),
|
|
11
|
+
anchorSide: z.nativeEnum(AnchorSide).optional(),
|
|
12
|
+
scale: z.number().nonnegative(),
|
|
13
|
+
scaleAnchor: z.nativeEnum(ScaleAnchor)
|
|
14
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ItemAreaSchema } from './ItemArea.schema';
|
|
3
|
+
|
|
4
|
+
export const Link = z.object({
|
|
5
|
+
url: z.string().min(1),
|
|
6
|
+
target: z.string().min(1)
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const CommonParamsBase = z.object({
|
|
10
|
+
sizing: z.string().min(1)
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const ItemBaseSchema = z.object({
|
|
14
|
+
id: z.string().min(1),
|
|
15
|
+
area: z.record(ItemAreaSchema),
|
|
16
|
+
hidden: z.record(z.boolean()),
|
|
17
|
+
link: Link.optional(),
|
|
18
|
+
commonParams: CommonParamsBase,
|
|
19
|
+
layoutParams: z.record(z.any()).optional()
|
|
20
|
+
});
|