@designcrowd/library.brand-page 6.0.19-ast-btns-4 → 6.0.19-ast-btns-6
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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-sjNsYRcW.js → CreatedOnBrandCrowd-de-de-Dqv1385w.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-AzNub21R.js → CreatedOnBrandCrowd-en-us-DuA7uGNz.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-5JGE8i4f.js → CreatedOnBrandCrowd-es-es-CaSTc5HM.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-D_s0VxsF.js → CreatedOnBrandCrowd-fr-ca-C9VKny9y.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-D_s0VxsF.js → CreatedOnBrandCrowd-fr-fr-C9VKny9y.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-DNzVKTkD.js → CreatedOnBrandCrowd-pt-br-ft2V8-xm.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-DNzVKTkD.js → CreatedOnBrandCrowd-pt-pt-ft2V8-xm.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-m9T1oefT.js → CreatedOnDcom-de-de-BdZxdxoi.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-DyN0S9k3.js → CreatedOnDcom-en-us-Dl0XbKU2.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-BvR812ln.js → CreatedOnDcom-es-es-WciMRblK.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-BcmRHpDd.js → CreatedOnDcom-fr-ca-CvjUqr4M.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-BY7TQCJy.js → CreatedOnDcom-fr-fr-CMALFpBz.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-B_bocMtD.js → CreatedOnDcom-pt-br-C0MjZ-_y.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-Fg8JoWmO.js → CreatedOnDcom-pt-pt-DV9I0m0I.js} +1 -1
- package/lib/src/brand-page/components/contact-form/ContactForm.mixin.js +5 -2
- package/lib/src/brand-page/models/index.d.ts +34 -26
- package/lib/src/brand-page/utils/migration/migration-19.js +60 -29
- package/lib/src/brand-page/utils/migration/migration-20.js +11 -7
- package/lib/src/brand-page/utils/migration/migration-21.d.ts +2 -0
- package/lib/src/brand-page/utils/migration/migration-21.js +27 -0
- package/lib/src/brand-page/utils/migration.util.js +17 -2
- package/lib/src/brand-page/utils/text.util.d.ts +1 -1
- package/lib/src/brand-page/utils/text.util.js +32 -0
- package/lib/src/brand-page/utils/vcard.util.js +4 -4
- package/lib/src/brand-page/utils/view-model.util.d.ts +1 -0
- package/lib/src/brand-page/utils/view-model.util.js +68 -20
- package/lib/src/brand-page/view-models.d.ts +3 -3
- package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +22 -0
- package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +22 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +14 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +39 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +28 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +66 -0
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +21 -100
- package/lib/src/brand-page-maker/models/content-settings-configs.d.ts +1 -0
- package/lib/src/brand-page-maker/models/content-settings.js +12 -6
- package/lib/src/brand-page-maker/utils/index.d.ts +2 -0
- package/lib/src/brand-page-maker/utils/index.js +2 -0
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +19 -5
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +35 -5
- package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +33 -0
- package/lib/src/brand-page-maker/utils/rte-render.util.js +50 -0
- package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +6 -0
- package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +158 -0
- package/lib/src/brand-page-maker/utils/rte-serializer.d.ts +5 -0
- package/lib/src/brand-page-maker/utils/rte-serializer.js +112 -0
- package/lib/src/brand-page-maker/validators/field.validator.js +10 -2
- package/lib/src/css/library.brand-page-brandCrowd.min.css +16 -16
- package/lib/src/css/library.brand-page-brandPage.min.css +16 -16
- package/lib/src/css/library.brand-page-designCom.min.css +16 -16
- package/lib/src/css/library.brand-page-preview.min.css +16 -16
- package/lib/src/index.d.ts +1 -0
- package/lib/src/index.mjs +143 -138
- package/lib/src/{lib-D_Zb6GIA.js → lib-CYYPMTsG.js} +41993 -41710
- package/lib/src/lib.js +2 -0
- package/lib/src/metadata-builder/builders/update-content-text-builder.js +20 -15
- package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +8 -3
- package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +2 -2
- package/lib/src/metadata-builder/helpers/rte.helpers.js +6 -3
- package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +14 -1
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +12 -3
- package/lib/src/shared/content-settings/models.d.ts +1 -0
- package/lib/src/shared/content-settings/store/content-settings-store.js +3 -0
- package/lib/src/utils/minify-brand-page-metadata.js +4 -1
- package/package.json +1 -1
- package/src/brand-page/components/contact-form/ContactForm.mixin.ts +5 -2
- package/src/brand-page/components/contact-form/ContactForm.vue +3 -0
- package/src/brand-page/components/text-wrapper/TextWrapper.vue +17 -6
- package/src/brand-page/models/index.ts +35 -26
- package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
- package/src/brand-page/utils/migration/migration-19.test.ts +47 -90
- package/src/brand-page/utils/migration/migration-19.ts +61 -34
- package/src/brand-page/utils/migration/migration-20.test.ts +49 -52
- package/src/brand-page/utils/migration/migration-20.ts +17 -27
- package/src/brand-page/utils/migration/migration-21.test.ts +112 -0
- package/src/brand-page/utils/migration/migration-21.ts +45 -0
- package/src/brand-page/utils/migration.util.ts +18 -2
- package/src/brand-page/utils/style.util.test.ts +51 -0
- package/src/brand-page/utils/text.util.test.ts +47 -0
- package/src/brand-page/utils/text.util.ts +43 -4
- package/src/brand-page/utils/vcard.util.ts +6 -4
- package/src/brand-page/utils/view-model.pageid-rte.test.ts +104 -0
- package/src/brand-page/utils/view-model.util.ts +94 -26
- package/src/brand-page/view-models.ts +3 -2
- package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +23 -0
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +52 -0
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +42 -0
- package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +113 -0
- package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +23 -107
- package/src/brand-page-maker/models/content-settings-configs.ts +3 -0
- package/src/brand-page-maker/models/content-settings.ts +12 -6
- package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +200 -0
- package/src/brand-page-maker/utils/index.ts +2 -0
- package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +4 -3
- package/src/brand-page-maker/utils/rich-text-editor.util.ts +50 -11
- package/src/brand-page-maker/utils/rte-features.test.ts +330 -0
- package/src/brand-page-maker/utils/rte-render-parity.test.ts +156 -0
- package/src/brand-page-maker/utils/rte-render.util.ts +77 -0
- package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +158 -0
- package/src/brand-page-maker/utils/rte-sanitize.util.ts +195 -0
- package/src/brand-page-maker/utils/rte-serializer.ts +172 -0
- package/src/brand-page-maker/validators/field.validator.test.ts +55 -0
- package/src/brand-page-maker/validators/field.validator.ts +10 -2
- package/src/lib.ts +9 -0
- package/src/metadata-builder/builders/update-content-text-builder.ts +32 -18
- package/src/metadata-builder/builders/update-content-text-style-builder.ts +14 -4
- package/src/metadata-builder/helpers/rte.helpers.ts +11 -7
- package/src/shared/content-settings/components/GenericTextInput.mixin.ts +17 -1
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +16 -3
- package/src/shared/content-settings/models.ts +3 -0
- package/src/shared/content-settings/store/content-settings-store.ts +3 -0
- package/src/utils/minify-brand-page-metadata.ts +4 -1
|
@@ -6,7 +6,7 @@ import brandPageClient from '../../clients/brand-page.client';
|
|
|
6
6
|
import { isEmail } from '../../../utils/validator.util';
|
|
7
7
|
import { ALIGN_CARD_CENTER, ALIGN_CARD_CENTER_IMAGE, ALIGN_CENTERED, ALIGN_LEFT_FULL_IMAGE, ALIGN_RIGHT_FULL_IMAGE, DISPLAY_STYLE_COLLAPSED, RECAPTCHA_ACTION_CONTACT_FORM, TEXT_ONLY, } from '../../constants';
|
|
8
8
|
import { getRenderText, contentPaddingStyle as toContentPaddingStyle, mutedTextForBackground, backgroundColorFromStyle, } from '@brand-page/utils';
|
|
9
|
-
import { hasRTEContent } from '@brand-page-maker/utils';
|
|
9
|
+
import { hasRTEContent, getPlainTextFromRTE } from '@brand-page-maker/utils';
|
|
10
10
|
import { useReCaptcha } from '../../composables/recaptcha.composable';
|
|
11
11
|
export default defineComponent({
|
|
12
12
|
props: {
|
|
@@ -120,7 +120,10 @@ export default defineComponent({
|
|
|
120
120
|
isEmail(this.viewModel.responseEmail));
|
|
121
121
|
},
|
|
122
122
|
title() {
|
|
123
|
-
|
|
123
|
+
// BP-5787: title may be RTE JSON (not a string), so `.trim()` would
|
|
124
|
+
// throw — read the visible text via getPlainTextFromRTE to decide
|
|
125
|
+
// whether to fall back to the default.
|
|
126
|
+
return getPlainTextFromRTE(this.viewModel.title)
|
|
124
127
|
? this.viewModel.title
|
|
125
128
|
: 'Contact Us';
|
|
126
129
|
},
|
|
@@ -7,6 +7,14 @@ import { TextStyles } from './styles.model';
|
|
|
7
7
|
import { LogoStyles } from './logo.model';
|
|
8
8
|
import type { LogoImageModel, LogoTemplateImageModel } from './logo.model';
|
|
9
9
|
import type { BrandPagePaymentConfig } from './payment.model';
|
|
10
|
+
import type { JSONContent } from '@tiptap/vue-3';
|
|
11
|
+
/**
|
|
12
|
+
* BP-5787 — a rich-text field is either a legacy inline-HTML string or a
|
|
13
|
+
* Tiptap JSON document. Typing RTE content fields as RichTextValue (instead of
|
|
14
|
+
* `string`) forces every consumer to handle both forms — surfacing at compile
|
|
15
|
+
* time any code that assumes a plain string (e.g. `.replace`, `.length`).
|
|
16
|
+
*/
|
|
17
|
+
export type RichTextValue = string | JSONContent;
|
|
10
18
|
export * from './brand-page-context.model';
|
|
11
19
|
export * from './payment.model';
|
|
12
20
|
export * from './content.model';
|
|
@@ -148,7 +156,7 @@ export interface TextContent extends Content {
|
|
|
148
156
|
/**
|
|
149
157
|
* For BrandPage/BrandContact only
|
|
150
158
|
*/
|
|
151
|
-
text:
|
|
159
|
+
text: RichTextValue;
|
|
152
160
|
/**
|
|
153
161
|
* For BrandSite
|
|
154
162
|
*/
|
|
@@ -171,9 +179,9 @@ export interface TextContent extends Content {
|
|
|
171
179
|
}
|
|
172
180
|
export interface ProfileContent extends Content {
|
|
173
181
|
avatarImageUrl: string;
|
|
174
|
-
name:
|
|
175
|
-
title:
|
|
176
|
-
description:
|
|
182
|
+
name: RichTextValue;
|
|
183
|
+
title: RichTextValue;
|
|
184
|
+
description: RichTextValue;
|
|
177
185
|
backgroundColor: BrandPageCssStyle;
|
|
178
186
|
colorReplacements: ColorReplacement[];
|
|
179
187
|
mediaType?: string;
|
|
@@ -191,7 +199,7 @@ export interface MapContent extends Content {
|
|
|
191
199
|
}
|
|
192
200
|
export interface MailingListContent extends Content {
|
|
193
201
|
acceptedFields?: 'emailOnly' | 'nameAndEmail';
|
|
194
|
-
title:
|
|
202
|
+
title: RichTextValue;
|
|
195
203
|
buttonText: string;
|
|
196
204
|
titleStyle: TextStyleDeclaration;
|
|
197
205
|
buttonTextStyle: TextStyleDeclaration;
|
|
@@ -243,10 +251,10 @@ export interface FormField {
|
|
|
243
251
|
export interface ContactFormContent extends Content {
|
|
244
252
|
type: 'contact-form';
|
|
245
253
|
displayStyle: string;
|
|
246
|
-
title:
|
|
247
|
-
description:
|
|
254
|
+
title: RichTextValue;
|
|
255
|
+
description: RichTextValue;
|
|
248
256
|
buttonText: string;
|
|
249
|
-
successMessage:
|
|
257
|
+
successMessage: RichTextValue;
|
|
250
258
|
fields: FormField[];
|
|
251
259
|
responseEmail: string;
|
|
252
260
|
imageUrl?: string;
|
|
@@ -265,8 +273,8 @@ export interface ContactFormContent extends Content {
|
|
|
265
273
|
export interface BannerSectionContent extends Content {
|
|
266
274
|
type: 'banner-section';
|
|
267
275
|
imageUrl: string;
|
|
268
|
-
title:
|
|
269
|
-
description:
|
|
276
|
+
title: RichTextValue;
|
|
277
|
+
description: RichTextValue;
|
|
270
278
|
buttonText: string | false;
|
|
271
279
|
url: string;
|
|
272
280
|
email?: string;
|
|
@@ -293,8 +301,8 @@ export declare const MEDIA_TYPES: {
|
|
|
293
301
|
};
|
|
294
302
|
export type MediaType = keyof typeof MEDIA_TYPES;
|
|
295
303
|
export interface TextMediaItem extends ImageItem {
|
|
296
|
-
title:
|
|
297
|
-
paragraph:
|
|
304
|
+
title: RichTextValue;
|
|
305
|
+
paragraph: RichTextValue;
|
|
298
306
|
itemsId: number;
|
|
299
307
|
buttonText?: string | false;
|
|
300
308
|
buttonPageId?: string;
|
|
@@ -313,8 +321,8 @@ export interface TextShapeItem extends TextMediaItem {
|
|
|
313
321
|
export interface ShapeItem extends ImageItem {
|
|
314
322
|
colorReplacements: ColorReplacement[];
|
|
315
323
|
mediaType: MediaType;
|
|
316
|
-
title?:
|
|
317
|
-
paragraph?:
|
|
324
|
+
title?: RichTextValue;
|
|
325
|
+
paragraph?: RichTextValue;
|
|
318
326
|
titleOverride?: string;
|
|
319
327
|
paragraphOverride?: string;
|
|
320
328
|
}
|
|
@@ -322,9 +330,9 @@ export interface TestimonialItems {
|
|
|
322
330
|
/** Stable per-item identifier. See ImageItem.id. */
|
|
323
331
|
id: string;
|
|
324
332
|
imageUrl: string;
|
|
325
|
-
name:
|
|
326
|
-
position:
|
|
327
|
-
testimonials:
|
|
333
|
+
name: RichTextValue;
|
|
334
|
+
position: RichTextValue;
|
|
335
|
+
testimonials: RichTextValue;
|
|
328
336
|
colorReplacements: ColorReplacement[];
|
|
329
337
|
mediaType: string;
|
|
330
338
|
size?: string;
|
|
@@ -337,7 +345,7 @@ export interface TestimonialItems {
|
|
|
337
345
|
export interface TextItem {
|
|
338
346
|
/** Stable per-item identifier. See ImageItem.id. */
|
|
339
347
|
id: string;
|
|
340
|
-
text:
|
|
348
|
+
text: RichTextValue;
|
|
341
349
|
textStyle: TextStyleDeclaration;
|
|
342
350
|
textOverride?: string;
|
|
343
351
|
}
|
|
@@ -354,8 +362,8 @@ export interface ImagesSectionContent extends Content, MediaSection {
|
|
|
354
362
|
enablePopup: boolean;
|
|
355
363
|
}
|
|
356
364
|
export interface MediaSection extends Content {
|
|
357
|
-
title:
|
|
358
|
-
description:
|
|
365
|
+
title: RichTextValue;
|
|
366
|
+
description: RichTextValue;
|
|
359
367
|
displayStyle: ImagesSectionContentDisplayStyle;
|
|
360
368
|
items: TextShapeItem[] | TextMediaItem[];
|
|
361
369
|
titleStyle: TextStyleDeclaration;
|
|
@@ -372,7 +380,7 @@ export interface ColorReplacement {
|
|
|
372
380
|
}
|
|
373
381
|
export interface TestimonialSectionContent extends Content {
|
|
374
382
|
type: 'testimonial-section';
|
|
375
|
-
title:
|
|
383
|
+
title: RichTextValue;
|
|
376
384
|
displayStyle: string;
|
|
377
385
|
items: TestimonialItems[];
|
|
378
386
|
titleStyle: TextStyleDeclaration;
|
|
@@ -386,8 +394,8 @@ export interface TestimonialSectionContent extends Content {
|
|
|
386
394
|
export interface AboutSectionContent extends Content {
|
|
387
395
|
type: 'about-section';
|
|
388
396
|
imageUrl: string;
|
|
389
|
-
title:
|
|
390
|
-
paragraph:
|
|
397
|
+
title: RichTextValue;
|
|
398
|
+
paragraph: RichTextValue;
|
|
391
399
|
displayStyle: string;
|
|
392
400
|
titleStyle: TextStyleDeclaration;
|
|
393
401
|
paragraphStyle: TextStyleDeclaration;
|
|
@@ -411,7 +419,7 @@ export declare const SOCIAL_LINKS_ICON_DISPLAY_STYLES: {
|
|
|
411
419
|
export type SocialLinksIconDisplayStyle = keyof typeof SOCIAL_LINKS_ICON_DISPLAY_STYLES;
|
|
412
420
|
export interface SocialLinksContent extends Content {
|
|
413
421
|
type: 'social-links';
|
|
414
|
-
title:
|
|
422
|
+
title: RichTextValue;
|
|
415
423
|
links: LinksItem[];
|
|
416
424
|
iconDisplayStyle: SocialLinksIconDisplayStyle;
|
|
417
425
|
iconFillStyles: BrandPageCssStyle;
|
|
@@ -434,8 +442,8 @@ export interface DividerSectionContent extends Content {
|
|
|
434
442
|
}
|
|
435
443
|
export interface BookingSectionContent extends Content {
|
|
436
444
|
type: 'booking-section';
|
|
437
|
-
title:
|
|
438
|
-
description:
|
|
445
|
+
title: RichTextValue;
|
|
446
|
+
description: RichTextValue;
|
|
439
447
|
buttonText: string;
|
|
440
448
|
url: string;
|
|
441
449
|
email?: string;
|
|
@@ -1,40 +1,71 @@
|
|
|
1
|
+
import { hasRTEContent } from '../../../brand-page-maker/utils';
|
|
2
|
+
import { useExternalRichTextEditor } from '../../../brand-page-maker/composables/rich-text-editor/useRichTextEditor';
|
|
1
3
|
/**
|
|
2
|
-
* BP-
|
|
4
|
+
* BP-5787 — migrate existing inline-HTML RTE content to Tiptap JSON.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* The editor now emits, and the renderer now reads, Tiptap JSON. Existing
|
|
7
|
+
* published/saved sites still hold RTE fields as HTML strings. This converts
|
|
8
|
+
* them on load:
|
|
9
|
+
* - maker: the loaded model is JSON, so the next save persists JSON.
|
|
10
|
+
* - web-renderer: lazy migrate-on-read — old sites render from JSON without
|
|
11
|
+
* needing a re-save.
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
13
|
+
* Runs on EVERY load (runAlways) so it also normalises content the AI section
|
|
14
|
+
* builders still emit as HTML — there is no need to change those builders or
|
|
15
|
+
* widen the content model types.
|
|
16
|
+
*
|
|
17
|
+
* Scope: only HTML-RTE strings (hasRTEContent) are converted. Plain strings,
|
|
18
|
+
* numbers, and already-JSON docs are left untouched, so the migration is
|
|
19
|
+
* idempotent (re-running on JSON content is a no-op — non-string values are
|
|
20
|
+
* skipped). Nested RTE inside item arrays (textItems, testimonials, images…)
|
|
21
|
+
* is handled by recursing into arrays; style objects are intentionally not
|
|
22
|
+
* descended into (they never hold RTE content).
|
|
12
23
|
*/
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
const convertRteStringsToJson = (value, editor) => {
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
value.forEach((item) => convertRteStringsToJson(item, editor));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!value || typeof value !== 'object')
|
|
30
|
+
return;
|
|
31
|
+
const record = value;
|
|
32
|
+
Object.keys(record).forEach((key) => {
|
|
33
|
+
const field = record[key];
|
|
34
|
+
if (typeof field === 'string' && hasRTEContent(field)) {
|
|
35
|
+
editor.commands.setContent(field);
|
|
36
|
+
record[key] = editor.getJSON();
|
|
37
|
+
}
|
|
38
|
+
else if (Array.isArray(field)) {
|
|
39
|
+
convertRteStringsToJson(field, editor);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
18
43
|
export const migration19 = async (migrationResult) => {
|
|
19
44
|
const { brandPageModel } = migrationResult;
|
|
20
|
-
|
|
21
|
-
|
|
45
|
+
// HTML→JSON parsing needs a DOM (Tiptap's editor). In a DOM-less context
|
|
46
|
+
// (pure-node SSR or unit tests) we can't parse HTML, so we leave the RTE
|
|
47
|
+
// fields as HTML strings — the renderer and the fraud/translation extractors
|
|
48
|
+
// all dual-read HTML, so this is safe. Conversion happens wherever a DOM is
|
|
49
|
+
// available (the browser editor, jsdom).
|
|
50
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
22
51
|
return migrationResult;
|
|
23
52
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
let externalEditor;
|
|
54
|
+
try {
|
|
55
|
+
externalEditor = useExternalRichTextEditor().externalEditor;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return migrationResult;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
for (const page of brandPageModel.pages) {
|
|
62
|
+
for (const content of page.contents) {
|
|
63
|
+
convertRteStringsToJson(content, externalEditor);
|
|
32
64
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
externalEditor.destroy();
|
|
69
|
+
}
|
|
39
70
|
return migrationResult;
|
|
40
71
|
};
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
const CONTAINER_BACKGROUND_TYPES = new Set([
|
|
2
|
+
'mailing-list',
|
|
3
|
+
'request-payment',
|
|
4
|
+
'support-me',
|
|
5
|
+
]);
|
|
2
6
|
export const migration20 = async (migrationResult) => {
|
|
3
7
|
const { brandPageModel } = migrationResult;
|
|
4
|
-
if (brandPageModel.brandPageType === BRAND_PAGE_TYPES.brandSite) {
|
|
5
|
-
return migrationResult;
|
|
6
|
-
}
|
|
7
8
|
const buttonFill = brandPageModel.styles?.buttonStyles?.style?.backgroundColor;
|
|
8
9
|
if (buttonFill === undefined) {
|
|
9
10
|
return migrationResult;
|
|
10
11
|
}
|
|
11
12
|
brandPageModel.pages.forEach((page) => {
|
|
12
13
|
page.contents.forEach((content) => {
|
|
13
|
-
if (content.type
|
|
14
|
+
if (!CONTAINER_BACKGROUND_TYPES.has(content.type)) {
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
16
17
|
const block = content;
|
|
17
|
-
block.
|
|
18
|
-
|
|
18
|
+
if (block.backgroundStyle?.backgroundColor !== undefined) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
block.backgroundStyle = {
|
|
22
|
+
...block.backgroundStyle,
|
|
19
23
|
backgroundColor: buttonFill,
|
|
20
24
|
};
|
|
21
25
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BRAND_PAGE_TYPES } from '../../models';
|
|
2
|
+
export const migration21 = async (migrationResult) => {
|
|
3
|
+
const { brandPageModel } = migrationResult;
|
|
4
|
+
if (brandPageModel.brandPageType === BRAND_PAGE_TYPES.brandSite) {
|
|
5
|
+
return migrationResult;
|
|
6
|
+
}
|
|
7
|
+
const buttonFill = brandPageModel.styles?.buttonStyles?.style?.backgroundColor;
|
|
8
|
+
if (buttonFill === undefined) {
|
|
9
|
+
return migrationResult;
|
|
10
|
+
}
|
|
11
|
+
brandPageModel.pages.forEach((page) => {
|
|
12
|
+
page.contents.forEach((content) => {
|
|
13
|
+
if (content.type !== 'contact-form') {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const block = content;
|
|
17
|
+
if (block.cardStyle?.backgroundColor !== undefined) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
block.cardStyle = {
|
|
21
|
+
...block.cardStyle,
|
|
22
|
+
backgroundColor: buttonFill,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
return migrationResult;
|
|
27
|
+
};
|
|
@@ -23,6 +23,7 @@ import { migration17 } from './migration/migration-17';
|
|
|
23
23
|
import { migration18 } from './migration/migration-18';
|
|
24
24
|
import { migration19 } from './migration/migration-19';
|
|
25
25
|
import { migration20 } from './migration/migration-20';
|
|
26
|
+
import { migration21 } from './migration/migration-21';
|
|
26
27
|
const MIGRATION_TABLE = [
|
|
27
28
|
{
|
|
28
29
|
version: 1,
|
|
@@ -143,14 +144,28 @@ const MIGRATION_TABLE = [
|
|
|
143
144
|
{
|
|
144
145
|
version: 19,
|
|
145
146
|
migrateFn: migration19,
|
|
147
|
+
// runAlways: normalise RTE to JSON on every load. Idempotent (only HTML
|
|
148
|
+
// strings are converted; JSON is skipped), so it also catches content the
|
|
149
|
+
// AI section builders still emit as HTML — converting it on the next read
|
|
150
|
+
// without a new migration or changes to those builders.
|
|
151
|
+
runAlways: true,
|
|
152
|
+
description: 'BP-5787: convert inline-HTML RTE content to Tiptap JSON on load (lazy migrate-on-read for web-renderer; persisted on next maker save; also normalises AI-built HTML)',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
version: 20,
|
|
156
|
+
migrateFn: migration20,
|
|
146
157
|
// runAlways: re-apply on every load so already-published sites pick up the seed
|
|
147
158
|
// without a version bump. Idempotent — only seeds backgroundColor when unset.
|
|
148
159
|
runAlways: true,
|
|
149
160
|
description: 'BP-5800: seed own container backgroundStyle from the button fill colour for mailing-list, request-payment and support-me so the container background is independent of the site button (existing sites render unchanged)',
|
|
150
161
|
},
|
|
151
162
|
{
|
|
152
|
-
version:
|
|
153
|
-
migrateFn:
|
|
163
|
+
version: 21,
|
|
164
|
+
migrateFn: migration21,
|
|
165
|
+
// runAlways: seed on every load so content already at the latest version
|
|
166
|
+
// (skipped by a version gate) still detaches. Idempotent — only seeds
|
|
167
|
+
// cardStyle.backgroundColor when unset, so a user colour is never clobbered.
|
|
168
|
+
runAlways: true,
|
|
154
169
|
description: 'BP-5800: contact-form BP/BC container colour moves to cardStyle — seed cardStyle from the button fill (its long-standing rendered colour) so existing sites render unchanged',
|
|
155
170
|
},
|
|
156
171
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const getRenderText: (text: string) => string;
|
|
2
|
-
export declare const escapeContentText: (text:
|
|
2
|
+
export declare const escapeContentText: (text: unknown) => string | undefined;
|
|
3
3
|
export declare const decodeHtmlEntities: (text: string) => string;
|
|
4
4
|
export declare const unescapeContentText: (text: string) => string;
|
|
5
5
|
export declare const encodeContentText: (text: string | undefined) => string | undefined;
|
|
@@ -1,11 +1,43 @@
|
|
|
1
1
|
import { ESCAPED_NEWLINE_CHARACTER } from '../../constants/ui';
|
|
2
|
+
/**
|
|
3
|
+
* BP-5787 — a Tiptap JSON document is an object with `type: 'doc'`.
|
|
4
|
+
* Inlined here (rather than importing from brand-page-maker) to avoid a
|
|
5
|
+
* circular import: rich-text-editor.util imports getRenderText from this file.
|
|
6
|
+
*/
|
|
7
|
+
const isTiptapDoc = (value) => typeof value === 'object' &&
|
|
8
|
+
value !== null &&
|
|
9
|
+
value.type === 'doc';
|
|
10
|
+
const jsonDocPlainText = (doc) => {
|
|
11
|
+
const parts = [];
|
|
12
|
+
const walk = (node) => {
|
|
13
|
+
if (typeof node?.text === 'string')
|
|
14
|
+
parts.push(node.text);
|
|
15
|
+
node?.content?.forEach(walk);
|
|
16
|
+
};
|
|
17
|
+
walk(doc);
|
|
18
|
+
return parts.join(' ').replace(/\s+/g, ' ').trim();
|
|
19
|
+
};
|
|
2
20
|
export const getRenderText = (text) => {
|
|
21
|
+
// BP-5787: RTE fields may now be Tiptap JSON objects. Non-strings pass
|
|
22
|
+
// through untouched so the renderer (TextWrapper) handles them via its
|
|
23
|
+
// JSON-aware path, instead of throwing on `.replace`.
|
|
24
|
+
if (typeof text !== 'string') {
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
3
27
|
return text.replace(new RegExp(`(${ESCAPED_NEWLINE_CHARACTER}|\\n)`, 'g'), '<br/>');
|
|
4
28
|
};
|
|
29
|
+
// Accepts a legacy inline-HTML string or a Tiptap JSON document (BP-5787):
|
|
30
|
+
// for JSON it returns the concatenated visible text; for HTML it strips tags.
|
|
5
31
|
export const escapeContentText = (text) => {
|
|
6
32
|
if (!text) {
|
|
7
33
|
return text;
|
|
8
34
|
}
|
|
35
|
+
if (isTiptapDoc(text)) {
|
|
36
|
+
return jsonDocPlainText(text);
|
|
37
|
+
}
|
|
38
|
+
if (typeof text !== 'string') {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
9
41
|
return text
|
|
10
42
|
.replace(/(<([^>]+)>)/gi, '')
|
|
11
43
|
.replace(/(\r\n|\r|\n)/g, ESCAPED_NEWLINE_CHARACTER);
|
|
@@ -150,9 +150,9 @@ const processSocialLinks = (socialLinksContent, vcardLines, addedEntries) => {
|
|
|
150
150
|
});
|
|
151
151
|
};
|
|
152
152
|
const addProfileInfo = (profile, vcardLines) => {
|
|
153
|
-
const name = sanitizeForVCard(profile.name || '');
|
|
154
|
-
const title = sanitizeForVCard(profile.title || '');
|
|
155
|
-
const description = sanitizeForVCard(profile.description || '');
|
|
153
|
+
const name = sanitizeForVCard(getPlainTextFromRTE(profile.name) || '');
|
|
154
|
+
const title = sanitizeForVCard(getPlainTextFromRTE(profile.title) || '');
|
|
155
|
+
const description = sanitizeForVCard(getPlainTextFromRTE(profile.description) || '');
|
|
156
156
|
if (name) {
|
|
157
157
|
const nameParts = name.trim().split(/\s+/);
|
|
158
158
|
const lastName = nameParts.length > 1 ? nameParts.pop() || '' : '';
|
|
@@ -205,6 +205,6 @@ const downloadVCard = (vcard, filename) => {
|
|
|
205
205
|
export const saveContact = (brandPageData) => {
|
|
206
206
|
const vcard = generateVCard(brandPageData);
|
|
207
207
|
const profile = findProfileContent(brandPageData);
|
|
208
|
-
const filename = generateVCardFilename(profile?.name);
|
|
208
|
+
const filename = generateVCardFilename(getPlainTextFromRTE(profile?.name));
|
|
209
209
|
downloadVCard(vcard, filename);
|
|
210
210
|
};
|
|
@@ -28,6 +28,7 @@ interface ViewModelMappingOptions {
|
|
|
28
28
|
setVideoUrlsWithToken?: (token: string, videoUrl: string, previewImageUrl: string, assetId: string) => void;
|
|
29
29
|
}
|
|
30
30
|
export declare const getBrandPageViewModelFromBrandPageModel: (model: BrandPageModel, brandPageToken: string | undefined, showBrandCrowdLogo: boolean | undefined, options: ViewModelMappingOptions) => BrandPageViewModel;
|
|
31
|
+
export declare const resolvePageIdsInRteJson: (doc: unknown, nav: NavViewModel, options: ViewModelMappingOptions) => unknown;
|
|
31
32
|
export declare const isLogoViewModel: (viewModel: ContentViewModel) => viewModel is LogoViewModel;
|
|
32
33
|
/** for service.brand-page website preview by logo draft token */
|
|
33
34
|
export declare const applyLogoImageToBrandPageViewModel: (viewModel: BrandPageViewModel, logoImage: LogoImageModel) => BrandPageViewModel;
|