@designcrowd/library.brand-page 5.18.4 → 5.18.5

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 (162) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-DQLtpdLe.js → CreatedOnBrandCrowd-de-de-ciKpCAd9.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CvEhlADd.js → CreatedOnBrandCrowd-en-us-CZbTS1AZ.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-pYQeTTC0.js → CreatedOnBrandCrowd-es-es-C8MExng5.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-CcE-iCEq.js → CreatedOnBrandCrowd-fr-ca-CO0L9NK2.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-CcE-iCEq.js → CreatedOnBrandCrowd-fr-fr-CO0L9NK2.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-OyhWWWyO.js → CreatedOnBrandCrowd-pt-br-zVDvBF2H.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-OyhWWWyO.js → CreatedOnBrandCrowd-pt-pt-zVDvBF2H.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-DH98FFLS.js → CreatedOnDcom-de-de-WrUSQdjf.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-DK67NN1r.js → CreatedOnDcom-en-us-BqC9YtkL.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-9-QU4BU6.js → CreatedOnDcom-es-es-BzH32Oya.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-U9J9H9VB.js → CreatedOnDcom-fr-ca-D7TYfCW0.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DqdzB1DJ.js → CreatedOnDcom-fr-fr-DVlyfBtb.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-c4UiWtkB.js → CreatedOnDcom-pt-br-CSRB91ey.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-Do2qJsYp.js → CreatedOnDcom-pt-pt-9KMj5gpc.js} +1 -1
  15. package/lib/src/brand-page/models/navigation-styles.model.d.ts +7 -0
  16. package/lib/src/brand-page/models/navigation-styles.model.js +25 -2
  17. package/lib/src/brand-page/utils/migration/migration-16.d.ts +2 -0
  18. package/lib/src/brand-page/utils/migration/migration-16.js +47 -0
  19. package/lib/src/brand-page/utils/migration.util.js +7 -0
  20. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +7 -2
  21. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +7 -2
  22. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +8 -0
  23. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +7 -2
  24. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +1 -0
  25. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +87 -2
  26. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.js +5 -0
  27. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +7 -2
  28. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +3 -0
  29. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.js +11 -0
  30. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +7 -2
  31. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +7 -2
  32. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +7 -2
  33. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +7 -2
  34. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +9 -0
  35. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +20 -1
  36. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +20 -0
  37. package/lib/src/brand-page-maker/composables/useEventTracker.js +17 -0
  38. package/lib/src/brand-page-maker/composables/useUndoRedoFlag.d.ts +17 -0
  39. package/lib/src/brand-page-maker/composables/useUndoRedoFlag.js +25 -0
  40. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +41 -2
  41. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +308 -14
  42. package/lib/src/brand-page-maker/stores/history/history-store.d.ts +83 -0
  43. package/lib/src/brand-page-maker/stores/history/history-store.js +495 -0
  44. package/lib/src/brand-page-maker/stores/logo-store/logo-store.d.ts +1 -0
  45. package/lib/src/brand-page-maker/stores/user/user-store.js +1 -0
  46. package/lib/src/bundled-translations-D1q3rSEz.js +175 -0
  47. package/lib/src/bundled-translations.de-DE-BxqLLwbh.js +175 -0
  48. package/lib/src/bundled-translations.es-ES-uh249NlJ.js +175 -0
  49. package/lib/src/bundled-translations.fr-CA-BEQmv2fX.js +175 -0
  50. package/lib/src/bundled-translations.fr-FR-BnNuCojH.js +175 -0
  51. package/lib/src/bundled-translations.pt-BR-CJUOxVlq.js +175 -0
  52. package/lib/src/bundled-translations.pt-PT-Ds9cMEqK.js +175 -0
  53. package/lib/src/bundles/bundled-translations.de-DE.json +3 -1
  54. package/lib/src/bundles/bundled-translations.es-ES.json +3 -1
  55. package/lib/src/bundles/bundled-translations.fr-CA.json +3 -1
  56. package/lib/src/bundles/bundled-translations.fr-FR.json +3 -1
  57. package/lib/src/bundles/bundled-translations.json +3 -1
  58. package/lib/src/bundles/bundled-translations.pt-BR.json +3 -1
  59. package/lib/src/bundles/bundled-translations.pt-PT.json +3 -1
  60. package/lib/src/constants/ui.d.ts +1 -0
  61. package/lib/src/css/library.brand-page-brandCrowd.min.css +77 -82
  62. package/lib/src/css/library.brand-page-brandPage.min.css +77 -83
  63. package/lib/src/css/library.brand-page-designCom.min.css +77 -82
  64. package/lib/src/css/library.brand-page-preview.min.css +77 -82
  65. package/lib/src/index.mjs +62 -60
  66. package/lib/src/{lib-BI3J4Mbk.js → lib-BXbo_N88.js} +35072 -34447
  67. package/lib/src/metadata-builder/builders/reorder-pages-builder.d.ts +13 -0
  68. package/lib/src/metadata-builder/builders/reorder-pages-builder.js +49 -0
  69. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +87 -2
  70. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.js +5 -0
  71. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.stories.js +1 -0
  72. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +23 -2
  73. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +16 -0
  74. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +7 -2
  75. package/lib/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.d.ts +3 -0
  76. package/lib/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.js +19 -2
  77. package/lib/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.js +18 -8
  78. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +64 -0
  79. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.js +6 -0
  80. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +19 -0
  81. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +11 -0
  82. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +10 -2
  83. package/lib/src/shared/logo-settings/store/editable-logo-store.d.ts +5 -1
  84. package/lib/src/shared/logo-settings/store/editable-logo-store.js +31 -0
  85. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.js +10 -8
  86. package/lib/src/shared/navigation-style-settings/store/index.js +1 -12
  87. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +24 -2
  88. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +10 -2
  89. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +23 -2
  90. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +16 -0
  91. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +23 -2
  92. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +16 -0
  93. package/package.json +1 -1
  94. package/src/brand-page/components/button/Button.vue +2 -10
  95. package/src/brand-page/components/contact-form/ContactForm.vue +2 -2
  96. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.vue +4 -1
  97. package/src/brand-page/models/navigation-styles.model.ts +39 -1
  98. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
  99. package/src/brand-page/utils/migration/migration-16.test.ts +319 -0
  100. package/src/brand-page/utils/migration/migration-16.ts +75 -0
  101. package/src/brand-page/utils/migration.util.ts +8 -0
  102. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +9 -0
  103. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +2 -3
  104. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +1 -0
  105. package/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.ts +5 -0
  106. package/src/brand-page-maker/components/color-palette/BpColorPalette.vue +9 -7
  107. package/src/brand-page-maker/components/cookie-collection/CookieSettings.vue +18 -17
  108. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.ts +13 -0
  109. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.vue +26 -3
  110. package/src/brand-page-maker/components/quick-link-bar/RedoButton.vue +66 -0
  111. package/src/brand-page-maker/components/quick-link-bar/UndoButton.vue +66 -0
  112. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.de-DE.json +6 -4
  113. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.es-ES.json +6 -4
  114. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.fr-CA.json +6 -4
  115. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.fr-FR.json +6 -4
  116. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.json +3 -1
  117. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.pt-BR.json +6 -4
  118. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.pt-PT.json +6 -4
  119. package/src/brand-page-maker/components/quick-link-bar/undo-redo-buttons.test.ts +116 -0
  120. package/src/brand-page-maker/components/save-contact/SaveContact.vue +12 -19
  121. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +10 -0
  122. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +24 -1
  123. package/src/brand-page-maker/composables/useEventTracker.ts +29 -0
  124. package/src/brand-page-maker/composables/useUndoRedoFlag.ts +30 -0
  125. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +377 -0
  126. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +371 -18
  127. package/src/brand-page-maker/stores/history/history-store.test.ts +1120 -0
  128. package/src/brand-page-maker/stores/history/history-store.ts +660 -0
  129. package/src/brand-page-maker/stores/logo-store/logo-store.ts +5 -0
  130. package/src/brand-page-maker/stores/user/user-store.ts +1 -0
  131. package/src/bundles/bundled-translations.de-DE.json +3 -1
  132. package/src/bundles/bundled-translations.es-ES.json +3 -1
  133. package/src/bundles/bundled-translations.fr-CA.json +3 -1
  134. package/src/bundles/bundled-translations.fr-FR.json +3 -1
  135. package/src/bundles/bundled-translations.json +3 -1
  136. package/src/bundles/bundled-translations.pt-BR.json +3 -1
  137. package/src/bundles/bundled-translations.pt-PT.json +3 -1
  138. package/src/constants/ui.ts +1 -0
  139. package/src/metadata-builder/builders/reorder-pages-builder.ts +80 -0
  140. package/src/shared/content-settings/components/color-field/ColorField.vue +10 -5
  141. package/src/shared/content-settings/components/content-settings/ContentSettings.mixin.ts +5 -0
  142. package/src/shared/content-settings/components/content-settings/ContentSettings.stories.ts +1 -0
  143. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.ts +19 -2
  144. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.vue +1 -1
  145. package/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.ts +26 -10
  146. package/src/shared/content-settings/components/logo-selection-field/LogoSelection.vue +63 -48
  147. package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.vue +4 -0
  148. package/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.ts +7 -0
  149. package/src/shared/content-settings/components/text-field/TextField.vue +1 -0
  150. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +33 -4
  151. package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +11 -0
  152. package/src/shared/logo-settings/store/editable-logo-store.ts +42 -1
  153. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.ts +17 -11
  154. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.vue +4 -1
  155. package/src/shared/navigation-style-settings/store/index.ts +1 -21
  156. package/lib/src/bundled-translations-B5Fkt3qd.js +0 -175
  157. package/lib/src/bundled-translations.de-DE-DpdVePM9.js +0 -175
  158. package/lib/src/bundled-translations.es-ES-BsIcrzZF.js +0 -175
  159. package/lib/src/bundled-translations.fr-CA-G1XCp2Qh.js +0 -175
  160. package/lib/src/bundled-translations.fr-FR-BdwLXOnB.js +0 -175
  161. package/lib/src/bundled-translations.pt-BR-Bvn5ePe7.js +0 -175
  162. package/lib/src/bundled-translations.pt-PT-CDtkb2k1.js +0 -175
@@ -1126,7 +1126,9 @@
1126
1126
  "quickLinkBar": {
1127
1127
  "myWebPageLabel": "Ma page Web :",
1128
1128
  "copyButton": "Copier",
1129
- "copiedButton": "Copié !"
1129
+ "copiedButton": "Copié !",
1130
+ "undoLabel": "Annuler",
1131
+ "redoLabel": "Rétablir"
1130
1132
  },
1131
1133
  "saveContactSettings": {
1132
1134
  "saveContentInfo": "Permettez aux visiteurs d'enregistrer vos coordonnées lorsqu'ils consultent votre carte professionnelle numérique",
@@ -1126,7 +1126,9 @@
1126
1126
  "quickLinkBar": {
1127
1127
  "myWebPageLabel": "Ma page Web :",
1128
1128
  "copyButton": "Copier",
1129
- "copiedButton": "Copié !"
1129
+ "copiedButton": "Copié !",
1130
+ "undoLabel": "Annuler",
1131
+ "redoLabel": "Rétablir"
1130
1132
  },
1131
1133
  "saveContactSettings": {
1132
1134
  "saveContentInfo": "Permettez aux visiteurs d'enregistrer vos coordonnées lorsqu'ils consultent votre carte de visite numérique",
@@ -1126,7 +1126,9 @@
1126
1126
  "quickLinkBar": {
1127
1127
  "myWebPageLabel": "My web page:",
1128
1128
  "copyButton": "Copy",
1129
- "copiedButton": "Copied!"
1129
+ "copiedButton": "Copied!",
1130
+ "undoLabel": "Undo",
1131
+ "redoLabel": "Redo"
1130
1132
  },
1131
1133
  "saveContactSettings": {
1132
1134
  "saveContentInfo": "Let visitors save your contact information when they view your Digital Business Card",
@@ -1126,7 +1126,9 @@
1126
1126
  "quickLinkBar": {
1127
1127
  "myWebPageLabel": "Minha página web:",
1128
1128
  "copyButton": "Copiar",
1129
- "copiedButton": "Copiado!"
1129
+ "copiedButton": "Copiado!",
1130
+ "undoLabel": "Desfazer",
1131
+ "redoLabel": "Refazer"
1130
1132
  },
1131
1133
  "saveContactSettings": {
1132
1134
  "saveContentInfo": "Permita que os visitantes salvem suas informações de contato quando visualizarem seu cartão de visita digital",
@@ -1126,7 +1126,9 @@
1126
1126
  "quickLinkBar": {
1127
1127
  "myWebPageLabel": "A minha página Web:",
1128
1128
  "copyButton": "Copiar",
1129
- "copiedButton": "Copiado!"
1129
+ "copiedButton": "Copiado!",
1130
+ "undoLabel": "Desfazer",
1131
+ "redoLabel": "Refazer"
1130
1132
  },
1131
1133
  "saveContactSettings": {
1132
1134
  "saveContentInfo": "Permita que os visitantes guardem a sua informação de contacto quando visualizarem o seu cartão de visita digital",
@@ -14,6 +14,7 @@ export interface UIUserFeatures {
14
14
  canUseGettyVideos: boolean;
15
15
  canUseGoogleAdsense: boolean;
16
16
  googleAdsenseEnabled: boolean;
17
+ canUseUndoRedo: boolean;
17
18
  }
18
19
 
19
20
  export const TEMPLATE_FIRST_PAGE_NO = 1;
@@ -0,0 +1,80 @@
1
+ import type { BrandPageModel, NavOrder } from '../../brand-page/models';
2
+ import {
3
+ BaseMetadataBuilder,
4
+ type BuilderResult,
5
+ type MetadataBuilderRequest,
6
+ } from '../models';
7
+ import type { Logger } from '../logger';
8
+
9
+ export interface ReorderPagesPayload {
10
+ pageOrder: string[];
11
+ }
12
+
13
+ export class ReorderPagesBuilder extends BaseMetadataBuilder<ReorderPagesPayload> {
14
+ constructor(
15
+ request: MetadataBuilderRequest<ReorderPagesPayload>,
16
+ logger?: Logger,
17
+ ) {
18
+ super(request, logger);
19
+ }
20
+
21
+ async validateAsync(): Promise<{ errors: string[] }> {
22
+ const errors: string[] = [];
23
+ const payload = this.request.payload;
24
+
25
+ if (!payload?.pageOrder?.length) {
26
+ errors.push('page_order is required for reorder_pages');
27
+ }
28
+
29
+ return { errors };
30
+ }
31
+
32
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
33
+ this.log('Build called');
34
+
35
+ const validation = await this.validateAsync();
36
+
37
+ if (validation.errors.length) {
38
+ return { request: this.request, model, errors: validation.errors };
39
+ }
40
+
41
+ const { pageOrder } = this.request.payload!;
42
+
43
+ // Build pageId → routeId lookup from nav.routes
44
+ const pageIdToRouteId = new Map<string, string>();
45
+
46
+ for (const route of model.nav.routes) {
47
+ pageIdToRouteId.set(route.pageId, route.id);
48
+ }
49
+
50
+ // Build new nav.orders from the flat page_order (array of pageIds)
51
+ const newOrders: NavOrder[] = [];
52
+ const seenRouteIds = new Set<string>();
53
+
54
+ for (const pageId of pageOrder) {
55
+ const routeId = pageIdToRouteId.get(pageId);
56
+
57
+ if (!routeId) continue;
58
+
59
+ newOrders.push({ routeId, subRoutes: [] });
60
+ seenRouteIds.add(routeId);
61
+ }
62
+
63
+ // Safety: append any routes not mentioned (don't lose pages)
64
+ for (const route of model.nav.routes) {
65
+ const routeId = pageIdToRouteId.get(route.pageId);
66
+
67
+ if (routeId && !seenRouteIds.has(routeId)) {
68
+ newOrders.push({ routeId, subRoutes: [] });
69
+ }
70
+ }
71
+
72
+ model.nav.orders = newOrders;
73
+
74
+ this.log({
75
+ message: `Reordered pages: ${pageOrder.join(', ')}`,
76
+ });
77
+
78
+ return { request: this.request, model };
79
+ }
80
+ }
@@ -269,12 +269,17 @@ const onColorMatchToggled = (value: boolean) => {
269
269
  } else {
270
270
  newColor.value = STYLE_THEME_KEYS.LogoTextColor;
271
271
  }
272
+ } else if (props.name === 'background') {
273
+ newColor.value = '';
274
+ } else if (props.disableTransparent) {
275
+ // Fields that disallow transparent (e.g. font colour) must not fall back to
276
+ // transparent when color match is turned off - resolve to the actual logo colour.
277
+ newColor.value =
278
+ color.value === STYLE_THEME_KEYS.LogoBackgroundColor
279
+ ? props.logoTheme.logoBackgroundColor.rgb
280
+ : props.logoTheme.logoTextColor.rgb;
272
281
  } else {
273
- if (props.name === 'background') {
274
- newColor.value = '';
275
- } else {
276
- newColor.value = 'transparent';
277
- }
282
+ newColor.value = 'transparent';
278
283
  }
279
284
 
280
285
  changeColor();
@@ -29,6 +29,7 @@ import {
29
29
  } from '@brand-page-maker/utils';
30
30
  import { useUiStyleSettingsStore } from '../../../style-settings/store';
31
31
  import { usePageNavigationStore } from '../../../../shared/page-navigation/store/page-navigation-store';
32
+ import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
32
33
 
33
34
  interface CategoryTab {
34
35
  category: string;
@@ -44,6 +45,7 @@ export default defineComponent({
44
45
  const styleSettingsStore = useUiStyleSettingsStore();
45
46
  const userStore = useBrandPageMakerUserStore();
46
47
  const eventTracker = useEventTracker();
48
+ const historyStore = useHistoryStore();
47
49
  const { externalEditor } = useExternalRichTextEditor();
48
50
 
49
51
  return {
@@ -65,6 +67,7 @@ export default defineComponent({
65
67
  pageSettingsStore,
66
68
  styleSettingsStore,
67
69
  eventTracker,
70
+ historyStore,
68
71
  externalEditor,
69
72
  };
70
73
  },
@@ -81,6 +84,8 @@ export default defineComponent({
81
84
  },
82
85
  mounted() {
83
86
  if (this.canUseRichTextEditor) {
87
+ // RTE rewrap on tab mount — fold into current head.
88
+ this.historyStore.mergeNextMutation();
84
89
  switchAllContentToRTE(
85
90
  this.pageSettingsStore,
86
91
  this.externalEditor,
@@ -32,6 +32,7 @@ export const Sample = (): any => {
32
32
  canUseGettyVideos: true,
33
33
  canUseGoogleAdsense: false,
34
34
  googleAdsenseEnabled: false,
35
+ canUseUndoRedo: false,
35
36
  };
36
37
 
37
38
  const pinia = setupStoryPinia();
@@ -76,6 +76,12 @@ export default defineComponent({
76
76
  schema: 'https',
77
77
  textIcon: 'globe',
78
78
  textIcons: [] as string[],
79
+ // Display-only override for the focused URL input. While a row is
80
+ // focused we strip the schema visually but MUST NOT mutate modelValue —
81
+ // doing so would land in the store and trigger a spurious undo/redo
82
+ // history entry between focus and blur (BP-5506).
83
+ focusedIndex: null as number | null,
84
+ focusedDisplay: '',
79
85
  }),
80
86
  computed: {
81
87
  items: {
@@ -162,15 +168,26 @@ export default defineComponent({
162
168
  },
163
169
 
164
170
  onFocusOverride(index: number, itemValue: string): void {
165
- const value = [...this.modelValue];
166
171
  const match = URL_RE.exec(itemValue);
167
172
 
168
173
  if (match) {
169
174
  this.schema = match[1];
170
- value[index][this.valuePropName] = match[2];
175
+ this.focusedIndex = index;
176
+ this.focusedDisplay = match[2];
177
+ } else {
178
+ this.focusedIndex = index;
179
+ this.focusedDisplay = itemValue;
171
180
  }
172
181
  },
182
+ inputDisplayValue(index: number): string {
183
+ return this.focusedIndex === index
184
+ ? this.focusedDisplay
185
+ : this.itemValue(index);
186
+ },
173
187
  onBlurOverride(index: number, itemValue: string): void {
188
+ this.focusedIndex = null;
189
+ this.focusedDisplay = '';
190
+
174
191
  const value = [...this.modelValue];
175
192
 
176
193
  let isValid = true;
@@ -70,7 +70,7 @@
70
70
  hasError[index],
71
71
  }"
72
72
  :placeholder="placeholderPrefix"
73
- :value="itemValue(index)"
73
+ :value="inputDisplayValue(index)"
74
74
  :data-test-tag="'image-url-text-input'"
75
75
  @focus="onFocusOverride(index, $event.target.value)"
76
76
  @blur="onBlurOverride(index, $event.target.value)"
@@ -1,4 +1,4 @@
1
- import { defineComponent, ref } from 'vue';
1
+ import { defineComponent, ref, watch } from 'vue';
2
2
  import type { PropType } from 'vue';
3
3
  import type { FormField } from '@brand-page';
4
4
  import { formFieldsTr } from '@src/composables/useTranslate';
@@ -49,18 +49,34 @@ export default defineComponent({
49
49
  },
50
50
  ];
51
51
 
52
+ const valueWithState = ref(
53
+ props.value.map((field) => ({
54
+ field,
55
+ isCollapsed: true,
56
+ isTypeDropdownOpen: false,
57
+ })),
58
+ );
59
+
60
+ // Re-sync when the store-bound value changes externally (e.g. undo/redo
61
+ // via history-store). Preserve per-row UI flags (isCollapsed,
62
+ // isTypeDropdownOpen) by index so an undo doesn't snap rows back to
63
+ // collapsed state.
64
+ watch(
65
+ () => props.value,
66
+ (newVal) => {
67
+ valueWithState.value = newVal.map((field, index) => ({
68
+ field,
69
+ isCollapsed: valueWithState.value[index]?.isCollapsed ?? true,
70
+ isTypeDropdownOpen:
71
+ valueWithState.value[index]?.isTypeDropdownOpen ?? false,
72
+ }));
73
+ },
74
+ );
75
+
52
76
  return {
53
77
  formFieldsTr,
54
78
  typeList,
55
- valueWithState: ref(
56
- props.value.map((field) => {
57
- return {
58
- field: field,
59
- isCollapsed: true,
60
- isTypeDropdownOpen: false,
61
- };
62
- }),
63
- ),
79
+ valueWithState,
64
80
  };
65
81
  },
66
82
  methods: {
@@ -59,7 +59,7 @@
59
59
  </template>
60
60
  </ButtonGroup>
61
61
  <div
62
- v-if="data.selectedOption === 1 && !containerStore.isPrePurchase"
62
+ v-if="selectedOption === 1 && !containerStore.isPrePurchase"
63
63
  class="tw-mb-4"
64
64
  >
65
65
  <div class="tw-flex tw-items-center">
@@ -90,7 +90,7 @@
90
90
  </div>
91
91
  </div>
92
92
  <div
93
- v-if="data.selectedOption === 1 && containerStore.isPrePurchase"
93
+ v-if="selectedOption === 1 && containerStore.isPrePurchase"
94
94
  class="tw-mb-4"
95
95
  >
96
96
  <LogoVariations
@@ -99,7 +99,7 @@
99
99
  @logo-variation-selected="onEditableLogoVariationSelected"
100
100
  />
101
101
  </div>
102
- <div v-if="data.selectedOption === 2" class="tw-mb-4">
102
+ <div v-if="selectedOption === 2" class="tw-mb-4">
103
103
  <UploadLogoModal>
104
104
  <template #main="mainProp">
105
105
  <LogoField
@@ -188,88 +188,100 @@ const logoTabs: Tab[] = reactive([
188
188
  ]);
189
189
 
190
190
  const data = reactive({
191
- selectedOption: 1,
192
- currentLogoIsByo: false,
191
+ // Sticky override for the "Logo / Upload" choice when isLogoVisible is true.
192
+ // Set when the user explicitly clicks Logo or Upload; cleared on undo/redo
193
+ // so the snapshot's logoDraftAsset.isByo decides instead. `null` means
194
+ // "derive from props".
195
+ manualVisibleChoice: null as null | 1 | 2,
193
196
  logoEditUrl: '',
194
197
  });
195
198
 
196
199
  const containerStore = useBrandPageContainerStore();
200
+ const logoStore = useLogoStore();
201
+ const editableLogoStore = useUiEditableLogoStore();
202
+ const sideBarStore = useUiSideBarStore();
203
+ const userStore = useBrandPageMakerUserStore();
204
+
205
+ const currentLogoIsByo = computed(() => {
206
+ const logoDraftAsset = logoStore.getLogoDraftAssetByToken(
207
+ props.selectedLogoDraftToken,
208
+ );
209
+
210
+ return !!logoDraftAsset?.isByo;
211
+ });
212
+
213
+ // Highlighted button. Derived purely from props + the user's most recent
214
+ // manual choice — never from event-handler-set local state. Undo/redo
215
+ // restores `props` from the history snapshot, which clears the manual
216
+ // override (see watcher below) so the highlight tracks whatever the
217
+ // snapshot says: None when isLogoVisible is false, Upload when the
218
+ // restored logo is BYO, Logo otherwise.
219
+ const selectedOption = computed<0 | 1 | 2>(() => {
220
+ if (!props.isLogoVisible) return 0;
221
+
222
+ if (currentLogoIsByo.value) return 2;
223
+
224
+ return data.manualVisibleChoice ?? 1;
225
+ });
197
226
 
198
227
  const options = computed<ButtonGroupOption[]>(() => {
199
228
  const optionList = [
200
229
  {
201
230
  label: commonTr('none'),
202
231
  value: '0',
203
- isSelected: data.selectedOption === 0,
232
+ isSelected: selectedOption.value === 0,
204
233
  iconName: `ban`,
205
234
  iconSize: 'sm',
206
235
  },
207
236
  {
208
237
  label: commonTr('logo'),
209
238
  value: '1',
210
- isSelected: data.selectedOption === 1,
239
+ isSelected: selectedOption.value === 1,
211
240
  iconName: `bc-mast`,
212
241
  iconSize: 'sm',
213
242
  },
214
243
  {
215
244
  label: commonTr('upload'),
216
245
  value: '2',
217
- isSelected: data.selectedOption === 2,
246
+ isSelected: selectedOption.value === 2,
218
247
  iconName: `upload-arrow`,
219
248
  iconSize: 'sm',
220
249
  },
221
250
  ];
222
251
 
223
- if (data.currentLogoIsByo) {
252
+ if (currentLogoIsByo.value) {
224
253
  return optionList.filter((x) => x.value !== '1');
225
254
  }
226
255
 
227
256
  return optionList;
228
257
  });
229
258
 
230
- const logoStore = useLogoStore();
231
- const editableLogoStore = useUiEditableLogoStore();
232
- const sideBarStore = useUiSideBarStore();
233
- const userStore = useBrandPageMakerUserStore();
234
-
235
- onMounted(() => updateState());
236
-
237
- watch(
238
- () => props.isLogoVisible,
239
- () => updateState(),
240
- );
241
-
242
- watch(
243
- () => props.selectedLogoDraftToken,
244
- () => updateState(),
245
- );
246
-
247
- watch(
248
- () => props.selectedLogo,
249
- () => updateState(),
250
- );
251
-
252
259
  const updateState = () => {
253
260
  const logoDraftAsset = logoStore.getLogoDraftAssetByToken(
254
261
  props.selectedLogoDraftToken,
255
262
  );
256
263
 
257
264
  data.logoEditUrl = logoDraftAsset?.logoEditUrl || '';
265
+ };
258
266
 
259
- if (!props.isLogoVisible) {
260
- data.selectedOption = 0;
261
- } else {
262
- if (logoDraftAsset?.isByo) {
263
- data.selectedOption = 2;
264
- } else {
265
- data.selectedOption = data.selectedOption || 1;
266
- }
267
- }
267
+ onMounted(() => updateState());
268
268
 
269
- if (logoDraftAsset?.isByo) {
270
- data.currentLogoIsByo = true;
271
- }
272
- };
269
+ // Only `selectedLogoDraftToken` clears the manual override — that's the
270
+ // signal for an *external* data change (upload landed, or undo/redo
271
+ // restored a different draft). `isLogoVisible` flips on the user's own
272
+ // click and propagates back through props, so resetting on it would
273
+ // clobber the choice the user just made (e.g. None → Upload would land
274
+ // on Logo because the watcher fires before the computed re-reads).
275
+ watch(
276
+ () => props.selectedLogoDraftToken,
277
+ () => {
278
+ data.manualVisibleChoice = null;
279
+ updateState();
280
+ },
281
+ );
282
+
283
+ watch(() => props.isLogoVisible, updateState);
284
+ watch(() => props.selectedLogo, updateState);
273
285
 
274
286
  const logoSettingStore = useUiLogoSettingsStore();
275
287
 
@@ -320,9 +332,14 @@ const onEditableLogoVariationSelected = (selectedLogoVariation: {
320
332
  };
321
333
 
322
334
  const onOptionSelected = (selected: ButtonGroupOption) => {
323
- data.selectedOption = parseInt(selected.value);
335
+ const next = parseInt(selected.value);
324
336
 
325
- emit('on-logo-visible-changed', data.selectedOption !== 0);
337
+ // Capture the user's choice between Logo (1) and Upload (2) so the
338
+ // highlight stays put while they interact with the visible options.
339
+ // `null` for None — the visibility flip handles that.
340
+ data.manualVisibleChoice = next === 1 || next === 2 ? next : null;
341
+
342
+ emit('on-logo-visible-changed', next !== 0);
326
343
  };
327
344
 
328
345
  const handleLogoVisibleToggle = () => {
@@ -363,8 +380,6 @@ const onLogoUpload = (response: CreateWebsiteByoUploadResult) => {
363
380
  if (response.logoDesignAttributes) {
364
381
  logoSettingStore.applyLogoDesignAttributes(response.logoDesignAttributes);
365
382
  }
366
-
367
- data.currentLogoIsByo = true;
368
383
  };
369
384
 
370
385
  const onEditLogoButtonClicked = () => {
@@ -24,6 +24,7 @@ import { Button } from '@designcrowd/fe-shared-lib';
24
24
  import { ref, computed, onMounted } from 'vue';
25
25
  import { useUiPageSettingsStore } from '../../../page-settings/store';
26
26
  import { useBrandPageMakerUserStore } from '../../../../brand-page-maker/stores';
27
+ import { useHistoryStore } from '../../../../brand-page-maker/stores/history/history-store';
27
28
  import { pageSettingTr } from '../../../../composables/useTranslate';
28
29
  import PageSelectorDropdown from '../../../page-navigation/PageSelectorDropdown.vue';
29
30
  import { usePageNavigationStore } from '../../../page-navigation/store/page-navigation-store';
@@ -50,6 +51,7 @@ const emit = defineEmits<{
50
51
  const pageNavigationStore = usePageNavigationStore();
51
52
  const pageSettingsStore = useUiPageSettingsStore();
52
53
  const userStore = useBrandPageMakerUserStore();
54
+ const historyStore = useHistoryStore();
53
55
 
54
56
  const selectedPage = ref('');
55
57
 
@@ -109,6 +111,8 @@ onMounted(() => {
109
111
  if (routeByPageId) {
110
112
  selectedPage.value = routeByPageId.displayName;
111
113
  } else {
114
+ // Stale page-id clear at mount — fold into current head.
115
+ historyStore.mergeNextMutation();
112
116
  emit('update:modelValue', {
113
117
  id: props.id,
114
118
  name: props.name,
@@ -15,6 +15,7 @@ import { useShopConfigStore } from '@shared/ecommerce-settings/store/shop-settin
15
15
  import { paymentConfigTr } from '@src/composables/useTranslate';
16
16
  import brandPageClient from '../../../../clients/brand-page-client';
17
17
  import { useUiPageSettingsStore } from '@shared/page-settings/store';
18
+ import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
18
19
 
19
20
  export default defineComponent({
20
21
  props: {
@@ -41,6 +42,7 @@ export default defineComponent({
41
42
  const uiPageSettingsStore = useUiPageSettingsStore();
42
43
  const eventManagerStore = useEventManagerStore();
43
44
  const shopConfigStore = useShopConfigStore();
45
+ const historyStore = useHistoryStore();
44
46
 
45
47
  return {
46
48
  brandPageToken: computed(() => brandPageContainerStore.brandPageToken),
@@ -51,6 +53,7 @@ export default defineComponent({
51
53
  eventManagerStore,
52
54
  shopConfigStore,
53
55
  uiPageSettingsStore,
56
+ historyStore,
54
57
  };
55
58
  },
56
59
  emits: ['update:modelValue'],
@@ -116,6 +119,10 @@ export default defineComponent({
116
119
  value,
117
120
  };
118
121
 
122
+ // Lazy paymentConfig hydration — fold into current head instead
123
+ // of pushing a phantom entry.
124
+ this.historyStore.mergeNextMutation();
125
+
119
126
  this.$emit('update:modelValue', payload);
120
127
  }
121
128
  },
@@ -3,6 +3,7 @@
3
3
  <RichTextEditor
4
4
  v-if="showRichTextEditor"
5
5
  :id="`${id}-${name}`"
6
+ ref="richTextEditorRef"
6
7
  v-model="text"
7
8
  :attrs="attrs"
8
9
  :label="label"
@@ -565,10 +565,15 @@ const onImageLinkToggleChange = (enabled: boolean, index: number): void => {
565
565
  emitChange();
566
566
  };
567
567
 
568
- onMounted(() => {
569
- options.value = cloneDeep(props.value);
570
- mediaType.value = props.settings.replaceImageSettings.mediaType;
571
- options.value = options.value.map((o) => ({
568
+ // Flips true while we're rebuilding `options` from `props.value`. Suppresses
569
+ // the deep options-watch's debounced emit so a sync from the store doesn't
570
+ // echo back to the store. Reset by the deep watcher on the next tick.
571
+ let isSyncingFromProps = false;
572
+
573
+ const applyValueWithDefaults = (
574
+ v: TextMediaItem[],
575
+ ): (TextMediaItem | TextShapeItem)[] => {
576
+ return cloneDeep(v).map((o) => ({
572
577
  ...o,
573
578
  mediaType: mediaType.value,
574
579
  imageLink: o.imageLink !== undefined ? o.imageLink : false,
@@ -578,15 +583,39 @@ onMounted(() => {
578
583
  email: o.email || '',
579
584
  tel: o.tel || '',
580
585
  })) as (TextMediaItem | TextShapeItem)[];
586
+ };
587
+
588
+ onMounted(() => {
589
+ mediaType.value = props.settings.replaceImageSettings.mediaType;
590
+ isSyncingFromProps = true;
591
+ options.value = applyValueWithDefaults(props.value);
581
592
 
582
593
  if (!changeDebounce.value) {
583
594
  changeDebounce.value = debounce(emitChange, DEBOUNCE_IN_MS);
584
595
  }
585
596
  });
586
597
 
598
+ // Re-sync the local `options` mirror when the store-bound value changes
599
+ // externally (e.g. undo/redo via history-store). Without this the sidebar
600
+ // keeps showing the pre-undo state while the canvas reflects the reverted
601
+ // store.
602
+ watch(
603
+ () => props.value,
604
+ (newVal) => {
605
+ isSyncingFromProps = true;
606
+ options.value = applyValueWithDefaults(newVal);
607
+ },
608
+ );
609
+
587
610
  watch(
588
611
  options,
589
612
  () => {
613
+ if (isSyncingFromProps) {
614
+ isSyncingFromProps = false;
615
+
616
+ return;
617
+ }
618
+
590
619
  if (changeDebounce.value) {
591
620
  changeDebounce.value();
592
621
  }
@@ -83,6 +83,17 @@ export default defineComponent({
83
83
  return 'tw-font-bold tw-text-xs';
84
84
  },
85
85
  },
86
+ watch: {
87
+ // Keeps the design/upload tab in sync with `enableEditMode` whenever it
88
+ // flips outside the user's tab clicks — undo/redo restoring a BYO upload
89
+ // (mode → false) or a designed logo (mode → true). User clicks within
90
+ // the same mode don't change `enableEditMode` and don't trigger this.
91
+ showEditLogoSetting(enabled: boolean) {
92
+ this.selectedTab = enabled
93
+ ? this.tabsWithTranslations[0]
94
+ : this.tabsWithTranslations[1];
95
+ },
96
+ },
86
97
  methods: {
87
98
  disableTextToggle(currentTextEnabled) {
88
99
  if (!this.isTextOnlyLogo || !currentTextEnabled) {