@questpie/admin 3.2.3 → 3.2.4
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/client/blocks/block-renderer.d.mts +2 -2
- package/dist/client/components/admin-link.d.mts +2 -2
- package/dist/client/components/blocks/block-library-sidebar.mjs +126 -113
- package/dist/client/components/fields/json-field.mjs +114 -118
- package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +38 -40
- package/dist/client/components/fields/rich-text-editor/image-popover.mjs +6 -17
- package/dist/client/components/fields/rich-text-editor/index.mjs +71 -87
- package/dist/client/components/history-sidebar.mjs +25 -10
- package/dist/client/components/media/media-picker-dialog.mjs +237 -254
- package/dist/client/components/preview/live-preview-mode.mjs +230 -203
- package/dist/client/components/primitives/asset-preview.mjs +1 -1
- package/dist/client/contexts/focus-context.d.mts +4 -0
- package/dist/client/contexts/focus-context.mjs +106 -86
- package/dist/client/preview/block-scope-context.d.mts +2 -2
- package/dist/client/preview/diff.mjs +110 -0
- package/dist/client/preview/preview-banner.d.mts +2 -2
- package/dist/client/preview/preview-field.d.mts +4 -4
- package/dist/client/preview/preview-field.mjs +125 -187
- package/dist/client/preview/use-collection-preview.mjs +49 -21
- package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
- package/dist/client/views/auth/auth-layout.d.mts +3 -3
- package/dist/client/views/auth/forgot-password-form.d.mts +2 -2
- package/dist/client/views/auth/login-form.d.mts +2 -2
- package/dist/client/views/auth/reset-password-form.d.mts +2 -2
- package/dist/client/views/auth/setup-form.d.mts +2 -2
- package/dist/client/views/collection/bulk-action-toolbar.mjs +135 -156
- package/dist/client/views/collection/field-renderer.mjs +162 -51
- package/dist/client/views/collection/form-view.mjs +43 -81
- package/dist/client/views/collection/table-view.mjs +12 -10
- package/dist/client/views/common/global-search.mjs +199 -182
- package/dist/client/views/globals/global-form-view.mjs +625 -621
- package/dist/client/views/layout/admin-router.mjs +84 -100
- package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
- package/dist/client/views/pages/dashboard-page.d.mts +2 -2
- package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
- package/dist/client/views/pages/invite-page.d.mts +2 -2
- package/dist/client/views/pages/reset-password-page.d.mts +2 -2
- package/dist/client/views/pages/setup-page.d.mts +2 -2
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
- package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
- package/dist/server/modules/admin/collections/assets.d.mts +39 -39
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +63 -63
- package/dist/server/modules/admin/collections/verification.d.mts +32 -32
- package/dist/server/modules/admin/index.d.mts +1 -1
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +11 -11
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/setup.d.mts +7 -7
- package/dist/server/modules/admin/routes/translations.d.mts +4 -4
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +41 -41
- package/dist/server/modules/audit/.generated/module.d.mts +6 -6
- package/dist/server/modules/audit/collections/audit-log.d.mts +78 -78
- package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
- package/package.json +3 -3
|
@@ -31,6 +31,8 @@ import { getLockUser, useLock } from "../../hooks/use-locks.mjs";
|
|
|
31
31
|
import { useReactiveFields } from "../../hooks/use-reactive-fields.mjs";
|
|
32
32
|
import { useServerActions } from "../../hooks/use-server-actions.mjs";
|
|
33
33
|
import { useTransitionStage } from "../../hooks/use-transition-stage.mjs";
|
|
34
|
+
import { applyPatchBatchImmutable, cloneSnapshot } from "../../preview/patch.mjs";
|
|
35
|
+
import { diffSnapshot, diffSnapshotAtPath } from "../../preview/diff.mjs";
|
|
34
36
|
import { detectManyToManyRelations, hasManyToManyRelations } from "../../utils/detect-relations.mjs";
|
|
35
37
|
import { shouldHandleAdminShortcut } from "../../utils/keyboard-shortcuts.mjs";
|
|
36
38
|
import { AdminViewHeader } from "../layout/admin-view-layout.mjs";
|
|
@@ -64,74 +66,6 @@ function extractReactiveConfigs(schema) {
|
|
|
64
66
|
for (const [fieldName, fieldDef] of Object.entries(schema.fields)) if (fieldDef.reactive) configs[fieldName] = fieldDef.reactive;
|
|
65
67
|
return configs;
|
|
66
68
|
}
|
|
67
|
-
function isPlainObject(value) {
|
|
68
|
-
if (!value || typeof value !== "object") return false;
|
|
69
|
-
const prototype = Object.getPrototypeOf(value);
|
|
70
|
-
return prototype === Object.prototype || prototype === null;
|
|
71
|
-
}
|
|
72
|
-
function clonePreviewSnapshot(value) {
|
|
73
|
-
if (typeof structuredClone === "function") try {
|
|
74
|
-
return structuredClone(value);
|
|
75
|
-
} catch {}
|
|
76
|
-
const json = JSON.stringify(value);
|
|
77
|
-
return json === void 0 ? value : JSON.parse(json);
|
|
78
|
-
}
|
|
79
|
-
function arePreviewValuesEqual(a, b) {
|
|
80
|
-
if (Object.is(a, b)) return true;
|
|
81
|
-
if (Array.isArray(a) || Array.isArray(b)) {
|
|
82
|
-
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
83
|
-
return a.every((value, index) => arePreviewValuesEqual(value, b[index]));
|
|
84
|
-
}
|
|
85
|
-
if (isPlainObject(a) || isPlainObject(b)) {
|
|
86
|
-
if (!isPlainObject(a) || !isPlainObject(b)) return false;
|
|
87
|
-
const aKeys = Object.keys(a);
|
|
88
|
-
const bKeys = Object.keys(b);
|
|
89
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
90
|
-
return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && arePreviewValuesEqual(a[key], b[key]));
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
function joinPreviewPath(basePath, key) {
|
|
95
|
-
return basePath ? `${basePath}.${key}` : key;
|
|
96
|
-
}
|
|
97
|
-
function diffPreviewSnapshots(previous, current, basePath = "") {
|
|
98
|
-
if (arePreviewValuesEqual(previous, current)) return [];
|
|
99
|
-
if (isPlainObject(previous) && isPlainObject(current)) {
|
|
100
|
-
const ops = [];
|
|
101
|
-
const keys = new Set([...Object.keys(previous), ...Object.keys(current)]);
|
|
102
|
-
for (const key of keys) {
|
|
103
|
-
const path = joinPreviewPath(basePath, key);
|
|
104
|
-
const previousHasKey = Object.prototype.hasOwnProperty.call(previous, key);
|
|
105
|
-
if (!Object.prototype.hasOwnProperty.call(current, key)) {
|
|
106
|
-
ops.push({
|
|
107
|
-
op: "remove",
|
|
108
|
-
path
|
|
109
|
-
});
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (!previousHasKey) {
|
|
113
|
-
ops.push({
|
|
114
|
-
op: "set",
|
|
115
|
-
path,
|
|
116
|
-
value: current[key]
|
|
117
|
-
});
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
ops.push(...diffPreviewSnapshots(previous[key], current[key], path));
|
|
121
|
-
}
|
|
122
|
-
return ops;
|
|
123
|
-
}
|
|
124
|
-
if (!basePath) return [{
|
|
125
|
-
op: "set",
|
|
126
|
-
path: "",
|
|
127
|
-
value: current
|
|
128
|
-
}];
|
|
129
|
-
return [{
|
|
130
|
-
op: "set",
|
|
131
|
-
path: basePath,
|
|
132
|
-
value: current
|
|
133
|
-
}];
|
|
134
|
-
}
|
|
135
69
|
function isSafePreviewEditPath(path) {
|
|
136
70
|
if (!path || path.length > 512) return false;
|
|
137
71
|
return path.split(".").every((segment) => segment.length > 0 && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor");
|
|
@@ -322,23 +256,48 @@ const PreviewPatchBridge = React.memo(function PreviewPatchBridge$1(t0) {
|
|
|
322
256
|
snapshotVersionRef.current = void 0;
|
|
323
257
|
return;
|
|
324
258
|
}
|
|
325
|
-
const initialSnapshot =
|
|
259
|
+
const initialSnapshot = cloneSnapshot(form.getValues());
|
|
326
260
|
previousSnapshotRef.current = initialSnapshot;
|
|
327
261
|
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(initialSnapshot);
|
|
328
|
-
|
|
262
|
+
let animationFrame = null;
|
|
263
|
+
const pendingOps = /* @__PURE__ */ new Map();
|
|
264
|
+
const flushPendingOps = () => {
|
|
265
|
+
animationFrame = null;
|
|
266
|
+
if (pendingOps.size === 0) return;
|
|
267
|
+
const ops = [...pendingOps.values()];
|
|
268
|
+
pendingOps.clear();
|
|
269
|
+
previewRef.current?.sendPatchBatch(ops, snapshotVersionRef.current);
|
|
270
|
+
};
|
|
271
|
+
const queuePatchOps = (ops_0) => {
|
|
272
|
+
for (const op of ops_0) if (op.path) pendingOps.set(op.path, op);
|
|
273
|
+
if (pendingOps.size === 0 || animationFrame !== null) return;
|
|
274
|
+
animationFrame = window.requestAnimationFrame(flushPendingOps);
|
|
275
|
+
};
|
|
276
|
+
const subscription = form.watch((values, info) => {
|
|
329
277
|
const previousSnapshot = previousSnapshotRef.current;
|
|
330
|
-
const
|
|
278
|
+
const nextValues = values;
|
|
331
279
|
if (!previousSnapshot) {
|
|
280
|
+
const nextSnapshot = cloneSnapshot(nextValues);
|
|
332
281
|
previousSnapshotRef.current = nextSnapshot;
|
|
333
282
|
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(nextSnapshot);
|
|
334
283
|
return;
|
|
335
284
|
}
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
|
|
285
|
+
const changedPath = info?.name;
|
|
286
|
+
let ops_1 = changedPath && isSafePreviewEditPath(changedPath) ? diffSnapshotAtPath(previousSnapshot, nextValues, changedPath) : diffSnapshot(previousSnapshot, nextValues);
|
|
287
|
+
if (ops_1.some(_temp)) {
|
|
288
|
+
const nextSnapshot_0 = cloneSnapshot(nextValues);
|
|
289
|
+
previousSnapshotRef.current = nextSnapshot_0;
|
|
290
|
+
snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(nextSnapshot_0);
|
|
291
|
+
pendingOps.clear();
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
ops_1 = ops_1.filter(_temp2);
|
|
295
|
+
if (ops_1.length === 0) return;
|
|
296
|
+
previousSnapshotRef.current = applyPatchBatchImmutable(previousSnapshot, ops_1);
|
|
297
|
+
queuePatchOps(ops_1);
|
|
340
298
|
});
|
|
341
299
|
return () => {
|
|
300
|
+
if (animationFrame !== null) window.cancelAnimationFrame(animationFrame);
|
|
342
301
|
subscription.unsubscribe();
|
|
343
302
|
};
|
|
344
303
|
};
|
|
@@ -458,7 +417,7 @@ const AutosaveIndicator = React.memo(function AutosaveIndicator$1(t0) {
|
|
|
458
417
|
$[1] = t1;
|
|
459
418
|
} else t1 = $[1];
|
|
460
419
|
const { isDirty } = useFormState(t1);
|
|
461
|
-
const [, forceUpdate] = React.useReducer(
|
|
420
|
+
const [, forceUpdate] = React.useReducer(_temp3, 0);
|
|
462
421
|
let t2;
|
|
463
422
|
if ($[2] !== forceUpdate || $[3] !== lastSaved) {
|
|
464
423
|
t2 = () => {
|
|
@@ -760,7 +719,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
760
719
|
});
|
|
761
720
|
const commitPreviewSnapshot = React.useCallback((data) => {
|
|
762
721
|
if (!isLivePreviewOpen) return;
|
|
763
|
-
previewRef.current?.sendCommit(
|
|
722
|
+
previewRef.current?.sendCommit(cloneSnapshot(data));
|
|
764
723
|
}, [isLivePreviewOpen]);
|
|
765
724
|
const triggerPreviewFullResync = React.useCallback((reason) => {
|
|
766
725
|
if (!isLivePreviewOpen) return;
|
|
@@ -780,7 +739,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
780
739
|
shouldTouch: true,
|
|
781
740
|
shouldValidate: true
|
|
782
741
|
});
|
|
783
|
-
setTimeout(() => scrollFieldIntoView(message.path), 0);
|
|
742
|
+
setTimeout(() => scrollFieldIntoView(message.path, { focus: false }), 0);
|
|
784
743
|
}, [form, schema]);
|
|
785
744
|
const handlePreviewResyncRequest = React.useCallback((reason_0) => {
|
|
786
745
|
triggerPreviewFullResync(reason_0 ?? "preview-request");
|
|
@@ -1822,10 +1781,13 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
|
|
|
1822
1781
|
children: formShell
|
|
1823
1782
|
});
|
|
1824
1783
|
}
|
|
1825
|
-
function _temp(
|
|
1826
|
-
return
|
|
1784
|
+
function _temp(op_0) {
|
|
1785
|
+
return !op_0.path;
|
|
1786
|
+
}
|
|
1787
|
+
function _temp2(op_1) {
|
|
1788
|
+
return op_1.path;
|
|
1827
1789
|
}
|
|
1828
|
-
function
|
|
1790
|
+
function _temp3(x) {
|
|
1829
1791
|
return x + 1;
|
|
1830
1792
|
}
|
|
1831
1793
|
|
|
@@ -134,12 +134,14 @@ function UploadCollectionSheet({ open, onOpenChange, collection, onUploaded }) {
|
|
|
134
134
|
const { uploadMany, isUploading, progress } = useUpload();
|
|
135
135
|
const [uploadedAssets, setUploadedAssets] = React.useState([]);
|
|
136
136
|
const [editAssetId, setEditAssetId] = React.useState(null);
|
|
137
|
-
React.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
const resetUploadSheetState = React.useCallback(() => {
|
|
138
|
+
setUploadedAssets([]);
|
|
139
|
+
setEditAssetId(null);
|
|
140
|
+
}, []);
|
|
141
|
+
const handleOpenChange = React.useCallback((nextOpen) => {
|
|
142
|
+
if (!nextOpen) resetUploadSheetState();
|
|
143
|
+
onOpenChange(nextOpen);
|
|
144
|
+
}, [onOpenChange, resetUploadSheetState]);
|
|
143
145
|
const handleValidationError = React.useCallback((errors) => {
|
|
144
146
|
for (const validationError of errors) toast.error(validationError.message);
|
|
145
147
|
}, []);
|
|
@@ -166,7 +168,7 @@ function UploadCollectionSheet({ open, onOpenChange, collection, onUploaded }) {
|
|
|
166
168
|
]);
|
|
167
169
|
return /* @__PURE__ */ jsx(Sheet, {
|
|
168
170
|
open,
|
|
169
|
-
onOpenChange,
|
|
171
|
+
onOpenChange: handleOpenChange,
|
|
170
172
|
modal: false,
|
|
171
173
|
children: /* @__PURE__ */ jsxs(SheetContent, {
|
|
172
174
|
side: "right",
|
|
@@ -206,7 +208,7 @@ function UploadCollectionSheet({ open, onOpenChange, collection, onUploaded }) {
|
|
|
206
208
|
className: "border-t px-6 py-4",
|
|
207
209
|
children: /* @__PURE__ */ jsx(Button, {
|
|
208
210
|
variant: "outline",
|
|
209
|
-
onClick: () =>
|
|
211
|
+
onClick: () => handleOpenChange(false),
|
|
210
212
|
disabled: isUploading,
|
|
211
213
|
children: t("common.close")
|
|
212
214
|
})
|
|
@@ -216,8 +218,8 @@ function UploadCollectionSheet({ open, onOpenChange, collection, onUploaded }) {
|
|
|
216
218
|
collection,
|
|
217
219
|
itemId: editAssetId,
|
|
218
220
|
open: !!editAssetId,
|
|
219
|
-
onOpenChange: (
|
|
220
|
-
if (!
|
|
221
|
+
onOpenChange: (nextOpen_0) => {
|
|
222
|
+
if (!nextOpen_0) setEditAssetId(null);
|
|
221
223
|
},
|
|
222
224
|
onSave: () => {
|
|
223
225
|
onUploaded?.();
|