@preprio/toolkit 0.5.0-rc.11 → 0.5.0-rc.12

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 (34) hide show
  1. package/README.md +7 -0
  2. package/dist/astro/index.cjs +31 -2
  3. package/dist/astro/index.js +31 -2
  4. package/dist/{chunk-KD3GK6TO.js → chunk-CABGJW5X.js} +30 -1
  5. package/dist/{chunk-URXSDJOU.js → chunk-H3AFUPYD.js} +9 -0
  6. package/dist/{chunk-AOHA5INA.js → chunk-HVTNUCF7.js} +1 -1
  7. package/dist/{chunk-IPMB32AZ.js → chunk-LWUSRWFE.js} +2 -2
  8. package/dist/{chunk-6423Q744.js → chunk-M7FT7XXV.js} +63 -16
  9. package/dist/{chunk-QSXIHRH4.js → chunk-TJ7VALNA.js} +1 -1
  10. package/dist/index.cjs +101 -16
  11. package/dist/index.d.cts +6 -1
  12. package/dist/index.d.ts +6 -1
  13. package/dist/index.js +6 -6
  14. package/dist/{live-preview-DLXGXRE5.js → live-preview-SFBGATWC.js} +3 -3
  15. package/dist/nextjs/components.cjs +71 -15
  16. package/dist/nextjs/components.js +3 -3
  17. package/dist/nextjs/index.cjs +31 -2
  18. package/dist/nextjs/index.js +31 -2
  19. package/dist/nextjs/live-action.js +3 -3
  20. package/dist/nextjs/live.cjs +30 -1
  21. package/dist/nextjs/live.js +1 -1
  22. package/dist/nuxt/index.cjs +1 -1
  23. package/dist/nuxt/index.js +1 -1
  24. package/dist/nuxt/{live-preview-2RCU35W5.js → live-preview-XMQQZHMZ.js} +30 -1
  25. package/dist/nuxt/live.cjs +30 -1
  26. package/dist/nuxt/live.js +1 -1
  27. package/dist/react/components.cjs +71 -15
  28. package/dist/react/components.js +71 -15
  29. package/dist/sveltekit/index.cjs +1 -1
  30. package/dist/sveltekit/index.js +1 -1
  31. package/dist/sveltekit/{live-preview-2RCU35W5.js → live-preview-XMQQZHMZ.js} +30 -1
  32. package/dist/sveltekit/live.cjs +30 -1
  33. package/dist/sveltekit/live.js +1 -1
  34. package/package.json +1 -1
@@ -371,6 +371,7 @@ function createIframeBridge(store, options = {}) {
371
371
  const onReferences = options.onReferences;
372
372
  const onAssets = options.onAssets;
373
373
  const onAllowNavigation = options.onAllowNavigation;
374
+ const onItemChanged = options.onItemChanged;
374
375
  let parentOrigin = null;
375
376
  const onKeyDown = (event) => {
376
377
  const key = event.key.toLowerCase();
@@ -461,6 +462,14 @@ function createIframeBridge(store, options = {}) {
461
462
  debug.warn("allowNavigation handler failed", error);
462
463
  }
463
464
  }
465
+ if (data?.event === "prepr:itemChanged") {
466
+ if (typeof data.itemId !== "string" || !data.itemId) return;
467
+ try {
468
+ onItemChanged?.(data.itemId);
469
+ } catch (error) {
470
+ debug.warn("itemChanged handler failed", error);
471
+ }
472
+ }
464
473
  };
465
474
  return {
466
475
  start() {
@@ -2604,6 +2613,7 @@ var ABSOLUTE_HTTP = /^https?:\/\//i;
2604
2613
  var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
2605
2614
  var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
2606
2615
  var ASSET_ATTR = "data-prepr-asset";
2616
+ var REMOVED_ATTR = "data-prepr-removed";
2607
2617
  var ASSET_SOURCE_ATTRIBUTES = [
2608
2618
  ...SOURCE_URL_ATTRIBUTES,
2609
2619
  ...SOURCE_SET_ATTRIBUTES
@@ -2613,7 +2623,8 @@ var TAG_ATTRIBUTES = [
2613
2623
  "data-prepr-id",
2614
2624
  "data-prepr-field",
2615
2625
  "data-prepr-entry-id",
2616
- ASSET_ATTR
2626
+ ASSET_ATTR,
2627
+ REMOVED_ATTR
2617
2628
  ];
2618
2629
  function decodeSegment(segment) {
2619
2630
  try {
@@ -2732,11 +2743,22 @@ function besideOtherItem(img, isCurrentItem) {
2732
2743
  return false;
2733
2744
  }
2734
2745
  function tagAssetImages(index, itemId, keys, options = {}) {
2735
- const { isCurrentItem } = options;
2746
+ const { isCurrentItem, removed } = options;
2747
+ let showsRemoved = false;
2736
2748
  for (const img of document.getElementsByTagName("img")) {
2737
2749
  const ours = img.hasAttribute(ASSET_ATTR);
2738
- if (!ours && img.hasAttribute("data-prepr-encoded")) continue;
2739
- const match = index.size ? matchAsset(img, index, keys) : void 0;
2750
+ let match = index.size ? matchAsset(img, index, keys) : void 0;
2751
+ let isRemoved = false;
2752
+ if (!match && removed?.size) {
2753
+ match = matchAsset(img, removed, keys);
2754
+ isRemoved = match !== void 0;
2755
+ }
2756
+ if (!ours && img.hasAttribute("data-prepr-encoded")) {
2757
+ const mark = isRemoved && (!isCurrentItem || isCurrentItem(img));
2758
+ setIfChanged(img, REMOVED_ATTR, mark ? "" : null);
2759
+ showsRemoved || (showsRemoved = mark);
2760
+ continue;
2761
+ }
2740
2762
  const asset = match && isCurrentItem && besideOtherItem(img, isCurrentItem) ? void 0 : match;
2741
2763
  if (!asset) {
2742
2764
  if (ours) untagAssetImage(img);
@@ -2747,10 +2769,14 @@ function tagAssetImages(index, itemId, keys, options = {}) {
2747
2769
  setIfChanged(img, "data-prepr-id", itemId);
2748
2770
  setIfChanged(img, "data-prepr-field", asset.field);
2749
2771
  setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
2772
+ setIfChanged(img, REMOVED_ATTR, isRemoved ? "" : null);
2773
+ showsRemoved || (showsRemoved = isRemoved);
2750
2774
  }
2775
+ return showsRemoved;
2751
2776
  }
2752
2777
  function untagAssetImages() {
2753
2778
  document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
2779
+ document.querySelectorAll(`img[${REMOVED_ATTR}]`).forEach((img) => img.removeAttribute(REMOVED_ATTR));
2754
2780
  }
2755
2781
 
2756
2782
  // src/core/visual-editor/navigation-guard.ts
@@ -2926,11 +2952,13 @@ var CHROME_SELECTOR = "header, footer, nav, [role=banner], [role=contentinfo], [
2926
2952
  var SECTIONING_SELECTOR = "article, aside, main, nav, section";
2927
2953
  var PLAIN_HEADER_FOOTER = "header:not([role]), footer:not([role])";
2928
2954
  var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
2929
- var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}`;
2955
+ var REMOVED_STYLES = `img[${REMOVED_ATTR}]{opacity:.2!important;transition:opacity .2s ease}@media (prefers-reduced-motion:reduce){img[${REMOVED_ATTR}]{transition:none}}`;
2956
+ var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}${REMOVED_STYLES}`;
2930
2957
  var SUSPENDED_ATTR = "data-prepr-ve-suspended";
2931
2958
  var FIND_RETRY_MS = 150;
2932
2959
  var FIND_RETRIES = 6;
2933
2960
  var MAX_CACHED_KEYS = 2e3;
2961
+ var SAVED_FADE_MS = 1e4;
2934
2962
  var LAYOUT_MUTATIONS = {
2935
2963
  childList: true,
2936
2964
  subtree: true,
@@ -2973,6 +3001,9 @@ function createVisualEditor(opts) {
2973
3001
  let guardEnabled = false;
2974
3002
  let assetIndex = /* @__PURE__ */ new Map();
2975
3003
  const assetKeys = /* @__PURE__ */ new Map();
3004
+ const removedAssets = /* @__PURE__ */ new Map();
3005
+ let saved = false;
3006
+ let savedTimeout = null;
2976
3007
  const guard = createNavigationGuard({
2977
3008
  onBlocked: (href) => opts.onNavigationBlocked?.(href),
2978
3009
  showsEditedContent: () => showsEditedContent()
@@ -3171,15 +3202,32 @@ function createVisualEditor(opts) {
3171
3202
  const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
3172
3203
  select(el, found, block);
3173
3204
  }
3205
+ const hasAssets = () => assetIndex.size > 0 || removedAssets.size > 0;
3174
3206
  function tagAssets() {
3175
3207
  if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
3176
- tagAssetImages(assetIndex, currentItemId, assetKeys, {
3177
- isCurrentItem: (el) => isCurrentItem(el)
3208
+ const showsRemoved = tagAssetImages(assetIndex, currentItemId, assetKeys, {
3209
+ isCurrentItem: (el) => isCurrentItem(el),
3210
+ removed: removedAssets
3178
3211
  });
3212
+ if (saved && !showsRemoved) forgetRemoved();
3213
+ }
3214
+ function forgetRemoved() {
3215
+ saved = false;
3216
+ if (savedTimeout) clearTimeout(savedTimeout);
3217
+ savedTimeout = null;
3218
+ if (!removedAssets.size) return;
3219
+ removedAssets.clear();
3220
+ if (running) tagAssets();
3221
+ }
3222
+ function itemSaved() {
3223
+ if (!removedAssets.size) return;
3224
+ saved = true;
3225
+ if (savedTimeout) clearTimeout(savedTimeout);
3226
+ savedTimeout = setTimeout(forgetRemoved, SAVED_FADE_MS);
3179
3227
  }
3180
3228
  const handleLayoutFrame = () => {
3181
3229
  mutationFrame = null;
3182
- if (assetIndex.size) tagAssets();
3230
+ if (hasAssets()) tagAssets();
3183
3231
  reselect();
3184
3232
  const hovered = overlay.getHoverTarget();
3185
3233
  if (hovered && !hovered.isConnected) {
@@ -3191,7 +3239,7 @@ function createVisualEditor(opts) {
3191
3239
  };
3192
3240
  const handleMutations = () => {
3193
3241
  if (mutationFrame !== null) return;
3194
- if (!selection && !overlay.getHoverTarget() && !assetIndex.size) return;
3242
+ if (!selection && !overlay.getHoverTarget() && !hasAssets()) return;
3195
3243
  mutationFrame = requestAnimationFrame(handleLayoutFrame);
3196
3244
  };
3197
3245
  function injectCursorStyles() {
@@ -3232,7 +3280,7 @@ function createVisualEditor(opts) {
3232
3280
  });
3233
3281
  mutationObserver = new MutationObserver(handleMutations);
3234
3282
  mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
3235
- if (assetIndex.size) tagAssets();
3283
+ if (hasAssets()) tagAssets();
3236
3284
  syncGuard();
3237
3285
  debug8.log("visual editor started");
3238
3286
  }
@@ -3308,18 +3356,24 @@ function createVisualEditor(opts) {
3308
3356
  });
3309
3357
  }
3310
3358
  function setCurrentItem(itemId, referenceList, componentIds) {
3359
+ if (itemId !== currentItemId) forgetRemoved();
3311
3360
  currentItemId = itemId;
3312
3361
  if (referenceList) setReferences(referenceList, componentIds);
3313
3362
  else if (componentIds) ownComponents = new Set(componentIds);
3314
- if (running && assetIndex.size) tagAssets();
3363
+ if (running && hasAssets()) tagAssets();
3315
3364
  const selected = overlay.getSelectedTarget();
3316
3365
  const owner = selected ? encodedFrom(selected) : null;
3317
3366
  if (owner && !isCurrentItem(owner)) clearSelection();
3318
3367
  }
3319
3368
  function setAssets(assets) {
3320
- const hadAssets = assetIndex.size > 0;
3321
- assetIndex = createAssetIndex(assets);
3322
- if (running && (hadAssets || assetIndex.size)) tagAssets();
3369
+ const hadAssets = hasAssets();
3370
+ const next = createAssetIndex(assets);
3371
+ for (const [key, asset] of assetIndex) {
3372
+ if (!next.has(key)) removedAssets.set(key, asset);
3373
+ }
3374
+ for (const key of next.keys()) removedAssets.delete(key);
3375
+ assetIndex = next;
3376
+ if (running && (hadAssets || hasAssets())) tagAssets();
3323
3377
  }
3324
3378
  function setReferences(referenceList, componentIds) {
3325
3379
  if (componentIds) ownComponents = new Set(componentIds);
@@ -3345,6 +3399,7 @@ function createVisualEditor(opts) {
3345
3399
  setCurrentItem,
3346
3400
  setReferences,
3347
3401
  setAssets,
3402
+ itemSaved,
3348
3403
  setLocale,
3349
3404
  focusField,
3350
3405
  setNavigationGuard,
@@ -3502,6 +3557,7 @@ function createPreprPreview(opts = {}) {
3502
3557
  },
3503
3558
  onAssets: (assets) => visualEditor?.setAssets(assets),
3504
3559
  onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
3560
+ onItemChanged: () => visualEditor?.itemSaved(),
3505
3561
  onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
3506
3562
  // The editor selecting a field scrolls the matching element into view. The
3507
3563
  // lookup relies on the attributes the stega scan writes, so it finds
@@ -3542,7 +3598,7 @@ function createPreprPreview(opts = {}) {
3542
3598
  }
3543
3599
 
3544
3600
  // src/version.ts
3545
- var VERSION = "0.5.0-rc.11";
3601
+ var VERSION = "0.5.0-rc.12";
3546
3602
 
3547
3603
  // src/core/pixel.ts
3548
3604
  var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
@@ -35,7 +35,7 @@ __export(sveltekit_exports, {
35
35
  module.exports = __toCommonJS(sveltekit_exports);
36
36
 
37
37
  // src/version.ts
38
- var VERSION = "0.5.0-rc.11";
38
+ var VERSION = "0.5.0-rc.12";
39
39
 
40
40
  // src/core/constants.ts
41
41
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.5.0-rc.11";
2
+ var VERSION = "0.5.0-rc.12";
3
3
 
4
4
  // src/core/constants.ts
5
5
  var COOKIE_PREVIEW_MODE = "Prepr-Preview-Mode";
@@ -160,8 +160,32 @@ function shapeLike(template, incoming) {
160
160
  }
161
161
  return out;
162
162
  }
163
+ function isAsset(value) {
164
+ return isPlainObject(value) && (typeof value.url === "string" || value.__typename === "Asset");
165
+ }
166
+ function hasId(list, entry) {
167
+ return entry._id !== void 0 && list.some((c) => isPlainObject(c) && c._id === entry._id);
168
+ }
169
+ function isAssetValue(value) {
170
+ return Array.isArray(value) ? value.length > 0 && value.every(isAsset) : isAsset(value);
171
+ }
172
+ function keepRemovedAssets(current, next) {
173
+ const listed = /* @__PURE__ */ new Set();
174
+ for (const entry of next) {
175
+ if (isPlainObject(entry) && entry._id !== void 0) listed.add(entry._id);
176
+ }
177
+ let out = next;
178
+ current.forEach((entry, position) => {
179
+ if (!isPlainObject(entry) || entry._id === void 0) return;
180
+ if (listed.has(entry._id)) return;
181
+ if (out === next) out = [...next];
182
+ out.splice(Math.min(position, out.length), 0, entry);
183
+ });
184
+ return out;
185
+ }
163
186
  function mergeStructured(current, incoming) {
164
187
  if (incoming === null || Array.isArray(incoming) && !incoming.length) {
188
+ if (isAssetValue(current)) return current;
165
189
  if (Array.isArray(current)) return current.length ? [] : current;
166
190
  return null;
167
191
  }
@@ -169,13 +193,18 @@ function mergeStructured(current, incoming) {
169
193
  if (current === null || current === void 0) return incoming;
170
194
  if (Array.isArray(current)) {
171
195
  const template = current.find(isPlainObject);
172
- return list.map((entry) => {
196
+ const next = list.map((entry) => {
173
197
  const match = current.find(
174
198
  (c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
175
199
  );
176
200
  if (isPlainObject(match)) return mergeObject(match, entry);
177
201
  return isPlainObject(template) ? shapeLike(template, entry) : entry;
178
202
  });
203
+ if (!isAssetValue(current) || list.some((entry) => !hasId(current, entry))) {
204
+ return next;
205
+ }
206
+ const kept = keepRemovedAssets(current, next);
207
+ return kept.length === current.length && kept.every((entry, i) => entry === current[i]) ? current : kept;
179
208
  }
180
209
  if (isPlainObject(current)) return mergeObject(current, list[0]);
181
210
  return current;
@@ -536,8 +536,32 @@ function shapeLike(template, incoming) {
536
536
  }
537
537
  return out;
538
538
  }
539
+ function isAsset(value) {
540
+ return isPlainObject(value) && (typeof value.url === "string" || value.__typename === "Asset");
541
+ }
542
+ function hasId(list, entry) {
543
+ return entry._id !== void 0 && list.some((c) => isPlainObject(c) && c._id === entry._id);
544
+ }
545
+ function isAssetValue(value) {
546
+ return Array.isArray(value) ? value.length > 0 && value.every(isAsset) : isAsset(value);
547
+ }
548
+ function keepRemovedAssets(current, next) {
549
+ const listed = /* @__PURE__ */ new Set();
550
+ for (const entry of next) {
551
+ if (isPlainObject(entry) && entry._id !== void 0) listed.add(entry._id);
552
+ }
553
+ let out = next;
554
+ current.forEach((entry, position) => {
555
+ if (!isPlainObject(entry) || entry._id === void 0) return;
556
+ if (listed.has(entry._id)) return;
557
+ if (out === next) out = [...next];
558
+ out.splice(Math.min(position, out.length), 0, entry);
559
+ });
560
+ return out;
561
+ }
539
562
  function mergeStructured(current, incoming) {
540
563
  if (incoming === null || Array.isArray(incoming) && !incoming.length) {
564
+ if (isAssetValue(current)) return current;
541
565
  if (Array.isArray(current)) return current.length ? [] : current;
542
566
  return null;
543
567
  }
@@ -545,13 +569,18 @@ function mergeStructured(current, incoming) {
545
569
  if (current === null || current === void 0) return incoming;
546
570
  if (Array.isArray(current)) {
547
571
  const template = current.find(isPlainObject);
548
- return list.map((entry) => {
572
+ const next = list.map((entry) => {
549
573
  const match = current.find(
550
574
  (c) => isPlainObject(c) && c._id !== void 0 && c._id === entry._id
551
575
  );
552
576
  if (isPlainObject(match)) return mergeObject(match, entry);
553
577
  return isPlainObject(template) ? shapeLike(template, entry) : entry;
554
578
  });
579
+ if (!isAssetValue(current) || list.some((entry) => !hasId(current, entry))) {
580
+ return next;
581
+ }
582
+ const kept = keepRemovedAssets(current, next);
583
+ return kept.length === current.length && kept.every((entry, i) => entry === current[i]) ? current : kept;
555
584
  }
556
585
  if (isPlainObject(current)) return mergeObject(current, list[0]);
557
586
  return current;
@@ -131,7 +131,7 @@ function isFramed() {
131
131
  return typeof window !== "undefined" && window.self !== window.top;
132
132
  }
133
133
  function startLive() {
134
- starting ?? (starting = import("./live-preview-2RCU35W5.js").then(
134
+ starting ?? (starting = import("./live-preview-XMQQZHMZ.js").then(
135
135
  ({ applyPatchLog, createLiveBridge, createLiveStore }) => {
136
136
  applyLog = applyPatchLog;
137
137
  const started = createLiveStore();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preprio/toolkit",
3
- "version": "0.5.0-rc.11",
3
+ "version": "0.5.0-rc.12",
4
4
  "description": "Prepr personalization, A/B testing and preview toolkit for React, Next.js, Nuxt, Astro, SvelteKit and Express",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Prepr B.V. (https://prepr.io)",