@designcrowd/library.brand-page 5.17.21 → 5.17.23-ast-subnav

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 (121) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-Brtp2yWB.js → CreatedOnBrandCrowd-de-de-Ct9Aewoo.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-BIJ5VoX4.js → CreatedOnBrandCrowd-en-us-BsEzGp5K.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-JrKK9K0u.js → CreatedOnBrandCrowd-es-es-UiIPmY0h.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-D_HvOZe-.js → CreatedOnBrandCrowd-fr-ca-CQmEXbU7.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-D_HvOZe-.js → CreatedOnBrandCrowd-fr-fr-CQmEXbU7.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-SSLb0vlp.js → CreatedOnBrandCrowd-pt-br-DoMI5QSQ.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-SSLb0vlp.js → CreatedOnBrandCrowd-pt-pt-DoMI5QSQ.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-BG39ozFo.js → CreatedOnDcom-de-de-gexV_wWX.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-D2NzqECZ.js → CreatedOnDcom-en-us-C2sLHLjJ.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-C55r2Lb4.js → CreatedOnDcom-es-es-CZFd666H.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-Bj-sAaJI.js → CreatedOnDcom-fr-ca-CPaRB_io.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-D8l0r-Tc.js → CreatedOnDcom-fr-fr-BQo5D5gE.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-CXHC6E1h.js → CreatedOnDcom-pt-br-CRr3CqOj.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-2dMvgnyi.js → CreatedOnDcom-pt-pt-DR6Vi-eZ.js} +1 -1
  15. package/lib/src/brand-page/components/banner-section/BannerSection.mixin.d.ts +1 -1
  16. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.d.ts +1 -0
  17. package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +20 -2
  18. package/lib/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.d.ts +8 -1
  19. package/lib/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.js +97 -3
  20. package/lib/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.js +6 -2
  21. package/lib/src/brand-page/models/index.d.ts +1 -1
  22. package/lib/src/brand-page/models/navigation-styles.model.d.ts +2 -0
  23. package/lib/src/brand-page/models/navigation-styles.model.js +20 -1
  24. package/lib/src/brand-page/utils/migration/migration-14.d.ts +2 -0
  25. package/lib/src/brand-page/utils/migration/migration-14.js +16 -0
  26. package/lib/src/brand-page/utils/migration/migration-15.d.ts +2 -0
  27. package/lib/src/brand-page/utils/migration/migration-15.js +37 -0
  28. package/lib/src/brand-page/utils/migration.util.js +12 -0
  29. package/lib/src/brand-page/utils/style.util.js +16 -2
  30. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.d.ts +1 -1
  31. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +1 -1
  32. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-site-template.examples.js +1 -1
  33. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +7 -1
  34. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +1 -1
  35. package/lib/src/brand-page-maker/constants/config-settings-constant.js +1 -10
  36. package/lib/src/brand-page-maker/models/content-settings-configs.d.ts +5 -0
  37. package/lib/src/brand-page-maker/models/content-settings.js +3 -0
  38. package/lib/src/brand-page-maker/models/navigation-style-configs.d.ts +1 -0
  39. package/lib/src/brand-page-maker/models/navigation-style-configs.js +5 -0
  40. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +170 -388
  41. package/lib/src/{bundled-translations-Bu_5FO26.js → bundled-translations-Cy6nun02.js} +1 -1
  42. package/lib/src/{bundled-translations.de-DE-CZTzknyj.js → bundled-translations.de-DE-BUZncCAM.js} +1 -1
  43. package/lib/src/{bundled-translations.es-ES-p86F-gOD.js → bundled-translations.es-ES-D4IPwZD-.js} +9 -9
  44. package/lib/src/{bundled-translations.fr-CA-CM5ZA8dQ.js → bundled-translations.fr-CA-BMgRrrwA.js} +9 -9
  45. package/lib/src/{bundled-translations.fr-FR-DV7sOgtb.js → bundled-translations.fr-FR-Ck7kYrK4.js} +1 -1
  46. package/lib/src/{bundled-translations.pt-BR-DkWWGrsz.js → bundled-translations.pt-BR--5_9viJ9.js} +1 -1
  47. package/lib/src/{bundled-translations.pt-PT-C5Td5lLs.js → bundled-translations.pt-PT-DAGUOQ7m.js} +13 -13
  48. package/lib/src/bundles/bundled-translations.de-DE.json +1 -0
  49. package/lib/src/bundles/bundled-translations.es-ES.json +1 -0
  50. package/lib/src/bundles/bundled-translations.fr-CA.json +1 -0
  51. package/lib/src/bundles/bundled-translations.fr-FR.json +1 -0
  52. package/lib/src/bundles/bundled-translations.json +1 -0
  53. package/lib/src/bundles/bundled-translations.pt-BR.json +1 -0
  54. package/lib/src/bundles/bundled-translations.pt-PT.json +1 -0
  55. package/lib/src/css/library.brand-page-brandCrowd.min.css +110 -103
  56. package/lib/src/css/library.brand-page-brandPage.min.css +110 -103
  57. package/lib/src/css/library.brand-page-designCom.min.css +110 -103
  58. package/lib/src/css/library.brand-page-preview.min.css +110 -103
  59. package/lib/src/index.mjs +1 -1
  60. package/lib/src/{lib-CrBvj1a5.js → lib-wvy4koSU.js} +22606 -22689
  61. package/lib/src/metadata-builder/builders/section-field-maps.js +1 -0
  62. package/lib/src/metadata-builder/builders/section-field-utils.js +5 -0
  63. package/lib/src/metadata-builder/helpers/rte.helpers.js +3 -0
  64. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +2 -1
  65. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.d.ts +4 -1
  66. package/lib/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.js +55 -2
  67. package/lib/src/shared/navigation-style-settings/store/index.d.ts +6 -0
  68. package/lib/src/shared/navigation-style-settings/store/index.js +15 -0
  69. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +27 -1
  70. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +25 -2
  71. package/package.json +1 -1
  72. package/src/brand-page/components/about-section/AboutSection.vue +63 -6
  73. package/src/brand-page/components/collapsible/Collapsible.vue +8 -2
  74. package/src/brand-page/components/contact-form/ContactForm.mixin.ts +21 -2
  75. package/src/brand-page/components/contact-form/ContactForm.vue +15 -15
  76. package/src/brand-page/components/image-gallery/ImageGalleryModal.mixin.ts +114 -5
  77. package/src/brand-page/components/image-gallery/ImageGalleryModal.vue +25 -8
  78. package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +19 -8
  79. package/src/brand-page/components/navigation/navigation-side-menu/NavigationSideMenu.mixin.ts +6 -2
  80. package/src/brand-page/components/text-wrapper/TextWrapper.vue +5 -1
  81. package/src/brand-page/models/index.ts +1 -1
  82. package/src/brand-page/models/navigation-styles.model.ts +31 -0
  83. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +15 -3
  84. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +4 -0
  85. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +8 -0
  86. package/src/brand-page/utils/migration/migration-14.ts +23 -0
  87. package/src/brand-page/utils/migration/migration-15.ts +62 -0
  88. package/src/brand-page/utils/migration.util.ts +14 -0
  89. package/src/brand-page/utils/style.util.ts +16 -2
  90. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +1 -1
  91. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-site-template.examples.ts +1 -1
  92. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +8 -1
  93. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +1 -1
  94. package/src/brand-page-maker/constants/config-settings-constant.ts +1 -10
  95. package/src/brand-page-maker/models/content-settings-configs.ts +6 -0
  96. package/src/brand-page-maker/models/content-settings.ts +3 -0
  97. package/src/brand-page-maker/models/i18n/models.de-DE.json +1 -0
  98. package/src/brand-page-maker/models/i18n/models.es-ES.json +1 -0
  99. package/src/brand-page-maker/models/i18n/models.fr-CA.json +1 -0
  100. package/src/brand-page-maker/models/i18n/models.fr-FR.json +1 -0
  101. package/src/brand-page-maker/models/i18n/models.json +1 -0
  102. package/src/brand-page-maker/models/i18n/models.pt-BR.json +1 -0
  103. package/src/brand-page-maker/models/i18n/models.pt-PT.json +1 -0
  104. package/src/brand-page-maker/models/navigation-style-configs.ts +5 -0
  105. package/src/brand-page-maker/utils/rich-text-editor.util.ts +274 -684
  106. package/src/bundles/bundled-translations.de-DE.json +1 -0
  107. package/src/bundles/bundled-translations.es-ES.json +1 -0
  108. package/src/bundles/bundled-translations.fr-CA.json +1 -0
  109. package/src/bundles/bundled-translations.fr-FR.json +1 -0
  110. package/src/bundles/bundled-translations.json +1 -0
  111. package/src/bundles/bundled-translations.pt-BR.json +1 -0
  112. package/src/bundles/bundled-translations.pt-PT.json +1 -0
  113. package/src/metadata-builder/builders/section-field-maps.ts +1 -0
  114. package/src/metadata-builder/builders/section-field-utils.ts +5 -0
  115. package/src/metadata-builder/helpers/rte.helpers.ts +4 -0
  116. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +2 -1
  117. package/src/shared/navigation-style-settings/components/navigation-style-settings/NavigationStyleSettings.mixin.ts +82 -4
  118. package/src/shared/navigation-style-settings/store/index.ts +30 -0
  119. package/lib/src/brand-page/components/about-section/AboutSection.mixin.d.ts +0 -83
  120. package/lib/src/brand-page/components/about-section/AboutSection.mixin.js +0 -65
  121. package/src/brand-page/components/about-section/AboutSection.mixin.ts +0 -78
@@ -26,6 +26,7 @@ import {
26
26
  TEXT_ONLY,
27
27
  } from '../../constants';
28
28
  import { getRenderText } from '@brand-page/utils';
29
+ import { hasRTEContent } from '@brand-page-maker/utils';
29
30
  import { useReCaptcha } from '../../composables/recaptcha.composable';
30
31
 
31
32
  export interface FieldValue {
@@ -171,7 +172,11 @@ export default defineComponent({
171
172
  return '';
172
173
  }
173
174
 
174
- return getRenderText(this.viewModel.description);
175
+ if (hasRTEContent(this.viewModel.description)) {
176
+ return this.viewModel.description;
177
+ } else {
178
+ return getRenderText(this.viewModel.description);
179
+ }
175
180
  },
176
181
 
177
182
  renderSuccessText(): string {
@@ -179,7 +184,21 @@ export default defineComponent({
179
184
  return '';
180
185
  }
181
186
 
182
- return getRenderText(this.viewModel.successMessage);
187
+ if (hasRTEContent(this.viewModel.successMessage)) {
188
+ return this.viewModel.successMessage;
189
+ } else {
190
+ return getRenderText(this.viewModel.successMessage);
191
+ }
192
+ },
193
+
194
+ successMessageColorStyle(): BrandPageCssStyle {
195
+ const color = this.viewModel.successMessageStyle?.color;
196
+
197
+ if (color) {
198
+ return { color };
199
+ }
200
+
201
+ return {};
183
202
  },
184
203
  },
185
204
  watch: {
@@ -37,7 +37,7 @@
37
37
  v-if="renderText"
38
38
  :text-style="viewModel.descriptionStyle"
39
39
  :text="renderText"
40
- element="p"
40
+ element="div"
41
41
  class="tw-text-center tw-mb-2 tw-text-base"
42
42
  />
43
43
  <div v-if="!isSubmitting">
@@ -97,10 +97,9 @@
97
97
  {{ submitError }}
98
98
  </p>
99
99
  </form>
100
- <div v-else>
100
+ <div v-else :style="successMessageColorStyle">
101
101
  <TextWrapper
102
102
  v-if="viewModel.successMessage"
103
- :text-style="viewModel.messageTextColor"
104
103
  :text="renderSuccessText"
105
104
  element="p"
106
105
  :class="'tw-text-center tw-font-bold tw-my-0.5'"
@@ -125,14 +124,14 @@
125
124
  v-if="viewModel.title"
126
125
  :text-style="viewModel.titleStyle"
127
126
  :text="viewModel.title"
128
- element="h1"
127
+ element="div"
129
128
  class="tw-text-center tw-mb-4 tw-text-base"
130
129
  />
131
130
  <TextWrapper
132
131
  v-if="renderText"
133
132
  :text-style="viewModel.descriptionStyle"
134
133
  :text="renderText"
135
- element="p"
134
+ element="div"
136
135
  class="tw-text-center tw-mb-4 tw-text-base"
137
136
  />
138
137
  <div v-if="!isSubmitting" class="tw-grid tw-grid-cols-2 tw-gap-4">
@@ -199,10 +198,9 @@
199
198
  {{ submitError }}
200
199
  </p>
201
200
  </form>
202
- <div v-else>
201
+ <div v-else :style="successMessageColorStyle">
203
202
  <TextWrapper
204
203
  v-if="viewModel.successMessage"
205
- :text-style="viewModel.messageTextColor"
206
204
  :text="renderSuccessText"
207
205
  element="p"
208
206
  :class="'tw-text-center tw-font-bold tw-my-0.5'"
@@ -226,14 +224,14 @@
226
224
  v-if="viewModel.title"
227
225
  :text-style="viewModel.titleStyle"
228
226
  :text="viewModel.title"
229
- element="h1"
227
+ element="div"
230
228
  class="tw-text-center tw-mb-4 tw-text-base"
231
229
  />
232
230
  <TextWrapper
233
231
  v-if="renderText"
234
232
  :text-style="viewModel.descriptionStyle"
235
233
  :text="renderText"
236
- element="p"
234
+ element="div"
237
235
  class="tw-text-center tw-mb-4 tw-text-base"
238
236
  />
239
237
  </div>
@@ -310,10 +308,13 @@
310
308
  class="tw-w-full tw-flex tw-justify-center tw-col-span-2"
311
309
  />
312
310
  </span>
313
- <div v-else class="tw-w-full tw-flex tw-justify-center">
311
+ <div
312
+ v-else
313
+ class="tw-w-full tw-flex tw-justify-center"
314
+ :style="successMessageColorStyle"
315
+ >
314
316
  <TextWrapper
315
317
  v-if="viewModel.successMessage"
316
- :text-style="viewModel.messageTextColor"
317
318
  :text="renderSuccessText"
318
319
  element="p"
319
320
  :class="'tw-text-center tw-font-bold tw-my-0.5'"
@@ -357,14 +358,14 @@
357
358
  v-if="viewModel.title && !isSubmitting"
358
359
  :text-style="viewModel.titleStyle"
359
360
  :text="viewModel.title"
360
- element="h1"
361
+ element="div"
361
362
  class="tw-w-full tw-mb-2"
362
363
  />
363
364
  <TextWrapper
364
365
  v-if="renderText"
365
366
  :text-style="viewModel.descriptionStyle"
366
367
  :text="renderText"
367
- element="p"
368
+ element="div"
368
369
  class="tw-text-center tw-mb-4 tw-text-base"
369
370
  />
370
371
  <form @submit.prevent="submitContactForm">
@@ -437,10 +438,9 @@
437
438
  </p>
438
439
  </form>
439
440
  </span>
440
- <div v-else>
441
+ <div v-else :style="successMessageColorStyle">
441
442
  <TextWrapper
442
443
  v-if="viewModel.successMessage"
443
- :text-style="viewModel.messageTextColor"
444
444
  :text="renderSuccessText"
445
445
  element="p"
446
446
  :class="'tw-text-center tw-font-bold tw-my-0.5'"
@@ -5,6 +5,15 @@ import ImageModalMixin from '../mixins/image-modal.mixin';
5
5
  import cloneDeep from 'lodash.clonedeep';
6
6
  import type { BrandPageContext, ImageItem } from '@brand-page/models';
7
7
 
8
+ // Number of slides on each side of the current slide to keep mounted with a
9
+ // real <img>. Slides outside this window render an empty placeholder so the
10
+ // browser does not request full-resolution images for the entire gallery on
11
+ // modal open — required to prevent mobile tab crashes on large galleries.
12
+ // 3 (= 7 slides total) is wide enough that a user dragging slowly across
13
+ // 2-3 slides never exposes a placeholder, while still capping decoded
14
+ // image memory well under mobile per-tab limits.
15
+ const IMAGE_RENDER_WINDOW = 3;
16
+
8
17
  export default defineComponent({
9
18
  mixins: [ComponentCommonMixin, ImageModalMixin],
10
19
  props: {
@@ -40,6 +49,7 @@ export default defineComponent({
40
49
  isImageModalLoading: false,
41
50
  carouselImages: [] as any[][] | undefined,
42
51
  showControl: true,
52
+ carouselComponentRef: 'image-gallery-carousel',
43
53
  };
44
54
  },
45
55
  computed: {
@@ -65,7 +75,7 @@ export default defineComponent({
65
75
 
66
76
  swiperOptions() {
67
77
  return {
68
- initialSlide: 0,
78
+ initialSlide: this.selectedImageIndex ?? 0,
69
79
  loop: this.images.length > 1,
70
80
  slidesPerGroup: 1,
71
81
  slidesPerView: 1,
@@ -99,10 +109,6 @@ export default defineComponent({
99
109
  }
100
110
 
101
111
  this.isImageModalLoading = true;
102
-
103
- this.swiperOptions.initialSlide = newIndex;
104
-
105
- // this.$nextTick(() => this.checkImageModalImageComplete());
106
112
  },
107
113
 
108
114
  images: {
@@ -154,5 +160,108 @@ export default defineComponent({
154
160
  onImageModalClose() {
155
161
  this.$emit('close');
156
162
  },
163
+
164
+ isSlideInRenderWindow(index: number): boolean {
165
+ if (this.currentImageIndex === undefined) {
166
+ return false;
167
+ }
168
+
169
+ const total = this.images.length;
170
+
171
+ if (total === 0) {
172
+ return false;
173
+ }
174
+
175
+ const direct = Math.abs(index - this.currentImageIndex);
176
+ // For looped galleries, the shortest path between two slides may wrap
177
+ // around the end. Use whichever distance is smaller.
178
+ const wrapped = total - direct;
179
+ const distance = Math.min(direct, wrapped);
180
+
181
+ return distance <= IMAGE_RENDER_WINDOW;
182
+ },
183
+
184
+ onCarouselLoaded() {
185
+ // Reach through the Carousel ref to bind directly to the underlying
186
+ // Swiper instance — the shared Carousel doesn't emit slide-change
187
+ // events. Keeps `currentImageIndex` in sync with user navigation
188
+ // (arrows, swipes, keyboard) so the render window slides with the
189
+ // user.
190
+ //
191
+ // NOTE: `currentImageIndex` must not be bound back to Carousel's
192
+ // `:slide-index` prop. The shared Carousel watches that prop and
193
+ // calls `swiper.update()` + `swiper.slideTo()` on every change.
194
+ // With `slideChangeTransitionStart` updating currentImageIndex on
195
+ // every swipe, that creates a feedback loop where Swiper redoes a
196
+ // full layout recalculation on every user gesture — visible as
197
+ // jank and capable of crashing the tab on real mobile devices.
198
+ const carousel = this.$refs[this.carouselComponentRef] as any;
199
+ const swiper = carousel?.swiper;
200
+
201
+ if (!swiper || typeof swiper.on !== 'function') {
202
+ return;
203
+ }
204
+
205
+ // Listen at the *start* of the transition so the incoming neighbour
206
+ // mounts and starts loading while the slide animation is still
207
+ // playing. Listening on `slideChange` (end of transition) leaves a
208
+ // gap during fast swipes where the next neighbour is still a
209
+ // placeholder when it reaches the viewport — visible as a flicker.
210
+ const handleSlideChange = () => {
211
+ // `realIndex` reflects the underlying slide index when loop mode
212
+ // duplicates slides; `activeIndex` would include those duplicates.
213
+ const next =
214
+ typeof swiper.realIndex === 'number'
215
+ ? swiper.realIndex
216
+ : swiper.activeIndex;
217
+
218
+ if (typeof next === 'number' && next !== this.currentImageIndex) {
219
+ this.currentImageIndex = next;
220
+ }
221
+ };
222
+
223
+ swiper.on('slideChangeTransitionStart', handleSlideChange);
224
+ // Also handle instant jumps that bypass the transition (e.g. when
225
+ // navigation arrows trigger no-animation slides).
226
+ swiper.on('slideChange', handleSlideChange);
227
+ },
228
+
229
+ // Only the currently-viewed slide's load lifecycle should drive the
230
+ // modal's Loader. Neighbour FallbackImages mounted by the render window
231
+ // emit `image-loading` synchronously in their onMounted hook; without
232
+ // these gates that emission flips `isImageModalLoading` true mid-swipe,
233
+ // briefly flashing the spinner over the slide the user is actually on
234
+ // — the flicker reported on real mobile devices.
235
+ onSlideImageLoading(index: number) {
236
+ if (index === this.currentImageIndex) {
237
+ this.imageLoading();
238
+ }
239
+ },
240
+
241
+ onSlideImageLoaded(index: number) {
242
+ if (index === this.currentImageIndex) {
243
+ this.checkImageLoading(false);
244
+ }
245
+ },
246
+
247
+ onSlideImageError(index: number) {
248
+ if (index === this.currentImageIndex) {
249
+ this.checkImageLoading(true);
250
+ }
251
+ },
252
+
253
+ placeholderStyle(slide: any): Record<string, string> {
254
+ // Sized so the placeholder occupies the exact space the loaded image
255
+ // would, preventing slide-height jumps when the render window swaps
256
+ // between placeholder and real image. Mirrors the image's own
257
+ // `max-width: 100%` + `max-height: 90vh` + intrinsic aspect ratio.
258
+ if (!slide?.width || !slide?.height) {
259
+ return {};
260
+ }
261
+
262
+ return {
263
+ aspectRatio: `${slide.width} / ${slide.height}`,
264
+ };
265
+ },
157
266
  },
158
267
  });
@@ -6,13 +6,15 @@
6
6
  @modal-closed="onImageModalClose"
7
7
  >
8
8
  <Carousel
9
+ v-if="isImageModalVisible"
10
+ :ref="carouselComponentRef"
9
11
  :classes="['tw-h-full', 'tw-max-h-full', 'tw-justify-center']"
10
12
  :cards="carouselImages"
11
13
  :options="swiperOptions"
12
- :slide-index="currentImageIndex"
13
14
  :show-bullets="false"
14
15
  :show-controls="showCarouselControls"
15
16
  :navigation-unique-indexer="navigationUniqueIndexer"
17
+ @on-carousel-load="onCarouselLoaded"
16
18
  >
17
19
  <template #arrow-left>
18
20
  <div id="button-prev" :class="navigationPrevClass">
@@ -30,7 +32,7 @@
30
32
  />
31
33
  </div>
32
34
  </template>
33
- <template #slide="{ slide }">
35
+ <template #slide="{ slide, index }">
34
36
  <div
35
37
  class="tw-text-center tw-flex tw-flex-col tw-items-center tw-justify-center tw-overflow-auto tw-h-auto"
36
38
  :class="{ 'tw-justify-center': !slide.imageTitle }"
@@ -41,18 +43,27 @@
41
43
  >
42
44
  {{ slide.imageTitle }}
43
45
  </h3>
44
- <Loader v-if="isImageModalLoading" class="tw-m-28" />
46
+ <Loader
47
+ v-if="isImageModalLoading && index === currentImageIndex"
48
+ class="tw-m-28"
49
+ />
45
50
  <FallbackImage
46
- :key="`modal-slide-${currentImageIndex}`"
47
- :ref="imageModalImageRef"
51
+ v-if="isSlideInRenderWindow(index)"
52
+ :key="`modal-slide-${index}`"
53
+ :ref="index === currentImageIndex ? imageModalImageRef : undefined"
48
54
  :image-url="slide.imageUrl"
49
55
  class="c-modal-image"
50
56
  :alt="slide.imageTitle || altText"
51
57
  :context="context"
52
58
  :content-index="contentIndex"
53
- @image-loaded="checkImageLoading(false)"
54
- @image-loading="imageLoading"
55
- @image-error="checkImageLoading(true)"
59
+ @image-loaded="onSlideImageLoaded(index)"
60
+ @image-loading="onSlideImageLoading(index)"
61
+ @image-error="onSlideImageError(index)"
62
+ />
63
+ <div
64
+ v-else
65
+ class="c-modal-image-placeholder"
66
+ :style="placeholderStyle(slide)"
56
67
  />
57
68
  </div>
58
69
  </template>
@@ -115,4 +126,10 @@ export default {
115
126
  @apply tw-max-w-full;
116
127
  max-height: 90vh;
117
128
  }
129
+
130
+ .c-modal-image-placeholder {
131
+ @apply tw-max-w-full;
132
+ width: 100%;
133
+ max-height: 90vh;
134
+ }
118
135
  </style>
@@ -95,11 +95,7 @@
95
95
  params: nestedItem.routeParams,
96
96
  }"
97
97
  class="dropdown-item"
98
- :style="[
99
- isNavItemActive(nestedItem)
100
- ? navigation.selectedButtonStyle
101
- : navigation.defaultButtonStyle,
102
- ]"
98
+ :style="getDropdownItemStyle(nestedItem)"
103
99
  @click="onDropdownItemClick"
104
100
  >
105
101
  {{ nestedItem.displayName }}
@@ -147,12 +143,16 @@ const hoveredSlide = ref<string | null>(null);
147
143
  const hoveredNavItem = ref<NavItemViewModel | null>(null);
148
144
  const slideRefs = ref<Record<string, HTMLElement>>({});
149
145
  const dropdownMenuRef = ref<HTMLElement | null>(null);
150
- const dropdownStyle = ref<Record<string, string>>({
146
+ const dropdownPositionStyle = ref<Record<string, string>>({
151
147
  position: 'fixed',
152
148
  top: '-9999px',
153
149
  left: '-9999px',
154
150
  zIndex: '1',
155
151
  });
152
+ const dropdownStyle = computed(() => ({
153
+ ...dropdownPositionStyle.value,
154
+ backgroundColor: props.navigation.subNavBackgroundColor ?? '#ffffff',
155
+ }));
156
156
  let hideTimeout: any = null;
157
157
  let hoverJustHappened = false; // This will prevent the mobile interaction clashing with desktop hovering
158
158
 
@@ -225,7 +225,7 @@ const updateDropdownPosition = (element: HTMLElement) => {
225
225
  left = containerRect.right - effectiveWidth;
226
226
  }
227
227
 
228
- dropdownStyle.value = {
228
+ dropdownPositionStyle.value = {
229
229
  ...getRootInheritedStyles(),
230
230
  position: 'fixed',
231
231
  top: `${rect.bottom + 4}px`,
@@ -549,6 +549,18 @@ onUnmounted(() => {
549
549
 
550
550
  const navItems = computed(() => props.pageNavigation.navItems);
551
551
 
552
+ const getDropdownItemStyle = (navItem: NavItemViewModel) => {
553
+ const isActive = isNavItemActive(navItem);
554
+ const baseStyle = isActive
555
+ ? props.navigation.selectedButtonStyle
556
+ : props.navigation.defaultButtonStyle;
557
+
558
+ return {
559
+ ...baseStyle,
560
+ backgroundColor: isActive ? baseStyle.backgroundColor : 'transparent',
561
+ };
562
+ };
563
+
552
564
  const dropdownMaxWidth = computed(() =>
553
565
  isMobileDisplay.value ? '240px' : '380px',
554
566
  );
@@ -618,7 +630,6 @@ const dropdownMaxWidth = computed(() =>
618
630
  }
619
631
 
620
632
  .dropdown-menu {
621
- background: white;
622
633
  min-width: 150px;
623
634
  max-height: 240px;
624
635
  overflow-x: hidden;
@@ -86,13 +86,17 @@ export default defineComponent({
86
86
  return navItem.relativePath === this.context.currentPageRelativePath;
87
87
  },
88
88
  getSubNavItemStyle(navItem: NavItemViewModel): BrandPageCssStyle {
89
- const baseStyle = this.isCurrentPage(navItem)
89
+ const isActive = this.isCurrentPage(navItem);
90
+ const baseStyle = isActive
90
91
  ? this.selectedButtonStyle
91
92
  : this.defaultButtonStyle;
92
93
 
93
94
  return {
94
95
  ...baseStyle,
95
- paddingLeft: '32px', // 16px additional indentation (base is 16px)
96
+ paddingLeft: '32px',
97
+ backgroundColor: isActive
98
+ ? baseStyle.backgroundColor
99
+ : this.styles.hamburgerMenuStyle.subNavBackgroundColor ?? '#ffffff',
96
100
  };
97
101
  },
98
102
  closeSideMenu(): void {
@@ -15,7 +15,7 @@
15
15
  </div>
16
16
  </template>
17
17
  <script setup lang="ts">
18
- import { hasRTEContent } from '@brand-page-maker/utils';
18
+ import { hasRTEContent, getPlainTextFromRTE } from '@brand-page-maker/utils';
19
19
  import {
20
20
  type FluidTextData,
21
21
  type TextStyleDeclaration,
@@ -77,6 +77,10 @@ const isRTEContent = computed(() => {
77
77
  });
78
78
 
79
79
  const sanitizeText = computed(() => {
80
+ if (isRTEContent.value && !getPlainTextFromRTE(props.text)) {
81
+ return '';
82
+ }
83
+
80
84
  return props.text;
81
85
  });
82
86
 
@@ -387,7 +387,7 @@ export interface ContactFormContent extends Content {
387
387
  descriptionStyle?: TextStyleDeclaration;
388
388
  buttonTextStyle?: TextStyleDeclaration;
389
389
  backgroundStyle?: BrandPageCssStyle;
390
- messageTextColor?: BrandPageCssStyle;
390
+ successMessageStyle?: TextStyleDeclaration;
391
391
  cardStyle?: BrandPageCssStyle;
392
392
  colorReplacements?: ColorReplacement[];
393
393
  mediaType?: string;
@@ -6,6 +6,7 @@ import {
6
6
  FONT_FAMILY_CONFIGS,
7
7
  FONT_SIZE_BODY,
8
8
  FONT_WEIGHT_NORMAL,
9
+ STYLE_THEME_KEYS,
9
10
  defaultHamburgerNavigation,
10
11
  type NavigationDisplayType,
11
12
  NAVIGATION_DISPLAY,
@@ -21,6 +22,7 @@ export class NavigationMenuStyles {
21
22
  public defaultButtonStyle: TextStyleDeclaration;
22
23
  public selectedButtonStyle: BrandPageCssStyle;
23
24
  public iconStyle: BrandPageCssStyle;
25
+ public subNavBackgroundColor?: string;
24
26
 
25
27
  constructor(
26
28
  options: Partial<NavigationMenuStyles>,
@@ -43,9 +45,32 @@ export class NavigationMenuStyles {
43
45
  defaultOptions.iconStyle,
44
46
  options.iconStyle || {},
45
47
  );
48
+
49
+ this.subNavBackgroundColor =
50
+ options.subNavBackgroundColor ?? defaultOptions.subNavBackgroundColor;
46
51
  }
47
52
  }
48
53
 
54
+ const getDefaultSubNavBackgroundColor = (
55
+ defaultTextColor: string | undefined,
56
+ ): string => {
57
+ if (
58
+ defaultTextColor === STYLE_THEME_KEYS.LogoTextColor ||
59
+ defaultTextColor === STYLE_THEME_KEYS.LogoTextMultiColor
60
+ ) {
61
+ return STYLE_THEME_KEYS.LogoBackgroundColor;
62
+ }
63
+
64
+ if (
65
+ defaultTextColor === STYLE_THEME_KEYS.LogoBackgroundColor ||
66
+ defaultTextColor === STYLE_THEME_KEYS.LogoBackgroundMultiColor
67
+ ) {
68
+ return STYLE_THEME_KEYS.LogoTextColor;
69
+ }
70
+
71
+ return '#ffffff';
72
+ };
73
+
49
74
  type NavigationStylesOptionType = Partial<
50
75
  Omit<NavigationDesktop | NavigationMobile, 'hamburgerMenuStyle'>
51
76
  > & {
@@ -60,6 +85,7 @@ abstract class NavigationStyles implements NavigationMenuStyles {
60
85
  public defaultButtonStyle: TextStyleDeclaration;
61
86
  public selectedButtonStyle: BrandPageCssStyle;
62
87
  public iconStyle: BrandPageCssStyle;
88
+ public subNavBackgroundColor?: string;
63
89
  public isLogoVisible: boolean;
64
90
  public hamburgerMenuStyle: NavigationMenuStyles;
65
91
  public viewport: ViewportsType;
@@ -119,6 +145,10 @@ abstract class NavigationStyles implements NavigationMenuStyles {
119
145
  option?.iconStyle || {},
120
146
  );
121
147
 
148
+ this.subNavBackgroundColor =
149
+ option?.subNavBackgroundColor ??
150
+ getDefaultSubNavBackgroundColor(option?.defaultButtonStyle?.color);
151
+
122
152
  this.isLogoVisible = option?.isLogoVisible || true;
123
153
 
124
154
  this.hamburgerMenuStyle = new NavigationMenuStyles(
@@ -141,6 +171,7 @@ abstract class NavigationStyles implements NavigationMenuStyles {
141
171
  color: theme?.secondaryColor,
142
172
  backgroundColor: theme?.primaryColor,
143
173
  },
174
+ subNavBackgroundColor: theme?.secondaryColor ?? '#ffffff',
144
175
  },
145
176
  );
146
177
  }
@@ -192,6 +192,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
192
192
  "backgroundColor": undefined,
193
193
  "color": undefined,
194
194
  },
195
+ "subNavBackgroundColor": "#ffffff",
195
196
  },
196
197
  "iconStyle": {},
197
198
  "isLogoVisible": true,
@@ -206,6 +207,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
206
207
  "borderTopWidth": "0px",
207
208
  "color": undefined,
208
209
  },
210
+ "subNavBackgroundColor": "#ffffff",
209
211
  "viewport": "desktop",
210
212
  },
211
213
  "navigationStyles": undefined,
@@ -263,6 +265,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
263
265
  "backgroundColor": undefined,
264
266
  "color": undefined,
265
267
  },
268
+ "subNavBackgroundColor": "#ffffff",
266
269
  },
267
270
  "iconStyle": {},
268
271
  "isLogoVisible": true,
@@ -277,6 +280,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
277
280
  "borderTopWidth": "0px",
278
281
  "color": undefined,
279
282
  },
283
+ "subNavBackgroundColor": "#ffffff",
280
284
  "viewport": "desktop",
281
285
  },
282
286
  "navigationStyles": undefined,
@@ -446,7 +450,7 @@ exports[`migration.util > Banner Contents - text Align and Font Family should no
446
450
  },
447
451
  },
448
452
  },
449
- "v": 13,
453
+ "v": 15,
450
454
  },
451
455
  "useOldLogoRenderSize": true,
452
456
  }
@@ -644,6 +648,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
644
648
  "backgroundColor": undefined,
645
649
  "color": undefined,
646
650
  },
651
+ "subNavBackgroundColor": "#ffffff",
647
652
  },
648
653
  "iconStyle": {},
649
654
  "isLogoVisible": true,
@@ -658,6 +663,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
658
663
  "borderTopWidth": "0px",
659
664
  "color": undefined,
660
665
  },
666
+ "subNavBackgroundColor": "#ffffff",
661
667
  "viewport": "desktop",
662
668
  },
663
669
  "navigationStyles": undefined,
@@ -715,6 +721,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
715
721
  "backgroundColor": undefined,
716
722
  "color": undefined,
717
723
  },
724
+ "subNavBackgroundColor": "#ffffff",
718
725
  },
719
726
  "iconStyle": {},
720
727
  "isLogoVisible": true,
@@ -729,6 +736,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
729
736
  "borderTopWidth": "0px",
730
737
  "color": undefined,
731
738
  },
739
+ "subNavBackgroundColor": "#ffffff",
732
740
  "viewport": "desktop",
733
741
  },
734
742
  "navigationStyles": undefined,
@@ -881,7 +889,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
881
889
  },
882
890
  },
883
891
  },
884
- "v": 13,
892
+ "v": 15,
885
893
  },
886
894
  "useOldLogoRenderSize": true,
887
895
  }
@@ -1079,6 +1087,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1079
1087
  "backgroundColor": undefined,
1080
1088
  "color": undefined,
1081
1089
  },
1090
+ "subNavBackgroundColor": "#ffffff",
1082
1091
  },
1083
1092
  "iconStyle": {},
1084
1093
  "isLogoVisible": true,
@@ -1093,6 +1102,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1093
1102
  "borderTopWidth": "0px",
1094
1103
  "color": undefined,
1095
1104
  },
1105
+ "subNavBackgroundColor": "#ffffff",
1096
1106
  "viewport": "desktop",
1097
1107
  },
1098
1108
  "navigationStyles": undefined,
@@ -1150,6 +1160,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1150
1160
  "backgroundColor": undefined,
1151
1161
  "color": undefined,
1152
1162
  },
1163
+ "subNavBackgroundColor": "#ffffff",
1153
1164
  },
1154
1165
  "iconStyle": {},
1155
1166
  "isLogoVisible": true,
@@ -1164,6 +1175,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1164
1175
  "borderTopWidth": "0px",
1165
1176
  "color": undefined,
1166
1177
  },
1178
+ "subNavBackgroundColor": "#ffffff",
1167
1179
  "viewport": "desktop",
1168
1180
  },
1169
1181
  "navigationStyles": undefined,
@@ -1323,7 +1335,7 @@ exports[`migration.util > migrateBrandPageModel should return brand page model w
1323
1335
  },
1324
1336
  },
1325
1337
  },
1326
- "v": 13,
1338
+ "v": 15,
1327
1339
  },
1328
1340
  "useOldLogoRenderSize": true,
1329
1341
  }