@caltho/site-kit 0.2.10 → 0.2.11
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/README.md +61 -12
- package/dist/cli/check.js +31 -1
- package/dist/content/index.d.ts +310 -23
- package/dist/content/index.js +142 -15
- package/dist/content/look.d.ts +260 -9
- package/dist/content/look.js +386 -22
- package/dist/finish.js +9 -1
- package/dist/fonts/families/dm-sans/LICENSE +93 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/dm-sans/dm-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/fraunces/LICENSE +93 -0
- package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/fraunces/fraunces-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/fraunces/fraunces-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/fraunces/fraunces-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/inter/LICENSE +93 -0
- package/dist/fonts/families/inter/inter-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/inter/inter-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/inter/inter-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/inter/inter-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/lora/LICENSE +93 -0
- package/dist/fonts/families/lora/lora-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/lora/lora-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/lora/lora-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/lora/lora-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/manrope/LICENSE +93 -0
- package/dist/fonts/families/manrope/manrope-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/manrope/manrope-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/newsreader/LICENSE +93 -0
- package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/newsreader/newsreader-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/newsreader/newsreader-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/newsreader/newsreader-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/outfit/LICENSE +93 -0
- package/dist/fonts/families/outfit/outfit-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/outfit/outfit-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/playfair-display/LICENSE +93 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/playfair-display/playfair-display-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/LICENSE +93 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/source-serif-4/source-serif-4-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/families/work-sans/LICENSE +93 -0
- package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-italic.woff2 +0 -0
- package/dist/fonts/families/work-sans/work-sans-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/families/work-sans/work-sans-latin-wght-italic.woff2 +0 -0
- package/dist/fonts/families/work-sans/work-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/google.json +1 -0
- package/dist/fonts-google.d.ts +35 -0
- package/dist/fonts-google.js +70 -0
- package/dist/fonts.css +365 -0
- package/dist/frame.test.d.ts +1 -0
- package/dist/frame.test.js +33 -0
- package/dist/migrate.d.ts +45 -0
- package/dist/migrate.js +284 -0
- package/dist/next/block-mark.d.ts +7 -0
- package/dist/next/block-mark.js +16 -0
- package/dist/next/blocks.d.ts +11 -0
- package/dist/next/blocks.js +35 -0
- package/dist/next/data.d.ts +4 -0
- package/dist/next/data.js +4 -0
- package/dist/next/frame.d.ts +34 -0
- package/dist/next/frame.js +70 -0
- package/dist/next/index.d.ts +6 -2
- package/dist/next/index.js +7 -3
- package/dist/next/menu-group.d.ts +11 -0
- package/dist/next/menu-group.js +33 -0
- package/dist/next/parts/LookStyle.js +3 -1
- package/dist/next/preview.js +282 -25
- package/dist/next/sections.d.ts +19 -10
- package/dist/next/sections.js +129 -17
- package/dist/prepare.d.ts +5 -1
- package/dist/prepare.js +59 -1
- package/dist/preview-match.d.ts +8 -0
- package/dist/preview-match.js +33 -1
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +26 -0
- package/dist/sections/icons.d.ts +47 -0
- package/dist/sections/icons.js +51 -0
- package/dist/sections/index.d.ts +165 -11
- package/dist/sections/index.js +129 -8
- package/dist/sections/presets.d.ts +13 -0
- package/dist/sections/presets.js +300 -0
- package/dist/sections.css +1260 -41
- package/dist/sections.test.js +276 -14
- package/package.json +8 -3
package/dist/sections/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export { ICON_NAMES, ICONS, type IconName } from "./icons.ts";
|
|
2
3
|
/** Photos in a grid, each with a caption if it has one: their work, the place, the team at it. */
|
|
3
4
|
export declare const gallery: z.ZodObject<{
|
|
4
5
|
heading: z.ZodOptional<z.ZodString>;
|
|
@@ -30,29 +31,50 @@ export declare const slideshow: z.ZodObject<{
|
|
|
30
31
|
}, z.core.$strip>>;
|
|
31
32
|
shape: z.ZodDefault<z.ZodEnum<{
|
|
32
33
|
square: "square";
|
|
33
|
-
wide: "wide";
|
|
34
34
|
landscape: "landscape";
|
|
35
|
+
wide: "wide";
|
|
35
36
|
}>>;
|
|
36
37
|
seconds: z.ZodDefault<z.ZodNumber>;
|
|
37
38
|
}, z.core.$strip>;
|
|
39
|
+
/**
|
|
40
|
+
* A YouTube or Vimeo address as the privacy-friendly player it's shown in (YouTube's no-cookie one,
|
|
41
|
+
* Vimeo's with tracking off), or null for any other address. Watch, share, Shorts and embed
|
|
42
|
+
* addresses all work.
|
|
43
|
+
*/
|
|
44
|
+
export declare function videoEmbed(url: string): {
|
|
45
|
+
src: string;
|
|
46
|
+
origin: string;
|
|
47
|
+
} | null;
|
|
48
|
+
/** Where a video part's players come from: a page showing one allows them in its frame-src. */
|
|
49
|
+
export declare const VIDEO_ORIGINS: readonly ["https://www.youtube-nocookie.com", "https://player.vimeo.com"];
|
|
38
50
|
export declare const parts: {
|
|
39
51
|
readonly heading: z.ZodObject<{
|
|
40
52
|
text: z.ZodString;
|
|
41
53
|
size: z.ZodDefault<z.ZodEnum<{
|
|
54
|
+
display: "display";
|
|
42
55
|
small: "small";
|
|
43
|
-
large: "large";
|
|
44
56
|
medium: "medium";
|
|
57
|
+
large: "large";
|
|
58
|
+
page: "page";
|
|
59
|
+
}>>;
|
|
60
|
+
font: z.ZodOptional<z.ZodEnum<{
|
|
61
|
+
heading: "heading";
|
|
62
|
+
text: "text";
|
|
45
63
|
}>>;
|
|
46
64
|
}, z.core.$strip>;
|
|
47
65
|
readonly text: z.ZodObject<{
|
|
48
66
|
text: z.ZodString;
|
|
67
|
+
font: z.ZodOptional<z.ZodEnum<{
|
|
68
|
+
heading: "heading";
|
|
69
|
+
text: "text";
|
|
70
|
+
}>>;
|
|
49
71
|
}, z.core.$strip>;
|
|
50
72
|
readonly photo: z.ZodObject<{
|
|
51
|
-
image: z.ZodObject<{
|
|
73
|
+
image: z.ZodOptional<z.ZodObject<{
|
|
52
74
|
src: z.ZodString;
|
|
53
75
|
alt: z.ZodString;
|
|
54
76
|
caption: z.ZodOptional<z.ZodString>;
|
|
55
|
-
}, z.core.$strip
|
|
77
|
+
}, z.core.$strip>>;
|
|
56
78
|
shape: z.ZodDefault<z.ZodEnum<{
|
|
57
79
|
square: "square";
|
|
58
80
|
landscape: "landscape";
|
|
@@ -63,10 +85,7 @@ export declare const parts: {
|
|
|
63
85
|
readonly button: z.ZodObject<{
|
|
64
86
|
label: z.ZodString;
|
|
65
87
|
href: z.ZodString;
|
|
66
|
-
style: z.ZodDefault<z.
|
|
67
|
-
main: "main";
|
|
68
|
-
quiet: "quiet";
|
|
69
|
-
}>>;
|
|
88
|
+
style: z.ZodDefault<z.ZodString>;
|
|
70
89
|
}, z.core.$strip>;
|
|
71
90
|
readonly list: z.ZodObject<{
|
|
72
91
|
items: z.ZodArray<z.ZodString>;
|
|
@@ -76,9 +95,25 @@ export declare const parts: {
|
|
|
76
95
|
numbers: "numbers";
|
|
77
96
|
}>>;
|
|
78
97
|
}, z.core.$strip>;
|
|
98
|
+
readonly items: z.ZodObject<{
|
|
99
|
+
items: z.ZodArray<z.ZodObject<{
|
|
100
|
+
title: z.ZodString;
|
|
101
|
+
text: z.ZodOptional<z.ZodString>;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
font: z.ZodOptional<z.ZodEnum<{
|
|
104
|
+
heading: "heading";
|
|
105
|
+
text: "text";
|
|
106
|
+
}>>;
|
|
107
|
+
numbered: z.ZodDefault<z.ZodBoolean>;
|
|
108
|
+
lines: z.ZodDefault<z.ZodBoolean>;
|
|
109
|
+
}, z.core.$strip>;
|
|
79
110
|
readonly quote: z.ZodObject<{
|
|
80
111
|
text: z.ZodString;
|
|
81
112
|
who: z.ZodOptional<z.ZodString>;
|
|
113
|
+
font: z.ZodOptional<z.ZodEnum<{
|
|
114
|
+
heading: "heading";
|
|
115
|
+
text: "text";
|
|
116
|
+
}>>;
|
|
82
117
|
}, z.core.$strip>;
|
|
83
118
|
readonly stats: z.ZodObject<{
|
|
84
119
|
items: z.ZodArray<z.ZodObject<{
|
|
@@ -114,14 +149,85 @@ export declare const parts: {
|
|
|
114
149
|
readonly space: z.ZodObject<{
|
|
115
150
|
size: z.ZodDefault<z.ZodEnum<{
|
|
116
151
|
small: "small";
|
|
152
|
+
medium: "medium";
|
|
117
153
|
large: "large";
|
|
154
|
+
}>>;
|
|
155
|
+
}, z.core.$strip>;
|
|
156
|
+
readonly table: z.ZodObject<{
|
|
157
|
+
columns: z.ZodArray<z.ZodString>;
|
|
158
|
+
rows: z.ZodArray<z.ZodObject<{
|
|
159
|
+
label: z.ZodString;
|
|
160
|
+
cells: z.ZodArray<z.ZodString>;
|
|
161
|
+
}, z.core.$strip>>;
|
|
162
|
+
highlight: z.ZodOptional<z.ZodNumber>;
|
|
163
|
+
}, z.core.$strip>;
|
|
164
|
+
readonly icon: z.ZodObject<{
|
|
165
|
+
name: z.ZodEnum<{
|
|
166
|
+
card: "card";
|
|
167
|
+
check: "check";
|
|
168
|
+
phone: "phone";
|
|
169
|
+
file: "file";
|
|
170
|
+
home: "home";
|
|
171
|
+
play: "play";
|
|
172
|
+
x: "x";
|
|
173
|
+
search: "search";
|
|
174
|
+
link: "link";
|
|
175
|
+
calendar: "calendar";
|
|
176
|
+
clock: "clock";
|
|
177
|
+
users: "users";
|
|
178
|
+
user: "user";
|
|
179
|
+
chat: "chat";
|
|
180
|
+
mail: "mail";
|
|
181
|
+
"map-pin": "map-pin";
|
|
182
|
+
chart: "chart";
|
|
183
|
+
folder: "folder";
|
|
184
|
+
invoice: "invoice";
|
|
185
|
+
shield: "shield";
|
|
186
|
+
lock: "lock";
|
|
187
|
+
star: "star";
|
|
188
|
+
heart: "heart";
|
|
189
|
+
bolt: "bolt";
|
|
190
|
+
gear: "gear";
|
|
191
|
+
building: "building";
|
|
192
|
+
truck: "truck";
|
|
193
|
+
wrench: "wrench";
|
|
194
|
+
leaf: "leaf";
|
|
195
|
+
sun: "sun";
|
|
196
|
+
globe: "globe";
|
|
197
|
+
camera: "camera";
|
|
198
|
+
"arrow-right": "arrow-right";
|
|
199
|
+
plus: "plus";
|
|
200
|
+
minus: "minus";
|
|
201
|
+
info: "info";
|
|
202
|
+
warning: "warning";
|
|
203
|
+
list: "list";
|
|
204
|
+
grid: "grid";
|
|
205
|
+
tag: "tag";
|
|
206
|
+
award: "award";
|
|
207
|
+
}>;
|
|
208
|
+
size: z.ZodDefault<z.ZodEnum<{
|
|
209
|
+
small: "small";
|
|
118
210
|
medium: "medium";
|
|
211
|
+
large: "large";
|
|
119
212
|
}>>;
|
|
120
213
|
}, z.core.$strip>;
|
|
214
|
+
readonly logos: z.ZodObject<{
|
|
215
|
+
title: z.ZodOptional<z.ZodString>;
|
|
216
|
+
images: z.ZodArray<z.ZodObject<{
|
|
217
|
+
src: z.ZodString;
|
|
218
|
+
alt: z.ZodString;
|
|
219
|
+
}, z.core.$strip>>;
|
|
220
|
+
}, z.core.$strip>;
|
|
221
|
+
readonly video: z.ZodObject<{
|
|
222
|
+
url: z.ZodOptional<z.ZodString>;
|
|
223
|
+
title: z.ZodString;
|
|
224
|
+
}, z.core.$strip>;
|
|
121
225
|
};
|
|
122
226
|
export type PartType = keyof typeof parts;
|
|
123
227
|
/** A section of the business's own: columns of parts, laid out as chosen, in the site's design. */
|
|
124
228
|
export declare const own: z.ZodObject<{
|
|
229
|
+
heading: z.ZodOptional<z.ZodString>;
|
|
230
|
+
intro: z.ZodOptional<z.ZodString>;
|
|
125
231
|
layout: z.ZodDefault<z.ZodEnum<{
|
|
126
232
|
one: "one";
|
|
127
233
|
two: "two";
|
|
@@ -135,10 +241,32 @@ export declare const own: z.ZodObject<{
|
|
|
135
241
|
center: "center";
|
|
136
242
|
}>>;
|
|
137
243
|
tone: z.ZodDefault<z.ZodEnum<{
|
|
138
|
-
|
|
244
|
+
soft: "soft";
|
|
139
245
|
plain: "plain";
|
|
246
|
+
brand: "brand";
|
|
247
|
+
dark: "dark";
|
|
140
248
|
}>>;
|
|
249
|
+
shape: z.ZodDefault<z.ZodEnum<{
|
|
250
|
+
card: "card";
|
|
251
|
+
band: "band";
|
|
252
|
+
}>>;
|
|
253
|
+
spacing: z.ZodDefault<z.ZodEnum<{
|
|
254
|
+
normal: "normal";
|
|
255
|
+
roomy: "roomy";
|
|
256
|
+
tight: "tight";
|
|
257
|
+
}>>;
|
|
258
|
+
background: z.ZodOptional<z.ZodObject<{
|
|
259
|
+
src: z.ZodString;
|
|
260
|
+
alt: z.ZodString;
|
|
261
|
+
}, z.core.$strip>>;
|
|
141
262
|
columns: z.ZodArray<z.ZodObject<{
|
|
263
|
+
card: z.ZodOptional<z.ZodBoolean>;
|
|
264
|
+
end: z.ZodOptional<z.ZodBoolean>;
|
|
265
|
+
place: z.ZodOptional<z.ZodEnum<{
|
|
266
|
+
top: "top";
|
|
267
|
+
middle: "middle";
|
|
268
|
+
bottom: "bottom";
|
|
269
|
+
}>>;
|
|
142
270
|
parts: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">>;
|
|
143
271
|
}, z.core.$strip>>;
|
|
144
272
|
}, z.core.$strip>;
|
|
@@ -173,12 +301,14 @@ export declare const sections: {
|
|
|
173
301
|
}, z.core.$strip>>;
|
|
174
302
|
shape: z.ZodDefault<z.ZodEnum<{
|
|
175
303
|
square: "square";
|
|
176
|
-
wide: "wide";
|
|
177
304
|
landscape: "landscape";
|
|
305
|
+
wide: "wide";
|
|
178
306
|
}>>;
|
|
179
307
|
seconds: z.ZodDefault<z.ZodNumber>;
|
|
180
308
|
}, z.core.$strip>;
|
|
181
309
|
readonly own: z.ZodObject<{
|
|
310
|
+
heading: z.ZodOptional<z.ZodString>;
|
|
311
|
+
intro: z.ZodOptional<z.ZodString>;
|
|
182
312
|
layout: z.ZodDefault<z.ZodEnum<{
|
|
183
313
|
one: "one";
|
|
184
314
|
two: "two";
|
|
@@ -192,13 +322,37 @@ export declare const sections: {
|
|
|
192
322
|
center: "center";
|
|
193
323
|
}>>;
|
|
194
324
|
tone: z.ZodDefault<z.ZodEnum<{
|
|
195
|
-
|
|
325
|
+
soft: "soft";
|
|
196
326
|
plain: "plain";
|
|
327
|
+
brand: "brand";
|
|
328
|
+
dark: "dark";
|
|
197
329
|
}>>;
|
|
330
|
+
shape: z.ZodDefault<z.ZodEnum<{
|
|
331
|
+
card: "card";
|
|
332
|
+
band: "band";
|
|
333
|
+
}>>;
|
|
334
|
+
spacing: z.ZodDefault<z.ZodEnum<{
|
|
335
|
+
normal: "normal";
|
|
336
|
+
roomy: "roomy";
|
|
337
|
+
tight: "tight";
|
|
338
|
+
}>>;
|
|
339
|
+
background: z.ZodOptional<z.ZodObject<{
|
|
340
|
+
src: z.ZodString;
|
|
341
|
+
alt: z.ZodString;
|
|
342
|
+
}, z.core.$strip>>;
|
|
198
343
|
columns: z.ZodArray<z.ZodObject<{
|
|
344
|
+
card: z.ZodOptional<z.ZodBoolean>;
|
|
345
|
+
end: z.ZodOptional<z.ZodBoolean>;
|
|
346
|
+
place: z.ZodOptional<z.ZodEnum<{
|
|
347
|
+
top: "top";
|
|
348
|
+
middle: "middle";
|
|
349
|
+
bottom: "bottom";
|
|
350
|
+
}>>;
|
|
199
351
|
parts: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>, ...z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>[]], "type">>;
|
|
200
352
|
}, z.core.$strip>>;
|
|
201
353
|
}, z.core.$strip>;
|
|
202
354
|
};
|
|
203
355
|
export type KitSectionType = keyof typeof sections;
|
|
204
356
|
export declare const isKitSection: (type: string) => type is KitSectionType;
|
|
357
|
+
/** Ready-made sections of the business's own, to add and then edit like any other (./presets.ts). */
|
|
358
|
+
export { presets, type OwnBlock, type Preset } from "./presets.ts";
|
package/dist/sections/index.js
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
// its own (README, "Sections"): each one's fields here, drawn by KitSection (../next/sections.tsx) and
|
|
3
3
|
// styled by sections.css, themed by the site's Look. A site takes them all into its blocks
|
|
4
4
|
// (`defineSite({ ...blocks, ...sections })`); one of its own by the same name is kept over the kit's.
|
|
5
|
+
import { buttonStyleName } from "../content/look.js";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { href, image, md, text } from "../content/index.js";
|
|
8
|
+
import { ICON_NAMES } from "./icons.js";
|
|
9
|
+
export { ICON_NAMES, ICONS } from "./icons.js";
|
|
7
10
|
/** Photos in a grid, each with a caption if it has one: their work, the place, the team at it. */
|
|
8
11
|
export const gallery = z
|
|
9
12
|
.object({
|
|
@@ -31,34 +34,150 @@ export const slideshow = z
|
|
|
31
34
|
seconds: z.number().int().min(3).max(15).default(5).meta({ title: "Seconds on each photo" }),
|
|
32
35
|
})
|
|
33
36
|
.meta({ title: "Slideshow", description: "Photos one at a time, fading from one to the next" });
|
|
37
|
+
/**
|
|
38
|
+
* A YouTube or Vimeo address as the privacy-friendly player it's shown in (YouTube's no-cookie one,
|
|
39
|
+
* Vimeo's with tracking off), or null for any other address. Watch, share, Shorts and embed
|
|
40
|
+
* addresses all work.
|
|
41
|
+
*/
|
|
42
|
+
export function videoEmbed(url) {
|
|
43
|
+
const yt = /^https:\/\/(?:www\.|m\.)?(?:youtube\.com\/(?:watch\?(?:[^#]*&)?v=|shorts\/|embed\/|live\/)|youtu\.be\/)([\w-]{11})(?![\w-])/.exec(url.trim());
|
|
44
|
+
if (yt)
|
|
45
|
+
return { src: `${YOUTUBE}/embed/${yt[1]}`, origin: YOUTUBE };
|
|
46
|
+
const vm = /^https:\/\/(?:www\.)?(?:vimeo\.com\/|player\.vimeo\.com\/video\/)(\d+)(?:\/([\da-f]+))?(?![\w])/.exec(url.trim());
|
|
47
|
+
if (vm)
|
|
48
|
+
return { src: `${VIMEO}/video/${vm[1]}?${vm[2] ? `h=${vm[2]}&` : ""}dnt=1`, origin: VIMEO };
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const YOUTUBE = "https://www.youtube-nocookie.com";
|
|
52
|
+
const VIMEO = "https://player.vimeo.com";
|
|
53
|
+
/** Where a video part's players come from: a page showing one allows them in its frame-src. */
|
|
54
|
+
export const VIDEO_ORIGINS = [YOUTUBE, VIMEO];
|
|
55
|
+
const VIDEO_HELP = "A YouTube or Vimeo address, like https://www.youtube.com/watch?v=… or https://vimeo.com/…";
|
|
56
|
+
// The same test as videoEmbed's, as a pattern CalOS can check with from the JSON Schema alone.
|
|
57
|
+
const videoUrl = z
|
|
58
|
+
.string()
|
|
59
|
+
.trim()
|
|
60
|
+
.regex(/^https:\/\/((www\.|m\.)?(youtube\.com\/(watch\?([^#]*&)?v=|shorts\/|embed\/|live\/)|youtu\.be\/)[\w-]{11}([^\w-].*)?|(www\.)?(vimeo\.com\/|player\.vimeo\.com\/video\/)\d+([^\w].*)?)$/, { error: VIDEO_HELP })
|
|
61
|
+
.meta({ title: "Video address", "x-error": VIDEO_HELP });
|
|
62
|
+
/** A table's cell: plain words, or "yes" / "no", drawn as a tick and a dash. */
|
|
63
|
+
const cell = z.string().trim().max(120);
|
|
34
64
|
/**
|
|
35
65
|
* The parts a section of the business's own is put together from in CalOS: each drawn in the site's
|
|
36
66
|
* own type, buttons and widths (the classes it gives KitSection), so what's built looks like the
|
|
37
67
|
* site, not a template. A fixed set, no colours or fonts of their own.
|
|
38
68
|
*/
|
|
69
|
+
/** Which of the Look's fonts a part's words are in, where it isn't the usual (a heading in the text's font). */
|
|
70
|
+
const font = z.enum(["heading", "text"]).optional().meta({ title: "Font", "x-labels": { heading: "Headings' font", text: "Text font" } });
|
|
39
71
|
const photo = image.extend({ caption: text.optional().meta({ title: "Caption" }) });
|
|
40
72
|
export const parts = {
|
|
41
|
-
heading: z
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
73
|
+
heading: z
|
|
74
|
+
.object({
|
|
75
|
+
text,
|
|
76
|
+
size: z
|
|
77
|
+
.enum(["display", "page", "large", "medium", "small"])
|
|
78
|
+
.default("medium")
|
|
79
|
+
.meta({ title: "Size", "x-labels": { display: "Display (the largest)", page: "Page heading", large: "Section heading", medium: "Medium", small: "Small" } }),
|
|
80
|
+
font,
|
|
81
|
+
})
|
|
82
|
+
.meta({ title: "Heading" }),
|
|
83
|
+
text: z.object({ text: md, font }).meta({ title: "Text" }),
|
|
84
|
+
photo: z.object({ image: photo.optional(), shape: z.enum(["as-taken", "landscape", "square", "portrait"]).default("as-taken").meta({ title: "Shape" }) }).meta({ title: "Photo" }),
|
|
85
|
+
button: z
|
|
86
|
+
.object({
|
|
87
|
+
label: text.meta({ title: "Label" }),
|
|
88
|
+
href,
|
|
89
|
+
// The theme's Main, Accent or Quiet, or one of the business's own button styles (Look › Buttons), by its slug.
|
|
90
|
+
style: buttonStyleName.default("main").meta({ title: "Style", "x-ui": "button-style" }),
|
|
91
|
+
})
|
|
92
|
+
.meta({ title: "Button" }),
|
|
45
93
|
list: z.object({ items: z.array(text).min(1).max(20).meta({ title: "Items" }), style: z.enum(["bullets", "checks", "numbers"]).default("bullets").meta({ title: "Style" }) }).meta({ title: "List" }),
|
|
46
|
-
|
|
94
|
+
// Things with a name and a line about each: services, steps, what's included. Numbered (01, 02…) or
|
|
95
|
+
// not, with a rule between them or none.
|
|
96
|
+
items: z
|
|
97
|
+
.object({
|
|
98
|
+
items: z
|
|
99
|
+
.array(z.object({ title: text.meta({ title: "Title" }), text: text.optional().meta({ title: "Line about it" }) }).meta({ title: "Item" }))
|
|
100
|
+
.min(1)
|
|
101
|
+
.max(12)
|
|
102
|
+
.meta({ title: "Items" }),
|
|
103
|
+
// Their titles in the headings' font, or the text's (a sans title under a serif heading).
|
|
104
|
+
font,
|
|
105
|
+
numbered: z.boolean().default(true).meta({ title: "Numbered (01, 02…)" }),
|
|
106
|
+
lines: z.boolean().default(true).meta({ title: "Lines between them" }),
|
|
107
|
+
})
|
|
108
|
+
.meta({ title: "Items with titles", description: "Services or steps: each a title and a line about it, numbered or not" }),
|
|
109
|
+
quote: z.object({ text: text.meta({ title: "Quote" }), who: text.optional().meta({ title: "Who said it" }), font }).meta({ title: "Quote" }),
|
|
47
110
|
stats: z.object({ items: z.array(z.object({ value: text.meta({ title: "Number" }), label: text.meta({ title: "What it is" }) })).min(1).max(6).meta({ title: "Facts" }) }).meta({ title: "Facts in a row", description: "8 guests · 4 cabins · 2 baths" }),
|
|
48
111
|
price: z.object({ amount: text.meta({ title: "Price" }), per: text.optional().meta({ title: "Per" }), note: text.optional().meta({ title: "Note" }) }).meta({ title: "Price" }),
|
|
49
112
|
gallery: z.object({ images: z.array(photo).min(1).max(30).meta({ title: "Photos" }), columns: z.enum(["2", "3", "4"]).default("3").meta({ title: "Photos across" }) }).meta({ title: "Photos in a grid" }),
|
|
50
113
|
slideshow: z.object({ images: z.array(photo).min(2).max(20).meta({ title: "Photos" }), seconds: z.number().int().min(3).max(15).default(5).meta({ title: "Seconds on each photo" }) }).meta({ title: "Slideshow" }),
|
|
51
114
|
space: z.object({ size: z.enum(["small", "medium", "large"]).default("medium").meta({ title: "Size" }) }).meta({ title: "Space" }),
|
|
115
|
+
// Rows of the same few things compared: plans, packages, what's included. The first column is the
|
|
116
|
+
// rows' labels (its heading may be ""); a cell "yes" or "no" is a tick or a dash.
|
|
117
|
+
table: z
|
|
118
|
+
.object({
|
|
119
|
+
columns: z.array(z.string().trim().max(60)).min(2).max(5).meta({ title: "Column headings", description: "The first is over the rows' labels and may be left empty" }),
|
|
120
|
+
rows: z
|
|
121
|
+
.array(z.object({ label: text.meta({ title: "Row" }), cells: z.array(cell).max(4).meta({ title: "Cells", description: "One for each column after the first: words, or yes or no for a tick or a dash" }) }).meta({ title: "Row" }))
|
|
122
|
+
.min(1)
|
|
123
|
+
.max(20)
|
|
124
|
+
.meta({ title: "Rows" }),
|
|
125
|
+
highlight: z.number().int().min(1).max(4).optional().meta({ title: "Column to pick out", description: "Which column (counting from 1 after the rows' labels) to pick out" }),
|
|
126
|
+
})
|
|
127
|
+
.meta({ title: "Table", description: "Rows compared across a few columns: plans, packages, what's included" }),
|
|
128
|
+
icon: z.object({ name: z.enum(ICON_NAMES).meta({ title: "Icon", "x-ui": "icon" }), size: z.enum(["small", "medium", "large"]).default("medium").meta({ title: "Size" }) }).meta({ title: "Icon" }),
|
|
129
|
+
logos: z
|
|
130
|
+
.object({ title: text.optional().meta({ title: "Line above" }), images: z.array(image).min(2).max(12).meta({ title: "Logos" }) })
|
|
131
|
+
.meta({ title: "Logos", description: "A row of the logos of clients, partners or memberships" }),
|
|
132
|
+
// Optional while the business finds its address: a quiet box in CalOS's preview, nothing on the site.
|
|
133
|
+
video: z.object({ url: videoUrl.optional(), title: text.meta({ title: "What the video is", description: "Said to someone using a screen reader" }) }).meta({ title: "Video", description: "A YouTube or Vimeo video" }),
|
|
52
134
|
};
|
|
53
|
-
|
|
135
|
+
/** What a part's own fields can't say alone: a table's rows each have a cell for every column, its headings are named, and the column it picks out is one of them. */
|
|
136
|
+
function partProblems(p, ctx) {
|
|
137
|
+
if (p.type !== "table")
|
|
138
|
+
return;
|
|
139
|
+
const t = p;
|
|
140
|
+
const n = t.columns.length - 1;
|
|
141
|
+
t.columns.forEach((c, i) => {
|
|
142
|
+
if (i > 0 && !c)
|
|
143
|
+
ctx.addIssue({ code: "custom", path: ["columns", i], message: "A column needs a heading" });
|
|
144
|
+
});
|
|
145
|
+
t.rows.forEach((r, i) => {
|
|
146
|
+
if (r.cells.length !== n)
|
|
147
|
+
ctx.addIssue({ code: "custom", path: ["rows", i, "cells"], message: `One cell for each of the ${n} column${n === 1 ? "" : "s"} after the first` });
|
|
148
|
+
});
|
|
149
|
+
if (t.highlight !== undefined && t.highlight > n)
|
|
150
|
+
ctx.addIssue({ code: "custom", path: ["highlight"], message: `The table has ${n} column${n === 1 ? "" : "s"} after the first` });
|
|
151
|
+
}
|
|
152
|
+
const part = z.discriminatedUnion("type", Object.keys(parts).map((type) => z.object({ type: z.literal(type) }).extend(parts[type].shape).meta(parts[type].meta()).superRefine(partProblems)));
|
|
54
153
|
/** A section of the business's own: columns of parts, laid out as chosen, in the site's design. */
|
|
55
154
|
export const own = z
|
|
56
155
|
.object({
|
|
156
|
+
// Its heading area: over its columns, in the site's section heading style.
|
|
157
|
+
heading: text.optional().meta({ title: "Heading" }),
|
|
158
|
+
intro: md.optional().meta({ title: "Intro" }),
|
|
57
159
|
layout: z.enum(["one", "two", "three", "four", "wide-left", "wide-right"]).default("one").meta({ title: "Columns" }),
|
|
58
160
|
align: z.enum(["left", "center"]).default("left").meta({ title: "Align" }),
|
|
59
|
-
|
|
161
|
+
// Plain: the page's own background; soft: a quiet tint of its text colour; brand: the brand
|
|
162
|
+
// colour; dark: the site's ink, words light on it. A photo behind (background) is under a scrim,
|
|
163
|
+
// words light over it, whatever the tone.
|
|
164
|
+
tone: z.enum(["plain", "soft", "brand", "dark"]).default("plain").meta({ title: "Background" }),
|
|
165
|
+
// On soft, brand or dark: a card inset within the content's width, in the theme's corners, or a
|
|
166
|
+
// band the page's full width. Plain has nothing to shape.
|
|
167
|
+
shape: z.enum(["card", "band"]).default("card").meta({ title: "Shape" }),
|
|
168
|
+
spacing: z.enum(["tight", "normal", "roomy"]).default("normal").meta({ title: "Space above and below" }),
|
|
169
|
+
background: image.optional().meta({ title: "Photo behind" }),
|
|
60
170
|
columns: z
|
|
61
|
-
.array(z
|
|
171
|
+
.array(z
|
|
172
|
+
.object({
|
|
173
|
+
card: z.boolean().optional().meta({ title: "Drawn as a card" }),
|
|
174
|
+
// Against the far edge: a row's second half ("Where we work … Philadelphia, PA / Princeton, NJ").
|
|
175
|
+
end: z.boolean().optional().meta({ title: "Against the right edge" }),
|
|
176
|
+
// Where its parts sit when a column beside it is taller: words at the foot of a tall photo.
|
|
177
|
+
place: z.enum(["top", "middle", "bottom"]).optional().meta({ title: "Up and down", "x-labels": { top: "At the top", middle: "In the middle", bottom: "At the bottom" } }),
|
|
178
|
+
parts: z.array(part).max(12).meta({ title: "Parts" }),
|
|
179
|
+
})
|
|
180
|
+
.meta({ title: "Column" }))
|
|
62
181
|
.min(1)
|
|
63
182
|
.max(4)
|
|
64
183
|
.meta({ title: "Columns' parts" }),
|
|
@@ -67,3 +186,5 @@ export const own = z
|
|
|
67
186
|
/** Every section the kit draws, by the type a block names. */
|
|
68
187
|
export const sections = { gallery, slideshow, own };
|
|
69
188
|
export const isKitSection = (type) => Object.prototype.hasOwnProperty.call(sections, type);
|
|
189
|
+
/** Ready-made sections of the business's own, to add and then edit like any other (./presets.ts). */
|
|
190
|
+
export { presets } from "./presets.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { own } from "./index.ts";
|
|
3
|
+
/** A section of the business's own, as content holds it. */
|
|
4
|
+
export type OwnBlock = {
|
|
5
|
+
type: "own";
|
|
6
|
+
} & z.input<typeof own>;
|
|
7
|
+
export type Preset = {
|
|
8
|
+
key: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
block: OwnBlock;
|
|
12
|
+
};
|
|
13
|
+
export declare const presets: Preset[];
|