@designcrowd/library.brand-page 6.2.9 → 6.2.10-ast-faq-2

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 (181) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-9uC6t5HU.js → CreatedOnBrandCrowd-de-de-BZrMq1gI.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-P6TzcklZ.js → CreatedOnBrandCrowd-en-us-DPC8TAr4.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-DimkGb1_.js → CreatedOnBrandCrowd-es-es-B94gSf_i.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-CL_iFuJw.js → CreatedOnBrandCrowd-fr-ca-DziGGjA1.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-CL_iFuJw.js → CreatedOnBrandCrowd-fr-fr-DziGGjA1.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-DSIjEoMv.js → CreatedOnBrandCrowd-pt-br-CfTCQ9nC.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-DSIjEoMv.js → CreatedOnBrandCrowd-pt-pt-CfTCQ9nC.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-CKRvl9Ky.js → CreatedOnDcom-de-de-C22-PnKc.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-ChplPDfS.js → CreatedOnDcom-en-us-CZqr71VB.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-DCIXsW9X.js → CreatedOnDcom-es-es-BinRuIc_.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-lakt1nLU.js → CreatedOnDcom-fr-ca-DYYBqMhv.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-RsGK1y5g.js → CreatedOnDcom-fr-fr-3ytJgZBj.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-DyA4XodM.js → CreatedOnDcom-pt-br-D1UqN_eq.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-BK51dTyw.js → CreatedOnDcom-pt-pt-BnIpbt_8.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/content.model.d.ts +1 -0
  20. package/lib/src/brand-page/models/content.model.js +1 -0
  21. package/lib/src/brand-page/models/index.d.ts +22 -0
  22. package/lib/src/brand-page/utils/content-callback-util.d.ts +2 -1
  23. package/lib/src/brand-page/utils/content-callback-util.js +3 -0
  24. package/lib/src/brand-page/utils/view-model.util.js +22 -0
  25. package/lib/src/brand-page/view-models.d.ts +7 -2
  26. package/lib/src/brand-page-maker/assessors/faq-accordion-color-field-visibility.assessor.d.ts +3 -0
  27. package/lib/src/brand-page-maker/assessors/faq-accordion-color-field-visibility.assessor.js +4 -0
  28. package/lib/src/brand-page-maker/assessors/index.d.ts +1 -0
  29. package/lib/src/brand-page-maker/assessors/index.js +1 -0
  30. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +9 -5
  31. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +9 -5
  32. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +6 -5
  33. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +7 -5
  34. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.js +5 -0
  35. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +6 -5
  36. package/lib/src/brand-page-maker/components/seo-settings/UseExistingDomainModal.mixin.d.ts +6 -5
  37. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +6 -5
  38. package/lib/src/brand-page-maker/components/upgrade-to-premium-button/UpgradeToPremiumButton.mixin.d.ts +6 -5
  39. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +13 -0
  40. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +33 -12
  41. package/lib/src/brand-page-maker/constants/config-settings-constant.d.ts +2 -1
  42. package/lib/src/brand-page-maker/constants/config-settings-constant.js +103 -4
  43. package/lib/src/brand-page-maker/convertors/faq-display-style.convertor.d.ts +3 -0
  44. package/lib/src/brand-page-maker/convertors/faq-display-style.convertor.js +49 -0
  45. package/lib/src/brand-page-maker/models/content-settings-configs.d.ts +1 -0
  46. package/lib/src/brand-page-maker/models/content-settings-configs.js +1 -0
  47. package/lib/src/brand-page-maker/models/content-settings.d.ts +4 -1
  48. package/lib/src/brand-page-maker/models/content-settings.js +47 -1
  49. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.d.ts +7 -6
  50. package/lib/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.js +52 -5
  51. package/lib/src/brand-page-maker/utils/apply-color-strategy.js +45 -17
  52. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +6 -0
  53. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +24 -1
  54. package/lib/src/brand-page-maker/validators/field.validator.js +2 -0
  55. package/lib/src/{bundled-translations-zfiA5Tsw.js → bundled-translations-CUJ-udgD.js} +17 -17
  56. package/lib/src/{bundled-translations.de-DE-ChsdSyAP.js → bundled-translations.de-DE-Ch7Y_CCO.js} +22 -22
  57. package/lib/src/{bundled-translations.es-ES-DflsWQK0.js → bundled-translations.es-ES-B4CWkXkC.js} +30 -30
  58. package/lib/src/{bundled-translations.fr-CA-fpDRuZ52.js → bundled-translations.fr-CA-Drtb5hJ2.js} +9 -9
  59. package/lib/src/{bundled-translations.fr-FR-Bub73Zt-.js → bundled-translations.fr-FR-CImFpWT4.js} +29 -29
  60. package/lib/src/{bundled-translations.pt-BR-BlnWE9TB.js → bundled-translations.pt-BR-BHluaLc_.js} +15 -15
  61. package/lib/src/{bundled-translations.pt-PT-DmzXlnaS.js → bundled-translations.pt-PT-BxL2VvW4.js} +37 -37
  62. package/lib/src/bundles/bundled-translations.de-DE.json +13 -5
  63. package/lib/src/bundles/bundled-translations.es-ES.json +14 -6
  64. package/lib/src/bundles/bundled-translations.fr-CA.json +14 -6
  65. package/lib/src/bundles/bundled-translations.fr-FR.json +14 -6
  66. package/lib/src/bundles/bundled-translations.json +9 -1
  67. package/lib/src/bundles/bundled-translations.pt-BR.json +13 -5
  68. package/lib/src/bundles/bundled-translations.pt-PT.json +13 -5
  69. package/lib/src/composables/useTranslate.d.ts +1 -1
  70. package/lib/src/composables/useTranslate.js +1 -1
  71. package/lib/src/css/library.brand-page-brandCrowd.min.css +164 -53
  72. package/lib/src/css/library.brand-page-brandPage.min.css +164 -53
  73. package/lib/src/css/library.brand-page-designCom.min.css +164 -53
  74. package/lib/src/css/library.brand-page-preview.min.css +164 -53
  75. package/lib/src/index.mjs +161 -160
  76. package/lib/src/{lib-Ca7GMpcR.js → lib-nAkVtlza.js} +34006 -33265
  77. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +1 -1
  78. package/lib/src/shared/color-strategies/color-strategies.js +4 -0
  79. package/lib/src/shared/color-strategies/rte-field-pairs.d.ts +12 -0
  80. package/lib/src/shared/color-strategies/rte-field-pairs.js +10 -0
  81. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +3 -0
  82. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +4 -1
  83. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +3 -0
  84. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +3 -0
  85. package/lib/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.mixin.d.ts +3 -0
  86. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.d.ts +12 -0
  87. package/lib/src/shared/content-settings/components/items-field/ItemsField.mixin.js +8 -0
  88. package/lib/src/shared/content-settings/components/payment-config-field/PaymentConfigField.mixin.d.ts +6 -5
  89. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +12 -0
  90. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.js +5 -0
  91. package/lib/src/shared/content-settings/models.d.ts +6 -0
  92. package/lib/src/shared/content-settings/store/content-settings-store.d.ts +5 -1
  93. package/lib/src/shared/content-settings/store/content-settings-store.js +12 -0
  94. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +6 -5
  95. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +9 -5
  96. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +9 -5
  97. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +6 -5
  98. package/lib/src/shared/side-bar/components/SideBar.mixin.d.ts +6 -5
  99. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +6 -5
  100. package/package.json +2 -2
  101. package/src/brand-page/components/faq-section/FaqSection.test.ts +403 -0
  102. package/src/brand-page/components/faq-section/FaqSection.vue +315 -0
  103. package/src/brand-page/components/navigation/navigation-carousel/NavigationCarousel.vue +1 -0
  104. package/src/brand-page/components/page/PageComponent.vue +2 -0
  105. package/src/brand-page/constants/misc.constants.ts +12 -0
  106. package/src/brand-page/constants/styles.constants.ts +2 -0
  107. package/src/brand-page/models/content.model.ts +1 -0
  108. package/src/brand-page/models/index.ts +24 -0
  109. package/src/brand-page/utils/content-callback-util.ts +5 -0
  110. package/src/brand-page/utils/view-model.util.test.ts +102 -0
  111. package/src/brand-page/utils/view-model.util.ts +43 -0
  112. package/src/brand-page/view-models.ts +10 -1
  113. package/src/brand-page-maker/assessors/faq-accordion-color-field-visibility.assessor.ts +9 -0
  114. package/src/brand-page-maker/assessors/index.ts +1 -0
  115. package/src/brand-page-maker/components/brandpage-settings/BrandPageSettings.test.ts +323 -0
  116. package/src/brand-page-maker/components/brandpage-settings/BrandPageSettings.vue +3 -3
  117. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.test.ts +165 -0
  118. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +11 -4
  119. package/src/brand-page-maker/components/sensitive-content/SensitiveContent.vue +23 -3
  120. package/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.ts +5 -0
  121. package/src/brand-page-maker/components/seo-settings/SeoPageSettings.vue +2 -0
  122. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +48 -12
  123. package/src/brand-page-maker/constants/config-settings-constant.ts +107 -1
  124. package/src/brand-page-maker/constants/faq-section-config.test.ts +108 -0
  125. package/src/brand-page-maker/convertors/faq-display-style.convertor.ts +61 -0
  126. package/src/brand-page-maker/convertors/i18n/converter.de-DE.json +23 -23
  127. package/src/brand-page-maker/convertors/i18n/converter.es-ES.json +23 -23
  128. package/src/brand-page-maker/convertors/i18n/converter.fr-CA.json +23 -23
  129. package/src/brand-page-maker/convertors/i18n/converter.fr-FR.json +23 -23
  130. package/src/brand-page-maker/convertors/i18n/converter.pt-BR.json +23 -23
  131. package/src/brand-page-maker/convertors/i18n/converter.pt-PT.json +23 -23
  132. package/src/brand-page-maker/models/content-settings-configs.ts +1 -0
  133. package/src/brand-page-maker/models/content-settings.ts +59 -0
  134. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.save.test.ts +332 -1
  135. package/src/brand-page-maker/stores/brandpage-container/brand-page-container-store.ts +83 -5
  136. package/src/brand-page-maker/utils/__snapshots__/apply-color-strategy.golden.test.ts.snap +3820 -300
  137. package/src/brand-page-maker/utils/apply-color-strategy.test.ts +123 -2
  138. package/src/brand-page-maker/utils/apply-color-strategy.ts +73 -19
  139. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +110 -0
  140. package/src/brand-page-maker/utils/rich-text-editor.util.ts +66 -0
  141. package/src/brand-page-maker/validators/field.validator.ts +2 -0
  142. package/src/bundles/bundled-translations.de-DE.json +13 -5
  143. package/src/bundles/bundled-translations.es-ES.json +14 -6
  144. package/src/bundles/bundled-translations.fr-CA.json +14 -6
  145. package/src/bundles/bundled-translations.fr-FR.json +14 -6
  146. package/src/bundles/bundled-translations.json +9 -1
  147. package/src/bundles/bundled-translations.pt-BR.json +13 -5
  148. package/src/bundles/bundled-translations.pt-PT.json +13 -5
  149. package/src/composables/useTranslate.ts +2 -1
  150. package/src/metadata-builder/builders/item-id-utils.test.ts +24 -0
  151. package/src/metadata-builder/parsers/section-tool-arguments.ts +2 -1
  152. package/src/shared/color-picker/components/i18n/color-picker.de-DE.json +12 -11
  153. package/src/shared/color-picker/components/i18n/color-picker.es-ES.json +12 -11
  154. package/src/shared/color-picker/components/i18n/color-picker.fr-CA.json +12 -11
  155. package/src/shared/color-picker/components/i18n/color-picker.fr-FR.json +12 -11
  156. package/src/shared/color-picker/components/i18n/color-picker.pt-BR.json +12 -11
  157. package/src/shared/color-picker/components/i18n/color-picker.pt-PT.json +12 -11
  158. package/src/shared/color-strategies/color-strategies.ts +4 -0
  159. package/src/shared/color-strategies/rte-field-pairs.test.ts +2 -0
  160. package/src/shared/color-strategies/rte-field-pairs.ts +23 -0
  161. package/src/shared/content-settings/components/color-field/ColorField.test.ts +29 -0
  162. package/src/shared/content-settings/components/content-settings/i18n/content-settings.de-DE.json +10 -1
  163. package/src/shared/content-settings/components/content-settings/i18n/content-settings.es-ES.json +10 -1
  164. package/src/shared/content-settings/components/content-settings/i18n/content-settings.fr-CA.json +10 -1
  165. package/src/shared/content-settings/components/content-settings/i18n/content-settings.fr-FR.json +10 -1
  166. package/src/shared/content-settings/components/content-settings/i18n/content-settings.json +10 -1
  167. package/src/shared/content-settings/components/content-settings/i18n/content-settings.pt-BR.json +10 -1
  168. package/src/shared/content-settings/components/content-settings/i18n/content-settings.pt-PT.json +10 -1
  169. package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.vue +2 -0
  170. package/src/shared/content-settings/components/faq-items-field/FaqItemsField.test.ts +357 -0
  171. package/src/shared/content-settings/components/faq-items-field/FaqItemsField.vue +274 -0
  172. package/src/shared/content-settings/components/focused-content-item-settings/FocusedContentItemSettings.vue +1 -0
  173. package/src/shared/content-settings/components/items-field/ItemsField.mixin.ts +8 -0
  174. package/src/shared/content-settings/components/items-field/ItemsField.test.ts +117 -0
  175. package/src/shared/content-settings/components/items-field/ItemsField.vue +9 -3
  176. package/src/shared/content-settings/components/textarea-field/TextareaField.mixin.ts +6 -0
  177. package/src/shared/content-settings/components/textarea-field/TextareaField.vue +1 -0
  178. package/src/shared/content-settings/models.ts +7 -0
  179. package/src/shared/content-settings/store/content-settings-store.test.ts +49 -0
  180. package/src/shared/content-settings/store/content-settings-store.ts +28 -0
  181. package/src/utils/minify-brand-page-metadata.test.ts +35 -0
@@ -24,6 +24,18 @@ export const useExternalRichTextEditor = () => {
24
24
  externalEditor,
25
25
  };
26
26
  };
27
+ /**
28
+ * Saves a field's style. An editor bound to a list item (`itemStyleTarget`) saves to that
29
+ * item's own style instead of the section's `${name}Style`.
30
+ */
31
+ export const updateFieldTextStyles = (store, itemStyleTarget, payload) => {
32
+ if (itemStyleTarget) {
33
+ store.updateListItemTextStyles({ ...payload, target: itemStyleTarget });
34
+ }
35
+ else {
36
+ store.updateTextStyles(payload);
37
+ }
38
+ };
27
39
  export const useRichTextEditor = (model, props, emit, fontSettings, fontStyleDropdownRef,
28
40
  // One entry of `textStyleOptions`; `undefined` when the selection is "none".
29
41
  selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyleTypeOptions, markedText, isHyperlinkPopupOpen, selectedLinkTypeValue) => {
@@ -204,6 +216,21 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
204
216
  });
205
217
  }
206
218
  });
219
+ // Set while a toolbar command runs. The toolbar has taken focus by then, so
220
+ // `isFocused` alone would tag the command's write as hydration and fold it into
221
+ // the previous undo entry — hidden only when a style write rides along and opens
222
+ // an entry of its own. Synchronous commands only: the spacing sliders dispatch
223
+ // after a debounce and are not covered.
224
+ let isRunningToolbarCommand = false;
225
+ const runAsToolbarCommand = (command) => {
226
+ isRunningToolbarCommand = true;
227
+ try {
228
+ return command();
229
+ }
230
+ finally {
231
+ isRunningToolbarCommand = false;
232
+ }
233
+ };
207
234
  // Update model when editor content changes. RTE content is stored as Tiptap
208
235
  // JSON (BP-5787). getJSON() is already in hand (the editor holds the doc) and
209
236
  // is sanitised natively — no getHTML(), no DOMPurify, no extra editor — so
@@ -217,15 +244,9 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
217
244
  // field → store chain and arms the history merge at the moment of the
218
245
  // store write — arming here at emit time would lapse before the
219
246
  // 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.
247
+ // normally, and so do synchronous toolbar commands (isRunningToolbarCommand).
227
248
  emit('update:modelValue', sanitizeRteJson(editor.getJSON()), {
228
- isHydrationWrite: !editor.isFocused,
249
+ isHydrationWrite: !editor.isFocused && !isRunningToolbarCommand,
229
250
  });
230
251
  });
231
252
  editor.on('selectionUpdate', () => {
@@ -303,7 +324,7 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
303
324
  // write lands ~100ms BEFORE the editor's canonical text write-back, and
304
325
  // an unflagged write here would open a plain capture burst that
305
326
  // swallows the flagged text write into a phantom entry (BP-5810).
306
- contentSettingStore.updateTextStyles({
327
+ updateFieldTextStyles(contentSettingStore, props.itemStyleTarget, {
307
328
  id: props.rawId,
308
329
  name: props.name,
309
330
  value: {
@@ -488,7 +509,7 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
488
509
  });
489
510
  }
490
511
  else {
491
- contentSettingStore.updateTextStyles({
512
+ updateFieldTextStyles(contentSettingStore, props.itemStyleTarget, {
492
513
  id: props.rawId,
493
514
  name: props.name,
494
515
  value: {
@@ -584,7 +605,7 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
584
605
  editor,
585
606
  onFocus,
586
607
  onBlur,
587
- triggerEditorStyleChange,
608
+ triggerEditorStyleChange: (editorOption) => runAsToolbarCommand(() => triggerEditorStyleChange(editorOption)),
588
609
  toggleBold,
589
610
  toggleItalic,
590
611
  toggleUnderline,
@@ -601,7 +622,7 @@ selectedGlobalTextStyles, onSelectTextStyleType, updateRichTextEditor, textStyle
601
622
  setInitialStyleSettings,
602
623
  copyGlobalStyleSettings,
603
624
  unsetAllMarks,
604
- handleHyperlink,
625
+ handleHyperlink: (...args) => runAsToolbarCommand(() => handleHyperlink(...args)),
605
626
  hyperlinkValue,
606
627
  setHyperlinkUrlValue,
607
628
  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,13 +1,14 @@
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, 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, } 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';
6
- import { imageFieldVisibilityAssessor, imageOptionVisibilityAssessor, imageGalleryFieldVisibilityAssessor, cardFillVisibilityAssessor, mediaSectionItemsFieldTypeAssessor, dividerToggleFieldVisibilityAssessor, shapeSectionItemsFieldTypeAssessor, colorReplacementVisibilityTypeAssessor, colorReplacementProfileVisibilityTypeAssessor, colorReplacementIconVisibilityTypeAssessor, imagesSectionCardStyleVisibilityAssessor, contactCardColorVisibilityAssessor, imageUrlVisibilityAssessor, displayTypeVisibilityAssessor, logoFieldVisibilityAssessor, shapesSectionCardStyleVisibilityAssessor, shopImageOptionVisibilityAssessor, shopItemColorVisibility, personalisationOverrideVisibility, } from '../assessors';
6
+ import { imageFieldVisibilityAssessor, imageOptionVisibilityAssessor, imageGalleryFieldVisibilityAssessor, cardFillVisibilityAssessor, mediaSectionItemsFieldTypeAssessor, dividerToggleFieldVisibilityAssessor, faqAccordionColorFieldVisibilityAssessor, shapeSectionItemsFieldTypeAssessor, colorReplacementVisibilityTypeAssessor, colorReplacementProfileVisibilityTypeAssessor, colorReplacementIconVisibilityTypeAssessor, imagesSectionCardStyleVisibilityAssessor, contactCardColorVisibilityAssessor, imageUrlVisibilityAssessor, displayTypeVisibilityAssessor, logoFieldVisibilityAssessor, shapesSectionCardStyleVisibilityAssessor, shopImageOptionVisibilityAssessor, shopItemColorVisibility, personalisationOverrideVisibility, } from '../assessors';
7
7
  import { contactFormCardColorDefaultGetter, paymentFillColorFieldDefaultGetter, paymentTextAndBorderColorFieldDefaultGetter, shopBorderColorFieldDefaultGetter, shopFillColorFieldDefaultGetter, textColorFieldDefaultGetter, } from '../default-getters';
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';
@@ -15,7 +16,7 @@ import { imagesSectionTextStyleVisibilityAssessor } from '../assessors/images-se
15
16
  import { bannerPlaceholderFieldTypeAssessor, bannerSectionFieldTypeAssessor, bannerSectionRoleTypeAssessor, } from '../assessors/banner-section-field.assessor';
16
17
  import { isValidSvgUrl } from '@brand-page/utils';
17
18
  import { brandPageOrbrandContactTypeVisibilityAssessor, brandSiteTypeVisibilityAssessor, } from '../assessors/brand-page-type-visibility.assessor';
18
- import { contentSettingsTr } from '@src/composables/useTranslate';
19
+ import { commonTr, contentSettingsTr } from '@src/composables/useTranslate';
19
20
  const MediaFieldNames = {
20
21
  aspectRatio: 'aspectRatio',
21
22
  iconUrl: 'iconUrl',
@@ -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
+ // FaqItemsField owns the question/answer inputs and the FAQ_MAX_ITEMS cap.
2858
+ items: {
2859
+ type: 'faq-items',
2860
+ defaultValue: [],
2861
+ groupId: FAQ_FIELD_GROUP_IDS.items,
2862
+ },
2863
+ displayStyle: {
2864
+ type: 'radio',
2865
+ role: 'multi',
2866
+ defaultValue: FAQ_DISPLAY_STYLES.centered,
2867
+ title: contentSettingsTr(`displayStyleTitle`),
2868
+ optionsConvertor: faqDisplayStyleConvertor,
2869
+ groupId: FAQ_FIELD_GROUP_IDS.style,
2870
+ variant: 'icon',
2871
+ },
2872
+ ...PADDING_CONTROL_FIELDS(FAQ_FIELD_GROUP_IDS.style),
2873
+ iconColor: {
2874
+ type: 'color',
2875
+ groupId: FAQ_FIELD_GROUP_IDS.style,
2876
+ defaultValue: {},
2877
+ isInline: true,
2878
+ showToggle: false,
2879
+ disableTransparent: true,
2880
+ visibilityAssessor: faqAccordionColorFieldVisibilityAssessor,
2881
+ title: commonTr(`iconColor`),
2882
+ valueConvertor: colorConvertor,
2883
+ },
2884
+ // Transparent allowed on purpose (it hides the line), unlike the Divider section's field.
2885
+ dividerColor: {
2886
+ type: 'color',
2887
+ groupId: FAQ_FIELD_GROUP_IDS.style,
2888
+ defaultValue: {},
2889
+ isInline: true,
2890
+ showToggle: false,
2891
+ visibilityAssessor: faqAccordionColorFieldVisibilityAssessor,
2892
+ title: contentSettingsTr(`dividerColorTitle`),
2893
+ valueConvertor: backgroundColorConvertor,
2894
+ },
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,49 @@
1
+ import { FAQ_DISPLAY_STYLES } from '@brand-page/constants';
2
+ const ICON_VIEW_BOX = '0 0 96 56';
3
+ export const faqDisplayStyleConvertor = {
4
+ convertBackToMultipleValues: true,
5
+ convert(value) {
6
+ return {
7
+ options: [
8
+ // An empty title suppresses ButtonGroup's label-under-icon paragraph.
9
+ {
10
+ title: '',
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: '',
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: '',
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
+ // Label-less buttons only fill their slot in ButtonGroup's grid mode.
35
+ columns: 3,
36
+ };
37
+ },
38
+ // The heading sits left beside the accordion in split, centred above it otherwise.
39
+ convertBack(config) {
40
+ const displayStyle = config.options.find((v) => v.isSelected)?.value ??
41
+ FAQ_DISPLAY_STYLES.centered;
42
+ const textAlign = displayStyle === FAQ_DISPLAY_STYLES.split ? 'left' : 'center';
43
+ return {
44
+ displayStyle,
45
+ titleStyle: { textAlign },
46
+ descriptionStyle: { textAlign },
47
+ };
48
+ },
49
+ };
@@ -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 'faq-items': "faq-items";
55
56
  readonly 'page-dropdown': "page-dropdown";
56
57
  readonly 'personalisation-override': "personalisation-override";
57
58
  };
@@ -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
+ 'faq-items': 'faq-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,52 @@ 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
+ iconColor: { color: '#555555' },
602
+ dividerColor: { backgroundColor: '#dcdcdc' },
603
+ };
604
+ };
559
605
  export const createInitialImagesSectionContent = (options) => {
560
606
  return {
561
607
  id: uuid.v4(),
@@ -1,7 +1,7 @@
1
1
  import { type getWebsiteContextualUpgradeResponse } from '../../../clients/brand-page-client';
2
2
  import { type LogoDraftAsset, type LogoTemplateAsset } from '@brand-page-maker/stores';
3
3
  import type { CustomDomainConfigModel, CustomDomainModel } from '@brand-page-maker/models';
4
- import { type AbTest } from '@brand-page';
4
+ import { type AbTest, type SensitiveContent } from '@brand-page';
5
5
  import type { WebsiteSlugCheckResponse } from '@brand-page-maker/models/api.model';
6
6
  import { WEBSITE_UPGRADE_CONTEXT_TYPES } from '@designcrowd/fe-shared-lib';
7
7
  import { type OnSearchTermChangedCallback } from '@shared/template-settings/store';
@@ -418,13 +418,14 @@ export declare const useBrandPageContainerStore: import("pinia").StoreDefinition
418
418
  startBrandPageSlugEditMode(): void;
419
419
  triggerLockedContentClickedEvent(contentType: string): Promise<void>;
420
420
  unpublishBrandPage(): Promise<void>;
421
- updateBrandPageSettings({ description, title }: {
422
- description: any;
423
- title: any;
421
+ updateBrandPageSettings(payload: {
422
+ description: string;
423
+ title: string;
424
424
  }): Promise<void>;
425
- updateSensitiveContent({ sensitiveContent }: {
426
- sensitiveContent: any;
425
+ updateSensitiveContent(payload: {
426
+ sensitiveContent: SensitiveContent;
427
427
  }): Promise<void>;
428
+ saveSiteBeforeFirstSettingsWrite(): Promise<boolean>;
428
429
  createBrandPageCustomDomain({ domainName }: {
429
430
  domainName: any;
430
431
  }): Promise<false | undefined>;
@@ -863,13 +863,17 @@ export const useBrandPageContainerStore = defineStore('brandPageContainer', {
863
863
  console.error(err);
864
864
  }
865
865
  },
866
- async updateBrandPageSettings({ description, title }) {
866
+ async updateBrandPageSettings(payload) {
867
+ if (!this.designDraftToken &&
868
+ !(await this.saveSiteBeforeFirstSettingsWrite())) {
869
+ return;
870
+ }
867
871
  const brandPageMakerStore = useBrandPageMakerStore();
868
872
  try {
869
873
  this.setIsPublishing({ isPublishing: true });
870
874
  const result = await brandPageClient.updateBrandPageMetaAsync(brandPageMakerStore.brandPageType, this.designDraftToken, {
871
- pageDescription: description,
872
- pageTitle: title,
875
+ pageDescription: payload.description,
876
+ pageTitle: payload.title,
873
877
  metaKeywords: [],
874
878
  });
875
879
  if (!result) {
@@ -893,12 +897,16 @@ export const useBrandPageContainerStore = defineStore('brandPageContainer', {
893
897
  await brandPageMakerStore.showNextNotificationMessage();
894
898
  }
895
899
  },
896
- async updateSensitiveContent({ sensitiveContent }) {
900
+ async updateSensitiveContent(payload) {
901
+ if (!this.designDraftToken &&
902
+ !(await this.saveSiteBeforeFirstSettingsWrite())) {
903
+ return;
904
+ }
897
905
  const brandPageMakerStore = useBrandPageMakerStore();
898
906
  try {
899
907
  this.setIsPublishing({ isPublishing: true });
900
908
  const result = await brandPageClient.updateSensitiveContentAsync(brandPageMakerStore.brandPageType, this.designDraftToken, {
901
- sensitiveContentType: sensitiveContent,
909
+ sensitiveContentType: payload.sensitiveContent,
902
910
  });
903
911
  if (!result) {
904
912
  await brandPageMakerStore.enqueueErrorMessage();
@@ -921,6 +929,45 @@ export const useBrandPageContainerStore = defineStore('brandPageContainer', {
921
929
  await brandPageMakerStore.showNextNotificationMessage();
922
930
  }
923
931
  },
932
+ // Pre-purchase drafts are saved by the host, never the maker — save-publish.md.
933
+ async saveSiteBeforeFirstSettingsWrite() {
934
+ if (this.isAdminMaker) {
935
+ return false;
936
+ }
937
+ const brandPageMakerStore = useBrandPageMakerStore();
938
+ if (this.isPrePurchase) {
939
+ window.dispatchEvent(new Event('SAVE_PROGRESS_CLICKED'));
940
+ // The host's saveDesign sets isSaving synchronously on the dispatch; if it
941
+ // didn't, no listener is wired and the click would otherwise go silent.
942
+ if (!this.isSaving) {
943
+ await brandPageMakerStore.enqueueErrorMessage();
944
+ await brandPageMakerStore.showNextNotificationMessage();
945
+ }
946
+ return false;
947
+ }
948
+ if (this.isSaving || this.isPublishing) {
949
+ await brandPageMakerStore.enqueueErrorMessage();
950
+ await brandPageMakerStore.showNextNotificationMessage();
951
+ return false;
952
+ }
953
+ this.setIsPublishing({ isPublishing: true });
954
+ // saveBrandPage enqueues its own error on a throw but not on an empty response.
955
+ const messagesBeforeSave = [
956
+ ...brandPageMakerStore.notificationMessageQueue,
957
+ ];
958
+ const result = await this.saveBrandPage({ saveOnly: true });
959
+ if (!result || !result.saved) {
960
+ const hasErrorQueued = brandPageMakerStore.notificationMessageQueue.some((message) => message.type === 'error' && !messagesBeforeSave.includes(message));
961
+ if (!hasErrorQueued) {
962
+ await brandPageMakerStore.enqueueErrorMessage();
963
+ }
964
+ this.setIsPublishing({ isPublishing: false });
965
+ await brandPageMakerStore.showNextNotificationMessage();
966
+ return false;
967
+ }
968
+ this.setTriggerSaveSuccessMessage({ triggerSaveSuccessMessage: false });
969
+ return true;
970
+ },
924
971
  async createBrandPageCustomDomain({ domainName }) {
925
972
  const brandPageMakerStore = useBrandPageMakerStore();
926
973
  const customDomainResponse = await brandPageClient.createBrandPageCustomDomainAsync(brandPageMakerStore.brandPageType, this.designDraftToken, domainName, false);
@@ -2,7 +2,8 @@ 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 { isTiptapDocument } from '@brand-page-maker/utils/rte-shape';
6
+ import { pairedRteTextField, RTE_ITEM_FIELD_PAIRS, } from '@shared/color-strategies/rte-field-pairs';
6
7
  /**
7
8
  * Field types a strategy can assign. `color` fields hold nothing but a colour and are
8
9
  * REPLACED via their `valueConvertor`; `text-style` fields hold typography *and* a
@@ -62,6 +63,17 @@ const sectionLayers = (assignable, content, context) => {
62
63
  surfaceFields.every(([, field]) => !isTransparentDisabled(field.disableTransparent, context?.brandPageType ?? BRAND_PAGE_TYPES.default)),
63
64
  };
64
65
  };
66
+ /**
67
+ * The strategy's text treatment for one text-style field: `color` set, text background
68
+ * cleared (matching the highlight strip in `setRteTextColor` — the toolbar writes both).
69
+ * The key is deleted rather than set undefined so `'backgroundColor' in style` checks
70
+ * downstream stay honest.
71
+ */
72
+ const withStrategyTextStyle = (current, ref) => {
73
+ const nextStyle = { ...(current ?? {}), color: ref };
74
+ delete nextStyle.backgroundColor;
75
+ return nextStyle;
76
+ };
65
77
  /**
66
78
  * Build the per-field colour writes a strategy stamps onto a section. Pure — reads the
67
79
  * section config + current content and returns a `{ fieldName: value }` partial. Each
@@ -97,16 +109,7 @@ export const buildColorStrategyValues = (config, content, strategyId, context, a
97
109
  }
98
110
  if (field.type === 'text-style') {
99
111
  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;
112
+ values[name] = withStrategyTextStyle(current, ref);
110
113
  // Also stamp the colour into the paired RTE document, if this field drives one.
111
114
  // `setRteTextColor` returns undefined for a plain string or legacy HTML RTE, so
112
115
  // that doubles as the "nothing to do" test — no separate type guard here.
@@ -133,6 +136,28 @@ export const buildColorStrategyValues = (config, content, strategyId, context, a
133
136
  }
134
137
  values[name] = field.valueConvertor.convertBack(ref);
135
138
  });
139
+ // Item-level RTE fields (each FAQ question) carry their own style and document, so
140
+ // the section's field loop above never sees them. Same two halves, per item, written
141
+ // back as one new list.
142
+ const itemPairs = typeof contentRecord.type === 'string'
143
+ ? (RTE_ITEM_FIELD_PAIRS[contentRecord.type] ?? [])
144
+ : [];
145
+ itemPairs.forEach((pair) => {
146
+ const ref = strategyRoleRefForField(strategyId, pair.styleField, content, context, layers, anchors);
147
+ const items = (values[pair.itemsField] ??
148
+ contentRecord[pair.itemsField]);
149
+ if (!ref || !Array.isArray(items)) {
150
+ return;
151
+ }
152
+ values[pair.itemsField] = items.map((item) => {
153
+ const nextDoc = setRteTextColor(item[pair.textField], ref);
154
+ return {
155
+ ...item,
156
+ [pair.styleField]: withStrategyTextStyle(item[pair.styleField], ref),
157
+ ...(nextDoc ? { [pair.textField]: nextDoc } : {}),
158
+ };
159
+ });
160
+ });
136
161
  return values;
137
162
  };
138
163
  /**
@@ -147,12 +172,15 @@ export const applyColorStrategyToItem = (store, { id, config, content, strategyI
147
172
  Object.entries(values).forEach(([name, value]) => {
148
173
  store.updateItemValue({ id, name, value });
149
174
  });
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');
175
+ // A recoloured RTE field is emitted as a whole Tiptap document (at the top level,
176
+ // or inside a rewritten item list); the paired style-object writes are plain
177
+ // objects. Only the former needs the live editor to re-seed, so signal it just
178
+ // when a doc was among the writes.
179
+ const replacedRteDoc = Object.values(values).some((value) => isTiptapDocument(value) ||
180
+ (Array.isArray(value) &&
181
+ value.some((item) => typeof item === 'object' &&
182
+ item !== null &&
183
+ Object.values(item).some(isTiptapDocument))));
156
184
  if (replacedRteDoc) {
157
185
  markRteResync?.();
158
186
  }
@@ -36,6 +36,12 @@ export declare const getPlainTextFromRTE: (content: unknown) => string;
36
36
  */
37
37
  export declare const getRteTextLength: (value: RichTextValue | null | undefined) => number;
38
38
  export declare const replaceTextInRTE: (existingHtml: string, newText: string) => string;
39
+ /**
40
+ * Convert a new list item's plain text to RTE, as adding its section would. Goes through
41
+ * `transformRTEField` (element-first colour), so an item that copied a sibling's
42
+ * strategy-coloured style bakes that colour rather than the linked global one.
43
+ */
44
+ export declare const switchRteItemFieldsToRTE: (sectionType: string, item: Record<string, unknown>, externalEditor: Editor, uiStyleSettingsStore?: StyleSettingsState) => void;
39
45
  export declare const transformToRTE: (uiPageSettingStore: PageSettingsState, externalEditor: Editor, uiStyleSettingsStore?: StyleSettingsState, newContent?: Content, type?: TextStyleType, newStyle?: TextStyleDeclaration) => void;
40
46
  export declare const switchAllContentToRTE: (uiPageSettingStore: PageSettingsState, externalEditor: Editor, uiStyleSettingsStore?: StyleSettingsState) => void;
41
47
  export declare const getUpdatedHTMLFromEditor: (externalEditor: Editor, contentText: RichTextValue, newContentStyle: TextStyleDeclaration) => string | import("@tiptap/core").DocumentType<Record<string, any> | undefined, import("@tiptap/core").NodeType<string, Record<string, any> | undefined, any, (import("@tiptap/core").NodeType<any, any, any, any> | import("@tiptap/core").TextType<import("@tiptap/core").MarkType<any, any>>)[]>[]>;