@xosen/site-sdk 0.0.13 → 0.0.15
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/dist/composables/pageDataStore.d.ts +9 -0
- package/dist/composables/useLivePreview.d.ts +15 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +211 -187
- package/dist/types/blocks.d.ts +2 -1
- package/dist/types/layout.d.ts +1 -0
- package/package.json +14 -14
- package/src/composables/pageDataStore.ts +24 -0
- package/src/composables/useDynamicLayout.ts +4 -2
- package/src/composables/useLivePreview.ts +12 -0
- package/src/composables/usePageData.ts +15 -7
- package/src/composables/useSiteComponents.ts +6 -2
- package/src/index.ts +1 -1
- package/src/types/blocks.ts +2 -1
- package/src/types/layout.ts +14 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared reactive store for page data loaded via usePageData.
|
|
3
|
+
* Allows useDynamicLayout to reactively read layout from fetched pages.
|
|
4
|
+
*/
|
|
5
|
+
export declare const pageDataStore: Record<string, any>;
|
|
6
|
+
/**
|
|
7
|
+
* Initialize store with preloaded data from __SITE_DATA__.
|
|
8
|
+
*/
|
|
9
|
+
export declare function initPageDataStore(): void;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { Router } from 'vue-router';
|
|
2
|
+
export interface PreviewSiteData {
|
|
3
|
+
config?: Record<string, any>;
|
|
4
|
+
skin?: Record<string, any>;
|
|
5
|
+
components?: Record<string, any>;
|
|
6
|
+
}
|
|
2
7
|
export interface PreviewMessage {
|
|
3
8
|
type: 'xosen-preview-update';
|
|
9
|
+
site?: PreviewSiteData;
|
|
4
10
|
page: {
|
|
5
11
|
slug: string;
|
|
6
12
|
locale: string;
|
|
@@ -50,5 +56,14 @@ export declare function useLivePreview(): {
|
|
|
50
56
|
meta?: Record<string, any> | undefined;
|
|
51
57
|
layout?: string | undefined;
|
|
52
58
|
} | null>;
|
|
59
|
+
previewSite: import("vue").Ref<{
|
|
60
|
+
config?: Record<string, any> | undefined;
|
|
61
|
+
skin?: Record<string, any> | undefined;
|
|
62
|
+
components?: Record<string, any> | undefined;
|
|
63
|
+
} | null, PreviewSiteData | {
|
|
64
|
+
config?: Record<string, any> | undefined;
|
|
65
|
+
skin?: Record<string, any> | undefined;
|
|
66
|
+
components?: Record<string, any> | undefined;
|
|
67
|
+
} | null>;
|
|
53
68
|
isPreviewMode: import("vue").Ref<boolean, boolean>;
|
|
54
69
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { useLocaleSwitcher } from './composables/useLocaleSwitcher.js';
|
|
|
10
10
|
export { useDynamicLayout, buildLayoutCSS } from './composables/useDynamicLayout.js';
|
|
11
11
|
export type { LayoutConfig, LayoutZone, GridBreakpoint, LayoutSchemaDefinition, LayoutField } from './types/layout.js';
|
|
12
12
|
export { layoutSchema, layoutPresets } from './types/layout.js';
|
|
13
|
-
export type { PreviewMessage } from './composables/useLivePreview.js';
|
|
13
|
+
export type { PreviewMessage, PreviewSiteData } from './composables/useLivePreview.js';
|
|
14
14
|
export { createSiteWorker } from './worker/index.js';
|
|
15
15
|
export type { WorkerEnv, WorkerConfig, PageData } from './worker/index.js';
|
|
16
16
|
export type { Block, BlockSettings, HeroBlockData, HtmlBlockData, CardsBlockData, ImageTextBlockData, PricingBlockData, PricingPlan, ContactsBlockData, Office, GalleryBlockData, MapBlockData, } from './types/blocks.js';
|
package/dist/index.js
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { ref as
|
|
2
|
-
import { useI18n as
|
|
3
|
-
import { useRoute as
|
|
4
|
-
function
|
|
5
|
-
const t =
|
|
1
|
+
import { ref as v, onMounted as S, watch as _, computed as u, onUnmounted as N, reactive as L, provide as P } from "vue";
|
|
2
|
+
import { useI18n as T } from "vue-i18n";
|
|
3
|
+
import { useRoute as I } from "vue-router";
|
|
4
|
+
function Z(o, a) {
|
|
5
|
+
const t = v(null), { locale: e } = T(), s = window.__SITE_DATA__, n = s?.locale;
|
|
6
6
|
function r() {
|
|
7
|
-
return
|
|
7
|
+
return s && s[o] && e.value === n ? (t.value = s[o], !0) : !1;
|
|
8
8
|
}
|
|
9
9
|
async function i() {
|
|
10
10
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const c = await fetch(`/api/content/${a}:${e.value}`);
|
|
12
|
+
c.ok && (t.value = await c.json());
|
|
13
13
|
} catch {
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
return
|
|
16
|
+
return S(() => {
|
|
17
17
|
r() || i();
|
|
18
|
-
}),
|
|
18
|
+
}), _(e, () => {
|
|
19
19
|
i();
|
|
20
20
|
}), t;
|
|
21
21
|
}
|
|
22
|
-
function
|
|
23
|
-
const o = window.__SITE_DATA__, a =
|
|
24
|
-
function
|
|
25
|
-
const
|
|
26
|
-
return typeof
|
|
22
|
+
function q() {
|
|
23
|
+
const o = window.__SITE_DATA__, a = u(() => o?.config || null), t = u(() => o?.components || {}), e = u(() => a.value?.navigation || []), s = u(() => a.value?.locales || []), n = u(() => a.value?.defaultLocale || "en"), r = u(() => a.value?.branding || {}), i = u(() => a.value?.footer || {}), c = u(() => a.value?.features || {});
|
|
24
|
+
function p(m) {
|
|
25
|
+
const g = c.value[m];
|
|
26
|
+
return typeof g == "boolean" ? g : typeof g == "object";
|
|
27
27
|
}
|
|
28
28
|
return {
|
|
29
29
|
config: a,
|
|
30
30
|
components: t,
|
|
31
31
|
navigation: e,
|
|
32
|
-
locales:
|
|
33
|
-
defaultLocale:
|
|
32
|
+
locales: s,
|
|
33
|
+
defaultLocale: n,
|
|
34
34
|
branding: r,
|
|
35
35
|
footer: i,
|
|
36
|
-
features:
|
|
37
|
-
hasFeature:
|
|
36
|
+
features: c,
|
|
37
|
+
hasFeature: p
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
function
|
|
40
|
+
function H(o) {
|
|
41
41
|
function a(t) {
|
|
42
42
|
const e = document.documentElement;
|
|
43
43
|
if (t.colors)
|
|
44
|
-
for (const [
|
|
45
|
-
|
|
44
|
+
for (const [s, n] of Object.entries(t.colors))
|
|
45
|
+
n && e.style.setProperty(`--x-color-${D(s)}`, n);
|
|
46
46
|
t.typography?.fontFamily && e.style.setProperty("--x-font-family", t.typography.fontFamily), t.typography?.headingFont && e.style.setProperty("--x-font-heading", t.typography.headingFont), t.typography?.baseFontSize && e.style.setProperty("--x-font-size", t.typography.baseFontSize), t.shape?.borderRadius && e.style.setProperty("--x-border-radius", t.shape.borderRadius), t.shape?.maxWidth && e.style.setProperty("--x-max-width", t.shape.maxWidth);
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
S(() => {
|
|
49
49
|
if (o) {
|
|
50
50
|
a(o);
|
|
51
51
|
return;
|
|
@@ -58,13 +58,13 @@ function Y(o) {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
-
function
|
|
61
|
+
function D(o) {
|
|
62
62
|
return o.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
63
63
|
}
|
|
64
|
-
function
|
|
64
|
+
function Q() {
|
|
65
65
|
const a = window.__SITE_DATA__?.apiBase || "";
|
|
66
|
-
async function t(
|
|
67
|
-
const r = await fetch(`${a}${
|
|
66
|
+
async function t(n) {
|
|
67
|
+
const r = await fetch(`${a}${n}`);
|
|
68
68
|
if (!r.ok)
|
|
69
69
|
throw new Error(`API error: ${r.status}`);
|
|
70
70
|
return r.json();
|
|
@@ -72,128 +72,141 @@ function X() {
|
|
|
72
72
|
async function e() {
|
|
73
73
|
return t("/v1/billing/tariffs");
|
|
74
74
|
}
|
|
75
|
-
async function
|
|
75
|
+
async function s() {
|
|
76
76
|
return t("/v1/billing/services");
|
|
77
77
|
}
|
|
78
78
|
return {
|
|
79
79
|
get: t,
|
|
80
80
|
getTariffs: e,
|
|
81
|
-
getProducts:
|
|
81
|
+
getProducts: s
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
function
|
|
84
|
+
function $() {
|
|
85
85
|
try {
|
|
86
86
|
return window.self !== window.top;
|
|
87
87
|
} catch {
|
|
88
88
|
return !0;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
function
|
|
92
|
-
|
|
91
|
+
function V(o) {
|
|
92
|
+
$() && o.beforeEach((a, t) => !t.name);
|
|
93
93
|
}
|
|
94
|
-
function
|
|
95
|
-
const o =
|
|
96
|
-
function
|
|
97
|
-
const n =
|
|
98
|
-
n?.type === "xosen-preview-update" && n.page && (o.value = n.page, a.value = !0);
|
|
94
|
+
function C() {
|
|
95
|
+
const o = v(null), a = v(null), t = v($());
|
|
96
|
+
function e(s) {
|
|
97
|
+
const n = s.data;
|
|
98
|
+
n?.type === "xosen-preview-update" && n.page && (o.value = n.page, n.site && (a.value = n.site), t.value = !0);
|
|
99
99
|
}
|
|
100
|
-
return
|
|
101
|
-
window.addEventListener("message",
|
|
102
|
-
}),
|
|
103
|
-
window.removeEventListener("message",
|
|
100
|
+
return S(() => {
|
|
101
|
+
window.addEventListener("message", e);
|
|
102
|
+
}), N(() => {
|
|
103
|
+
window.removeEventListener("message", e);
|
|
104
104
|
}), {
|
|
105
105
|
previewPage: o,
|
|
106
|
-
|
|
106
|
+
previewSite: a,
|
|
107
|
+
isPreviewMode: t
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
|
-
const
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
|
|
110
|
+
const R = /* @__PURE__ */ Symbol("pageContext"), k = L({});
|
|
111
|
+
function j() {
|
|
112
|
+
const o = window.__SITE_DATA__;
|
|
113
|
+
if (o)
|
|
114
|
+
for (const a of Object.keys(o))
|
|
115
|
+
a.startsWith("page:") && (k[a] = o[a]);
|
|
116
|
+
}
|
|
117
|
+
j();
|
|
118
|
+
function ee(o) {
|
|
119
|
+
const { locale: a } = T(), { previewPage: t, isPreviewMode: e } = C(), s = window.__SITE_DATA__, n = v(null), r = v(!0), i = v(!1), c = u(() => typeof o == "string" ? o : o.value), p = u(() => `page:${c.value}`), m = u(() => {
|
|
120
|
+
const l = e.value ? t.value : n.value;
|
|
113
121
|
if (!l) return {};
|
|
114
|
-
const { blocks:
|
|
115
|
-
return
|
|
122
|
+
const { blocks: f, meta: d, slug: W, locale: J, ...E } = l;
|
|
123
|
+
return E;
|
|
116
124
|
});
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
P(R, m);
|
|
126
|
+
const g = u(() => e.value && t.value?.slug === c.value ? t.value.blocks || [] : n.value?.blocks || []);
|
|
127
|
+
function b(l) {
|
|
128
|
+
n.value = l, l && (k[p.value] = l);
|
|
129
|
+
}
|
|
130
|
+
function y() {
|
|
131
|
+
const l = s?.[p.value];
|
|
132
|
+
return l && a.value === s?.locale ? (b(l), i.value = !1, w(), !0) : !1;
|
|
122
133
|
}
|
|
123
|
-
async function
|
|
134
|
+
async function h() {
|
|
124
135
|
r.value = !0, i.value = !1;
|
|
125
136
|
try {
|
|
126
|
-
const l = await fetch(`/api/content/${
|
|
127
|
-
l.ok ? (
|
|
137
|
+
const l = await fetch(`/api/content/${p.value}:${a.value}`);
|
|
138
|
+
l.ok ? (b(await l.json()), w()) : (b(null), i.value = !0);
|
|
128
139
|
} catch {
|
|
129
|
-
|
|
140
|
+
b(null), i.value = !0;
|
|
130
141
|
} finally {
|
|
131
142
|
r.value = !1;
|
|
132
143
|
}
|
|
133
144
|
}
|
|
134
|
-
function
|
|
135
|
-
if (!
|
|
136
|
-
if (
|
|
137
|
-
const
|
|
138
|
-
document.title = `${
|
|
145
|
+
function w() {
|
|
146
|
+
if (!n.value) return;
|
|
147
|
+
if (n.value.title) {
|
|
148
|
+
const f = s?.config?.branding, d = f?.siteName ? ` — ${f.siteName}` : "";
|
|
149
|
+
document.title = `${n.value.title}${d}`;
|
|
139
150
|
}
|
|
140
|
-
const l =
|
|
151
|
+
const l = n.value.meta?.description;
|
|
141
152
|
if (l) {
|
|
142
|
-
const
|
|
143
|
-
|
|
153
|
+
const f = document.querySelector('meta[name="description"]');
|
|
154
|
+
f && f.setAttribute("content", l);
|
|
144
155
|
}
|
|
145
156
|
}
|
|
146
|
-
return
|
|
147
|
-
l?.slug ===
|
|
148
|
-
...
|
|
157
|
+
return _(t, (l) => {
|
|
158
|
+
l?.slug === c.value && (r.value = !1, i.value = !1, (l.title || l.blocks) && (n.value = {
|
|
159
|
+
...n.value || {},
|
|
149
160
|
...l.title ? { title: l.title } : {},
|
|
150
161
|
...l.blocks ? { blocks: l.blocks } : {}
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
|
|
154
|
-
}),
|
|
155
|
-
|
|
162
|
+
}, w()));
|
|
163
|
+
}), S(() => {
|
|
164
|
+
y() ? r.value = !1 : h();
|
|
165
|
+
}), _(a, () => h()), _(c, () => {
|
|
166
|
+
y() ? r.value = !1 : h();
|
|
156
167
|
}), {
|
|
157
|
-
page:
|
|
158
|
-
blocks:
|
|
159
|
-
pageContext:
|
|
168
|
+
page: n,
|
|
169
|
+
blocks: g,
|
|
170
|
+
pageContext: m,
|
|
160
171
|
loading: r,
|
|
161
172
|
notFound: i,
|
|
162
173
|
isPreviewMode: e,
|
|
163
|
-
reload:
|
|
174
|
+
reload: h
|
|
164
175
|
};
|
|
165
176
|
}
|
|
166
|
-
function
|
|
167
|
-
const { locale: o } =
|
|
168
|
-
async function
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
function te() {
|
|
178
|
+
const { locale: o } = T(), a = window.__SITE_DATA__, t = a?.locale, e = v({ ...a?.components || {} });
|
|
179
|
+
async function s(r) {
|
|
180
|
+
if (!$()) {
|
|
181
|
+
if (r === t) {
|
|
182
|
+
e.value = { ...a?.components || {} };
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const i = await fetch(`/api/content/components:${r}`);
|
|
187
|
+
i.ok && (e.value = await i.json());
|
|
188
|
+
} catch {
|
|
189
|
+
}
|
|
177
190
|
}
|
|
178
191
|
}
|
|
179
|
-
|
|
180
|
-
o.value !== t &&
|
|
192
|
+
_(o, (r) => s(r)), S(() => {
|
|
193
|
+
o.value !== t && s(o.value);
|
|
181
194
|
});
|
|
182
|
-
function
|
|
183
|
-
return
|
|
195
|
+
function n(r) {
|
|
196
|
+
return u(() => e.value[r] || null);
|
|
184
197
|
}
|
|
185
198
|
return {
|
|
186
199
|
/** All components (reactive, locale-aware) */
|
|
187
200
|
components: e,
|
|
188
201
|
/** Get a single component by key */
|
|
189
|
-
getComponent:
|
|
202
|
+
getComponent: n,
|
|
190
203
|
/** Force refetch for current locale */
|
|
191
|
-
reload: () =>
|
|
204
|
+
reload: () => s(o.value)
|
|
192
205
|
};
|
|
193
206
|
}
|
|
194
|
-
function
|
|
195
|
-
const { locale: o } =
|
|
196
|
-
function
|
|
207
|
+
function ae() {
|
|
208
|
+
const { locale: o } = T(), a = window.__SITE_DATA__, t = u(() => a?.config?.locales || []), e = u(() => a?.config?.defaultLocale || "en"), s = u(() => t.value.length > 1);
|
|
209
|
+
function n(i) {
|
|
197
210
|
o.value = i, localStorage.setItem("x-site-locale", i);
|
|
198
211
|
}
|
|
199
212
|
function r(i) {
|
|
@@ -203,17 +216,17 @@ function Q() {
|
|
|
203
216
|
locale: o,
|
|
204
217
|
locales: t,
|
|
205
218
|
defaultLocale: e,
|
|
206
|
-
hasMultipleLocales:
|
|
207
|
-
switchLocale:
|
|
219
|
+
hasMultipleLocales: s,
|
|
220
|
+
switchLocale: n,
|
|
208
221
|
resolveText: r
|
|
209
222
|
};
|
|
210
223
|
}
|
|
211
|
-
const
|
|
224
|
+
const O = {
|
|
212
225
|
sm: 600,
|
|
213
226
|
md: 960,
|
|
214
227
|
lg: 1280,
|
|
215
228
|
xl: 1920
|
|
216
|
-
},
|
|
229
|
+
}, z = {
|
|
217
230
|
grid: {
|
|
218
231
|
default: {
|
|
219
232
|
areas: ["header", "main", "footer"],
|
|
@@ -225,7 +238,7 @@ const R = {
|
|
|
225
238
|
main: { type: "slot" },
|
|
226
239
|
footer: { component: "footer" }
|
|
227
240
|
}
|
|
228
|
-
},
|
|
241
|
+
}, F = {
|
|
229
242
|
grid: {
|
|
230
243
|
default: {
|
|
231
244
|
areas: ["main"]
|
|
@@ -235,61 +248,67 @@ const R = {
|
|
|
235
248
|
main: { type: "slot" }
|
|
236
249
|
}
|
|
237
250
|
};
|
|
238
|
-
function
|
|
251
|
+
function x(o, a) {
|
|
239
252
|
const t = [];
|
|
240
253
|
if (o.areas?.length) {
|
|
241
|
-
const e = o.areas.map((
|
|
254
|
+
const e = o.areas.map((s) => `"${s}"`).join(" ");
|
|
242
255
|
t.push(`grid-template-areas: ${e}`);
|
|
243
256
|
}
|
|
244
257
|
return o.columns && t.push(`grid-template-columns: ${o.columns}`), o.rows && t.push(`grid-template-rows: ${o.rows}`), o.gap && t.push(`gap: ${o.gap}`), t.length ? `${a} { ${t.join("; ")}; }` : "";
|
|
245
258
|
}
|
|
246
|
-
function
|
|
259
|
+
function G(o, a) {
|
|
247
260
|
const t = [`${a} { display: grid; min-height: 100vh; }`], e = o.grid;
|
|
248
261
|
if (!e) return t.join(`
|
|
249
262
|
`);
|
|
250
263
|
if (e.default) {
|
|
251
|
-
const
|
|
252
|
-
|
|
264
|
+
const s = x(e.default, a);
|
|
265
|
+
s && t.push(s);
|
|
253
266
|
}
|
|
254
|
-
for (const
|
|
255
|
-
const
|
|
256
|
-
if (!
|
|
257
|
-
const r =
|
|
258
|
-
r && t.push(`@media (min-width: ${
|
|
267
|
+
for (const s of ["sm", "md", "lg", "xl"]) {
|
|
268
|
+
const n = e[s];
|
|
269
|
+
if (!n) continue;
|
|
270
|
+
const r = x(n, a);
|
|
271
|
+
r && t.push(`@media (min-width: ${O[s]}px) { ${r} }`);
|
|
259
272
|
}
|
|
260
273
|
return t.join(`
|
|
261
274
|
`);
|
|
262
275
|
}
|
|
263
|
-
function
|
|
264
|
-
const o =
|
|
276
|
+
function oe() {
|
|
277
|
+
const o = I(), { previewPage: a, isPreviewMode: t } = C(), e = window.__SITE_DATA__, s = `dl-${Math.random().toString(36).slice(2, 8)}`, n = u(() => ["dynamic-layout", s]), r = u(() => {
|
|
265
278
|
if (o.path === "/_preview_")
|
|
266
279
|
return a.value?.layout || "landing";
|
|
267
280
|
if (t.value && a.value?.layout)
|
|
268
281
|
return a.value.layout;
|
|
269
|
-
const
|
|
270
|
-
return e?.[
|
|
271
|
-
}), i =
|
|
272
|
-
const
|
|
273
|
-
if (
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
282
|
+
const p = o.path === "/" ? "page:home" : `page:${o.params.slug || ""}`;
|
|
283
|
+
return (k[p] || e?.[p])?.layout || "default";
|
|
284
|
+
}), i = u(() => {
|
|
285
|
+
const p = e?.config?.layouts;
|
|
286
|
+
if (p) {
|
|
287
|
+
const m = p[r.value] || p.default;
|
|
288
|
+
if (m?.zones) return m;
|
|
276
289
|
}
|
|
277
|
-
return r.value === "landing" ?
|
|
278
|
-
}),
|
|
290
|
+
return r.value === "landing" ? F : z;
|
|
291
|
+
}), c = u(() => G(i.value, `.${s}`));
|
|
279
292
|
return {
|
|
280
293
|
layoutName: r,
|
|
281
294
|
layoutConfig: i,
|
|
282
|
-
layoutCSS:
|
|
283
|
-
layoutClass:
|
|
295
|
+
layoutCSS: c,
|
|
296
|
+
layoutClass: n
|
|
284
297
|
};
|
|
285
298
|
}
|
|
286
|
-
const
|
|
299
|
+
const ne = {
|
|
287
300
|
type: "layout",
|
|
288
301
|
displayName: "Layout",
|
|
289
302
|
description: "CSS Grid layout with responsive breakpoints and component zones",
|
|
290
303
|
fields: [
|
|
291
304
|
// Mobile (default)
|
|
292
|
-
{
|
|
305
|
+
{
|
|
306
|
+
key: "grid.default.areas",
|
|
307
|
+
type: "grid-areas",
|
|
308
|
+
label: "Grid Areas (mobile)",
|
|
309
|
+
breakpoint: "default",
|
|
310
|
+
required: !0
|
|
311
|
+
},
|
|
293
312
|
{ key: "grid.default.columns", type: "text", label: "Columns (mobile)", breakpoint: "default" },
|
|
294
313
|
{ key: "grid.default.rows", type: "text", label: "Rows (mobile)", breakpoint: "default" },
|
|
295
314
|
{ key: "grid.default.gap", type: "text", label: "Gap (mobile)", breakpoint: "default" },
|
|
@@ -317,7 +336,7 @@ const ee = {
|
|
|
317
336
|
{ key: "class", type: "text", label: "CSS Class" },
|
|
318
337
|
{ key: "container", type: "switch", label: "Wrap in Container" }
|
|
319
338
|
]
|
|
320
|
-
},
|
|
339
|
+
}, se = {
|
|
321
340
|
default: {
|
|
322
341
|
displayName: "Full Width",
|
|
323
342
|
config: {
|
|
@@ -375,7 +394,12 @@ const ee = {
|
|
|
375
394
|
config: {
|
|
376
395
|
grid: {
|
|
377
396
|
default: { areas: ["header", "main", "aside", "footer"], columns: "1fr", rows: "auto 1fr auto auto" },
|
|
378
|
-
md: {
|
|
397
|
+
md: {
|
|
398
|
+
areas: ["header header header", "main main aside", "footer footer footer"],
|
|
399
|
+
columns: "1fr 1fr 300px",
|
|
400
|
+
rows: "auto 1fr auto",
|
|
401
|
+
gap: "24px"
|
|
402
|
+
}
|
|
379
403
|
},
|
|
380
404
|
zones: {
|
|
381
405
|
header: { component: "nav-menu" },
|
|
@@ -385,27 +409,27 @@ const ee = {
|
|
|
385
409
|
}
|
|
386
410
|
}
|
|
387
411
|
}
|
|
388
|
-
},
|
|
389
|
-
function
|
|
412
|
+
}, M = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|webp|avif|map|json|txt|xml|webmanifest)$/;
|
|
413
|
+
function U(o, a) {
|
|
390
414
|
const e = new URL(o.url).searchParams.get("lang");
|
|
391
415
|
if (e && a.supportedLocales?.includes(e)) return e;
|
|
392
416
|
if (a.supportedLocales?.length) {
|
|
393
|
-
const
|
|
394
|
-
for (const
|
|
395
|
-
if (
|
|
417
|
+
const s = o.headers.get("Accept-Language") || "";
|
|
418
|
+
for (const n of a.supportedLocales)
|
|
419
|
+
if (s.includes(n)) return n;
|
|
396
420
|
}
|
|
397
421
|
return a.defaultLocale;
|
|
398
422
|
}
|
|
399
|
-
function
|
|
423
|
+
function K(o, a, t) {
|
|
400
424
|
if (!o.pathname.startsWith("/api/content/")) return null;
|
|
401
425
|
const e = decodeURIComponent(o.pathname.replace("/api/content/", ""));
|
|
402
426
|
return e ? (async () => {
|
|
403
|
-
let
|
|
404
|
-
if (!
|
|
405
|
-
const
|
|
406
|
-
|
|
427
|
+
let s = await a.SITE_CONTENT.get(e);
|
|
428
|
+
if (!s) {
|
|
429
|
+
const n = e.lastIndexOf(":");
|
|
430
|
+
n > 0 && e.slice(n + 1) !== t && (s = await a.SITE_CONTENT.get(e.slice(0, n + 1) + t));
|
|
407
431
|
}
|
|
408
|
-
return
|
|
432
|
+
return s ? new Response(s, {
|
|
409
433
|
headers: {
|
|
410
434
|
"Content-Type": "application/json",
|
|
411
435
|
"Cache-Control": "public, max-age=60",
|
|
@@ -414,54 +438,54 @@ function G(o, a, t) {
|
|
|
414
438
|
}) : Response.json({ error: "Not found" }, { status: 404 });
|
|
415
439
|
})() : Promise.resolve(Response.json({ error: "Key is required" }, { status: 400 }));
|
|
416
440
|
}
|
|
417
|
-
function
|
|
441
|
+
function A(o, a, t) {
|
|
418
442
|
let e = o;
|
|
419
443
|
if (a.title) {
|
|
420
|
-
const
|
|
421
|
-
e = e.replace(/<title>[^<]*<\/title>/, `<title>${a.title}${
|
|
444
|
+
const s = t ? ` — ${t}` : "";
|
|
445
|
+
e = e.replace(/<title>[^<]*<\/title>/, `<title>${a.title}${s}</title>`);
|
|
422
446
|
}
|
|
423
447
|
return a.meta?.description && (e = e.replace(/(<meta\s+name="description"\s+content=")[^"]*(")/, `$1${a.meta.description}$2`)), a.meta?.ogTitle && (e = e.replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/, `$1${a.meta.ogTitle}$2`)), a.html && (e = e.replace('<div id="app"></div>', `<div id="app">${a.html}</div>`)), e;
|
|
424
448
|
}
|
|
425
|
-
function
|
|
449
|
+
function re(o) {
|
|
426
450
|
return {
|
|
427
451
|
async fetch(a, t) {
|
|
428
|
-
const e = new URL(a.url),
|
|
429
|
-
if (
|
|
430
|
-
if (
|
|
452
|
+
const e = new URL(a.url), s = K(e, t, o.defaultLocale);
|
|
453
|
+
if (s) return s;
|
|
454
|
+
if (M.test(e.pathname))
|
|
431
455
|
return t.ASSETS.fetch(a);
|
|
432
|
-
const
|
|
433
|
-
let
|
|
434
|
-
const
|
|
456
|
+
const n = U(a, o), r = new URL("/index.html", a.url);
|
|
457
|
+
let c = await (await t.ASSETS.fetch(new Request(r))).text();
|
|
458
|
+
const p = o.defaultLocale, [m, g] = await Promise.all([
|
|
435
459
|
t.SITE_CONTENT.get("config"),
|
|
436
|
-
t.SITE_CONTENT.get(`content:${
|
|
437
|
-
]),
|
|
438
|
-
let
|
|
439
|
-
if (!
|
|
440
|
-
const
|
|
441
|
-
|
|
460
|
+
t.SITE_CONTENT.get(`content:${n}`)
|
|
461
|
+
]), b = m ? JSON.parse(m) : {};
|
|
462
|
+
let y = g ? JSON.parse(g) : {};
|
|
463
|
+
if (!g && n !== p) {
|
|
464
|
+
const f = await t.SITE_CONTENT.get(`content:${p}`);
|
|
465
|
+
f && (y = JSON.parse(f));
|
|
442
466
|
}
|
|
443
467
|
const h = e.pathname.match(/^\/p\/(.+)$/);
|
|
444
468
|
if (h) {
|
|
445
|
-
const
|
|
446
|
-
if (!
|
|
447
|
-
let
|
|
448
|
-
!
|
|
469
|
+
const f = h[1];
|
|
470
|
+
if (!y[f]) {
|
|
471
|
+
let d = await t.SITE_CONTENT.get(`page:${f}:${n}`);
|
|
472
|
+
!d && n !== p && (d = await t.SITE_CONTENT.get(`page:${f}:${p}`)), d && (y[f] = JSON.parse(d));
|
|
449
473
|
}
|
|
450
474
|
}
|
|
451
|
-
const
|
|
452
|
-
locale:
|
|
453
|
-
config:
|
|
475
|
+
const w = {
|
|
476
|
+
locale: n,
|
|
477
|
+
config: b
|
|
454
478
|
};
|
|
455
|
-
for (const [
|
|
456
|
-
|
|
479
|
+
for (const [f, d] of Object.entries(y))
|
|
480
|
+
w[`page:${f}`] = d;
|
|
457
481
|
if (h) {
|
|
458
|
-
const
|
|
459
|
-
|
|
482
|
+
const f = h[1], d = w[`page:${f}`];
|
|
483
|
+
d && (c = A(c, d, o.siteName));
|
|
460
484
|
}
|
|
461
|
-
e.pathname === "/" &&
|
|
462
|
-
const
|
|
463
|
-
return
|
|
464
|
-
</head>`), new Response(
|
|
485
|
+
e.pathname === "/" && y.home && (c = A(c, y.home, o.siteName));
|
|
486
|
+
const l = `<script>window.__SITE_DATA__ = ${JSON.stringify(w)};<\/script>`;
|
|
487
|
+
return c = c.replace("</head>", `${l}
|
|
488
|
+
</head>`), new Response(c, {
|
|
465
489
|
headers: {
|
|
466
490
|
"Content-Type": "text/html;charset=utf-8",
|
|
467
491
|
"Cache-Control": "public, max-age=60"
|
|
@@ -471,20 +495,20 @@ function ae(o) {
|
|
|
471
495
|
};
|
|
472
496
|
}
|
|
473
497
|
export {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
498
|
+
R as PAGE_CONTEXT_KEY,
|
|
499
|
+
G as buildLayoutCSS,
|
|
500
|
+
re as createSiteWorker,
|
|
501
|
+
$ as isInPreview,
|
|
502
|
+
se as layoutPresets,
|
|
503
|
+
ne as layoutSchema,
|
|
504
|
+
V as setupPreviewRouter,
|
|
505
|
+
oe as useDynamicLayout,
|
|
506
|
+
C as useLivePreview,
|
|
507
|
+
ae as useLocaleSwitcher,
|
|
508
|
+
ee as usePageData,
|
|
509
|
+
Q as useSiteApi,
|
|
510
|
+
te as useSiteComponents,
|
|
511
|
+
q as useSiteConfig,
|
|
512
|
+
Z as useSiteData,
|
|
513
|
+
H as useSkin
|
|
490
514
|
};
|
package/dist/types/blocks.d.ts
CHANGED
|
@@ -18,7 +18,8 @@ export interface HeroBlockData {
|
|
|
18
18
|
badge?: string;
|
|
19
19
|
image?: string;
|
|
20
20
|
backgroundImage?: string;
|
|
21
|
-
|
|
21
|
+
/** Overlay: false to disable, number (0-1) for opacity, or CSS string for custom gradient */
|
|
22
|
+
overlay?: boolean | number | string;
|
|
22
23
|
buttons?: Array<{
|
|
23
24
|
text: string;
|
|
24
25
|
url: string;
|
package/dist/types/layout.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xosen/site-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Shared Vue components and composables for Xosen site templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,23 +24,23 @@
|
|
|
24
24
|
"dist",
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
29
|
+
"dev": "vite build --watch",
|
|
30
|
+
"clean": "rm -rf dist"
|
|
31
|
+
},
|
|
27
32
|
"peerDependencies": {
|
|
28
33
|
"vue": "^3.5.0",
|
|
29
34
|
"vue-i18n": "^11.0.0",
|
|
30
35
|
"vue-router": "^5.0.0"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
|
-
"vue": "
|
|
34
|
-
"vue-i18n": "
|
|
35
|
-
"vue-router": "
|
|
36
|
-
"vue-tsc": "
|
|
37
|
-
"typescript": "
|
|
38
|
-
"vite": "
|
|
39
|
-
"@vitejs/plugin-vue": "
|
|
40
|
-
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
43
|
-
"dev": "vite build --watch",
|
|
44
|
-
"clean": "rm -rf dist"
|
|
38
|
+
"vue": "catalog:",
|
|
39
|
+
"vue-i18n": "catalog:",
|
|
40
|
+
"vue-router": "catalog:",
|
|
41
|
+
"vue-tsc": "catalog:",
|
|
42
|
+
"typescript": "catalog:",
|
|
43
|
+
"vite": "catalog:",
|
|
44
|
+
"@vitejs/plugin-vue": "catalog:"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { reactive } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared reactive store for page data loaded via usePageData.
|
|
5
|
+
* Allows useDynamicLayout to reactively read layout from fetched pages.
|
|
6
|
+
*/
|
|
7
|
+
export const pageDataStore = reactive<Record<string, any>>({});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Initialize store with preloaded data from __SITE_DATA__.
|
|
11
|
+
*/
|
|
12
|
+
export function initPageDataStore() {
|
|
13
|
+
const siteData = (window as any).__SITE_DATA__;
|
|
14
|
+
if (!siteData) return;
|
|
15
|
+
|
|
16
|
+
for (const key of Object.keys(siteData)) {
|
|
17
|
+
if (key.startsWith('page:')) {
|
|
18
|
+
pageDataStore[key] = siteData[key];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Auto-init on import
|
|
24
|
+
initPageDataStore();
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { computed
|
|
1
|
+
import { computed } from 'vue';
|
|
2
2
|
import { useRoute } from 'vue-router';
|
|
3
3
|
|
|
4
4
|
import type { SiteData } from '../types/config.js';
|
|
5
5
|
import type { GridBreakpoint, LayoutConfig } from '../types/layout.js';
|
|
6
6
|
import { useLivePreview } from './useLivePreview.js';
|
|
7
|
+
import { pageDataStore } from './pageDataStore.js';
|
|
7
8
|
|
|
8
9
|
const BREAKPOINTS: Record<string, number> = {
|
|
9
10
|
sm: 600,
|
|
@@ -102,7 +103,8 @@ export function useDynamicLayout() {
|
|
|
102
103
|
return previewPage.value.layout;
|
|
103
104
|
}
|
|
104
105
|
const pageKey = route.path === '/' ? 'page:home' : `page:${route.params.slug || ''}`;
|
|
105
|
-
|
|
106
|
+
// Read from reactive store (updated by usePageData on fetch)
|
|
107
|
+
const pageData = pageDataStore[pageKey] || siteData?.[pageKey];
|
|
106
108
|
return pageData?.layout || 'default';
|
|
107
109
|
});
|
|
108
110
|
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { ref, onMounted, onUnmounted } from 'vue';
|
|
2
2
|
import type { Router } from 'vue-router';
|
|
3
3
|
|
|
4
|
+
export interface PreviewSiteData {
|
|
5
|
+
config?: Record<string, any>;
|
|
6
|
+
skin?: Record<string, any>;
|
|
7
|
+
components?: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
export interface PreviewMessage {
|
|
5
11
|
type: 'xosen-preview-update';
|
|
12
|
+
site?: PreviewSiteData;
|
|
6
13
|
page: {
|
|
7
14
|
slug: string;
|
|
8
15
|
locale: string;
|
|
@@ -45,12 +52,16 @@ export function setupPreviewRouter(router: Router) {
|
|
|
45
52
|
*/
|
|
46
53
|
export function useLivePreview() {
|
|
47
54
|
const previewPage = ref<PreviewMessage['page'] | null>(null);
|
|
55
|
+
const previewSite = ref<PreviewSiteData | null>(null);
|
|
48
56
|
const isPreviewMode = ref(isInPreview());
|
|
49
57
|
|
|
50
58
|
function handleMessage(event: MessageEvent) {
|
|
51
59
|
const data = event.data;
|
|
52
60
|
if (data?.type === 'xosen-preview-update' && data.page) {
|
|
53
61
|
previewPage.value = data.page;
|
|
62
|
+
if (data.site) {
|
|
63
|
+
previewSite.value = data.site;
|
|
64
|
+
}
|
|
54
65
|
isPreviewMode.value = true;
|
|
55
66
|
}
|
|
56
67
|
}
|
|
@@ -65,6 +76,7 @@ export function useLivePreview() {
|
|
|
65
76
|
|
|
66
77
|
return {
|
|
67
78
|
previewPage,
|
|
79
|
+
previewSite,
|
|
68
80
|
isPreviewMode,
|
|
69
81
|
};
|
|
70
82
|
}
|
|
@@ -5,6 +5,7 @@ import type { Block } from '../types/blocks.js';
|
|
|
5
5
|
import type { PageConfig, PageContext, SiteData } from '../types/config.js';
|
|
6
6
|
import { PAGE_CONTEXT_KEY } from '../types/config.js';
|
|
7
7
|
import { useLivePreview } from './useLivePreview.js';
|
|
8
|
+
import { pageDataStore } from './pageDataStore.js';
|
|
8
9
|
|
|
9
10
|
export interface UsePageDataReturn {
|
|
10
11
|
/** Resolved page data (from preload, API, or preview) */
|
|
@@ -39,14 +40,14 @@ export function usePageData(slug: Ref<string> | string): UsePageDataReturn {
|
|
|
39
40
|
const loading = ref(true);
|
|
40
41
|
const notFound = ref(false);
|
|
41
42
|
|
|
42
|
-
const resolvedSlug = computed(() => typeof slug === 'string' ? slug : slug.value);
|
|
43
|
+
const resolvedSlug = computed(() => (typeof slug === 'string' ? slug : slug.value));
|
|
43
44
|
const pageKey = computed(() => `page:${resolvedSlug.value}`);
|
|
44
45
|
|
|
45
46
|
// Page context: page metadata without blocks (for injection)
|
|
46
47
|
const pageContext = computed<PageContext>(() => {
|
|
47
48
|
const p = isPreviewMode.value ? previewPage.value : page.value;
|
|
48
49
|
if (!p) return {};
|
|
49
|
-
const { blocks, meta, slug: _s, locale: _l, ...rest } = p as any;
|
|
50
|
+
const { blocks: _blocks, meta: _meta, slug: _s, locale: _l, ...rest } = p as any;
|
|
50
51
|
return rest;
|
|
51
52
|
});
|
|
52
53
|
|
|
@@ -61,10 +62,17 @@ export function usePageData(slug: Ref<string> | string): UsePageDataReturn {
|
|
|
61
62
|
return page.value?.blocks || [];
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
function setPage(data: PageConfig | null) {
|
|
66
|
+
page.value = data;
|
|
67
|
+
if (data) {
|
|
68
|
+
pageDataStore[pageKey.value] = data;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
function loadFromPreloaded(): boolean {
|
|
65
73
|
const preloaded = siteData?.[pageKey.value];
|
|
66
74
|
if (preloaded && locale.value === siteData?.locale) {
|
|
67
|
-
|
|
75
|
+
setPage(preloaded as PageConfig);
|
|
68
76
|
notFound.value = false;
|
|
69
77
|
applyMeta();
|
|
70
78
|
return true;
|
|
@@ -78,14 +86,14 @@ export function usePageData(slug: Ref<string> | string): UsePageDataReturn {
|
|
|
78
86
|
try {
|
|
79
87
|
const res = await fetch(`/api/content/${pageKey.value}:${locale.value}`);
|
|
80
88
|
if (res.ok) {
|
|
81
|
-
|
|
89
|
+
setPage(await res.json());
|
|
82
90
|
applyMeta();
|
|
83
91
|
} else {
|
|
84
|
-
|
|
92
|
+
setPage(null);
|
|
85
93
|
notFound.value = true;
|
|
86
94
|
}
|
|
87
95
|
} catch {
|
|
88
|
-
|
|
96
|
+
setPage(null);
|
|
89
97
|
notFound.value = true;
|
|
90
98
|
} finally {
|
|
91
99
|
loading.value = false;
|
|
@@ -113,7 +121,7 @@ export function usePageData(slug: Ref<string> | string): UsePageDataReturn {
|
|
|
113
121
|
notFound.value = false;
|
|
114
122
|
if (p.title || p.blocks) {
|
|
115
123
|
page.value = {
|
|
116
|
-
...(page.value || {} as PageConfig),
|
|
124
|
+
...(page.value || ({} as PageConfig)),
|
|
117
125
|
...(p.title ? { title: p.title } : {}),
|
|
118
126
|
...(p.blocks ? { blocks: p.blocks as Block[] } : {}),
|
|
119
127
|
};
|
|
@@ -2,6 +2,7 @@ import { ref, computed, watch, onMounted, type Ref } from 'vue';
|
|
|
2
2
|
import { useI18n } from 'vue-i18n';
|
|
3
3
|
|
|
4
4
|
import type { SiteData } from '../types/config.js';
|
|
5
|
+
import { isInPreview } from './useLivePreview.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Locale-aware access to site components (navigation, footer, sidebar, etc.).
|
|
@@ -14,12 +15,15 @@ export function useSiteComponents() {
|
|
|
14
15
|
const siteData = (window as any).__SITE_DATA__ as SiteData | undefined;
|
|
15
16
|
const initialLocale = siteData?.locale;
|
|
16
17
|
|
|
17
|
-
const components = ref<Record<string, any>>({ ...(siteData as any)?.components || {} });
|
|
18
|
+
const components = ref<Record<string, any>>({ ...((siteData as any)?.components || {}) });
|
|
18
19
|
|
|
19
20
|
async function fetchComponents(loc: string) {
|
|
21
|
+
// Skip fetching in preview mode — components come via postMessage
|
|
22
|
+
if (isInPreview()) return;
|
|
23
|
+
|
|
20
24
|
// If same as initial locale, use preloaded data
|
|
21
25
|
if (loc === initialLocale) {
|
|
22
|
-
components.value = { ...(siteData as any)?.components || {} };
|
|
26
|
+
components.value = { ...((siteData as any)?.components || {}) };
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
25
29
|
try {
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { useLocaleSwitcher } from './composables/useLocaleSwitcher.js';
|
|
|
11
11
|
export { useDynamicLayout, buildLayoutCSS } from './composables/useDynamicLayout.js';
|
|
12
12
|
export type { LayoutConfig, LayoutZone, GridBreakpoint, LayoutSchemaDefinition, LayoutField } from './types/layout.js';
|
|
13
13
|
export { layoutSchema, layoutPresets } from './types/layout.js';
|
|
14
|
-
export type { PreviewMessage } from './composables/useLivePreview.js';
|
|
14
|
+
export type { PreviewMessage, PreviewSiteData } from './composables/useLivePreview.js';
|
|
15
15
|
|
|
16
16
|
// Worker
|
|
17
17
|
export { createSiteWorker } from './worker/index.js';
|
package/src/types/blocks.ts
CHANGED
|
@@ -20,7 +20,8 @@ export interface HeroBlockData {
|
|
|
20
20
|
badge?: string;
|
|
21
21
|
image?: string;
|
|
22
22
|
backgroundImage?: string;
|
|
23
|
-
|
|
23
|
+
/** Overlay: false to disable, number (0-1) for opacity, or CSS string for custom gradient */
|
|
24
|
+
overlay?: boolean | number | string;
|
|
24
25
|
buttons?: Array<{ text: string; url: string; variant?: 'primary' | 'outline' | 'white' }>;
|
|
25
26
|
stats?: Array<{ value: string; label: string }>;
|
|
26
27
|
}
|
package/src/types/layout.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface LayoutZone {
|
|
|
14
14
|
type?: 'slot';
|
|
15
15
|
component?: string;
|
|
16
16
|
class?: string;
|
|
17
|
+
style?: Record<string, string>;
|
|
17
18
|
container?: boolean;
|
|
18
19
|
containerStyle?: Record<string, string>;
|
|
19
20
|
}
|
|
@@ -50,7 +51,13 @@ export const layoutSchema: LayoutSchemaDefinition = {
|
|
|
50
51
|
description: 'CSS Grid layout with responsive breakpoints and component zones',
|
|
51
52
|
fields: [
|
|
52
53
|
// Mobile (default)
|
|
53
|
-
{
|
|
54
|
+
{
|
|
55
|
+
key: 'grid.default.areas',
|
|
56
|
+
type: 'grid-areas',
|
|
57
|
+
label: 'Grid Areas (mobile)',
|
|
58
|
+
breakpoint: 'default',
|
|
59
|
+
required: true,
|
|
60
|
+
},
|
|
54
61
|
{ key: 'grid.default.columns', type: 'text', label: 'Columns (mobile)', breakpoint: 'default' },
|
|
55
62
|
{ key: 'grid.default.rows', type: 'text', label: 'Rows (mobile)', breakpoint: 'default' },
|
|
56
63
|
{ key: 'grid.default.gap', type: 'text', label: 'Gap (mobile)', breakpoint: 'default' },
|
|
@@ -145,7 +152,12 @@ export const layoutPresets: Record<string, { displayName: string; config: Layout
|
|
|
145
152
|
config: {
|
|
146
153
|
grid: {
|
|
147
154
|
default: { areas: ['header', 'main', 'aside', 'footer'], columns: '1fr', rows: 'auto 1fr auto auto' },
|
|
148
|
-
md: {
|
|
155
|
+
md: {
|
|
156
|
+
areas: ['header header header', 'main main aside', 'footer footer footer'],
|
|
157
|
+
columns: '1fr 1fr 300px',
|
|
158
|
+
rows: 'auto 1fr auto',
|
|
159
|
+
gap: '24px',
|
|
160
|
+
},
|
|
149
161
|
},
|
|
150
162
|
zones: {
|
|
151
163
|
header: { component: 'nav-menu' },
|