@designcrowd/library.brand-page 6.2.2 → 6.2.3-ast-faq

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 (147) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-7P2gNr-R.js → CreatedOnBrandCrowd-de-de-BRisMzFY.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-Dl7BymW6.js → CreatedOnBrandCrowd-en-us-CDDsL9GX.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-CO4FvWVw.js → CreatedOnBrandCrowd-es-es-BkZrEo4j.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-DHNqakQu.js → CreatedOnBrandCrowd-fr-ca-sx_ury7R.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-DHNqakQu.js → CreatedOnBrandCrowd-fr-fr-sx_ury7R.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-CGUC-ZbP.js → CreatedOnBrandCrowd-pt-br-DCWiAfkU.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-CGUC-ZbP.js → CreatedOnBrandCrowd-pt-pt-DCWiAfkU.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-Dvds-Vmy.js → CreatedOnDcom-de-de-C1S7f22L.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-fd3C-8Tp.js → CreatedOnDcom-en-us-DgTlWaXE.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DTXZW35-.js → CreatedOnDcom-es-es-CxSQJe_V.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-DRCYOKPs.js → CreatedOnDcom-fr-ca-Bbam2tt6.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DpIB8x2x.js → CreatedOnDcom-fr-fr-Cb0XDRPL.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-Esnxy8wX.js → CreatedOnDcom-pt-br-BjdX3-E3.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-CaiVHFXR.js → CreatedOnDcom-pt-pt-BXpbfopB.js} +1 -1
  15. package/lib/src/brand-page/constants/misc.constants.d.ts +8 -0
  16. package/lib/src/brand-page/constants/misc.constants.js +7 -0
  17. package/lib/src/brand-page/constants/styles.constants.d.ts +2 -0
  18. package/lib/src/brand-page/constants/styles.constants.js +2 -0
  19. package/lib/src/brand-page/models/brand-page-context.model.d.ts +0 -2
  20. package/lib/src/brand-page/models/brand-page-context.model.js +0 -12
  21. package/lib/src/brand-page/models/content.model.d.ts +1 -0
  22. package/lib/src/brand-page/models/content.model.js +1 -0
  23. package/lib/src/brand-page/models/index.d.ts +19 -0
  24. package/lib/src/brand-page/utils/content-callback-util.d.ts +2 -1
  25. package/lib/src/brand-page/utils/content-callback-util.js +3 -0
  26. package/lib/src/brand-page/utils/model.util.d.ts +2 -1
  27. package/lib/src/brand-page/utils/model.util.js +3 -0
  28. package/lib/src/brand-page/utils/view-model.util.d.ts +2 -0
  29. package/lib/src/brand-page/utils/view-model.util.js +24 -0
  30. package/lib/src/brand-page/view-models.d.ts +9 -2
  31. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +0 -1
  32. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.js +0 -9
  33. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +10 -42
  34. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +10 -42
  35. package/lib/src/brand-page-maker/components/preview/Preview.mixin.d.ts +0 -14
  36. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +0 -42
  37. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +0 -42
  38. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +0 -42
  39. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +0 -42
  40. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +0 -42
  41. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +0 -42
  42. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +13 -0
  43. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +64 -30
  44. package/lib/src/brand-page-maker/constants/config-settings-constant.d.ts +2 -1
  45. package/lib/src/brand-page-maker/constants/config-settings-constant.js +101 -2
  46. package/lib/src/brand-page-maker/convertors/faq-display-style.convertor.d.ts +3 -0
  47. package/lib/src/brand-page-maker/convertors/faq-display-style.convertor.js +47 -0
  48. package/lib/src/brand-page-maker/models/content-settings-configs.d.ts +22 -1
  49. package/lib/src/brand-page-maker/models/content-settings-configs.js +1 -0
  50. package/lib/src/brand-page-maker/models/content-settings.d.ts +4 -1
  51. package/lib/src/brand-page-maker/models/content-settings.js +45 -1
  52. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +0 -42
  53. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +1 -0
  54. package/lib/src/brand-page-maker/utils/apply-color-strategy.js +47 -17
  55. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +6 -0
  56. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +24 -1
  57. package/lib/src/brand-page-maker/validators/field.validator.js +2 -0
  58. package/lib/src/bundled-translations-DY9YvuL-.js +179 -0
  59. package/lib/src/bundles/bundled-translations.json +20 -2
  60. package/lib/src/composables/useTranslate.d.ts +1 -1
  61. package/lib/src/composables/useTranslate.js +1 -1
  62. package/lib/src/css/library.brand-page-brandCrowd.min.css +143 -36
  63. package/lib/src/css/library.brand-page-brandPage.min.css +143 -36
  64. package/lib/src/css/library.brand-page-designCom.min.css +143 -36
  65. package/lib/src/css/library.brand-page-preview.min.css +143 -36
  66. package/lib/src/index.mjs +159 -158
  67. package/lib/src/{lib-V7aqfuiB.js → lib-DwpSAbLZ.js} +26889 -26178
  68. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +1 -1
  69. package/lib/src/shared/color-strategies/color-strategies.js +4 -0
  70. package/lib/src/shared/color-strategies/rte-field-pairs.d.ts +12 -0
  71. package/lib/src/shared/color-strategies/rte-field-pairs.js +10 -0
  72. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +10 -0
  73. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +11 -1
  74. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +10 -0
  75. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +10 -0
  76. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +10 -0
  77. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.d.ts +14 -1
  78. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.js +11 -1
  79. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +0 -42
  80. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +12 -0
  81. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.js +5 -0
  82. package/lib/src/shared/content-settings/models.d.ts +6 -0
  83. package/lib/src/shared/content-settings/store/content-settings-store.d.ts +14 -1
  84. package/lib/src/shared/content-settings/store/content-settings-store.js +24 -2
  85. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +0 -42
  86. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +10 -42
  87. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +10 -42
  88. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +0 -56
  89. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +0 -42
  90. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +0 -42
  91. package/package.json +2 -2
  92. package/src/brand-page/components/faq-section/FaqSection.test.ts +292 -0
  93. package/src/brand-page/components/faq-section/FaqSection.vue +299 -0
  94. package/src/brand-page/components/page/PageComponent.vue +2 -0
  95. package/src/brand-page/components/section-heading/SectionHeading.vue +52 -0
  96. package/src/brand-page/components/text-wrapper/TextWrapper.test.ts +33 -0
  97. package/src/brand-page/constants/misc.constants.ts +12 -0
  98. package/src/brand-page/constants/styles.constants.ts +2 -0
  99. package/src/brand-page/models/brand-page-context.model.ts +0 -23
  100. package/src/brand-page/models/content.model.ts +1 -0
  101. package/src/brand-page/models/index.ts +21 -0
  102. package/src/brand-page/utils/content-callback-util.ts +5 -0
  103. package/src/brand-page/utils/model.util.ts +7 -0
  104. package/src/brand-page/utils/view-model.util.test.ts +96 -0
  105. package/src/brand-page/utils/view-model.util.ts +49 -0
  106. package/src/brand-page/view-models.ts +12 -1
  107. package/src/brand-page-maker/components/action-bar/ActionBar.mixin.ts +0 -10
  108. package/src/brand-page-maker/components/action-bar/ActionBarNavigation.test.ts +8 -49
  109. package/src/brand-page-maker/components/action-bar/ActionBarNavigation.vue +1 -1
  110. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.test.ts +165 -0
  111. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +37 -49
  112. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +87 -30
  113. package/src/brand-page-maker/constants/config-settings-constant.ts +108 -0
  114. package/src/brand-page-maker/constants/faq-section-config.test.ts +151 -0
  115. package/src/brand-page-maker/convertors/faq-display-style.convertor.ts +59 -0
  116. package/src/brand-page-maker/convertors/i18n/converter.json +4 -1
  117. package/src/brand-page-maker/models/content-settings-configs.ts +24 -0
  118. package/src/brand-page-maker/models/content-settings.ts +57 -0
  119. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +2 -0
  120. package/src/brand-page-maker/utils/__snapshots__/apply-color-strategy.golden.test.ts.snap +4063 -895
  121. package/src/brand-page-maker/utils/apply-color-strategy.test.ts +104 -2
  122. package/src/brand-page-maker/utils/apply-color-strategy.ts +77 -19
  123. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +110 -0
  124. package/src/brand-page-maker/utils/rich-text-editor.util.ts +66 -0
  125. package/src/brand-page-maker/validators/field.validator.ts +2 -0
  126. package/src/bundles/bundled-translations.json +20 -2
  127. package/src/composables/useTranslate.ts +2 -1
  128. package/src/metadata-builder/builders/item-id-utils.test.ts +24 -0
  129. package/src/metadata-builder/parsers/section-tool-arguments.ts +2 -1
  130. package/src/metadata-builder/parsers/section-tool-map.test.ts +12 -0
  131. package/src/shared/color-strategies/color-strategies.ts +4 -0
  132. package/src/shared/color-strategies/rte-field-pairs.ts +23 -0
  133. package/src/shared/content-settings/components/content-settings/i18n/content-settings.json +17 -1
  134. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.vue +3 -0
  135. package/src/shared/content-settings/components/items-field/ItemsField.mixin.ts +11 -1
  136. package/src/shared/content-settings/components/items-field/ItemsField.vue +8 -2
  137. package/src/shared/content-settings/components/list-items-field/ListItemsField.test.ts +429 -0
  138. package/src/shared/content-settings/components/list-items-field/ListItemsField.vue +262 -0
  139. package/src/shared/content-settings/components/textarea-field/TextareaField.mixin.ts +6 -0
  140. package/src/shared/content-settings/components/textarea-field/TextareaField.vue +1 -0
  141. package/src/shared/content-settings/models.ts +7 -0
  142. package/src/shared/content-settings/store/content-settings-store.test.ts +70 -0
  143. package/src/shared/content-settings/store/content-settings-store.ts +37 -3
  144. package/src/shared/ecommerce-settings/util/refreshDuplicatedMetadata.test.ts +19 -0
  145. package/src/shared/page-settings/store/page-settings-store.test.ts +0 -71
  146. package/src/utils/minify-brand-page-metadata.test.ts +35 -0
  147. package/lib/src/bundled-translations-BYUfWBT3.js +0 -179
@@ -24,6 +24,24 @@ export const useExternalRichTextEditor = () => {
24
24
  externalEditor,
25
25
  };
26
26
  };
27
+ /**
28
+ * An editor bound to a list item (`itemStyleTarget`) writes its toolbar styles to that
29
+ * item's own style field instead of the section-level `${name}Style`. Returns false when
30
+ * the editor has no item target, so the caller falls through to its section-level write.
31
+ */
32
+ export const writeListItemStyles = (store, props, value, isHydrationWrite) => {
33
+ if (!props.itemStyleTarget) {
34
+ return false;
35
+ }
36
+ store.updateListItemTextStyles({
37
+ id: props.rawId,
38
+ name: props.name,
39
+ value,
40
+ target: props.itemStyleTarget,
41
+ isHydrationWrite,
42
+ });
43
+ return true;
44
+ };
27
45
  export const useRichTextEditor = (model, props, emit, fontSettings, fontStyleDropdownRef,
28
46
  // One entry of `textStyleOptions`; `undefined` when the selection is "none".
29
47
  selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyleTypeOptions, markedText, isHyperlinkPopupOpen, selectedLinkTypeValue) => {
@@ -204,6 +222,21 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
204
222
  });
205
223
  }
206
224
  });
225
+ // Set while a toolbar command runs. The toolbar has taken focus by then, so
226
+ // `isFocused` alone would tag the command's write as hydration and fold it into
227
+ // the previous undo entry — hidden only when a style write rides along and opens
228
+ // an entry of its own. Synchronous commands only: the spacing sliders dispatch
229
+ // after a debounce and are not covered.
230
+ let isRunningToolbarCommand = false;
231
+ const runAsToolbarCommand = (command) => {
232
+ isRunningToolbarCommand = true;
233
+ try {
234
+ return command();
235
+ }
236
+ finally {
237
+ isRunningToolbarCommand = false;
238
+ }
239
+ };
207
240
  // Update model when editor content changes. RTE content is stored as Tiptap
208
241
  // JSON (BP-5787). getJSON() is already in hand (the editor holds the doc) and
209
242
  // is sanitised natively — no getHTML(), no DOMPurify, no extra editor — so
@@ -217,15 +250,9 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
217
250
  // field → store chain and arms the history merge at the moment of the
218
251
  // store write — arming here at emit time would lapse before the
219
252
  // debounce fires. Genuine edits happen while focused and commit
220
- // normally.
221
- //
222
- // Known limitation: a programmatic toolbar command run on an editor the
223
- // user hasn't focused yet would also merge (losing that undo step). Today
224
- // the RTE only mounts inside an already-open settings drawer and the user
225
- // focuses it before formatting, so this doesn't occur — revisit if a
226
- // consumer ever drives toolbar actions on an unfocused editor.
253
+ // normally, and so do synchronous toolbar commands (isRunningToolbarCommand).
227
254
  emit('update:modelValue', sanitizeRteJson(editor.getJSON()), {
228
- isHydrationWrite: !editor.isFocused,
255
+ isHydrationWrite: !editor.isFocused && !isRunningToolbarCommand,
229
256
  });
230
257
  });
231
258
  editor.on('selectionUpdate', () => {
@@ -303,26 +330,30 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
303
330
  // write lands ~100ms BEFORE the editor's canonical text write-back, and
304
331
  // an unflagged write here would open a plain capture burst that
305
332
  // swallows the flagged text write into a phantom entry (BP-5810).
306
- contentSettingStore.updateTextStyles({
307
- id: props.rawId,
308
- name: props.name,
309
- value: {
310
- fontSize: globalStyles.fontSize,
311
- fontFamily: globalStyles.fontFamily,
312
- fontWeight: globalStyles.fontWeight,
313
- fontStyle: globalStyles.fontStyle,
314
- textDecoration: globalStyles.textDecoration,
315
- // Keep an element colour override (a creation-time contrast bake, or a
316
- // colour picked while linked) — only fall back to the global style's colour
317
- // when the element has none, mirroring `resolveTextColor`'s element-first
318
- // cascade. Blindly taking `globalStyles.color` here re-derived the linked
319
- // colour on every RTE mount and clobbered the override.
320
- color: props.styles?.color || globalStyles.color,
321
- lineHeight: globalStyles.lineHeight,
322
- letterSpacing: globalStyles.letterSpacing,
323
- },
324
- isHydrationWrite: !editor.isFocused,
325
- });
333
+ const syncedStyles = {
334
+ fontSize: globalStyles.fontSize,
335
+ fontFamily: globalStyles.fontFamily,
336
+ fontWeight: globalStyles.fontWeight,
337
+ fontStyle: globalStyles.fontStyle,
338
+ textDecoration: globalStyles.textDecoration,
339
+ // Keep an element colour override (a creation-time contrast bake, or a
340
+ // colour picked while linked) — only fall back to the global style's colour
341
+ // when the element has none, mirroring `resolveTextColor`'s element-first
342
+ // cascade. Blindly taking `globalStyles.color` here re-derived the linked
343
+ // colour on every RTE mount and clobbered the override.
344
+ color: props.styles?.color || globalStyles.color,
345
+ lineHeight: globalStyles.lineHeight,
346
+ letterSpacing: globalStyles.letterSpacing,
347
+ };
348
+ const isHydrationWrite = !editor.isFocused;
349
+ if (!writeListItemStyles(contentSettingStore, props, syncedStyles, isHydrationWrite)) {
350
+ contentSettingStore.updateTextStyles({
351
+ id: props.rawId,
352
+ name: props.name,
353
+ value: syncedStyles,
354
+ isHydrationWrite,
355
+ });
356
+ }
326
357
  // sync toolbar editor styles
327
358
  fontSettings.selectedFont = globalStyles?.fontFamily ?? 'Lato';
328
359
  fontSettings.selectedFontSize = globalStyles?.fontSize ?? '2.25em';
@@ -476,6 +507,9 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
476
507
  syncContentSettings(fontStyleChanges);
477
508
  };
478
509
  const syncContentSettings = (fontStyleChanges) => {
510
+ if (writeListItemStyles(contentSettingStore, props, fontStyleChanges)) {
511
+ return;
512
+ }
479
513
  if (props.name === 'textItems') {
480
514
  contentSettingStore.updateTextItemTextStyleTypeWithStyles({
481
515
  id: props.rawId,
@@ -584,7 +618,7 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
584
618
  editor,
585
619
  onFocus,
586
620
  onBlur,
587
- triggerEditorStyleChange,
621
+ triggerEditorStyleChange: (editorOption) => runAsToolbarCommand(() => triggerEditorStyleChange(editorOption)),
588
622
  toggleBold,
589
623
  toggleItalic,
590
624
  toggleUnderline,
@@ -601,7 +635,7 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
601
635
  setInitialStyleSettings,
602
636
  copyGlobalStyleSettings,
603
637
  unsetAllMarks,
604
- handleHyperlink,
638
+ handleHyperlink: (...args) => runAsToolbarCommand(() => handleHyperlink(...args)),
605
639
  hyperlinkValue,
606
640
  setHyperlinkUrlValue,
607
641
  shouldShow,
@@ -1,4 +1,4 @@
1
- import type { LogoContent, FacebookContent, GeneralLinkContent, PhoneContent, EmailContent, ImageContent, InstagramContent, LinkedInContent, PinterestContent, ProfileContent, TextContent, TiktokContent, TwitchContent, XContent, VideoContent, WebsiteContent, YoutubeContent, ContentType, MapContent, ImageGalleryContent, MailingListContent, RequestPaymentContent, SupportMeContent, ContactFormContent, BannerSectionContent, SocialLinksContent, ImagesSectionContent, TestimonialSectionContent, AboutSectionContent, DividerSectionContent, ShapesSectionContent, ShopContent, BookingSectionContent } from '@brand-page';
1
+ import type { LogoContent, FacebookContent, GeneralLinkContent, PhoneContent, EmailContent, ImageContent, InstagramContent, LinkedInContent, PinterestContent, ProfileContent, TextContent, TiktokContent, TwitchContent, XContent, VideoContent, WebsiteContent, YoutubeContent, ContentType, MapContent, ImageGalleryContent, MailingListContent, RequestPaymentContent, SupportMeContent, ContactFormContent, BannerSectionContent, SocialLinksContent, ImagesSectionContent, TestimonialSectionContent, AboutSectionContent, DividerSectionContent, ShapesSectionContent, ShopContent, BookingSectionContent, FaqSectionContent } from '@brand-page';
2
2
  import type { ContentSettingsConfig, ContentSettingsField, ContentSettingsMultiField } from '../models';
3
3
  export declare const LOGO_SETTINGS_CONFIG: () => ContentSettingsConfig<Omit<LogoContent, "version" | "logoVariation" | "logoImageHeight" | "logoImageWidth" | "logoLastUpdate" | "logoImageBase64Url">>;
4
4
  export declare const GENERAL_LINK_CONTENT_SETTINGS_CONFIG: () => ContentSettingsConfig<GeneralLinkContent>;
@@ -51,4 +51,5 @@ export declare const SHOP_SECTION_SETTINGS_CONFIG: () => ContentSettingsConfig<S
51
51
  isShippingEnabled: boolean;
52
52
  }>;
53
53
  export declare const BOOKING_SECTION_SETTINGS_CONFIG: () => ContentSettingsConfig<BookingSectionContent>;
54
+ export declare const FAQ_SECTION_SETTINGS_CONFIG: () => ContentSettingsConfig<FaqSectionContent>;
54
55
  export declare const CONTENT_SETTINGS_CONFIGS: () => Record<ContentType, ContentSettingsConfig<any>>;
@@ -1,5 +1,5 @@
1
- import { SOCIAL_LINKS_ICON_DISPLAY_STYLES, BRAND_PAGE_TYPES, DIVIDER_STYLES, MEDIA_TYPES, BUTTON_LINK_TYPE, FONT_SIZE_BODY, LINE_HEIGHT_BODY, MIN_TEXT_SIZE_PX, MAX_TEXT_SIZE_PX, TEXT_COLUMNS, DEFAULT_SHAPE_SIZE, PAYMENT_OPTION_DISPLAY_TYPES, PAYMENT_OPTION_IMAGE_TYPES, GettyVideoMedia, } from '@brand-page';
2
- import { createInitialButtonContent, createInitialImageContent, createInitialProfileContent, createInitialTextContent, createInitialVideoContent, createInitialLogoContent, createInitialMapContent, createInitialImageGalleryContent, createInitialGeneralLinkContent, createInitialMailingListContent, createInitialRequestPaymentContent, createInitialSupportMeContent, createInitialContactFormContent, CONTENT_SETTINGS_DISPLAY_MODES, createInitialBannerSectionContent, createInitialSocialLinksContent, createInitialImagesSectionContent, createInitialTestimonialSectionContent, createInitialAboutSectionContent, createInitialDividerSectionContent, createInitialShapesSectionContent, VARIANT_DISPLAY_MODES, createInitialShopContent, createInitialBookingSectionContent, } from '../models';
1
+ import { FAQ_DISPLAY_STYLES, FAQ_MAX_ITEMS, SOCIAL_LINKS_ICON_DISPLAY_STYLES, BRAND_PAGE_TYPES, DIVIDER_STYLES, MEDIA_TYPES, BUTTON_LINK_TYPE, FONT_SIZE_BODY, LINE_HEIGHT_BODY, MIN_TEXT_SIZE_PX, MAX_TEXT_SIZE_PX, TEXT_COLUMNS, DEFAULT_SHAPE_SIZE, PAYMENT_OPTION_DISPLAY_TYPES, PAYMENT_OPTION_IMAGE_TYPES, GettyVideoMedia, } from '@brand-page';
2
+ import { createInitialButtonContent, createInitialImageContent, createInitialProfileContent, createInitialTextContent, createInitialVideoContent, createInitialLogoContent, createInitialMapContent, createInitialImageGalleryContent, createInitialGeneralLinkContent, createInitialMailingListContent, createInitialRequestPaymentContent, createInitialSupportMeContent, createInitialContactFormContent, CONTENT_SETTINGS_DISPLAY_MODES, createInitialBannerSectionContent, createInitialSocialLinksContent, createInitialImagesSectionContent, createInitialTestimonialSectionContent, createInitialAboutSectionContent, createInitialDividerSectionContent, createInitialShapesSectionContent, VARIANT_DISPLAY_MODES, createInitialShopContent, createInitialBookingSectionContent, createInitialFaqSectionContent, createInitialFaqQuestionStyle, createInitialFaqAnswerStyle, } from '../models';
3
3
  import * as uuid from 'uuid';
4
4
  import { colorConvertor, backgroundColorConvertor, borderColorConvertor, displayStyleConvertor, galleryTypeConvertor, imageAspectRatioConvertor, textAlignmentOptionsConvertor, videoContentStateConvertor, fillColorConvertor, bannerAlignDisplayStyleConvertor, aboutDisplayStyleConvertor, dividerStyleConvertor, shapeSizeOptionsConvertor, buttonLinkStyleConvertor, textSizeValueConvertor, textColumnStyleConvertor, contactLayoutStyleConvertor, paymentLinkDisplayStyleConvertor, shopDisplayConvertor, shopAspectRatioConvertor, } from '../convertors';
5
5
  import { ALIGN_CENTERED, DISPLAY_STYLE_COLLAPSED, DISPLAY_STYLE_PREVIEW, } from '@brand-page';
@@ -8,6 +8,7 @@ import { contactFormCardColorDefaultGetter, paymentFillColorFieldDefaultGetter,
8
8
  import { socialLinksIconDisplayStyleConvertor } from '../convertors/social-links-icon-display-style.convertor';
9
9
  import { getTextMediaOptionsConverterByMediaType } from '../convertors/text-image-display-style.convertor';
10
10
  import { testimonialDisplayStyleConvertor } from '../convertors/testimonial-display-style-convertor';
11
+ import { faqDisplayStyleConvertor } from '../convertors/faq-display-style.convertor';
11
12
  import { pageValidator, urlValidator } from '../validators';
12
13
  import { imagesSectionAspectRatioVisibilityAssessor } from '../assessors/images-section-aspect-ratio-visibility.assessor';
13
14
  import { bannerLinkTypeVisibilityAssessor, bannerSectionPageVisibilityAssessor, bannerSectionUrlVisibilityAssessor, bannerDisplayTypeVisibilityAssessor, bannerOverlayOptionsVisibilityAssessor, } from '../assessors/banner-section-description-visibility.assessor';
@@ -2807,6 +2808,103 @@ export const BOOKING_SECTION_SETTINGS_CONFIG = () => ({
2807
2808
  isEnabled: true,
2808
2809
  isLocked: false,
2809
2810
  });
2811
+ const FAQ_FIELD_GROUP_IDS = {
2812
+ settings: 'settings',
2813
+ items: 'items',
2814
+ style: 'style',
2815
+ };
2816
+ export const FAQ_SECTION_SETTINGS_CONFIG = () => ({
2817
+ type: 'faq-section',
2818
+ category: 'general',
2819
+ createInitialContent: createInitialFaqSectionContent,
2820
+ displayMode: CONTENT_SETTINGS_DISPLAY_MODES.focused,
2821
+ defaultIconName: 'question-circle-filled',
2822
+ displayName: contentSettingsTr(`faqSectionDisplayName`),
2823
+ fieldGroups: [
2824
+ {
2825
+ id: FAQ_FIELD_GROUP_IDS.settings,
2826
+ displayName: contentSettingsTr(`settingsDisplayName`),
2827
+ isCollapsed: false,
2828
+ },
2829
+ {
2830
+ id: FAQ_FIELD_GROUP_IDS.items,
2831
+ displayName: contentSettingsTr(`faqItemsDisplayName`),
2832
+ isCollapsed: false,
2833
+ },
2834
+ {
2835
+ id: FAQ_FIELD_GROUP_IDS.style,
2836
+ displayName: contentSettingsTr(`styleDisplayName`),
2837
+ isCollapsed: true,
2838
+ },
2839
+ ],
2840
+ fields: {
2841
+ titleStyle: DEFAULT_TITLE_STYLE_SETTINGS_FILED_WITH_TITLE(),
2842
+ descriptionStyle: DEFAULT_DESCRIPTION_STYLE_FILED(),
2843
+ title: {
2844
+ type: 'text',
2845
+ placeholder: contentSettingsTr(`titlePlaceholder`),
2846
+ title: contentSettingsTr(`titleTitle`),
2847
+ groupId: FAQ_FIELD_GROUP_IDS.settings,
2848
+ maxLength: 200,
2849
+ },
2850
+ description: {
2851
+ type: 'textarea',
2852
+ placeholder: contentSettingsTr(`descriptionPlaceholder`),
2853
+ title: contentSettingsTr(`descriptionTitle`),
2854
+ groupId: FAQ_FIELD_GROUP_IDS.settings,
2855
+ maxLength: 1000,
2856
+ },
2857
+ items: {
2858
+ type: 'list-items',
2859
+ defaultValue: [],
2860
+ groupId: FAQ_FIELD_GROUP_IDS.items,
2861
+ maxLength: FAQ_MAX_ITEMS,
2862
+ itemFields: [
2863
+ {
2864
+ key: 'question',
2865
+ kind: 'text',
2866
+ styleKey: 'questionStyle',
2867
+ defaultStyle: () => createInitialFaqQuestionStyle(),
2868
+ maxLength: 200,
2869
+ title: contentSettingsTr(`faqQuestionTitle`),
2870
+ placeholder: contentSettingsTr(`faqQuestionPlaceholder`),
2871
+ defaultValue: () => contentSettingsTr(`faqNewQuestion`),
2872
+ },
2873
+ {
2874
+ key: 'answer',
2875
+ kind: 'textarea',
2876
+ styleKey: 'answerStyle',
2877
+ defaultStyle: () => createInitialFaqAnswerStyle(),
2878
+ maxLength: 1000,
2879
+ title: contentSettingsTr(`faqAnswerTitle`),
2880
+ placeholder: contentSettingsTr(`faqAnswerPlaceholder`),
2881
+ defaultValue: () => contentSettingsTr(`faqNewAnswer`),
2882
+ },
2883
+ ],
2884
+ },
2885
+ displayStyle: {
2886
+ type: 'radio',
2887
+ role: 'multi',
2888
+ defaultValue: FAQ_DISPLAY_STYLES.centered,
2889
+ title: contentSettingsTr(`displayStyleTitle`),
2890
+ optionsConvertor: faqDisplayStyleConvertor,
2891
+ groupId: FAQ_FIELD_GROUP_IDS.style,
2892
+ variant: 'icon',
2893
+ },
2894
+ ...PADDING_CONTROL_FIELDS(FAQ_FIELD_GROUP_IDS.style),
2895
+ backgroundStyle: {
2896
+ type: 'color',
2897
+ groupId: FAQ_FIELD_GROUP_IDS.style,
2898
+ defaultValue: {},
2899
+ isInline: true,
2900
+ showToggle: false,
2901
+ title: contentSettingsTr(`backgroundColorTitle`),
2902
+ valueConvertor: backgroundColorConvertor,
2903
+ },
2904
+ },
2905
+ isEnabled: true,
2906
+ isLocked: false,
2907
+ });
2810
2908
  export const CONTENT_SETTINGS_CONFIGS = () => ({
2811
2909
  logo: LOGO_SETTINGS_CONFIG(),
2812
2910
  'general-link': GENERAL_LINK_CONTENT_SETTINGS_CONFIG(),
@@ -2840,4 +2938,5 @@ export const CONTENT_SETTINGS_CONFIGS = () => ({
2840
2938
  'shapes-section': SHAPES_SECTION_SETTINGS_CONFIG(),
2841
2939
  'shop-section': SHOP_SECTION_SETTINGS_CONFIG(),
2842
2940
  'booking-section': BOOKING_SECTION_SETTINGS_CONFIG(),
2941
+ 'faq-section': FAQ_SECTION_SETTINGS_CONFIG(),
2843
2942
  });
@@ -0,0 +1,3 @@
1
+ import type { ButtonGroupConfig, ValueConvertor } from '../models';
2
+ import type { FaqSectionContent } from '@brand-page/models';
3
+ export declare const faqDisplayStyleConvertor: ValueConvertor<string | null, ButtonGroupConfig, Partial<FaqSectionContent>>;
@@ -0,0 +1,47 @@
1
+ import { FAQ_DISPLAY_STYLES } from '@brand-page/constants';
2
+ import { converterTr } from '@src/composables/useTranslate';
3
+ const ICON_VIEW_BOX = '0 0 96 56';
4
+ export const faqDisplayStyleConvertor = {
5
+ convertBackToMultipleValues: true,
6
+ convert(value) {
7
+ return {
8
+ options: [
9
+ {
10
+ title: converterTr(`faqCenteredAccordion`),
11
+ value: FAQ_DISPLAY_STYLES.centered,
12
+ isSelected: value === FAQ_DISPLAY_STYLES.centered,
13
+ iconName: 'display-style-faq-1',
14
+ iconViewBox: ICON_VIEW_BOX,
15
+ dataAttribute: 'data-test-btn-faq-centered',
16
+ },
17
+ {
18
+ title: converterTr(`faqSplitAccordion`),
19
+ value: FAQ_DISPLAY_STYLES.split,
20
+ isSelected: value === FAQ_DISPLAY_STYLES.split,
21
+ iconName: 'display-style-faq-2',
22
+ iconViewBox: ICON_VIEW_BOX,
23
+ dataAttribute: 'data-test-btn-faq-split',
24
+ },
25
+ {
26
+ title: converterTr(`faqGrid`),
27
+ value: FAQ_DISPLAY_STYLES.grid,
28
+ isSelected: value === FAQ_DISPLAY_STYLES.grid,
29
+ iconName: 'display-style-faq-3',
30
+ iconViewBox: ICON_VIEW_BOX,
31
+ dataAttribute: 'data-test-btn-faq-grid',
32
+ },
33
+ ],
34
+ };
35
+ },
36
+ // The heading sits left beside the accordion in split, centred above it otherwise.
37
+ convertBack(config) {
38
+ const displayStyle = config.options.find((v) => v.isSelected)?.value ??
39
+ FAQ_DISPLAY_STYLES.centered;
40
+ const textAlign = displayStyle === FAQ_DISPLAY_STYLES.split ? 'left' : 'center';
41
+ return {
42
+ displayStyle,
43
+ titleStyle: { textAlign },
44
+ descriptionStyle: { textAlign },
45
+ };
46
+ },
47
+ };
@@ -1,4 +1,4 @@
1
- import type { BrandPageContext, BrandPageType, Content, ContentType } from '@brand-page';
1
+ import type { BrandPageContext, BrandPageType, Content, ContentType, TextStyleDeclaration } from '@brand-page';
2
2
  import type { ContentReplaceImageSettings } from '@shared/content-settings/models';
3
3
  import type { UIUserFeatures } from '@src/constants/ui';
4
4
  import type { OptionsConvertor, ValueConvertor } from './convertor.model';
@@ -52,6 +52,7 @@ export declare const ITEM_FIELD_TYPES: {
52
52
  readonly 'color-replacements': "color-replacements";
53
53
  readonly 'testimonial-items': "testimonial-items";
54
54
  readonly 'text-items': "text-items";
55
+ readonly 'list-items': "list-items";
55
56
  readonly 'page-dropdown': "page-dropdown";
56
57
  readonly 'personalisation-override': "personalisation-override";
57
58
  };
@@ -104,6 +105,26 @@ export interface ContentSettingsField<TContent> {
104
105
  * Defaults to `${fieldName}Style` when not specified.
105
106
  */
106
107
  styleName?: string;
108
+ /** `list-items` only: the per-item inputs, in order. The first `text` input labels the row. */
109
+ itemFields?: ListItemFieldSchema[];
110
+ }
111
+ export interface ListItemFieldSchema {
112
+ key: string;
113
+ /** The plain input used when the rich-text editor is unavailable. */
114
+ kind: 'text' | 'textarea';
115
+ title: string;
116
+ placeholder?: string;
117
+ maxLength?: number;
118
+ /**
119
+ * The item field that holds this text's style. Set it to make the text rich text —
120
+ * the same editor as a section title, writing this item's own style — whenever the
121
+ * section allows rich text. Without rich text, a font-style control edits it instead.
122
+ */
123
+ styleKey?: string;
124
+ /** Style a first item starts with; later items copy the last item's style. */
125
+ defaultStyle?: () => TextStyleDeclaration;
126
+ /** Value a newly added item starts with. */
127
+ defaultValue?: () => string;
107
128
  }
108
129
  export interface ContentSettingsMultiField<TContent, TField> extends Omit<ContentSettingsField<TContent>, 'type' | 'role'> {
109
130
  type: ItemMultiFieldType;
@@ -55,6 +55,7 @@ export const ITEM_FIELD_TYPES = {
55
55
  'color-replacements': 'color-replacements',
56
56
  'testimonial-items': 'testimonial-items',
57
57
  'text-items': 'text-items',
58
+ 'list-items': 'list-items',
58
59
  'page-dropdown': 'page-dropdown',
59
60
  'personalisation-override': 'personalisation-override',
60
61
  };
@@ -1,5 +1,5 @@
1
1
  import { BrandPageModel } from '@brand-page';
2
- import type { ButtonContent, ContentType, ImageContent, ProfileContent, TextContent, VideoContent, LogoContent, LogoTheme, MapContent, ImageGalleryContent, GeneralLinkContent, MailingListContent, RequestPaymentContent, ContactFormContent, BannerSectionContent, SocialLinksContent, ImagesSectionContent, TestimonialSectionContent, AboutSectionContent, DividerSectionContent, ShapesSectionContent, BrandPageType, SupportMeContent, ShopContent, BookingSectionContent } from '@brand-page';
2
+ import type { ButtonContent, ContentType, ImageContent, ProfileContent, TextContent, VideoContent, LogoContent, LogoTheme, MapContent, ImageGalleryContent, GeneralLinkContent, MailingListContent, RequestPaymentContent, ContactFormContent, BannerSectionContent, SocialLinksContent, ImagesSectionContent, TestimonialSectionContent, AboutSectionContent, DividerSectionContent, ShapesSectionContent, BrandPageType, SupportMeContent, ShopContent, BookingSectionContent, TextStyleDeclaration, FaqSectionContent } from '@brand-page';
3
3
  import type { CreateInitialContentFunction } from './content-settings-configs';
4
4
  import type { CreateInitialContentOptions } from '@shared/content-settings/models';
5
5
  export declare const createInitialButtonContent: (type: ContentType, iconUrl: string, options?: CreateInitialContentOptions) => () => ButtonContent;
@@ -18,6 +18,9 @@ export declare const createInitialSupportMeContent: (options?: CreateInitialCont
18
18
  export declare const createInitialContactFormContent: CreateInitialContentFunction<ContactFormContent>;
19
19
  export declare const createInitialBannerSectionContent: CreateInitialContentFunction<BannerSectionContent>;
20
20
  export declare const createInitialBookingSectionContent: CreateInitialContentFunction<BookingSectionContent>;
21
+ export declare const createInitialFaqQuestionStyle: (applyAutoScale?: boolean) => TextStyleDeclaration;
22
+ export declare const createInitialFaqAnswerStyle: (applyAutoScale?: boolean) => TextStyleDeclaration;
23
+ export declare const createInitialFaqSectionContent: CreateInitialContentFunction<FaqSectionContent>;
21
24
  export declare const createInitialImagesSectionContent: CreateInitialContentFunction<ImagesSectionContent>;
22
25
  export declare const createInitialTestimonialSectionContent: CreateInitialContentFunction<TestimonialSectionContent>;
23
26
  export declare const createInitialAboutSectionContent: CreateInitialContentFunction<AboutSectionContent>;
@@ -1,4 +1,4 @@
1
- import { BrandPageModel, PageStyles, FORM_FIELD_IDS, PAYMENT_OPTION_DISPLAY_TYPES, PAYMENT_OPTION_IMAGE_TYPES, SOCIAL_LINKS_ICON_DISPLAY_STYLES, PageHeaderMobile, PageHeaderDesktop, PageFooterMobile, PageFooterDesktop, MEDIA_TYPES, BUTTON_LINK_TYPE, DISPLAY_STYLE_PREVIEW, SUPPORT_ME_CUSTOM_PRODUCT_NAME, DEFAULT_LOGO_HEIGHT, DEFAULT_LOGO_WIDTH, FONT_SIZE_BODY, LINE_HEIGHT_BODY, TEXT_STYLE_TYPES, TEXT_COLUMNS, DEFAULT_LOGO_BACKGROUND_COLOR_RGB, DEFAULT_LOGO_TEXT_COLOR_RGB, EMPTY_LOGO_IMAGE_MODEL, BRAND_PAGE_TYPES, SENSITIVE_CONTENT_TYPES, CONTACT_FORM_LAYOUT_DEFAULT_PADDING, TESTIMONIAL_DEFAULT_PADDING, } from '@brand-page';
1
+ import { BrandPageModel, PageStyles, FORM_FIELD_IDS, PAYMENT_OPTION_DISPLAY_TYPES, PAYMENT_OPTION_IMAGE_TYPES, SOCIAL_LINKS_ICON_DISPLAY_STYLES, PageHeaderMobile, PageHeaderDesktop, PageFooterMobile, PageFooterDesktop, MEDIA_TYPES, BUTTON_LINK_TYPE, DISPLAY_STYLE_PREVIEW, SUPPORT_ME_CUSTOM_PRODUCT_NAME, DEFAULT_LOGO_HEIGHT, DEFAULT_LOGO_WIDTH, FONT_SIZE_BODY, LINE_HEIGHT_BODY, TEXT_STYLE_TYPES, TEXT_COLUMNS, DEFAULT_LOGO_BACKGROUND_COLOR_RGB, DEFAULT_LOGO_TEXT_COLOR_RGB, EMPTY_LOGO_IMAGE_MODEL, BRAND_PAGE_TYPES, SENSITIVE_CONTENT_TYPES, CONTACT_FORM_LAYOUT_DEFAULT_PADDING, TESTIMONIAL_DEFAULT_PADDING, FAQ_DISPLAY_STYLES, } from '@brand-page';
2
2
  import * as uuid from 'uuid';
3
3
  import { getColorShades, DEFAULT_CARD_SURFACE_REFERENCE, } from '@brand-page/utils';
4
4
  import { contentSettingsTr } from '@src/composables/useTranslate';
@@ -556,6 +556,50 @@ export const createInitialBookingSectionContent = (options) => {
556
556
  },
557
557
  };
558
558
  };
559
+ // Questions follow the Headings role, answers the Paragraphs role.
560
+ export const createInitialFaqQuestionStyle = (applyAutoScale = false) => ({
561
+ applyAutoScale,
562
+ textStyleType: TEXT_STYLE_TYPES.headings,
563
+ textAlign: 'left',
564
+ });
565
+ export const createInitialFaqAnswerStyle = (applyAutoScale = false) => ({
566
+ applyAutoScale,
567
+ textStyleType: TEXT_STYLE_TYPES.paragraphs,
568
+ textAlign: 'left',
569
+ });
570
+ export const createInitialFaqSectionContent = (options) => {
571
+ const applyAutoScale = options?.brandPageType === BRAND_PAGE_TYPES.brandSite;
572
+ return {
573
+ id: uuid.v4(),
574
+ type: 'faq-section',
575
+ title: contentSettingsTr(`defaultFaqTitle`),
576
+ description: contentSettingsTr(`defaultFaqDescription`),
577
+ displayStyle: FAQ_DISPLAY_STYLES.centered,
578
+ topPadding: 32,
579
+ bottomPadding: 32,
580
+ // One sample item; the design's four are only there to show a longer list.
581
+ items: [
582
+ {
583
+ id: uuid.v4(),
584
+ question: contentSettingsTr(`defaultFaqQuestion`),
585
+ questionStyle: createInitialFaqQuestionStyle(applyAutoScale),
586
+ answer: contentSettingsTr(`defaultFaqAnswer`),
587
+ answerStyle: createInitialFaqAnswerStyle(applyAutoScale),
588
+ },
589
+ ],
590
+ titleStyle: {
591
+ applyAutoScale,
592
+ textStyleType: TEXT_STYLE_TYPES.titles,
593
+ textAlign: 'center',
594
+ },
595
+ descriptionStyle: {
596
+ applyAutoScale,
597
+ textStyleType: TEXT_STYLE_TYPES.paragraphs,
598
+ textAlign: 'center',
599
+ },
600
+ backgroundStyle: {},
601
+ };
602
+ };
559
603
  export const createInitialImagesSectionContent = (options) => {
560
604
  return {
561
605
  id: uuid.v4(),
@@ -158,20 +158,6 @@ export declare const useBrandPageContainerStore: import("pinia").StoreDefinition
158
158
  isVariation: boolean;
159
159
  isExcluded: boolean;
160
160
  } | undefined;
161
- bp_5873_live_preview_prepurchase?: {
162
- key: string;
163
- value: string;
164
- isControl: boolean;
165
- isVariation: boolean;
166
- isExcluded: boolean;
167
- } | undefined;
168
- bp_5873_live_preview_postpurchase?: {
169
- key: string;
170
- value: string;
171
- isControl: boolean;
172
- isVariation: boolean;
173
- isExcluded: boolean;
174
- } | undefined;
175
161
  };
176
162
  isSubsequentPublish: boolean;
177
163
  isContextualUpgradeModalVisible: boolean;
@@ -247,20 +233,6 @@ export declare const useBrandPageContainerStore: import("pinia").StoreDefinition
247
233
  isVariation: boolean;
248
234
  isExcluded: boolean;
249
235
  } | undefined;
250
- bp_5873_live_preview_prepurchase?: {
251
- key: string;
252
- value: string;
253
- isControl: boolean;
254
- isVariation: boolean;
255
- isExcluded: boolean;
256
- } | undefined;
257
- bp_5873_live_preview_postpurchase?: {
258
- key: string;
259
- value: string;
260
- isControl: boolean;
261
- isVariation: boolean;
262
- isExcluded: boolean;
263
- } | undefined;
264
236
  };
265
237
  isSubsequentPublish: boolean;
266
238
  isContextualUpgradeModalVisible: boolean;
@@ -336,20 +308,6 @@ export declare const useBrandPageContainerStore: import("pinia").StoreDefinition
336
308
  isVariation: boolean;
337
309
  isExcluded: boolean;
338
310
  } | undefined;
339
- bp_5873_live_preview_prepurchase?: {
340
- key: string;
341
- value: string;
342
- isControl: boolean;
343
- isVariation: boolean;
344
- isExcluded: boolean;
345
- } | undefined;
346
- bp_5873_live_preview_postpurchase?: {
347
- key: string;
348
- value: string;
349
- isControl: boolean;
350
- isVariation: boolean;
351
- isExcluded: boolean;
352
- } | undefined;
353
311
  };
354
312
  isSubsequentPublish: boolean;
355
313
  isContextualUpgradeModalVisible: boolean;
@@ -1477,6 +1477,7 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
1477
1477
  setVideoUrlsWithToken: videoStore.setVideoUrlsWithToken,
1478
1478
  isCustomDomain: false,
1479
1479
  brandPageSlug: brandPageContainerStore.brandPageSlug,
1480
+ activeItemIdByContentId: useUiContentSettingsStore().activeItemIdByContentId,
1480
1481
  });
1481
1482
  },
1482
1483
  gettyVideoIds() {
@@ -2,7 +2,7 @@ import { BRAND_PAGE_TYPES } from '@brand-page';
2
2
  import { FIELD_SLOT_MAP, raisedLayerFor, strategyRoleRefForField, } from '@shared/color-strategies/color-strategies';
3
3
  import { isTransparentDisabled, } from '@shared/content-settings/disable-transparent.util';
4
4
  import { setRteTextColor } from '@shared/color-strategies/rte-color';
5
- import { pairedRteTextField } from '@shared/color-strategies/rte-field-pairs';
5
+ import { pairedRteTextField, RTE_ITEM_FIELD_PAIRS, } from '@shared/color-strategies/rte-field-pairs';
6
6
  /**
7
7
  * Field types a strategy can assign. `color` fields hold nothing but a colour and are
8
8
  * REPLACED via their `valueConvertor`; `text-style` fields hold typography *and* a
@@ -62,6 +62,17 @@ const sectionLayers = (assignable, content, context) => {
62
62
  surfaceFields.every(([, field]) => !isTransparentDisabled(field.disableTransparent, context?.brandPageType ?? BRAND_PAGE_TYPES.default)),
63
63
  };
64
64
  };
65
+ /**
66
+ * The strategy's text treatment for one text-style field: `color` set, text background
67
+ * cleared (matching the highlight strip in `setRteTextColor` — the toolbar writes both).
68
+ * The key is deleted rather than set undefined so `'backgroundColor' in style` checks
69
+ * downstream stay honest.
70
+ */
71
+ const withStrategyTextStyle = (current, ref) => {
72
+ const nextStyle = { ...(current ?? {}), color: ref };
73
+ delete nextStyle.backgroundColor;
74
+ return nextStyle;
75
+ };
65
76
  /**
66
77
  * Build the per-field colour writes a strategy stamps onto a section. Pure — reads the
67
78
  * section config + current content and returns a `{ fieldName: value }` partial. Each
@@ -97,16 +108,7 @@ export const buildColorStrategyValues = (config, content, strategyId, context, a
97
108
  }
98
109
  if (field.type === 'text-style') {
99
110
  const current = contentRecord[name];
100
- const nextStyle = {
101
- ...(current ?? {}),
102
- color: ref,
103
- };
104
- // A strategy owns the text treatment, so it clears the text background colour
105
- // too — matching the highlight strip in `setRteTextColor` (the toolbar writes
106
- // both). Delete the key rather than set it undefined so `'backgroundColor' in
107
- // style` checks downstream stay honest.
108
- delete nextStyle.backgroundColor;
109
- values[name] = nextStyle;
111
+ values[name] = withStrategyTextStyle(current, ref);
110
112
  // Also stamp the colour into the paired RTE document, if this field drives one.
111
113
  // `setRteTextColor` returns undefined for a plain string or legacy HTML RTE, so
112
114
  // that doubles as the "nothing to do" test — no separate type guard here.
@@ -133,8 +135,33 @@ export const buildColorStrategyValues = (config, content, strategyId, context, a
133
135
  }
134
136
  values[name] = field.valueConvertor.convertBack(ref);
135
137
  });
138
+ // Item-level RTE fields (each FAQ question) carry their own style and document, so
139
+ // the section's field loop above never sees them. Same two halves, per item, written
140
+ // back as one new list.
141
+ const itemPairs = typeof contentRecord.type === 'string'
142
+ ? (RTE_ITEM_FIELD_PAIRS[contentRecord.type] ?? [])
143
+ : [];
144
+ itemPairs.forEach((pair) => {
145
+ const ref = strategyRoleRefForField(strategyId, pair.styleField, content, context, layers, anchors);
146
+ const items = (values[pair.itemsField] ??
147
+ contentRecord[pair.itemsField]);
148
+ if (!ref || !Array.isArray(items)) {
149
+ return;
150
+ }
151
+ values[pair.itemsField] = items.map((item) => {
152
+ const nextDoc = setRteTextColor(item[pair.textField], ref);
153
+ return {
154
+ ...item,
155
+ [pair.styleField]: withStrategyTextStyle(item[pair.styleField], ref),
156
+ ...(nextDoc ? { [pair.textField]: nextDoc } : {}),
157
+ };
158
+ });
159
+ });
136
160
  return values;
137
161
  };
162
+ const isRteDoc = (value) => typeof value === 'object' &&
163
+ value !== null &&
164
+ value.type === 'doc';
138
165
  /**
139
166
  * Apply a strategy to one content item. Shared by both settings surfaces — the focused
140
167
  * section panel and the inline list item (divider) — so the write semantics can't drift.
@@ -147,12 +174,15 @@ export const applyColorStrategyToItem = (store, { id, config, content, strategyI
147
174
  Object.entries(values).forEach(([name, value]) => {
148
175
  store.updateItemValue({ id, name, value });
149
176
  });
150
- // A recoloured RTE field is emitted as a whole Tiptap document; the paired
151
- // style-object writes are plain objects. Only the former needs the live editor
152
- // to re-seed, so signal it just when a doc was among the writes.
153
- const replacedRteDoc = Object.values(values).some((value) => typeof value === 'object' &&
154
- value !== null &&
155
- value.type === 'doc');
177
+ // A recoloured RTE field is emitted as a whole Tiptap document (at the top level,
178
+ // or inside a rewritten item list); the paired style-object writes are plain
179
+ // objects. Only the former needs the live editor to re-seed, so signal it just
180
+ // when a doc was among the writes.
181
+ const replacedRteDoc = Object.values(values).some((value) => isRteDoc(value) ||
182
+ (Array.isArray(value) &&
183
+ value.some((item) => typeof item === 'object' &&
184
+ item !== null &&
185
+ Object.values(item).some(isRteDoc))));
156
186
  if (replacedRteDoc) {
157
187
  markRteResync?.();
158
188
  }