@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.
Files changed (61) hide show
  1. package/dist/client/blocks/block-renderer.d.mts +2 -2
  2. package/dist/client/components/admin-link.d.mts +2 -2
  3. package/dist/client/components/blocks/block-library-sidebar.mjs +126 -113
  4. package/dist/client/components/fields/json-field.mjs +114 -118
  5. package/dist/client/components/fields/rich-text-editor/bubble-menu.mjs +38 -40
  6. package/dist/client/components/fields/rich-text-editor/image-popover.mjs +6 -17
  7. package/dist/client/components/fields/rich-text-editor/index.mjs +71 -87
  8. package/dist/client/components/history-sidebar.mjs +25 -10
  9. package/dist/client/components/media/media-picker-dialog.mjs +237 -254
  10. package/dist/client/components/preview/live-preview-mode.mjs +230 -203
  11. package/dist/client/components/primitives/asset-preview.mjs +1 -1
  12. package/dist/client/contexts/focus-context.d.mts +4 -0
  13. package/dist/client/contexts/focus-context.mjs +106 -86
  14. package/dist/client/preview/block-scope-context.d.mts +2 -2
  15. package/dist/client/preview/diff.mjs +110 -0
  16. package/dist/client/preview/preview-banner.d.mts +2 -2
  17. package/dist/client/preview/preview-field.d.mts +4 -4
  18. package/dist/client/preview/preview-field.mjs +125 -187
  19. package/dist/client/preview/use-collection-preview.mjs +49 -21
  20. package/dist/client/views/auth/accept-invite-form.d.mts +2 -2
  21. package/dist/client/views/auth/auth-layout.d.mts +3 -3
  22. package/dist/client/views/auth/forgot-password-form.d.mts +2 -2
  23. package/dist/client/views/auth/login-form.d.mts +2 -2
  24. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  25. package/dist/client/views/auth/setup-form.d.mts +2 -2
  26. package/dist/client/views/collection/bulk-action-toolbar.mjs +135 -156
  27. package/dist/client/views/collection/field-renderer.mjs +162 -51
  28. package/dist/client/views/collection/form-view.mjs +43 -81
  29. package/dist/client/views/collection/table-view.mjs +12 -10
  30. package/dist/client/views/common/global-search.mjs +199 -182
  31. package/dist/client/views/globals/global-form-view.mjs +625 -621
  32. package/dist/client/views/layout/admin-router.mjs +84 -100
  33. package/dist/client/views/pages/accept-invite-page.d.mts +2 -2
  34. package/dist/client/views/pages/dashboard-page.d.mts +2 -2
  35. package/dist/client/views/pages/forgot-password-page.d.mts +2 -2
  36. package/dist/client/views/pages/invite-page.d.mts +2 -2
  37. package/dist/client/views/pages/reset-password-page.d.mts +2 -2
  38. package/dist/client/views/pages/setup-page.d.mts +2 -2
  39. package/dist/server/modules/admin/collections/account.d.mts +50 -50
  40. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  41. package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
  42. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  43. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  44. package/dist/server/modules/admin/collections/assets.d.mts +39 -39
  45. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  46. package/dist/server/modules/admin/collections/user.d.mts +63 -63
  47. package/dist/server/modules/admin/collections/verification.d.mts +32 -32
  48. package/dist/server/modules/admin/index.d.mts +1 -1
  49. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  50. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  51. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  52. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  53. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  54. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  55. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  56. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  57. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +41 -41
  58. package/dist/server/modules/audit/.generated/module.d.mts +6 -6
  59. package/dist/server/modules/audit/collections/audit-log.d.mts +78 -78
  60. package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
  61. 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 = clonePreviewSnapshot(form.getValues());
259
+ const initialSnapshot = cloneSnapshot(form.getValues());
326
260
  previousSnapshotRef.current = initialSnapshot;
327
261
  snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(initialSnapshot);
328
- const subscription = form.watch((values) => {
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 nextSnapshot = clonePreviewSnapshot(values);
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 ops = diffPreviewSnapshots(previousSnapshot, nextSnapshot).filter(_temp);
337
- previousSnapshotRef.current = nextSnapshot;
338
- if (ops.length === 0) return;
339
- previewRef.current?.sendPatchBatch(ops, snapshotVersionRef.current);
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(_temp2, 0);
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(clonePreviewSnapshot(data));
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(op) {
1826
- return op.path;
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 _temp2(x) {
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.useEffect(() => {
138
- if (!open) {
139
- setUploadedAssets([]);
140
- setEditAssetId(null);
141
- }
142
- }, [open]);
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: () => onOpenChange(false),
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: (nextOpen) => {
220
- if (!nextOpen) setEditAssetId(null);
221
+ onOpenChange: (nextOpen_0) => {
222
+ if (!nextOpen_0) setEditAssetId(null);
221
223
  },
222
224
  onSave: () => {
223
225
  onUploaded?.();