@xosen/site-sdk 0.0.10 → 0.0.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/dist/composables/useLocaleSwitcher.d.ts +12 -0
- package/dist/composables/useSiteComponents.d.ts +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +122 -75
- package/package.json +1 -1
- package/src/composables/useLocaleSwitcher.ts +40 -0
- package/src/composables/useSiteComponents.ts +59 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale switcher composable.
|
|
3
|
+
* Manages locale state, available locales, and persistence.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useLocaleSwitcher(): {
|
|
6
|
+
locale: import("vue").WritableComputedRef<string, string>;
|
|
7
|
+
locales: import("vue").ComputedRef<string[]>;
|
|
8
|
+
defaultLocale: import("vue").ComputedRef<string>;
|
|
9
|
+
hasMultipleLocales: import("vue").ComputedRef<boolean>;
|
|
10
|
+
switchLocale: (loc: string) => void;
|
|
11
|
+
resolveText: (text: string | Record<string, string>) => string;
|
|
12
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Ref } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Locale-aware access to site components (navigation, footer, sidebar, etc.).
|
|
4
|
+
*
|
|
5
|
+
* On initial load, reads from preloaded __SITE_DATA__.components.
|
|
6
|
+
* When locale changes, fetches updated components from the API.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useSiteComponents(): {
|
|
9
|
+
/** All components (reactive, locale-aware) */
|
|
10
|
+
components: Ref<Record<string, any>, Record<string, any>>;
|
|
11
|
+
/** Get a single component by key */
|
|
12
|
+
getComponent: <T = any>(key: string) => Ref<T | null>;
|
|
13
|
+
/** Force refetch for current locale */
|
|
14
|
+
reload: () => Promise<void>;
|
|
15
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export { useSiteApi } from './composables/useSiteApi.js';
|
|
|
5
5
|
export { useLivePreview, setupPreviewRouter, isInPreview } from './composables/useLivePreview.js';
|
|
6
6
|
export { usePageData } from './composables/usePageData.js';
|
|
7
7
|
export type { UsePageDataReturn } from './composables/usePageData.js';
|
|
8
|
+
export { useSiteComponents } from './composables/useSiteComponents.js';
|
|
9
|
+
export { useLocaleSwitcher } from './composables/useLocaleSwitcher.js';
|
|
8
10
|
export type { PreviewMessage } from './composables/useLivePreview.js';
|
|
9
11
|
export { createSiteWorker } from './worker/index.js';
|
|
10
12
|
export type { WorkerEnv, WorkerConfig, PageData } from './worker/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { ref as y, onMounted as
|
|
2
|
-
import { useI18n as
|
|
1
|
+
import { ref as y, onMounted as T, watch as _, computed as u, onUnmounted as C, provide as L } from "vue";
|
|
2
|
+
import { useI18n as $ } from "vue-i18n";
|
|
3
3
|
function k(o, n) {
|
|
4
|
-
const t = y(null), { locale: e } =
|
|
4
|
+
const t = y(null), { locale: e } = $(), s = window.__SITE_DATA__, a = s?.locale;
|
|
5
5
|
function l() {
|
|
6
6
|
return s && s[o] && e.value === a ? (t.value = s[o], !0) : !1;
|
|
7
7
|
}
|
|
8
|
-
async function
|
|
8
|
+
async function i() {
|
|
9
9
|
try {
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const c = await fetch(`/api/content/${n}:${e.value}`);
|
|
11
|
+
c.ok && (t.value = await c.json());
|
|
12
12
|
} catch {
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
return
|
|
16
|
-
l() ||
|
|
15
|
+
return T(() => {
|
|
16
|
+
l() || i();
|
|
17
17
|
}), _(e, () => {
|
|
18
|
-
|
|
18
|
+
i();
|
|
19
19
|
}), t;
|
|
20
20
|
}
|
|
21
21
|
function M() {
|
|
22
|
-
const o = window.__SITE_DATA__, n =
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
return typeof
|
|
22
|
+
const o = window.__SITE_DATA__, n = u(() => o?.config || null), t = u(() => o?.components || {}), e = u(() => n.value?.navigation || []), s = u(() => n.value?.locales || []), a = u(() => n.value?.defaultLocale || "en"), l = u(() => n.value?.branding || {}), i = u(() => n.value?.footer || {}), c = u(() => n.value?.features || {});
|
|
23
|
+
function v(w) {
|
|
24
|
+
const g = c.value[w];
|
|
25
|
+
return typeof g == "boolean" ? g : typeof g == "object";
|
|
26
26
|
}
|
|
27
27
|
return {
|
|
28
28
|
config: n,
|
|
@@ -31,9 +31,9 @@ function M() {
|
|
|
31
31
|
locales: s,
|
|
32
32
|
defaultLocale: a,
|
|
33
33
|
branding: l,
|
|
34
|
-
footer:
|
|
35
|
-
features:
|
|
36
|
-
hasFeature:
|
|
34
|
+
footer: i,
|
|
35
|
+
features: c,
|
|
36
|
+
hasFeature: v
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
function U(o) {
|
|
@@ -44,7 +44,7 @@ function U(o) {
|
|
|
44
44
|
a && e.style.setProperty(`--x-color-${N(s)}`, a);
|
|
45
45
|
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);
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
T(() => {
|
|
48
48
|
if (o) {
|
|
49
49
|
n(o);
|
|
50
50
|
return;
|
|
@@ -96,73 +96,118 @@ function P() {
|
|
|
96
96
|
const s = e.data;
|
|
97
97
|
s?.type === "xosen-preview-update" && s.page && (o.value = s.page, n.value = !0);
|
|
98
98
|
}
|
|
99
|
-
return
|
|
99
|
+
return T(() => {
|
|
100
100
|
window.addEventListener("message", t);
|
|
101
|
-
}),
|
|
101
|
+
}), C(() => {
|
|
102
102
|
window.removeEventListener("message", t);
|
|
103
103
|
}), {
|
|
104
104
|
previewPage: o,
|
|
105
105
|
isPreviewMode: n
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
|
-
const
|
|
108
|
+
const x = /* @__PURE__ */ Symbol("pageContext");
|
|
109
109
|
function K(o) {
|
|
110
|
-
const { locale: n } =
|
|
110
|
+
const { locale: n } = $(), { previewPage: t, isPreviewMode: e } = P(), s = window.__SITE_DATA__, a = y(null), l = y(!0), i = y(!1), c = u(() => typeof o == "string" ? o : o.value), v = u(() => `page:${c.value}`), w = u(() => {
|
|
111
111
|
const r = e.value ? t.value : a.value;
|
|
112
112
|
if (!r) return {};
|
|
113
|
-
const { blocks:
|
|
113
|
+
const { blocks: h, meta: f, slug: d, locale: O, ...A } = r;
|
|
114
114
|
return A;
|
|
115
115
|
});
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
function
|
|
119
|
-
const r = s?.[
|
|
120
|
-
return r && n.value === s?.locale ? (a.value = r,
|
|
116
|
+
L(x, w);
|
|
117
|
+
const g = u(() => e.value && t.value?.slug === c.value ? t.value.blocks || [] : a.value?.blocks || []);
|
|
118
|
+
function S() {
|
|
119
|
+
const r = s?.[v.value];
|
|
120
|
+
return r && n.value === s?.locale ? (a.value = r, i.value = !1, m(), !0) : !1;
|
|
121
121
|
}
|
|
122
122
|
async function p() {
|
|
123
|
-
l.value = !0,
|
|
123
|
+
l.value = !0, i.value = !1;
|
|
124
124
|
try {
|
|
125
|
-
const r = await fetch(`/api/content/${
|
|
126
|
-
r.ok ? (a.value = await r.json(),
|
|
125
|
+
const r = await fetch(`/api/content/${v.value}:${n.value}`);
|
|
126
|
+
r.ok ? (a.value = await r.json(), m()) : (a.value = null, i.value = !0);
|
|
127
127
|
} catch {
|
|
128
|
-
a.value = null,
|
|
128
|
+
a.value = null, i.value = !0;
|
|
129
129
|
} finally {
|
|
130
130
|
l.value = !1;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
function
|
|
133
|
+
function m() {
|
|
134
134
|
if (!a.value) return;
|
|
135
135
|
if (a.value.title) {
|
|
136
|
-
const
|
|
137
|
-
document.title = `${a.value.title}${
|
|
136
|
+
const h = s?.config?.branding, f = h?.siteName ? ` — ${h.siteName}` : "";
|
|
137
|
+
document.title = `${a.value.title}${f}`;
|
|
138
138
|
}
|
|
139
139
|
const r = a.value.meta?.description;
|
|
140
140
|
if (r) {
|
|
141
|
-
const
|
|
142
|
-
|
|
141
|
+
const h = document.querySelector('meta[name="description"]');
|
|
142
|
+
h && h.setAttribute("content", r);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
return _(t, (r) => {
|
|
146
|
-
r?.slug ===
|
|
146
|
+
r?.slug === c.value && (l.value = !1, i.value = !1, (r.title || r.blocks) && (a.value = {
|
|
147
147
|
...a.value || {},
|
|
148
148
|
...r.title ? { title: r.title } : {},
|
|
149
149
|
...r.blocks ? { blocks: r.blocks } : {}
|
|
150
|
-
},
|
|
151
|
-
}),
|
|
152
|
-
|
|
153
|
-
}), _(n, () => p()), _(
|
|
154
|
-
|
|
150
|
+
}, m()));
|
|
151
|
+
}), T(() => {
|
|
152
|
+
S() ? l.value = !1 : p();
|
|
153
|
+
}), _(n, () => p()), _(c, () => {
|
|
154
|
+
S() ? l.value = !1 : p();
|
|
155
155
|
}), {
|
|
156
156
|
page: a,
|
|
157
|
-
blocks:
|
|
157
|
+
blocks: g,
|
|
158
158
|
pageContext: w,
|
|
159
159
|
loading: l,
|
|
160
|
-
notFound:
|
|
160
|
+
notFound: i,
|
|
161
161
|
isPreviewMode: e,
|
|
162
162
|
reload: p
|
|
163
163
|
};
|
|
164
164
|
}
|
|
165
|
-
|
|
165
|
+
function W() {
|
|
166
|
+
const { locale: o } = $(), n = window.__SITE_DATA__, t = n?.locale, e = y({ ...n?.components || {} });
|
|
167
|
+
async function s(l) {
|
|
168
|
+
if (l === t) {
|
|
169
|
+
e.value = { ...n?.components || {} };
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const i = await fetch(`/api/content/components:${l}`);
|
|
174
|
+
i.ok && (e.value = await i.json());
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
_(o, (l) => s(l)), T(() => {
|
|
179
|
+
o.value !== t && s(o.value);
|
|
180
|
+
});
|
|
181
|
+
function a(l) {
|
|
182
|
+
return u(() => e.value[l] || null);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
/** All components (reactive, locale-aware) */
|
|
186
|
+
components: e,
|
|
187
|
+
/** Get a single component by key */
|
|
188
|
+
getComponent: a,
|
|
189
|
+
/** Force refetch for current locale */
|
|
190
|
+
reload: () => s(o.value)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function B() {
|
|
194
|
+
const { locale: o } = $(), n = window.__SITE_DATA__, t = u(() => n?.config?.locales || []), e = u(() => n?.config?.defaultLocale || "en"), s = u(() => t.value.length > 1);
|
|
195
|
+
function a(i) {
|
|
196
|
+
o.value = i, localStorage.setItem("x-site-locale", i);
|
|
197
|
+
}
|
|
198
|
+
function l(i) {
|
|
199
|
+
return typeof i == "string" ? i : i[o.value] || i[e.value] || Object.values(i)[0] || "";
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
locale: o,
|
|
203
|
+
locales: t,
|
|
204
|
+
defaultLocale: e,
|
|
205
|
+
hasMultipleLocales: s,
|
|
206
|
+
switchLocale: a,
|
|
207
|
+
resolveText: l
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const I = /\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|webp|avif|map|json|txt|xml|webmanifest)$/;
|
|
166
211
|
function R(o, n) {
|
|
167
212
|
const e = new URL(o.url).searchParams.get("lang");
|
|
168
213
|
if (e && n.supportedLocales?.includes(e)) return e;
|
|
@@ -173,7 +218,7 @@ function R(o, n) {
|
|
|
173
218
|
}
|
|
174
219
|
return n.defaultLocale;
|
|
175
220
|
}
|
|
176
|
-
function
|
|
221
|
+
function j(o, n, t) {
|
|
177
222
|
if (!o.pathname.startsWith("/api/content/")) return null;
|
|
178
223
|
const e = decodeURIComponent(o.pathname.replace("/api/content/", ""));
|
|
179
224
|
return e ? (async () => {
|
|
@@ -191,7 +236,7 @@ function O(o, n, t) {
|
|
|
191
236
|
}) : Response.json({ error: "Not found" }, { status: 404 });
|
|
192
237
|
})() : Promise.resolve(Response.json({ error: "Key is required" }, { status: 400 }));
|
|
193
238
|
}
|
|
194
|
-
function
|
|
239
|
+
function b(o, n, t) {
|
|
195
240
|
let e = o;
|
|
196
241
|
if (n.title) {
|
|
197
242
|
const s = t ? ` — ${t}` : "";
|
|
@@ -199,46 +244,46 @@ function $(o, n, t) {
|
|
|
199
244
|
}
|
|
200
245
|
return n.meta?.description && (e = e.replace(/(<meta\s+name="description"\s+content=")[^"]*(")/, `$1${n.meta.description}$2`)), n.meta?.ogTitle && (e = e.replace(/(<meta\s+property="og:title"\s+content=")[^"]*(")/, `$1${n.meta.ogTitle}$2`)), n.html && (e = e.replace('<div id="app"></div>', `<div id="app">${n.html}</div>`)), e;
|
|
201
246
|
}
|
|
202
|
-
function
|
|
247
|
+
function X(o) {
|
|
203
248
|
return {
|
|
204
249
|
async fetch(n, t) {
|
|
205
|
-
const e = new URL(n.url), s =
|
|
250
|
+
const e = new URL(n.url), s = j(e, t, o.defaultLocale);
|
|
206
251
|
if (s) return s;
|
|
207
|
-
if (
|
|
252
|
+
if (I.test(e.pathname))
|
|
208
253
|
return t.ASSETS.fetch(n);
|
|
209
254
|
const a = R(n, o), l = new URL("/index.html", n.url);
|
|
210
|
-
let
|
|
211
|
-
const
|
|
255
|
+
let c = await (await t.ASSETS.fetch(new Request(l))).text();
|
|
256
|
+
const v = o.defaultLocale, [w, g] = await Promise.all([
|
|
212
257
|
t.SITE_CONTENT.get("config"),
|
|
213
258
|
t.SITE_CONTENT.get(`content:${a}`)
|
|
214
|
-
]),
|
|
215
|
-
let p =
|
|
216
|
-
if (!
|
|
217
|
-
const
|
|
218
|
-
|
|
259
|
+
]), S = w ? JSON.parse(w) : {};
|
|
260
|
+
let p = g ? JSON.parse(g) : {};
|
|
261
|
+
if (!g && a !== v) {
|
|
262
|
+
const f = await t.SITE_CONTENT.get(`content:${v}`);
|
|
263
|
+
f && (p = JSON.parse(f));
|
|
219
264
|
}
|
|
220
|
-
const
|
|
221
|
-
if (
|
|
222
|
-
const
|
|
223
|
-
if (!p[
|
|
224
|
-
let d = await t.SITE_CONTENT.get(`page:${
|
|
225
|
-
!d && a !==
|
|
265
|
+
const m = e.pathname.match(/^\/p\/(.+)$/);
|
|
266
|
+
if (m) {
|
|
267
|
+
const f = m[1];
|
|
268
|
+
if (!p[f]) {
|
|
269
|
+
let d = await t.SITE_CONTENT.get(`page:${f}:${a}`);
|
|
270
|
+
!d && a !== v && (d = await t.SITE_CONTENT.get(`page:${f}:${v}`)), d && (p[f] = JSON.parse(d));
|
|
226
271
|
}
|
|
227
272
|
}
|
|
228
273
|
const r = {
|
|
229
274
|
locale: a,
|
|
230
|
-
config:
|
|
275
|
+
config: S
|
|
231
276
|
};
|
|
232
|
-
for (const [
|
|
233
|
-
r[`page:${
|
|
234
|
-
if (
|
|
235
|
-
const
|
|
236
|
-
d && (
|
|
277
|
+
for (const [f, d] of Object.entries(p))
|
|
278
|
+
r[`page:${f}`] = d;
|
|
279
|
+
if (m) {
|
|
280
|
+
const f = m[1], d = r[`page:${f}`];
|
|
281
|
+
d && (c = b(c, d, o.siteName));
|
|
237
282
|
}
|
|
238
|
-
e.pathname === "/" && p.home && (
|
|
239
|
-
const
|
|
240
|
-
return
|
|
241
|
-
</head>`), new Response(
|
|
283
|
+
e.pathname === "/" && p.home && (c = b(c, p.home, o.siteName));
|
|
284
|
+
const h = `<script>window.__SITE_DATA__ = ${JSON.stringify(r)};<\/script>`;
|
|
285
|
+
return c = c.replace("</head>", `${h}
|
|
286
|
+
</head>`), new Response(c, {
|
|
242
287
|
headers: {
|
|
243
288
|
"Content-Type": "text/html;charset=utf-8",
|
|
244
289
|
"Cache-Control": "public, max-age=60"
|
|
@@ -248,13 +293,15 @@ function W(o) {
|
|
|
248
293
|
};
|
|
249
294
|
}
|
|
250
295
|
export {
|
|
251
|
-
|
|
252
|
-
|
|
296
|
+
x as PAGE_CONTEXT_KEY,
|
|
297
|
+
X as createSiteWorker,
|
|
253
298
|
E as isInPreview,
|
|
254
299
|
z as setupPreviewRouter,
|
|
255
300
|
P as useLivePreview,
|
|
301
|
+
B as useLocaleSwitcher,
|
|
256
302
|
K as usePageData,
|
|
257
303
|
J as useSiteApi,
|
|
304
|
+
W as useSiteComponents,
|
|
258
305
|
M as useSiteConfig,
|
|
259
306
|
k as useSiteData,
|
|
260
307
|
U as useSkin
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
import { useI18n } from 'vue-i18n';
|
|
3
|
+
|
|
4
|
+
import type { SiteData } from '../types/config.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Locale switcher composable.
|
|
8
|
+
* Manages locale state, available locales, and persistence.
|
|
9
|
+
*/
|
|
10
|
+
export function useLocaleSwitcher() {
|
|
11
|
+
const { locale } = useI18n();
|
|
12
|
+
const siteData = (window as any).__SITE_DATA__ as SiteData | undefined;
|
|
13
|
+
|
|
14
|
+
const locales = computed(() => siteData?.config?.locales || []);
|
|
15
|
+
const defaultLocale = computed(() => siteData?.config?.defaultLocale || 'en');
|
|
16
|
+
const hasMultipleLocales = computed(() => locales.value.length > 1);
|
|
17
|
+
|
|
18
|
+
function switchLocale(loc: string) {
|
|
19
|
+
locale.value = loc;
|
|
20
|
+
localStorage.setItem('x-site-locale', loc);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a text value that may be a string or a locale map.
|
|
25
|
+
* e.g. "Home" or { "en": "Home", "uk": "Головна" }
|
|
26
|
+
*/
|
|
27
|
+
function resolveText(text: string | Record<string, string>): string {
|
|
28
|
+
if (typeof text === 'string') return text;
|
|
29
|
+
return text[locale.value] || text[defaultLocale.value] || Object.values(text)[0] || '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
locale,
|
|
34
|
+
locales,
|
|
35
|
+
defaultLocale,
|
|
36
|
+
hasMultipleLocales,
|
|
37
|
+
switchLocale,
|
|
38
|
+
resolveText,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ref, computed, watch, onMounted, type Ref } from 'vue';
|
|
2
|
+
import { useI18n } from 'vue-i18n';
|
|
3
|
+
|
|
4
|
+
import type { SiteData } from '../types/config.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Locale-aware access to site components (navigation, footer, sidebar, etc.).
|
|
8
|
+
*
|
|
9
|
+
* On initial load, reads from preloaded __SITE_DATA__.components.
|
|
10
|
+
* When locale changes, fetches updated components from the API.
|
|
11
|
+
*/
|
|
12
|
+
export function useSiteComponents() {
|
|
13
|
+
const { locale } = useI18n();
|
|
14
|
+
const siteData = (window as any).__SITE_DATA__ as SiteData | undefined;
|
|
15
|
+
const initialLocale = siteData?.locale;
|
|
16
|
+
|
|
17
|
+
const components = ref<Record<string, any>>({ ...(siteData as any)?.components || {} });
|
|
18
|
+
|
|
19
|
+
async function fetchComponents(loc: string) {
|
|
20
|
+
// If same as initial locale, use preloaded data
|
|
21
|
+
if (loc === initialLocale) {
|
|
22
|
+
components.value = { ...(siteData as any)?.components || {} };
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(`/api/content/components:${loc}`);
|
|
27
|
+
if (res.ok) {
|
|
28
|
+
components.value = await res.json();
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
// Keep current
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
watch(locale, (newLocale) => fetchComponents(newLocale));
|
|
36
|
+
|
|
37
|
+
onMounted(() => {
|
|
38
|
+
if (locale.value !== initialLocale) {
|
|
39
|
+
fetchComponents(locale.value);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific component's data by key.
|
|
45
|
+
* Returns a computed ref that updates on locale change.
|
|
46
|
+
*/
|
|
47
|
+
function getComponent<T = any>(key: string): Ref<T | null> {
|
|
48
|
+
return computed(() => components.value[key] || null) as Ref<T | null>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
/** All components (reactive, locale-aware) */
|
|
53
|
+
components,
|
|
54
|
+
/** Get a single component by key */
|
|
55
|
+
getComponent,
|
|
56
|
+
/** Force refetch for current locale */
|
|
57
|
+
reload: () => fetchComponents(locale.value),
|
|
58
|
+
};
|
|
59
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export { useSiteApi } from './composables/useSiteApi.js';
|
|
|
6
6
|
export { useLivePreview, setupPreviewRouter, isInPreview } from './composables/useLivePreview.js';
|
|
7
7
|
export { usePageData } from './composables/usePageData.js';
|
|
8
8
|
export type { UsePageDataReturn } from './composables/usePageData.js';
|
|
9
|
+
export { useSiteComponents } from './composables/useSiteComponents.js';
|
|
10
|
+
export { useLocaleSwitcher } from './composables/useLocaleSwitcher.js';
|
|
9
11
|
export type { PreviewMessage } from './composables/useLivePreview.js';
|
|
10
12
|
|
|
11
13
|
// Worker
|