@designcrowd/library.brand-page 6.0.6-json-prereq-7 → 6.0.6-undo-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.
- package/lib/src/{CreatedOnBrandCrowd-de-de-SPVLPi8n.js → CreatedOnBrandCrowd-de-de-BSFTp6yl.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-en-us-CvtrCPHd.js → CreatedOnBrandCrowd-en-us-rPQrOY1L.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-es-es-BSsac7jB.js → CreatedOnBrandCrowd-es-es-awd8-QFa.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-fr-BauENV2a.js → CreatedOnBrandCrowd-fr-ca-CwJ4J7Ow.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-fr-ca-BauENV2a.js → CreatedOnBrandCrowd-fr-fr-CwJ4J7Ow.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-pt-Be0hawmr.js → CreatedOnBrandCrowd-pt-br-BXYfTLlD.js} +1 -1
- package/lib/src/{CreatedOnBrandCrowd-pt-br-Be0hawmr.js → CreatedOnBrandCrowd-pt-pt-BXYfTLlD.js} +1 -1
- package/lib/src/{CreatedOnDcom-de-de-CTjE2Tmn.js → CreatedOnDcom-de-de-kX6u1lNK.js} +1 -1
- package/lib/src/{CreatedOnDcom-en-us-DnZRp5Pg.js → CreatedOnDcom-en-us-Bkq5sgFs.js} +1 -1
- package/lib/src/{CreatedOnDcom-es-es-J-9qvjy8.js → CreatedOnDcom-es-es-zCAC9ujj.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-ca-BDNHqm4G.js → CreatedOnDcom-fr-ca-BYz6Wzg5.js} +1 -1
- package/lib/src/{CreatedOnDcom-fr-fr-B_2-ceuO.js → CreatedOnDcom-fr-fr-CN-wAaSH.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-br-76lJVrAY.js → CreatedOnDcom-pt-br-BrK4Ngg-.js} +1 -1
- package/lib/src/{CreatedOnDcom-pt-pt-TQp3CNcu.js → CreatedOnDcom-pt-pt-AfI_g7xJ.js} +1 -1
- package/lib/src/brand-page/models/index.d.ts +26 -34
- package/lib/src/brand-page/utils/logo.util.d.ts +14 -0
- package/lib/src/brand-page/utils/logo.util.js +19 -0
- package/lib/src/brand-page/utils/migration.util.js +0 -11
- package/lib/src/brand-page/utils/text.util.d.ts +1 -1
- package/lib/src/brand-page/utils/text.util.js +0 -32
- package/lib/src/brand-page/utils/vcard.util.js +4 -4
- package/lib/src/brand-page/utils/view-model.util.d.ts +0 -1
- package/lib/src/brand-page/utils/view-model.util.js +20 -68
- package/lib/src/brand-page/view-models.d.ts +3 -3
- package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +1 -1
- package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +1 -1
- package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +115 -20
- package/lib/src/brand-page-maker/composables/useUndoRedoFlag.d.ts +6 -7
- package/lib/src/brand-page-maker/composables/useUndoRedoFlag.js +7 -12
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +1 -1
- package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +12 -7
- package/lib/src/brand-page-maker/stores/history/history-store.d.ts +6 -0
- package/lib/src/brand-page-maker/stores/history/history-store.js +11 -1
- package/lib/src/brand-page-maker/utils/index.d.ts +0 -2
- package/lib/src/brand-page-maker/utils/index.js +0 -2
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +5 -19
- package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +5 -35
- package/lib/src/brand-page-maker/validators/field.validator.js +2 -10
- package/lib/src/css/library.brand-page-brandCrowd.min.css +1 -1
- package/lib/src/css/library.brand-page-brandPage.min.css +1 -1
- package/lib/src/css/library.brand-page-designCom.min.css +1 -1
- package/lib/src/css/library.brand-page-preview.min.css +1 -1
- package/lib/src/index.d.ts +0 -1
- package/lib/src/index.mjs +145 -150
- package/lib/src/{lib-Bh9Y5_-x.js → lib-BYFWAvIq.js} +55864 -56119
- package/lib/src/lib.js +0 -2
- package/lib/src/metadata-builder/builders/reorder-pages-builder.d.ts +13 -0
- package/lib/src/metadata-builder/builders/reorder-pages-builder.js +49 -0
- package/lib/src/metadata-builder/builders/update-content-text-builder.js +15 -20
- package/lib/src/metadata-builder/builders/update-content-text-style-builder.js +3 -5
- package/lib/src/metadata-builder/helpers/rte.helpers.d.ts +1 -1
- package/lib/src/metadata-builder/helpers/rte.helpers.js +3 -6
- package/lib/src/shared/content-settings/components/GenericTextInput.mixin.js +1 -14
- package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +1 -1
- package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.js +3 -12
- package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.d.ts +1 -0
- package/lib/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.js +18 -7
- package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +1 -1
- package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +1 -1
- package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +1 -1
- package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +1 -1
- package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +1 -1
- package/lib/src/utils/minify-brand-page-metadata.js +1 -4
- package/package.json +1 -1
- package/src/brand-page/components/text-wrapper/TextWrapper.vue +6 -17
- package/src/brand-page/models/index.ts +26 -35
- package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +3 -3
- package/src/brand-page/utils/logo.util.test.ts +54 -1
- package/src/brand-page/utils/logo.util.ts +23 -0
- package/src/brand-page/utils/migration.util.ts +0 -12
- package/src/brand-page/utils/style.util.test.ts +0 -51
- package/src/brand-page/utils/text.util.ts +4 -43
- package/src/brand-page/utils/vcard.util.ts +4 -6
- package/src/brand-page/utils/view-model.util.ts +26 -94
- package/src/brand-page/view-models.ts +2 -3
- package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +1 -1
- package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +123 -22
- package/src/brand-page-maker/composables/useUndoRedoFlag.ts +7 -13
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +53 -0
- package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +13 -16
- package/src/brand-page-maker/stores/history/history-store.test.ts +51 -0
- package/src/brand-page-maker/stores/history/history-store.ts +19 -0
- package/src/brand-page-maker/utils/index.ts +0 -2
- package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +3 -4
- package/src/brand-page-maker/utils/rich-text-editor.util.ts +11 -50
- package/src/brand-page-maker/validators/field.validator.ts +2 -10
- package/src/lib.ts +0 -9
- package/src/metadata-builder/builders/reorder-pages-builder.ts +80 -0
- package/src/metadata-builder/builders/update-content-text-builder.ts +18 -32
- package/src/metadata-builder/builders/update-content-text-style-builder.ts +4 -6
- package/src/metadata-builder/helpers/rte.helpers.ts +7 -11
- package/src/shared/content-settings/components/GenericTextInput.mixin.ts +1 -17
- package/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.ts +3 -16
- package/src/shared/content-settings/components/text-style-field/TextStyleField.mixin.ts +19 -11
- package/src/utils/minify-brand-page-metadata.ts +1 -4
- package/lib/src/brand-page/utils/migration/migration-18.d.ts +0 -2
- package/lib/src/brand-page/utils/migration/migration-18.js +0 -71
- package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.d.ts +0 -22
- package/lib/src/brand-page-maker/__fixtures__/complex-rte.fixture.js +0 -22
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.d.ts +0 -14
- package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.js +0 -39
- package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.d.ts +0 -28
- package/lib/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.js +0 -66
- package/lib/src/brand-page-maker/utils/rte-render.util.d.ts +0 -33
- package/lib/src/brand-page-maker/utils/rte-render.util.js +0 -50
- package/lib/src/brand-page-maker/utils/rte-sanitize.util.d.ts +0 -6
- package/lib/src/brand-page-maker/utils/rte-sanitize.util.js +0 -158
- package/lib/src/brand-page-maker/utils/rte-serializer.d.ts +0 -5
- package/lib/src/brand-page-maker/utils/rte-serializer.js +0 -112
- package/src/brand-page/utils/migration/migration-18.test.ts +0 -72
- package/src/brand-page/utils/migration/migration-18.ts +0 -83
- package/src/brand-page/utils/text.util.test.ts +0 -47
- package/src/brand-page/utils/view-model.pageid-rte.test.ts +0 -104
- package/src/brand-page-maker/__fixtures__/complex-rte.fixture.ts +0 -23
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.test.ts +0 -52
- package/src/brand-page-maker/composables/rich-text-editor/extensions/FontFamily.ts +0 -42
- package/src/brand-page-maker/composables/rich-text-editor/getRteExtensions.ts +0 -113
- package/src/brand-page-maker/utils/__snapshots__/rte-render-parity.test.ts.snap +0 -200
- package/src/brand-page-maker/utils/rte-render-parity.test.ts +0 -156
- package/src/brand-page-maker/utils/rte-render.util.ts +0 -77
- package/src/brand-page-maker/utils/rte-sanitize-json.test.ts +0 -158
- package/src/brand-page-maker/utils/rte-sanitize.util.ts +0 -195
- package/src/brand-page-maker/utils/rte-serializer.ts +0 -172
- package/src/brand-page-maker/validators/field.validator.test.ts +0 -55
|
@@ -12,7 +12,12 @@ import {
|
|
|
12
12
|
PageHeaderMobile,
|
|
13
13
|
PageStyles,
|
|
14
14
|
} from '../models';
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
BrandPageModel,
|
|
17
|
+
BrandPageType,
|
|
18
|
+
LogoContent,
|
|
19
|
+
PageModel,
|
|
20
|
+
} from '../models';
|
|
16
21
|
import * as logoUtil from './logo.util';
|
|
17
22
|
|
|
18
23
|
interface GetLogoVariationIndexFromLogoImageUrlTestCase {
|
|
@@ -212,4 +217,52 @@ describe('logo-util', () => {
|
|
|
212
217
|
logoImage.logoDraftToken,
|
|
213
218
|
);
|
|
214
219
|
});
|
|
220
|
+
|
|
221
|
+
describe('getLogoImageFromModel', () => {
|
|
222
|
+
// Distinct tokens in the content block vs the header so each assertion
|
|
223
|
+
// proves which location the helper read from.
|
|
224
|
+
const buildModel = (brandPageType: BrandPageType): BrandPageModel => {
|
|
225
|
+
const model = createTestBrandPageModel();
|
|
226
|
+
|
|
227
|
+
model.brandPageType = brandPageType;
|
|
228
|
+
(model.pages[0].contents[0] as LogoContent).logoImage = {
|
|
229
|
+
logoDraftToken: 'content-token',
|
|
230
|
+
logoVariation: 0,
|
|
231
|
+
logoLastUpdate: undefined,
|
|
232
|
+
};
|
|
233
|
+
model.pageHeaderDesktop.logoImage = {
|
|
234
|
+
logoDraftToken: 'header-token',
|
|
235
|
+
logoVariation: 0,
|
|
236
|
+
logoLastUpdate: undefined,
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
return model;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
test('reads the header logo for brand-site', () => {
|
|
243
|
+
const model = buildModel(BRAND_PAGE_TYPES.brandSite);
|
|
244
|
+
|
|
245
|
+
expect(logoUtil.getLogoImageFromModel(model)?.logoDraftToken).toBe(
|
|
246
|
+
'header-token',
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('reads the content-block logo for brand-page', () => {
|
|
251
|
+
const model = buildModel(BRAND_PAGE_TYPES.default);
|
|
252
|
+
|
|
253
|
+
expect(logoUtil.getLogoImageFromModel(model)?.logoDraftToken).toBe(
|
|
254
|
+
'content-token',
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Regression guard: brand-contact previously fell through to the header
|
|
259
|
+
// branch (the `=== brandPage` check) and read a stale header logo on undo.
|
|
260
|
+
test('reads the content-block logo for brand-contact', () => {
|
|
261
|
+
const model = buildModel(BRAND_PAGE_TYPES.brandContact);
|
|
262
|
+
|
|
263
|
+
expect(logoUtil.getLogoImageFromModel(model)?.logoDraftToken).toBe(
|
|
264
|
+
'content-token',
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
215
268
|
});
|
|
@@ -394,6 +394,29 @@ export const calculateOptimizedLogoDimensions = (
|
|
|
394
394
|
return { width: currentWidth, height: currentHeight };
|
|
395
395
|
};
|
|
396
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Resolves the "current" logo image from a brand-page MODEL (the raw,
|
|
399
|
+
* deserialized shape — not the live stores, whose contents are wrapped in
|
|
400
|
+
* `settings.content`).
|
|
401
|
+
*
|
|
402
|
+
* Brand-site keeps its logo in the page header; brand-page and brand-contact
|
|
403
|
+
* keep it as a logo CONTENT block on the home page — the same `isBrandSite ?
|
|
404
|
+
* header : content` split the logo-settings store getters/setter use. This is
|
|
405
|
+
* the single source of truth for model-level logo reads: initial load and
|
|
406
|
+
* history-restore both call it so the two paths can't drift. They previously
|
|
407
|
+
* hard-coded `=== brandPage`, which silently excluded brand-contact and read a
|
|
408
|
+
* stale header logo on undo.
|
|
409
|
+
*/
|
|
410
|
+
export const getLogoImageFromModel = (
|
|
411
|
+
model: BrandPageModel,
|
|
412
|
+
): LogoImageModel | undefined => {
|
|
413
|
+
if (model.brandPageType === BRAND_PAGE_TYPES.brandSite) {
|
|
414
|
+
return model.pageHeaderDesktop?.logoImage;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return model.pages?.[0]?.contents?.find(isLogoContent)?.logoImage;
|
|
418
|
+
};
|
|
419
|
+
|
|
397
420
|
export const recalculateLogoContentDimensions = async (
|
|
398
421
|
pageData: any,
|
|
399
422
|
editableLogoVariations: {
|
|
@@ -20,7 +20,6 @@ import { migration14 } from './migration/migration-14';
|
|
|
20
20
|
import { migration15 } from './migration/migration-15';
|
|
21
21
|
import { migration16 } from './migration/migration-16';
|
|
22
22
|
import { migration17 } from './migration/migration-17';
|
|
23
|
-
import { migration18 } from './migration/migration-18';
|
|
24
23
|
|
|
25
24
|
export interface MigrationData {
|
|
26
25
|
brandPageModel: BrandPageModel;
|
|
@@ -161,17 +160,6 @@ const MIGRATION_TABLE = [
|
|
|
161
160
|
description:
|
|
162
161
|
'Padding Control backfill (BP-5699 Tier 2 + BP-5700 Store): backfill existing top/bottom spacing as explicit padding for text (12px), images/social links/shapes/booking (32px), store (16px), contact form (per layout), banner and about (per display style)',
|
|
163
162
|
},
|
|
164
|
-
{
|
|
165
|
-
version: 18,
|
|
166
|
-
migrateFn: migration18,
|
|
167
|
-
// runAlways: normalise RTE to JSON on every load. Idempotent (only HTML
|
|
168
|
-
// strings are converted; JSON is skipped), so it also catches content the
|
|
169
|
-
// AI section builders still emit as HTML — converting it on the next read
|
|
170
|
-
// without a new migration or changes to those builders.
|
|
171
|
-
runAlways: true,
|
|
172
|
-
description:
|
|
173
|
-
'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)',
|
|
174
|
-
},
|
|
175
163
|
] as MigrationTableItem[];
|
|
176
164
|
|
|
177
165
|
export const GetLatestModelVersion = () =>
|
|
@@ -87,57 +87,6 @@ describe('style.util', () => {
|
|
|
87
87
|
);
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
test('applyLogoThemeToBrandPageModel preserves var() token refs in JSON RTE content (BP-5787)', () => {
|
|
91
|
-
// Arrange — title stored as Tiptap JSON with a theme-token colour mark.
|
|
92
|
-
const logoTheme = logoWithFlatColorsTheme;
|
|
93
|
-
const titleDoc = {
|
|
94
|
-
type: 'doc',
|
|
95
|
-
content: [
|
|
96
|
-
{
|
|
97
|
-
type: 'heading',
|
|
98
|
-
attrs: { level: 2, textAlign: 'center' },
|
|
99
|
-
content: [
|
|
100
|
-
{
|
|
101
|
-
type: 'text',
|
|
102
|
-
marks: [
|
|
103
|
-
{
|
|
104
|
-
type: 'textStyle',
|
|
105
|
-
attrs: { color: `var(--${STYLE_THEME_KEYS.LogoTextColor})` },
|
|
106
|
-
},
|
|
107
|
-
],
|
|
108
|
-
text: 'Themed',
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
};
|
|
114
|
-
const brandPageModel = new BrandPageModel({
|
|
115
|
-
...BRAND_SITE,
|
|
116
|
-
pages: [
|
|
117
|
-
{
|
|
118
|
-
title: '1',
|
|
119
|
-
slug: '1',
|
|
120
|
-
contents: [
|
|
121
|
-
{
|
|
122
|
-
...createInitialBannerSectionContent(),
|
|
123
|
-
title: titleDoc,
|
|
124
|
-
} as any,
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Act
|
|
131
|
-
const { model } = applyLogoThemeToBrandPageModel(logoTheme, brandPageModel);
|
|
132
|
-
|
|
133
|
-
// Assert — JSON RTE content is an object, so the var() token is NOT
|
|
134
|
-
// flattened to rgb(); it survives so a new theme can still propagate.
|
|
135
|
-
const title = JSON.stringify((model.pages[0].contents[0] as any).title);
|
|
136
|
-
|
|
137
|
-
expect(title).toContain(`var(--${STYLE_THEME_KEYS.LogoTextColor})`);
|
|
138
|
-
expect(title).not.toContain(logoTheme.logoTextColor.rgb);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
90
|
test('applyLogoThemeToBrandPageModel should apply logo theme color to content color when content color has a color matched theme key', () => {
|
|
142
91
|
// Arrange
|
|
143
92
|
const logoTheme = logoWithFlatColorsTheme;
|
|
@@ -1,56 +1,17 @@
|
|
|
1
1
|
import { ESCAPED_NEWLINE_CHARACTER } from '../../constants/ui';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* BP-5787 — a Tiptap JSON document is an object with `type: 'doc'`.
|
|
5
|
-
* Inlined here (rather than importing from brand-page-maker) to avoid a
|
|
6
|
-
* circular import: rich-text-editor.util imports getRenderText from this file.
|
|
7
|
-
*/
|
|
8
|
-
const isTiptapDoc = (value: unknown): value is { type: 'doc' } =>
|
|
9
|
-
typeof value === 'object' &&
|
|
10
|
-
value !== null &&
|
|
11
|
-
(value as { type?: string }).type === 'doc';
|
|
12
|
-
|
|
13
|
-
const jsonDocPlainText = (doc: unknown): string => {
|
|
14
|
-
const parts: string[] = [];
|
|
15
|
-
|
|
16
|
-
const walk = (node: any): void => {
|
|
17
|
-
if (typeof node?.text === 'string') parts.push(node.text);
|
|
18
|
-
|
|
19
|
-
node?.content?.forEach(walk);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
walk(doc);
|
|
23
|
-
|
|
24
|
-
return parts.join(' ').replace(/\s+/g, ' ').trim();
|
|
25
|
-
};
|
|
26
|
-
|
|
27
3
|
export const getRenderText = (text: string): string => {
|
|
28
|
-
// BP-5787: RTE fields may now be Tiptap JSON objects. Non-strings pass
|
|
29
|
-
// through untouched so the renderer (TextWrapper) handles them via its
|
|
30
|
-
// JSON-aware path, instead of throwing on `.replace`.
|
|
31
|
-
if (typeof text !== 'string') {
|
|
32
|
-
return text as unknown as string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
4
|
return text.replace(
|
|
36
5
|
new RegExp(`(${ESCAPED_NEWLINE_CHARACTER}|\\n)`, 'g'),
|
|
37
6
|
'<br/>',
|
|
38
7
|
);
|
|
39
8
|
};
|
|
40
9
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
10
|
+
export const escapeContentText = (
|
|
11
|
+
text: string | undefined,
|
|
12
|
+
): string | undefined => {
|
|
44
13
|
if (!text) {
|
|
45
|
-
return text
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (isTiptapDoc(text)) {
|
|
49
|
-
return jsonDocPlainText(text);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (typeof text !== 'string') {
|
|
53
|
-
return undefined;
|
|
14
|
+
return text;
|
|
54
15
|
}
|
|
55
16
|
|
|
56
17
|
return text
|
|
@@ -235,11 +235,9 @@ const addProfileInfo = (
|
|
|
235
235
|
profile: ProfileViewModel,
|
|
236
236
|
vcardLines: string[],
|
|
237
237
|
): void => {
|
|
238
|
-
const name = sanitizeForVCard(
|
|
239
|
-
const title = sanitizeForVCard(
|
|
240
|
-
const description = sanitizeForVCard(
|
|
241
|
-
getPlainTextFromRTE(profile.description) || '',
|
|
242
|
-
);
|
|
238
|
+
const name = sanitizeForVCard(profile.name || '');
|
|
239
|
+
const title = sanitizeForVCard(profile.title || '');
|
|
240
|
+
const description = sanitizeForVCard(profile.description || '');
|
|
243
241
|
|
|
244
242
|
if (name) {
|
|
245
243
|
const nameParts = name.trim().split(/\s+/);
|
|
@@ -318,7 +316,7 @@ export const saveContact = (brandPageData: {
|
|
|
318
316
|
}): void => {
|
|
319
317
|
const vcard = generateVCard(brandPageData);
|
|
320
318
|
const profile = findProfileContent(brandPageData);
|
|
321
|
-
const filename = generateVCardFilename(
|
|
319
|
+
const filename = generateVCardFilename(profile?.name);
|
|
322
320
|
|
|
323
321
|
downloadVCard(vcard, filename);
|
|
324
322
|
};
|
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
getRouteNameByPageIndex,
|
|
120
120
|
getRouteParamsByPage,
|
|
121
121
|
} from './route.util';
|
|
122
|
-
import { hasRTEContent
|
|
122
|
+
import { hasRTEContent } from '../../brand-page-maker/utils';
|
|
123
123
|
import {
|
|
124
124
|
getPageIdFromHref,
|
|
125
125
|
isHrefPageId,
|
|
@@ -1518,86 +1518,6 @@ const mapPageIdReferencesToRelativePaths = (
|
|
|
1518
1518
|
});
|
|
1519
1519
|
};
|
|
1520
1520
|
|
|
1521
|
-
// Resolve a `pageid:<id>` internal-link href to the published relative path.
|
|
1522
|
-
// Returns the resolved href, or null when `href` is not an internal pageid link
|
|
1523
|
-
// (external/absolute links are left untouched). Falls back to the site root when
|
|
1524
|
-
// the pageid can't be matched to a nav route.
|
|
1525
|
-
const resolvePageIdHref = (
|
|
1526
|
-
href: string,
|
|
1527
|
-
nav: NavViewModel,
|
|
1528
|
-
options: ViewModelMappingOptions,
|
|
1529
|
-
): string | null => {
|
|
1530
|
-
// Only process internal links (no protocol, doesn't start with /)
|
|
1531
|
-
if (!href || href.match(/^(https?:\/\/|\/)/) || !isHrefPageId(href)) {
|
|
1532
|
-
return null;
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
const pageId = getPageIdFromHref(href);
|
|
1536
|
-
const route = findNavItemByPageId(nav.navItems, pageId);
|
|
1537
|
-
const brandPageSlugRoute = options.brandPageSlug
|
|
1538
|
-
? `/${options.brandPageSlug}`
|
|
1539
|
-
: '/';
|
|
1540
|
-
|
|
1541
|
-
if (route) {
|
|
1542
|
-
if (route.isRoot) {
|
|
1543
|
-
return brandPageSlugRoute;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
return `${brandPageSlugRoute}${brandPageSlugRoute === '/' ? '' : '/'}${route.relativePath}`;
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
// fallback to root if pageid cannot be resolved.
|
|
1550
|
-
return brandPageSlugRoute;
|
|
1551
|
-
};
|
|
1552
|
-
|
|
1553
|
-
// BP-5847: walk a Tiptap JSON doc and rewrite every link mark whose href is a
|
|
1554
|
-
// `pageid:` reference into its published relative path. Returns a NEW doc when
|
|
1555
|
-
// something changed (so the shared model doc is never mutated), else the same
|
|
1556
|
-
// reference.
|
|
1557
|
-
export const resolvePageIdsInRteJson = (
|
|
1558
|
-
doc: unknown,
|
|
1559
|
-
nav: NavViewModel,
|
|
1560
|
-
options: ViewModelMappingOptions,
|
|
1561
|
-
): unknown => {
|
|
1562
|
-
let changed = false;
|
|
1563
|
-
|
|
1564
|
-
const walk = (node: any): any => {
|
|
1565
|
-
if (!node || typeof node !== 'object') {
|
|
1566
|
-
return node;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
let next = node;
|
|
1570
|
-
|
|
1571
|
-
if (Array.isArray(node.marks)) {
|
|
1572
|
-
const marks = node.marks.map((mark: any) => {
|
|
1573
|
-
if (mark?.type === 'link' && typeof mark.attrs?.href === 'string') {
|
|
1574
|
-
const resolved = resolvePageIdHref(mark.attrs.href, nav, options);
|
|
1575
|
-
|
|
1576
|
-
if (resolved !== null) {
|
|
1577
|
-
changed = true;
|
|
1578
|
-
|
|
1579
|
-
return { ...mark, attrs: { ...mark.attrs, href: resolved } };
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
return mark;
|
|
1584
|
-
});
|
|
1585
|
-
|
|
1586
|
-
next = { ...next, marks };
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
if (Array.isArray(node.content)) {
|
|
1590
|
-
next = { ...next, content: node.content.map(walk) };
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
return next;
|
|
1594
|
-
};
|
|
1595
|
-
|
|
1596
|
-
const result = walk(doc);
|
|
1597
|
-
|
|
1598
|
-
return changed ? result : doc;
|
|
1599
|
-
};
|
|
1600
|
-
|
|
1601
1521
|
const dynamicallyMapPageIdInHyperlinksToRelativePaths = (
|
|
1602
1522
|
content: any,
|
|
1603
1523
|
nav: NavViewModel,
|
|
@@ -1610,15 +1530,7 @@ const dynamicallyMapPageIdInHyperlinksToRelativePaths = (
|
|
|
1610
1530
|
return;
|
|
1611
1531
|
}
|
|
1612
1532
|
|
|
1613
|
-
if (
|
|
1614
|
-
// BP-5847: RTE fields may now be Tiptap JSON. Resolve pageid link marks
|
|
1615
|
-
// here too, or internal links render as raw `pageid:` and don't navigate.
|
|
1616
|
-
(content as unknown as Record<string, unknown>)[key] =
|
|
1617
|
-
resolvePageIdsInRteJson(value, nav, options);
|
|
1618
|
-
} else if (
|
|
1619
|
-
typeof value === 'object' &&
|
|
1620
|
-
(key === 'items' || key === 'textItems')
|
|
1621
|
-
) {
|
|
1533
|
+
if (typeof value === 'object' && (key === 'items' || key === 'textItems')) {
|
|
1622
1534
|
if (Array.isArray(value)) {
|
|
1623
1535
|
value.forEach((item) => {
|
|
1624
1536
|
dynamicallyMapPageIdInHyperlinksToRelativePaths(item, nav, options);
|
|
@@ -1630,11 +1542,31 @@ const dynamicallyMapPageIdInHyperlinksToRelativePaths = (
|
|
|
1630
1542
|
(content as unknown as Record<string, unknown>)[key] = value.replace(
|
|
1631
1543
|
/<a\s+([^>]*?)href="([^"]*?)"([^>]*?)>/gi,
|
|
1632
1544
|
(match, existingAttrsStart, href, existingAttrsEnd) => {
|
|
1633
|
-
|
|
1545
|
+
// Only process internal links (no protocol, doesn't start with /)
|
|
1546
|
+
if (href && !href.match(/^(https?:\/\/|\/)/) && isHrefPageId(href)) {
|
|
1547
|
+
const pageId = getPageIdFromHref(href);
|
|
1548
|
+
|
|
1549
|
+
const route = findNavItemByPageId(nav.navItems, pageId);
|
|
1550
|
+
|
|
1551
|
+
const brandPageSlugRoute = options.brandPageSlug
|
|
1552
|
+
? `/${options.brandPageSlug}`
|
|
1553
|
+
: '/';
|
|
1554
|
+
|
|
1555
|
+
if (route) {
|
|
1556
|
+
let href = `${brandPageSlugRoute}`;
|
|
1557
|
+
|
|
1558
|
+
if (!route.isRoot) {
|
|
1559
|
+
href = `${brandPageSlugRoute}${brandPageSlugRoute === '/' ? '' : '/'}${route.relativePath}`;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
return `<a ${existingAttrsStart}href="${href}"${existingAttrsEnd}>`;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// fallback to root if pageid cannot be resolved.
|
|
1566
|
+
return `<a ${existingAttrsStart}href="${brandPageSlugRoute}"${existingAttrsEnd}>`;
|
|
1567
|
+
}
|
|
1634
1568
|
|
|
1635
|
-
return
|
|
1636
|
-
? match
|
|
1637
|
-
: `<a ${existingAttrsStart}href="${resolved}"${existingAttrsEnd}>`;
|
|
1569
|
+
return match;
|
|
1638
1570
|
},
|
|
1639
1571
|
);
|
|
1640
1572
|
}
|
|
@@ -41,7 +41,6 @@ import type {
|
|
|
41
41
|
BackgroundShape,
|
|
42
42
|
PersonalisationRole,
|
|
43
43
|
SaveContactOptions,
|
|
44
|
-
RichTextValue,
|
|
45
44
|
} from './models';
|
|
46
45
|
import type { ImagesSectionContentDisplayStyle } from './constants';
|
|
47
46
|
|
|
@@ -197,8 +196,8 @@ export interface TextImageSectionViewModel {
|
|
|
197
196
|
id: string;
|
|
198
197
|
type: 'images-section';
|
|
199
198
|
role: ContentType;
|
|
200
|
-
title:
|
|
201
|
-
description:
|
|
199
|
+
title: string;
|
|
200
|
+
description: string;
|
|
202
201
|
displayStyle: ImagesSectionContentDisplayStyle;
|
|
203
202
|
aspectRatio: AspectRatio;
|
|
204
203
|
areImagesFullWidth: boolean;
|
|
@@ -291,7 +291,7 @@ export default defineComponent({
|
|
|
291
291
|
|
|
292
292
|
// Capture baseline for undo/redo when the feature is active for this
|
|
293
293
|
// session — see useUndoRedoFlag.ts for the gate (LaunchDarkly flag +
|
|
294
|
-
//
|
|
294
|
+
// non-admin maker; all three brands).
|
|
295
295
|
if (useUndoRedoFlag().value) {
|
|
296
296
|
useHistoryStore().captureBaseline();
|
|
297
297
|
}
|
|
@@ -8,7 +8,18 @@ import {
|
|
|
8
8
|
watch,
|
|
9
9
|
} from 'vue';
|
|
10
10
|
import { Editor } from '@tiptap/vue-3';
|
|
11
|
-
import
|
|
11
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
12
|
+
import Underline from '@tiptap/extension-underline';
|
|
13
|
+
import TextStyle from '@tiptap/extension-text-style';
|
|
14
|
+
import TextAlign from '@tiptap/extension-text-align';
|
|
15
|
+
import FontFamily from '@tiptap/extension-font-family';
|
|
16
|
+
import ColorExtension from '@tiptap/extension-color';
|
|
17
|
+
import { FontSize } from './extensions/FontSize';
|
|
18
|
+
import { LetterSpacing } from './extensions/LetterSpacing';
|
|
19
|
+
import { LineHeight } from './extensions/LineHeight';
|
|
20
|
+
import Highlight from '@tiptap/extension-highlight';
|
|
21
|
+
import Placeholder from '@tiptap/extension-placeholder';
|
|
22
|
+
import Link from '@tiptap/extension-link';
|
|
12
23
|
import type { StyleTextFontFamilyOption } from '@shared/style-settings/models';
|
|
13
24
|
import { createFormattingActions } from './formatting';
|
|
14
25
|
import { useTextAlign } from './textAlign';
|
|
@@ -20,7 +31,7 @@ import {
|
|
|
20
31
|
emToPx,
|
|
21
32
|
isStyleChanged,
|
|
22
33
|
pxToEm,
|
|
23
|
-
|
|
34
|
+
sanitiseClientSideRTE,
|
|
24
35
|
skipStyleDetection,
|
|
25
36
|
} from '@brand-page-maker/utils';
|
|
26
37
|
import type { HyperlinkType, TextStyles } from '@brand-page';
|
|
@@ -38,7 +49,9 @@ import {
|
|
|
38
49
|
import { useUiContentSettingsStore } from '@shared/content-settings/store';
|
|
39
50
|
import { useHistoryStore } from '@brand-page-maker/stores/history/history-store';
|
|
40
51
|
import { AllSelection } from 'prosemirror-state';
|
|
41
|
-
import {
|
|
52
|
+
import { MaxLength } from './extensions/MaxLength';
|
|
53
|
+
import { AutoLink } from './extensions/AutoLink';
|
|
54
|
+
import { isHrefPageId, useHyperlink } from './hyperlink';
|
|
42
55
|
import { useBubbleMenu } from './bubbleMenu';
|
|
43
56
|
import {
|
|
44
57
|
hasLogoColorVar,
|
|
@@ -116,7 +129,44 @@ export const useExternalRichTextEditor = (): {
|
|
|
116
129
|
} => {
|
|
117
130
|
const externalEditor = new Editor({
|
|
118
131
|
editable: false,
|
|
119
|
-
extensions:
|
|
132
|
+
extensions: [
|
|
133
|
+
StarterKit,
|
|
134
|
+
Underline,
|
|
135
|
+
TextStyle.configure({ mergeNestedSpanStyles: true }),
|
|
136
|
+
TextAlign.configure({
|
|
137
|
+
types: ['heading', 'paragraph'],
|
|
138
|
+
}),
|
|
139
|
+
FontFamily.configure({
|
|
140
|
+
types: ['textStyle'],
|
|
141
|
+
}),
|
|
142
|
+
FontSize,
|
|
143
|
+
LetterSpacing,
|
|
144
|
+
LineHeight,
|
|
145
|
+
ColorExtension.configure({
|
|
146
|
+
types: ['textStyle'],
|
|
147
|
+
}),
|
|
148
|
+
Highlight.configure({
|
|
149
|
+
multicolor: true,
|
|
150
|
+
}),
|
|
151
|
+
Link.configure({
|
|
152
|
+
openOnClick: false,
|
|
153
|
+
defaultProtocol: 'https',
|
|
154
|
+
autolink: true,
|
|
155
|
+
HTMLAttributes: (attrs) => {
|
|
156
|
+
const href = attrs.href;
|
|
157
|
+
const isExternal =
|
|
158
|
+
href && (href.startsWith('http://') || href.startsWith('https://'));
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
rel: 'noopener noreferrer',
|
|
162
|
+
target: isExternal ? '_blank' : null,
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
isAllowedUri: (url, ctx) =>
|
|
166
|
+
ctx.defaultValidate(url) || isHrefPageId(url),
|
|
167
|
+
}),
|
|
168
|
+
AutoLink,
|
|
169
|
+
],
|
|
120
170
|
});
|
|
121
171
|
|
|
122
172
|
return {
|
|
@@ -168,13 +218,54 @@ export const useRichTextEditor = (
|
|
|
168
218
|
const editor = new Editor({
|
|
169
219
|
content: model.value ?? '',
|
|
170
220
|
editable: !props.readOnly,
|
|
171
|
-
extensions:
|
|
172
|
-
|
|
173
|
-
|
|
221
|
+
extensions: [
|
|
222
|
+
StarterKit.configure({
|
|
223
|
+
bulletList: false,
|
|
224
|
+
orderedList: false,
|
|
225
|
+
listItem: false,
|
|
226
|
+
horizontalRule: false,
|
|
227
|
+
codeBlock: false,
|
|
228
|
+
code: false,
|
|
229
|
+
}),
|
|
230
|
+
Underline,
|
|
231
|
+
TextStyle.configure({ mergeNestedSpanStyles: true }),
|
|
232
|
+
TextAlign.configure({
|
|
233
|
+
types: ['heading', 'paragraph'],
|
|
234
|
+
}),
|
|
235
|
+
FontFamily.configure({
|
|
236
|
+
types: ['textStyle'],
|
|
237
|
+
}),
|
|
238
|
+
FontSize,
|
|
239
|
+
LetterSpacing,
|
|
240
|
+
LineHeight,
|
|
241
|
+
ColorExtension.configure({
|
|
242
|
+
types: ['textStyle'],
|
|
243
|
+
}),
|
|
244
|
+
Highlight.configure({
|
|
245
|
+
multicolor: true,
|
|
246
|
+
}),
|
|
247
|
+
Placeholder.configure({
|
|
174
248
|
placeholder: props.placeholder,
|
|
175
|
-
|
|
176
|
-
},
|
|
177
|
-
|
|
249
|
+
}),
|
|
250
|
+
MaxLength.configure({ maxLength: props.maxLength }),
|
|
251
|
+
Link.configure({
|
|
252
|
+
openOnClick: false,
|
|
253
|
+
defaultProtocol: 'https',
|
|
254
|
+
autolink: true,
|
|
255
|
+
HTMLAttributes: (attrs) => {
|
|
256
|
+
const href = attrs.href;
|
|
257
|
+
const isExternal =
|
|
258
|
+
href && (href.startsWith('http://') || href.startsWith('https://'));
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
target: isExternal ? '_blank' : null,
|
|
262
|
+
};
|
|
263
|
+
},
|
|
264
|
+
isAllowedUri: (url, ctx) =>
|
|
265
|
+
ctx.defaultValidate(url) || isHrefPageId(url),
|
|
266
|
+
}),
|
|
267
|
+
AutoLink,
|
|
268
|
+
],
|
|
178
269
|
editorProps: {
|
|
179
270
|
handleDOMEvents: {
|
|
180
271
|
drop(view, event: DragEvent) {
|
|
@@ -385,13 +476,7 @@ export const useRichTextEditor = (
|
|
|
385
476
|
|
|
386
477
|
const isExternalMutation = updateRichTextEditor.value || appliedSinceLast;
|
|
387
478
|
|
|
388
|
-
|
|
389
|
-
// seen as "changed" against the editor's HTML (which would re-run
|
|
390
|
-
// setContent and reset the user's just-applied styles).
|
|
391
|
-
const hasChanged =
|
|
392
|
-
JSON.stringify(editor.getJSON()) !== JSON.stringify(newValue);
|
|
393
|
-
|
|
394
|
-
if (isExternalMutation && hasChanged) {
|
|
479
|
+
if (isExternalMutation && editor.getHTML() !== newValue) {
|
|
395
480
|
editor.commands.setContent(newValue, false);
|
|
396
481
|
updateRichTextEditor.value = false;
|
|
397
482
|
//text style type is not switching when reordering
|
|
@@ -414,12 +499,28 @@ export const useRichTextEditor = (
|
|
|
414
499
|
}
|
|
415
500
|
});
|
|
416
501
|
|
|
417
|
-
// Update model when editor content changes
|
|
418
|
-
// JSON (BP-5787). getJSON() is already in hand (the editor holds the doc) and
|
|
419
|
-
// is sanitised natively — no getHTML(), no DOMPurify, no extra editor — so
|
|
420
|
-
// the per-keystroke path allocates nothing heavy.
|
|
502
|
+
// Update model when editor content changes
|
|
421
503
|
editor.on('update', () => {
|
|
422
|
-
|
|
504
|
+
const sanitizedData = sanitiseClientSideRTE(editor.getHTML());
|
|
505
|
+
|
|
506
|
+
// An update fired while the editor is unfocused is TipTap rewriting the
|
|
507
|
+
// model back into its canonical HTML during mount rehydration or a
|
|
508
|
+
// programmatic setContent — not a user edit. Fold it into the current
|
|
509
|
+
// head so merely selecting a section doesn't push a phantom undo entry
|
|
510
|
+
// (BP-5810). The construction-time arm above lapses before this async
|
|
511
|
+
// write-back fires, so re-arm here. Genuine edits happen while focused and
|
|
512
|
+
// still push a new entry.
|
|
513
|
+
//
|
|
514
|
+
// Known limitation: a programmatic toolbar command run on an editor the
|
|
515
|
+
// user hasn't focused yet would also merge (losing that undo step). Today
|
|
516
|
+
// the RTE only mounts inside an already-open settings drawer and the user
|
|
517
|
+
// focuses it before formatting, so this doesn't occur — revisit if a
|
|
518
|
+
// consumer ever drives toolbar actions on an unfocused editor.
|
|
519
|
+
if (!editor.isFocused && !historyStore.isApplyingSnapshot) {
|
|
520
|
+
historyStore.mergeNextMutation();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
emit('update:modelValue', sanitizedData.sanitizedHtml);
|
|
423
524
|
});
|
|
424
525
|
|
|
425
526
|
editor.on('selectionUpdate', () => {
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
import { computed, type ComputedRef } from 'vue';
|
|
2
2
|
import { useBrandPageMakerUserStore } from '@brand-page-maker/stores/user';
|
|
3
|
-
import { useBrandPageMakerStore } from '@brand-page-maker/stores/brandpage-maker/brandpage-maker-store';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
|
-
* Returns whether undo/redo is active for the current maker session.
|
|
7
|
-
*
|
|
5
|
+
* Returns whether undo/redo is active for the current maker session. Both
|
|
6
|
+
* conditions must hold:
|
|
8
7
|
*
|
|
9
8
|
* 1. `userStore.features.canUseUndoRedo` is true. The flag is populated by
|
|
10
9
|
* the consumer (BrandCrowd.Net / web-renderer) via the
|
|
11
10
|
* `WebsiteDesignUserFeatures.CanUseUndoRedo` boolean on the ViewModel —
|
|
12
|
-
* same precedent as RichTextEditor / GettyVideos / AITextGenerator.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* 3. The session is NOT in admin-maker mode (`userStore.isAdmin`). The
|
|
11
|
+
* same precedent as RichTextEditor / GettyVideos / AITextGenerator. The
|
|
12
|
+
* one flag governs all three brands (brand-site, brand-page,
|
|
13
|
+
* brand-contact) — there is no per-brand gating.
|
|
14
|
+
* 2. The session is NOT in admin-maker mode (`userStore.isAdmin`). The
|
|
17
15
|
* admin maker uses the same components but a different editing flow
|
|
18
16
|
* (template authoring) where undo/redo is out of scope.
|
|
19
17
|
*/
|
|
20
18
|
export function useUndoRedoFlag(): ComputedRef<boolean> {
|
|
21
19
|
const userStore = useBrandPageMakerUserStore();
|
|
22
|
-
const brandPageMakerStore = useBrandPageMakerStore();
|
|
23
20
|
|
|
24
21
|
return computed(
|
|
25
|
-
() =>
|
|
26
|
-
userStore.features.canUseUndoRedo &&
|
|
27
|
-
brandPageMakerStore.isBrandSite &&
|
|
28
|
-
!userStore.isAdmin,
|
|
22
|
+
() => userStore.features.canUseUndoRedo && !userStore.isAdmin,
|
|
29
23
|
);
|
|
30
24
|
}
|