@typecms/sdk 0.1.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/README.md +151 -0
- package/dist/client-BW6BWpfF.d.mts +92 -0
- package/dist/flags/index.d.mts +100 -0
- package/dist/flags/index.mjs +99 -0
- package/dist/flags/index.mjs.map +1 -0
- package/dist/index.d.mts +67 -0
- package/dist/index.mjs +174 -0
- package/dist/index.mjs.map +1 -0
- package/dist/overlay-CLYAeNwg.d.mts +43 -0
- package/dist/preview/dom.d.mts +76 -0
- package/dist/preview/dom.mjs +352 -0
- package/dist/preview/dom.mjs.map +1 -0
- package/dist/preview/index.d.mts +438 -0
- package/dist/preview/index.mjs +736 -0
- package/dist/preview/index.mjs.map +1 -0
- package/dist/preview/next.d.mts +99 -0
- package/dist/preview/next.mjs +31 -0
- package/dist/preview/next.mjs.map +1 -0
- package/dist/preview/svelte.d.mts +99 -0
- package/dist/preview/svelte.mjs +437 -0
- package/dist/preview/svelte.mjs.map +1 -0
- package/dist/preview/vue.d.mts +113 -0
- package/dist/preview/vue.mjs +587 -0
- package/dist/preview/vue.mjs.map +1 -0
- package/dist/types/index.d.mts +358 -0
- package/dist/types/index.mjs +8 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/types-BNHDdA2G.d.mts +117 -0
- package/package.json +90 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
// src/preview/vue.ts
|
|
2
|
+
import {
|
|
3
|
+
shallowRef,
|
|
4
|
+
computed,
|
|
5
|
+
inject,
|
|
6
|
+
toValue
|
|
7
|
+
} from "vue";
|
|
8
|
+
|
|
9
|
+
// src/preview/client.ts
|
|
10
|
+
var DEFAULT_ALLOWED_ORIGINS = [
|
|
11
|
+
"https://app.typecms.com",
|
|
12
|
+
"https://staging.typecms.com",
|
|
13
|
+
"http://localhost:3000",
|
|
14
|
+
"http://localhost:3001"
|
|
15
|
+
];
|
|
16
|
+
function createPreviewClient(config = {}) {
|
|
17
|
+
const {
|
|
18
|
+
allowedOrigins = DEFAULT_ALLOWED_ORIGINS,
|
|
19
|
+
onInit,
|
|
20
|
+
onUpdate,
|
|
21
|
+
onNavigate,
|
|
22
|
+
onFocus,
|
|
23
|
+
debug = false
|
|
24
|
+
} = config;
|
|
25
|
+
let state = {
|
|
26
|
+
enabled: false,
|
|
27
|
+
entryId: null,
|
|
28
|
+
projectId: null,
|
|
29
|
+
locale: null,
|
|
30
|
+
typeId: null,
|
|
31
|
+
values: {},
|
|
32
|
+
focusedPath: null
|
|
33
|
+
};
|
|
34
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
35
|
+
let isConnected = false;
|
|
36
|
+
let parentOrigin = null;
|
|
37
|
+
function log(...args) {
|
|
38
|
+
if (debug) {
|
|
39
|
+
console.log("[TypeCMS Preview]", ...args);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function setState(updates) {
|
|
43
|
+
state = { ...state, ...updates };
|
|
44
|
+
listeners.forEach((listener) => listener(state));
|
|
45
|
+
}
|
|
46
|
+
function isAllowedOrigin(origin) {
|
|
47
|
+
return allowedOrigins.some((allowed) => {
|
|
48
|
+
if (allowed === "*") return true;
|
|
49
|
+
const expected = allowed.endsWith("/*") ? allowed.slice(0, -2) : allowed;
|
|
50
|
+
return origin === expected;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function sendMessage(message) {
|
|
54
|
+
if (!parentOrigin) {
|
|
55
|
+
log("Cannot send message: no parent origin");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (typeof window === "undefined" || !window.parent) {
|
|
59
|
+
log("Cannot send message: not in iframe");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
log("Sending message:", message.type);
|
|
63
|
+
window.parent.postMessage(message, parentOrigin);
|
|
64
|
+
}
|
|
65
|
+
function handleMessage(event) {
|
|
66
|
+
if (!isAllowedOrigin(event.origin)) {
|
|
67
|
+
log("Ignored message from untrusted origin:", event.origin);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const message = event.data;
|
|
71
|
+
if (!message?.type?.startsWith("typecms:preview:")) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
log("Received message:", message.type, message);
|
|
75
|
+
parentOrigin = event.origin;
|
|
76
|
+
switch (message.type) {
|
|
77
|
+
case "typecms:preview:init": {
|
|
78
|
+
const initMsg = message;
|
|
79
|
+
setState({
|
|
80
|
+
enabled: true,
|
|
81
|
+
entryId: initMsg.payload.entryId,
|
|
82
|
+
projectId: initMsg.payload.projectId,
|
|
83
|
+
locale: initMsg.payload.locale,
|
|
84
|
+
typeId: initMsg.payload.typeId,
|
|
85
|
+
values: {},
|
|
86
|
+
focusedPath: null
|
|
87
|
+
});
|
|
88
|
+
onInit?.(initMsg.payload);
|
|
89
|
+
const readyMessage = {
|
|
90
|
+
type: "typecms:preview:ready",
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
payload: {
|
|
93
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "/"
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
sendMessage(readyMessage);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "typecms:preview:update": {
|
|
100
|
+
const updateMsg = message;
|
|
101
|
+
if (updateMsg.payload.values) {
|
|
102
|
+
setState({ values: updateMsg.payload.values });
|
|
103
|
+
} else {
|
|
104
|
+
const newValues = { ...state.values };
|
|
105
|
+
setNestedValue(newValues, updateMsg.payload.path, updateMsg.payload.value);
|
|
106
|
+
setState({ values: newValues });
|
|
107
|
+
}
|
|
108
|
+
onUpdate?.(updateMsg.payload);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "typecms:preview:navigate": {
|
|
112
|
+
onNavigate?.(message.payload.url);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "typecms:preview:focus": {
|
|
116
|
+
setState({ focusedPath: message.payload.path });
|
|
117
|
+
onFocus?.(message.payload.path);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
/**
|
|
124
|
+
* Start listening for preview messages.
|
|
125
|
+
* Call this on mount.
|
|
126
|
+
*/
|
|
127
|
+
connect() {
|
|
128
|
+
if (typeof window === "undefined") {
|
|
129
|
+
log("Cannot connect: not in browser");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (isConnected) {
|
|
133
|
+
log("Already connected");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
log("Connecting...");
|
|
137
|
+
window.addEventListener("message", handleMessage);
|
|
138
|
+
isConnected = true;
|
|
139
|
+
if (window.parent && window.parent !== window) {
|
|
140
|
+
const hello = {
|
|
141
|
+
type: "typecms:preview:ready",
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
payload: { pathname: window.location?.pathname ?? "/" }
|
|
144
|
+
};
|
|
145
|
+
for (const allowed of allowedOrigins) {
|
|
146
|
+
const target = allowed === "*" ? "*" : allowed.endsWith("/*") ? allowed.slice(0, -2) : allowed;
|
|
147
|
+
try {
|
|
148
|
+
window.parent.postMessage(hello, target);
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
/**
|
|
155
|
+
* Stop listening for preview messages.
|
|
156
|
+
* Call this on unmount.
|
|
157
|
+
*/
|
|
158
|
+
disconnect() {
|
|
159
|
+
if (typeof window === "undefined") return;
|
|
160
|
+
log("Disconnecting...");
|
|
161
|
+
window.removeEventListener("message", handleMessage);
|
|
162
|
+
isConnected = false;
|
|
163
|
+
parentOrigin = null;
|
|
164
|
+
setState({
|
|
165
|
+
enabled: false,
|
|
166
|
+
entryId: null,
|
|
167
|
+
projectId: null,
|
|
168
|
+
locale: null,
|
|
169
|
+
typeId: null,
|
|
170
|
+
values: {},
|
|
171
|
+
focusedPath: null
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
/**
|
|
175
|
+
* Get current preview state.
|
|
176
|
+
*/
|
|
177
|
+
getState() {
|
|
178
|
+
return state;
|
|
179
|
+
},
|
|
180
|
+
/**
|
|
181
|
+
* Check if preview mode is active.
|
|
182
|
+
*/
|
|
183
|
+
isEnabled() {
|
|
184
|
+
return state.enabled;
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* Get a preview value by path.
|
|
188
|
+
* Returns undefined if no preview value exists.
|
|
189
|
+
*/
|
|
190
|
+
getValue(path) {
|
|
191
|
+
return getNestedValue(state.values, path);
|
|
192
|
+
},
|
|
193
|
+
/**
|
|
194
|
+
* Subscribe to state changes.
|
|
195
|
+
*/
|
|
196
|
+
subscribe(listener) {
|
|
197
|
+
listeners.add(listener);
|
|
198
|
+
return () => listeners.delete(listener);
|
|
199
|
+
},
|
|
200
|
+
/**
|
|
201
|
+
* Notify the editor that the preview has navigated.
|
|
202
|
+
*/
|
|
203
|
+
notifyNavigation(pathname) {
|
|
204
|
+
const message = {
|
|
205
|
+
type: "typecms:preview:ready",
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
payload: { pathname }
|
|
208
|
+
};
|
|
209
|
+
sendMessage(message);
|
|
210
|
+
},
|
|
211
|
+
/**
|
|
212
|
+
* Notify the editor that a field was clicked in the preview.
|
|
213
|
+
* The editor will scroll to the corresponding field.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* // On click of an element with data-preview-field attribute:
|
|
218
|
+
* const path = el.closest('[data-preview-field]')?.dataset.previewField
|
|
219
|
+
* if (path) preview.notifyFieldFocus(path)
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
notifyFieldFocus(path) {
|
|
223
|
+
const message = {
|
|
224
|
+
type: "typecms:preview:focus",
|
|
225
|
+
timestamp: Date.now(),
|
|
226
|
+
payload: { path }
|
|
227
|
+
};
|
|
228
|
+
sendMessage(message);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
233
|
+
function setNestedValue(obj, path, value) {
|
|
234
|
+
const parts = path.split(".");
|
|
235
|
+
let current = obj;
|
|
236
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
237
|
+
const part = parts[i];
|
|
238
|
+
const arrayMatch2 = part.match(/^(\w+)\[(\d+)\]$/);
|
|
239
|
+
if (arrayMatch2) {
|
|
240
|
+
const [, name, indexStr] = arrayMatch2;
|
|
241
|
+
const index = parseInt(indexStr, 10);
|
|
242
|
+
if (UNSAFE_KEYS.has(name)) continue;
|
|
243
|
+
if (!current[name]) {
|
|
244
|
+
current[name] = [];
|
|
245
|
+
}
|
|
246
|
+
const arr = current[name];
|
|
247
|
+
if (!arr[index]) {
|
|
248
|
+
arr[index] = {};
|
|
249
|
+
}
|
|
250
|
+
current = arr[index];
|
|
251
|
+
} else {
|
|
252
|
+
if (UNSAFE_KEYS.has(part)) continue;
|
|
253
|
+
if (!current[part] || typeof current[part] !== "object") {
|
|
254
|
+
current[part] = {};
|
|
255
|
+
}
|
|
256
|
+
current = current[part];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const lastPart = parts[parts.length - 1];
|
|
260
|
+
const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
|
|
261
|
+
if (arrayMatch) {
|
|
262
|
+
const [, name, indexStr] = arrayMatch;
|
|
263
|
+
const index = parseInt(indexStr, 10);
|
|
264
|
+
if (UNSAFE_KEYS.has(name)) return;
|
|
265
|
+
if (!current[name]) {
|
|
266
|
+
current[name] = [];
|
|
267
|
+
}
|
|
268
|
+
current[name][index] = value;
|
|
269
|
+
} else {
|
|
270
|
+
if (UNSAFE_KEYS.has(lastPart)) return;
|
|
271
|
+
current[lastPart] = value;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function getNestedValue(obj, path) {
|
|
275
|
+
const parts = path.split(".");
|
|
276
|
+
let current = obj;
|
|
277
|
+
for (const part of parts) {
|
|
278
|
+
if (current === null || current === void 0) {
|
|
279
|
+
return void 0;
|
|
280
|
+
}
|
|
281
|
+
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
282
|
+
if (arrayMatch) {
|
|
283
|
+
const [, name, indexStr] = arrayMatch;
|
|
284
|
+
const index = parseInt(indexStr, 10);
|
|
285
|
+
const arr = current[name];
|
|
286
|
+
if (!arr) return void 0;
|
|
287
|
+
current = arr[index];
|
|
288
|
+
} else {
|
|
289
|
+
current = current[part];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return current;
|
|
293
|
+
}
|
|
294
|
+
function deepMerge(target, source) {
|
|
295
|
+
const result = { ...target };
|
|
296
|
+
for (const key of Object.keys(source)) {
|
|
297
|
+
const sourceValue = source[key];
|
|
298
|
+
const targetValue = result[key];
|
|
299
|
+
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue) && targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
300
|
+
result[key] = deepMerge(
|
|
301
|
+
targetValue,
|
|
302
|
+
sourceValue
|
|
303
|
+
);
|
|
304
|
+
} else {
|
|
305
|
+
result[key] = sourceValue;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/preview/overlay.ts
|
|
312
|
+
var DEFAULT_OPTIONS = {
|
|
313
|
+
color: "#3b82f6",
|
|
314
|
+
zIndex: 9999,
|
|
315
|
+
scrollIntoView: true,
|
|
316
|
+
attribute: "data-preview-field"
|
|
317
|
+
};
|
|
318
|
+
function attachPreviewOverlay(client, options = {}) {
|
|
319
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
320
|
+
return () => {
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const { color, zIndex, scrollIntoView, attribute } = { ...DEFAULT_OPTIONS, ...options };
|
|
324
|
+
const box = document.createElement("div");
|
|
325
|
+
box.setAttribute("data-typecms-preview-overlay", "");
|
|
326
|
+
Object.assign(box.style, {
|
|
327
|
+
position: "fixed",
|
|
328
|
+
display: "none",
|
|
329
|
+
pointerEvents: "none",
|
|
330
|
+
boxSizing: "border-box",
|
|
331
|
+
border: `2px solid ${color}`,
|
|
332
|
+
borderRadius: "4px",
|
|
333
|
+
zIndex: String(zIndex),
|
|
334
|
+
transition: "top 120ms ease, left 120ms ease, width 120ms ease, height 120ms ease"
|
|
335
|
+
});
|
|
336
|
+
document.body.appendChild(box);
|
|
337
|
+
let target = null;
|
|
338
|
+
let lastPath = null;
|
|
339
|
+
function position() {
|
|
340
|
+
if (!target || !target.isConnected) {
|
|
341
|
+
box.style.display = "none";
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const rect = target.getBoundingClientRect();
|
|
345
|
+
Object.assign(box.style, {
|
|
346
|
+
display: "block",
|
|
347
|
+
top: `${rect.top - 2}px`,
|
|
348
|
+
left: `${rect.left - 2}px`,
|
|
349
|
+
width: `${rect.width + 4}px`,
|
|
350
|
+
height: `${rect.height + 4}px`
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function update(state) {
|
|
354
|
+
const path = state.enabled ? state.focusedPath : null;
|
|
355
|
+
if (path === lastPath) return;
|
|
356
|
+
lastPath = path;
|
|
357
|
+
target = path ? document.querySelector(`[${attribute}="${CSS.escape(path)}"]`) : null;
|
|
358
|
+
if (target && scrollIntoView) {
|
|
359
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
360
|
+
}
|
|
361
|
+
position();
|
|
362
|
+
}
|
|
363
|
+
window.addEventListener("scroll", position, true);
|
|
364
|
+
window.addEventListener("resize", position);
|
|
365
|
+
const unsubscribe = client.subscribe(update);
|
|
366
|
+
update(client.getState());
|
|
367
|
+
return () => {
|
|
368
|
+
unsubscribe();
|
|
369
|
+
window.removeEventListener("scroll", position, true);
|
|
370
|
+
window.removeEventListener("resize", position);
|
|
371
|
+
box.remove();
|
|
372
|
+
target = null;
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/preview/richtext.ts
|
|
377
|
+
function isPlainObject(value) {
|
|
378
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
379
|
+
}
|
|
380
|
+
function shareStructure(prev, next) {
|
|
381
|
+
if (prev === next || prev === void 0) {
|
|
382
|
+
return next;
|
|
383
|
+
}
|
|
384
|
+
if (Array.isArray(prev) && Array.isArray(next)) {
|
|
385
|
+
return shareArray(prev, next);
|
|
386
|
+
}
|
|
387
|
+
if (isPlainObject(prev) && isPlainObject(next)) {
|
|
388
|
+
return shareObject(prev, next);
|
|
389
|
+
}
|
|
390
|
+
return next;
|
|
391
|
+
}
|
|
392
|
+
function shareArray(prev, next) {
|
|
393
|
+
const prevLength = prev.length;
|
|
394
|
+
const nextLength = next.length;
|
|
395
|
+
const result = new Array(nextLength);
|
|
396
|
+
let tail = 0;
|
|
397
|
+
const maxTail = Math.min(prevLength, nextLength);
|
|
398
|
+
while (tail < maxTail) {
|
|
399
|
+
const prevItem = prev[prevLength - 1 - tail];
|
|
400
|
+
const shared = shareStructure(prevItem, next[nextLength - 1 - tail]);
|
|
401
|
+
if (shared !== prevItem) break;
|
|
402
|
+
result[nextLength - 1 - tail] = shared;
|
|
403
|
+
tail++;
|
|
404
|
+
}
|
|
405
|
+
const prevHeadLength = prevLength - tail;
|
|
406
|
+
for (let i = 0; i < nextLength - tail; i++) {
|
|
407
|
+
result[i] = i < prevHeadLength ? shareStructure(prev[i], next[i]) : next[i];
|
|
408
|
+
}
|
|
409
|
+
if (prevLength === nextLength) {
|
|
410
|
+
let allShared = true;
|
|
411
|
+
for (let i = 0; i < nextLength; i++) {
|
|
412
|
+
if (result[i] !== prev[i]) {
|
|
413
|
+
allShared = false;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (allShared) return prev;
|
|
418
|
+
}
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
function shareObject(prev, next) {
|
|
422
|
+
const nextKeys = Object.keys(next);
|
|
423
|
+
const result = {};
|
|
424
|
+
let allShared = nextKeys.length === Object.keys(prev).length;
|
|
425
|
+
for (const key of nextKeys) {
|
|
426
|
+
const hasPrev = Object.prototype.hasOwnProperty.call(prev, key);
|
|
427
|
+
const shared = hasPrev ? shareStructure(prev[key], next[key]) : next[key];
|
|
428
|
+
result[key] = shared;
|
|
429
|
+
if (!hasPrev || shared !== prev[key]) {
|
|
430
|
+
allShared = false;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return allShared ? prev : result;
|
|
434
|
+
}
|
|
435
|
+
function createRichTextPreview(options = {}) {
|
|
436
|
+
let lastDoc = options.initial;
|
|
437
|
+
return {
|
|
438
|
+
apply(nextDoc) {
|
|
439
|
+
const shared = shareStructure(lastDoc, nextDoc);
|
|
440
|
+
lastDoc = shared;
|
|
441
|
+
return shared;
|
|
442
|
+
},
|
|
443
|
+
reset() {
|
|
444
|
+
lastDoc = void 0;
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/preview/vue.ts
|
|
450
|
+
var PREVIEW_INJECTION_KEY = /* @__PURE__ */ Symbol("typecms-preview");
|
|
451
|
+
function createPreviewContext(options = {}) {
|
|
452
|
+
const { overlay, ...config } = options;
|
|
453
|
+
const client = createPreviewClient(config);
|
|
454
|
+
const state = shallowRef(client.getState());
|
|
455
|
+
let unsubscribe = null;
|
|
456
|
+
let detachOverlay = null;
|
|
457
|
+
function connect() {
|
|
458
|
+
if (unsubscribe) return;
|
|
459
|
+
unsubscribe = client.subscribe((next) => {
|
|
460
|
+
state.value = next;
|
|
461
|
+
});
|
|
462
|
+
client.connect();
|
|
463
|
+
if (overlay) {
|
|
464
|
+
detachOverlay = attachPreviewOverlay(client, overlay === true ? {} : overlay);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function disconnect() {
|
|
468
|
+
unsubscribe?.();
|
|
469
|
+
unsubscribe = null;
|
|
470
|
+
detachOverlay?.();
|
|
471
|
+
detachOverlay = null;
|
|
472
|
+
client.disconnect();
|
|
473
|
+
state.value = client.getState();
|
|
474
|
+
}
|
|
475
|
+
const isPreview = computed(() => state.value.enabled);
|
|
476
|
+
function mergeContent(content) {
|
|
477
|
+
if (!state.value.enabled || Object.keys(state.value.values).length === 0) {
|
|
478
|
+
return content;
|
|
479
|
+
}
|
|
480
|
+
return deepMerge(content, state.value.values);
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
state,
|
|
484
|
+
isPreview,
|
|
485
|
+
getValue: (path) => client.getValue(path),
|
|
486
|
+
mergeContent,
|
|
487
|
+
notifyNavigation: (pathname) => client.notifyNavigation(pathname),
|
|
488
|
+
notifyFieldFocus: (path) => client.notifyFieldFocus(path),
|
|
489
|
+
connect,
|
|
490
|
+
disconnect
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
var TypecmsPreview = {
|
|
494
|
+
install(app, options = {}) {
|
|
495
|
+
const context = createPreviewContext(options);
|
|
496
|
+
context.connect();
|
|
497
|
+
app.provide(PREVIEW_INJECTION_KEY, context);
|
|
498
|
+
const originalUnmount = app.unmount;
|
|
499
|
+
app.unmount = function unmount(...args) {
|
|
500
|
+
context.disconnect();
|
|
501
|
+
return originalUnmount.apply(this, args);
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
function usePreviewContext() {
|
|
506
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
507
|
+
if (!context) {
|
|
508
|
+
throw new Error("usePreviewContext requires the TypecmsPreview plugin to be installed");
|
|
509
|
+
}
|
|
510
|
+
return context;
|
|
511
|
+
}
|
|
512
|
+
function useIsPreview() {
|
|
513
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
514
|
+
return computed(() => context?.state.value.enabled ?? false);
|
|
515
|
+
}
|
|
516
|
+
function usePreviewState() {
|
|
517
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
518
|
+
return computed(() => context?.state.value ?? null);
|
|
519
|
+
}
|
|
520
|
+
function usePreviewValue(path, fallback) {
|
|
521
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
522
|
+
return computed(() => {
|
|
523
|
+
const fallbackValue = toValue(fallback);
|
|
524
|
+
if (!context || !context.state.value.enabled) {
|
|
525
|
+
return fallbackValue;
|
|
526
|
+
}
|
|
527
|
+
const previewValue = getNestedValue(context.state.value.values, toValue(path));
|
|
528
|
+
return previewValue !== void 0 ? previewValue : fallbackValue;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function usePreviewContent(content) {
|
|
532
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
533
|
+
return computed(() => {
|
|
534
|
+
const value = toValue(content);
|
|
535
|
+
if (!context || !context.state.value.enabled) {
|
|
536
|
+
return value;
|
|
537
|
+
}
|
|
538
|
+
return context.mergeContent(value);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
function useFocusedField() {
|
|
542
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
543
|
+
return computed(() => context?.state.value.focusedPath ?? null);
|
|
544
|
+
}
|
|
545
|
+
function usePreviewNavigation() {
|
|
546
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
547
|
+
return context ? context.notifyNavigation : () => {
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function usePreviewFieldFocus() {
|
|
551
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
552
|
+
return context ? context.notifyFieldFocus : () => {
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function usePreviewRichText(path, fallback) {
|
|
556
|
+
const context = inject(PREVIEW_INJECTION_KEY, null);
|
|
557
|
+
const preview = createRichTextPreview();
|
|
558
|
+
let lastPath = null;
|
|
559
|
+
return computed(() => {
|
|
560
|
+
const pathValue = toValue(path);
|
|
561
|
+
if (lastPath !== null && lastPath !== pathValue) {
|
|
562
|
+
preview.reset();
|
|
563
|
+
}
|
|
564
|
+
lastPath = pathValue;
|
|
565
|
+
const fallbackValue = toValue(fallback);
|
|
566
|
+
if (!context || !context.state.value.enabled) {
|
|
567
|
+
return fallbackValue;
|
|
568
|
+
}
|
|
569
|
+
const previewValue = getNestedValue(context.state.value.values, pathValue);
|
|
570
|
+
return preview.apply(previewValue !== void 0 ? previewValue : fallbackValue);
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
export {
|
|
574
|
+
PREVIEW_INJECTION_KEY,
|
|
575
|
+
TypecmsPreview,
|
|
576
|
+
createPreviewContext,
|
|
577
|
+
useFocusedField,
|
|
578
|
+
useIsPreview,
|
|
579
|
+
usePreviewContent,
|
|
580
|
+
usePreviewContext,
|
|
581
|
+
usePreviewFieldFocus,
|
|
582
|
+
usePreviewNavigation,
|
|
583
|
+
usePreviewRichText,
|
|
584
|
+
usePreviewState,
|
|
585
|
+
usePreviewValue
|
|
586
|
+
};
|
|
587
|
+
//# sourceMappingURL=vue.mjs.map
|