@dev.smartpricing/message-composer-layer 4.2.12 → 4.2.13-ux.0

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.
@@ -149,6 +149,7 @@ function resetForm() {
149
149
  backgroundColor: '#F3F4F6',
150
150
  contentBackgroundColor: '#FFFFFF',
151
151
  textColor: '#000000',
152
+ linkColor: undefined,
152
153
  fontFamily: 'Arial',
153
154
  }
154
155
  defaultButtonSettings.value = {
@@ -3,6 +3,7 @@ import type {
3
3
  BrandDefaultEmailSettings,
4
4
  BrandDefaultButtonSettings,
5
5
  } from '@dev.smartpricing/message-composer-utils/types'
6
+ import { DEFAULT_EMAIL_GLOBAL_SETTINGS } from '@dev.smartpricing/message-composer-utils/types'
6
7
  import { getPaletteSync } from 'colorthief'
7
8
 
8
9
  import FontFamilySelect from '~/components/Common/FontFamilySelect.vue'
@@ -169,7 +170,7 @@ function removeColor(index: number) {
169
170
  <!-- Default email colors -->
170
171
  <div class="space-y-2">
171
172
  <p class="text-sm font-medium">{{ $t('pages.brands.fields.default_email_colors') }}</p>
172
- <div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
173
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
173
174
  <ColorInput
174
175
  :brand-colors="colors"
175
176
  v-model="defaultEmailSettings.backgroundColor"
@@ -185,6 +186,12 @@ function removeColor(index: number) {
185
186
  v-model="defaultEmailSettings.textColor"
186
187
  :label="$t('pages.brands.fields.email_text_color')"
187
188
  />
189
+ <ColorInput
190
+ :brand-colors="colors"
191
+ v-model="defaultEmailSettings.linkColor"
192
+ :default-value="defaultEmailSettings.textColor ?? DEFAULT_EMAIL_GLOBAL_SETTINGS.textColor"
193
+ :label="$t('pages.brands.fields.email_link_color')"
194
+ />
188
195
  </div>
189
196
  </div>
190
197
 
@@ -9,6 +9,7 @@ import type {
9
9
  RefineTextResponse,
10
10
  } from '@dev.smartpricing/message-composer-utils/types'
11
11
  import {
12
+ buildLinkHTMLAttributes,
12
13
  convertToWhatsappMarkdown,
13
14
  getTipTapExtensions,
14
15
  sanitizeContent,
@@ -39,6 +40,7 @@ const props = withDefaults(
39
40
  horizontalRule?: boolean
40
41
  alignment?: boolean
41
42
  link?: boolean
43
+ linkColor?: string
42
44
  emoji?: boolean
43
45
  }>(),
44
46
  {
@@ -134,9 +136,12 @@ const starterKitConfig = computed(() => {
134
136
  link: props.link
135
137
  ? {
136
138
  openOnClick: false,
137
- HTMLAttributes: {
138
- class: 'text-primary-600 underline',
139
- },
139
+ // Callers that don't opt in with `linkColor` (e.g. ButtonSettings, whose
140
+ // links aren't part of the block-level linkColor feature) keep the old
141
+ // fixed styling instead of an unset `--link-color` custom property.
142
+ HTMLAttributes: props.linkColor
143
+ ? { class: 'underline', ...buildLinkHTMLAttributes({ mode: 'live' }) }
144
+ : { class: 'text-primary-600 underline' },
140
145
  }
141
146
  : false,
142
147
  heading: false,
@@ -373,7 +378,7 @@ defineExpose({
373
378
  </script>
374
379
 
375
380
  <template>
376
- <div :data-testid="richTextEditorTestIds.root">
381
+ <div :data-testid="richTextEditorTestIds.root" :style="{ '--link-color': linkColor }">
377
382
  <div class="rounded-md border border-accented overflow-hidden">
378
383
  <UEditor
379
384
  ref="editorRef"
@@ -18,6 +18,7 @@ const editor = useEditor({
18
18
  extensions: getTipTapExtensions({
19
19
  preset: 'email',
20
20
  dynamicValues: true,
21
+ linkColor: { mode: 'live' },
21
22
  }),
22
23
  })
23
24
 
@@ -47,6 +48,7 @@ defineOptions({
47
48
  fontFamily: block.settings.fontFamily ?? emailEditorStore.resolvedSettings.fontFamily,
48
49
  fontSize: `${block.settings.fontSize}px`,
49
50
  color: block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
51
+ '--link-color': block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
50
52
  }"
51
53
  >
52
54
  <EditorContent :editor="editor" class="pointer-events-none" />
@@ -23,6 +23,19 @@ const content = computed({
23
23
  },
24
24
  })
25
25
 
26
+ const resolvedLinkColor = computed(
27
+ () => props.block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
28
+ )
29
+
30
+ function matchTextColor() {
31
+ emailEditorStore.updateBlock(props.block.id, {
32
+ settings: {
33
+ ...props.block.settings,
34
+ linkColor: props.block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
35
+ },
36
+ })
37
+ }
38
+
26
39
  function resetTextStyle() {
27
40
  emailEditorStore.updateBlock(props.block.id, {
28
41
  settings: {
@@ -38,6 +51,7 @@ function resetColors() {
38
51
  settings: {
39
52
  ...props.block.settings,
40
53
  textColor: DEFAULT_FOOTER_SETTINGS.textColor,
54
+ linkColor: DEFAULT_FOOTER_SETTINGS.linkColor,
41
55
  backgroundColor: DEFAULT_FOOTER_SETTINGS.backgroundColor,
42
56
  },
43
57
  })
@@ -63,6 +77,7 @@ defineOptions({
63
77
  underline
64
78
  alignment
65
79
  link
80
+ :link-color="resolvedLinkColor"
66
81
  emoji
67
82
  dynamic-values
68
83
  />
@@ -113,6 +128,25 @@ defineOptions({
113
128
  :default-value="emailEditorStore.resolvedSettings.textColor"
114
129
  />
115
130
 
131
+ <!-- Link Color -->
132
+ <ColorInput
133
+ v-model="block.settings.linkColor"
134
+ :label="$t('editor.email.blocks.heading.settings.link')"
135
+ :default-value="emailEditorStore.resolvedSettings.linkColor"
136
+ :data-testid="footerSettingsTestIds.linkColorInput"
137
+ >
138
+ <template #trailing>
139
+ <UButton
140
+ size="xs"
141
+ color="neutral"
142
+ variant="ghost"
143
+ :label="$t('editor.email.blocks.heading.settings.match_text_color')"
144
+ :data-testid="footerSettingsTestIds.matchTextColorButton"
145
+ @click.stop="matchTextColor"
146
+ />
147
+ </template>
148
+ </ColorInput>
149
+
116
150
  <!-- Background Color -->
117
151
  <ColorInput
118
152
  v-model="block.settings.backgroundColor"
@@ -18,6 +18,7 @@ const editor = useEditor({
18
18
  extensions: getTipTapExtensions({
19
19
  preset: 'email',
20
20
  dynamicValues: true,
21
+ linkColor: { mode: 'live' },
21
22
  }),
22
23
  })
23
24
 
@@ -47,6 +48,7 @@ defineOptions({
47
48
  fontFamily: block.settings.fontFamily ?? emailEditorStore.resolvedSettings.fontFamily,
48
49
  fontSize: `${block.settings.fontSize}px`,
49
50
  color: block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
51
+ '--link-color': block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
50
52
  }"
51
53
  >
52
54
  <EditorContent :editor="editor" class="pointer-events-none" />
@@ -30,6 +30,19 @@ const content = computed({
30
30
  },
31
31
  })
32
32
 
33
+ const resolvedLinkColor = computed(
34
+ () => props.block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
35
+ )
36
+
37
+ function matchTextColor() {
38
+ emailEditorStore.updateBlock(props.block.id, {
39
+ settings: {
40
+ ...props.block.settings,
41
+ linkColor: props.block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
42
+ },
43
+ })
44
+ }
45
+
33
46
  const typeOptions = HEADING_TAGS.slice(0, 3).map((heading) => ({
34
47
  value: heading,
35
48
  label: t(`editor.email.blocks.text_types.${heading}`),
@@ -51,6 +64,7 @@ function resetColors() {
51
64
  settings: {
52
65
  ...props.block.settings,
53
66
  textColor: DEFAULT_HEADING_SETTINGS.textColor,
67
+ linkColor: DEFAULT_HEADING_SETTINGS.linkColor,
54
68
  backgroundColor: DEFAULT_HEADING_SETTINGS.backgroundColor,
55
69
  },
56
70
  })
@@ -83,6 +97,7 @@ defineOptions({
83
97
  underline
84
98
  alignment
85
99
  link
100
+ :link-color="resolvedLinkColor"
86
101
  emoji
87
102
  improve
88
103
  generate
@@ -146,6 +161,25 @@ defineOptions({
146
161
  :default-value="emailEditorStore.resolvedSettings.textColor"
147
162
  />
148
163
 
164
+ <!-- Link Color -->
165
+ <ColorInput
166
+ v-model="block.settings.linkColor"
167
+ :label="$t('editor.email.blocks.heading.settings.link')"
168
+ :default-value="emailEditorStore.resolvedSettings.linkColor"
169
+ :data-testid="headingSettingsTestIds.linkColorInput"
170
+ >
171
+ <template #trailing>
172
+ <UButton
173
+ size="xs"
174
+ color="neutral"
175
+ variant="ghost"
176
+ :label="$t('editor.email.blocks.heading.settings.match_text_color')"
177
+ :data-testid="headingSettingsTestIds.matchTextColorButton"
178
+ @click.stop="matchTextColor"
179
+ />
180
+ </template>
181
+ </ColorInput>
182
+
149
183
  <!-- Background Color -->
150
184
  <ColorInput
151
185
  v-model="block.settings.backgroundColor"
@@ -18,6 +18,7 @@ const editor = useEditor({
18
18
  extensions: getTipTapExtensions({
19
19
  preset: 'email',
20
20
  dynamicValues: true,
21
+ linkColor: { mode: 'live' },
21
22
  }),
22
23
  })
23
24
 
@@ -47,6 +48,7 @@ defineOptions({
47
48
  fontFamily: block.settings.fontFamily ?? emailEditorStore.resolvedSettings.fontFamily,
48
49
  fontSize: `${block.settings.fontSize}px`,
49
50
  color: block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
51
+ '--link-color': block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
50
52
  }"
51
53
  >
52
54
  <EditorContent :editor="editor" class="pointer-events-none" />
@@ -30,6 +30,19 @@ const content = computed({
30
30
  },
31
31
  })
32
32
 
33
+ const resolvedLinkColor = computed(
34
+ () => props.block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
35
+ )
36
+
37
+ function matchTextColor() {
38
+ emailEditorStore.updateBlock(props.block.id, {
39
+ settings: {
40
+ ...props.block.settings,
41
+ linkColor: props.block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
42
+ },
43
+ })
44
+ }
45
+
33
46
  const typeOptions = TEXT_TAGS.map((tag) => ({
34
47
  value: tag,
35
48
  label: t(`editor.email.blocks.text_types.${tag}`),
@@ -51,6 +64,7 @@ function resetColors() {
51
64
  settings: {
52
65
  ...props.block.settings,
53
66
  textColor: DEFAULT_PARAGRAPH_SETTINGS.textColor,
67
+ linkColor: DEFAULT_PARAGRAPH_SETTINGS.linkColor,
54
68
  backgroundColor: DEFAULT_PARAGRAPH_SETTINGS.backgroundColor,
55
69
  },
56
70
  })
@@ -85,6 +99,7 @@ defineOptions({
85
99
  horizontal-rule
86
100
  alignment
87
101
  link
102
+ :link-color="resolvedLinkColor"
88
103
  emoji
89
104
  improve
90
105
  generate
@@ -147,6 +162,25 @@ defineOptions({
147
162
  :default-value="emailEditorStore.resolvedSettings.textColor"
148
163
  />
149
164
 
165
+ <!-- Link Color -->
166
+ <ColorInput
167
+ v-model="block.settings.linkColor"
168
+ :label="$t('editor.email.blocks.heading.settings.link')"
169
+ :default-value="emailEditorStore.resolvedSettings.linkColor"
170
+ :data-testid="paragraphSettingsTestIds.linkColorInput"
171
+ >
172
+ <template #trailing>
173
+ <UButton
174
+ size="xs"
175
+ color="neutral"
176
+ variant="ghost"
177
+ :label="$t('editor.email.blocks.heading.settings.match_text_color')"
178
+ :data-testid="paragraphSettingsTestIds.matchTextColorButton"
179
+ @click.stop="matchTextColor"
180
+ />
181
+ </template>
182
+ </ColorInput>
183
+
150
184
  <!-- Background Color -->
151
185
  <ColorInput
152
186
  v-model="block.settings.backgroundColor"
@@ -17,6 +17,7 @@ const editor = useEditor({
17
17
  extensions: getTipTapExtensions({
18
18
  preset: 'email',
19
19
  dynamicValues: true,
20
+ linkColor: { mode: 'live' },
20
21
  }),
21
22
  })
22
23
 
@@ -45,6 +46,7 @@ defineOptions({
45
46
  fontFamily: block.settings.fontFamily ?? emailEditorStore.resolvedSettings.fontFamily,
46
47
  fontSize: `${block.settings.fontSize}px`,
47
48
  color: block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
49
+ '--link-color': block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
48
50
  textAlign: block.settings.alignment,
49
51
  }"
50
52
  >
@@ -24,6 +24,19 @@ const content = computed({
24
24
  },
25
25
  })
26
26
 
27
+ const resolvedLinkColor = computed(
28
+ () => props.block.settings.linkColor ?? emailEditorStore.resolvedSettings.linkColor,
29
+ )
30
+
31
+ function matchTextColor() {
32
+ emailEditorStore.updateBlock(props.block.id, {
33
+ settings: {
34
+ ...props.block.settings,
35
+ linkColor: props.block.settings.textColor ?? emailEditorStore.resolvedSettings.textColor,
36
+ },
37
+ })
38
+ }
39
+
27
40
  function resetTextStyle() {
28
41
  emailEditorStore.updateBlock(props.block.id, {
29
42
  settings: {
@@ -39,6 +52,7 @@ function resetColors() {
39
52
  settings: {
40
53
  ...props.block.settings,
41
54
  textColor: DEFAULT_UNSUBSCRIBE_LINK_SETTINGS.textColor,
55
+ linkColor: DEFAULT_UNSUBSCRIBE_LINK_SETTINGS.linkColor,
42
56
  backgroundColor: DEFAULT_UNSUBSCRIBE_LINK_SETTINGS.backgroundColor,
43
57
  },
44
58
  })
@@ -63,6 +77,7 @@ defineOptions({
63
77
  italic
64
78
  underline
65
79
  link
80
+ :link-color="resolvedLinkColor"
66
81
  dynamic-values
67
82
  :data-testid="unsubscribeLinkSettingsTestIds.contentEditor"
68
83
  />
@@ -113,6 +128,24 @@ defineOptions({
113
128
  :data-testid="unsubscribeLinkSettingsTestIds.textColorInput"
114
129
  />
115
130
 
131
+ <ColorInput
132
+ v-model="block.settings.linkColor"
133
+ :label="$t('editor.email.blocks.heading.settings.link')"
134
+ :default-value="emailEditorStore.resolvedSettings.linkColor"
135
+ :data-testid="unsubscribeLinkSettingsTestIds.linkColorInput"
136
+ >
137
+ <template #trailing>
138
+ <UButton
139
+ size="xs"
140
+ color="neutral"
141
+ variant="ghost"
142
+ :label="$t('editor.email.blocks.heading.settings.match_text_color')"
143
+ :data-testid="unsubscribeLinkSettingsTestIds.matchTextColorButton"
144
+ @click.stop="matchTextColor"
145
+ />
146
+ </template>
147
+ </ColorInput>
148
+
116
149
  <ColorInput
117
150
  v-model="block.settings.backgroundColor"
118
151
  :label="$t('editor.email.blocks.heading.settings.backgroundBlock')"
@@ -617,6 +617,8 @@ export const footerSettingsTestIds = {
617
617
  container: 'footer-settings-container',
618
618
  fontSizeField: 'footer-settings-font-size-field',
619
619
  fontSizeInput: 'footer-settings-font-size-input',
620
+ linkColorInput: 'footer-settings-link-color-input',
621
+ matchTextColorButton: 'footer-settings-match-text-color-button',
620
622
  } as const
621
623
 
622
624
  export const gridBlockTestIds = {
@@ -655,6 +657,8 @@ export const headingSettingsTestIds = {
655
657
  tagSelect: 'heading-settings-tag-select',
656
658
  fontSizeField: 'heading-settings-font-size-field',
657
659
  fontSizeInput: 'heading-settings-font-size-input',
660
+ linkColorInput: 'heading-settings-link-color-input',
661
+ matchTextColorButton: 'heading-settings-match-text-color-button',
658
662
  } as const
659
663
 
660
664
  export const htmlBlockTestIds = {
@@ -693,6 +697,8 @@ export const paragraphSettingsTestIds = {
693
697
  tagSelect: 'paragraph-settings-tag-select',
694
698
  fontSizeField: 'paragraph-settings-font-size-field',
695
699
  fontSizeInput: 'paragraph-settings-font-size-input',
700
+ linkColorInput: 'paragraph-settings-link-color-input',
701
+ matchTextColorButton: 'paragraph-settings-match-text-color-button',
696
702
  } as const
697
703
 
698
704
  export const socialsBlockTestIds = {
@@ -734,6 +740,8 @@ export const unsubscribeLinkSettingsTestIds = {
734
740
  fontSizeInput: 'unsubscribe-link-settings-font-size-input',
735
741
  textColorInput: 'unsubscribe-link-settings-text-color-input',
736
742
  backgroundColorInput: 'unsubscribe-link-settings-background-color-input',
743
+ linkColorInput: 'unsubscribe-link-settings-link-color-input',
744
+ matchTextColorButton: 'unsubscribe-link-settings-match-text-color-button',
737
745
  } as const
738
746
 
739
747
  export const previewByIdTestIds = {
@@ -266,6 +266,8 @@ export default {
266
266
  color: 'Farbe',
267
267
  content: 'Inhalt',
268
268
  font: 'Schriftart',
269
+ link: 'Link',
270
+ match_text_color: 'An Textfarbe anpassen',
269
271
  size: 'Größe',
270
272
  text: 'Text',
271
273
  textStyle: 'Textstil',
@@ -693,6 +695,7 @@ export default {
693
695
  default_font: 'Standardschriftart',
694
696
  email_bg_color: 'Hintergrund',
695
697
  email_content_bg_color: 'Inhalt-Hintergrund',
698
+ email_link_color: 'Link-Farbe',
696
699
  email_text_color: 'Text',
697
700
  extracting_colors: 'Farben werden aus dem Logo extrahiert...',
698
701
  logo: 'Logo',
@@ -260,6 +260,8 @@ export default {
260
260
  color: 'Color',
261
261
  content: 'Content',
262
262
  font: 'Font',
263
+ link: 'Link',
264
+ match_text_color: 'Match text color',
263
265
  size: 'Size',
264
266
  text: 'Text',
265
267
  textStyle: 'Text style',
@@ -682,6 +684,7 @@ export default {
682
684
  default_font: 'Default font',
683
685
  email_bg_color: 'Background',
684
686
  email_content_bg_color: 'Content background',
687
+ email_link_color: 'Link color',
685
688
  email_text_color: 'Text',
686
689
  extracting_colors: 'Extracting colors from logo...',
687
690
  logo: 'Logo',
@@ -261,6 +261,8 @@ export default {
261
261
  color: 'Color',
262
262
  content: 'Contenido',
263
263
  font: 'Fuente',
264
+ link: 'Enlace',
265
+ match_text_color: 'Igualar al color del texto',
264
266
  size: 'Tamaño',
265
267
  text: 'Texto',
266
268
  textStyle: 'Estilo del texto',
@@ -691,6 +693,7 @@ export default {
691
693
  default_font: 'Fuente predeterminada',
692
694
  email_bg_color: 'Fondo',
693
695
  email_content_bg_color: 'Fondo contenido',
696
+ email_link_color: 'Color de enlace',
694
697
  email_text_color: 'Texto',
695
698
  extracting_colors: 'Extrayendo colores del logo...',
696
699
  logo: 'Logo',
@@ -262,6 +262,8 @@ export default {
262
262
  color: 'Couleur',
263
263
  content: 'Contenu',
264
264
  font: 'Police',
265
+ link: 'Lien',
266
+ match_text_color: 'Assortir à la couleur du texte',
265
267
  size: 'Taille',
266
268
  text: 'Texte',
267
269
  textStyle: 'Style de texte',
@@ -695,6 +697,7 @@ export default {
695
697
  default_font: 'Police par défaut',
696
698
  email_bg_color: 'Fond',
697
699
  email_content_bg_color: 'Fond contenu',
700
+ email_link_color: 'Couleur du lien',
698
701
  email_text_color: 'Texte',
699
702
  extracting_colors: 'Extraction des couleurs du logo...',
700
703
  logo: 'Logo',
@@ -261,6 +261,8 @@ export default {
261
261
  color: 'Colore',
262
262
  content: 'Contenuto',
263
263
  font: 'Font',
264
+ link: 'Link',
265
+ match_text_color: 'Abbina al colore del testo',
264
266
  size: 'Dimensione',
265
267
  text: 'Testo',
266
268
  textStyle: 'Stile testo',
@@ -688,6 +690,7 @@ export default {
688
690
  default_font: 'Font predefinito',
689
691
  email_bg_color: 'Sfondo',
690
692
  email_content_bg_color: 'Sfondo contenuto',
693
+ email_link_color: 'Colore link',
691
694
  email_text_color: 'Testo',
692
695
  extracting_colors: 'Estrazione dei colori dal logo...',
693
696
  logo: 'Logo',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.smartpricing/message-composer-layer",
3
- "version": "4.2.12",
3
+ "version": "4.2.13-ux.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",
@@ -36,7 +36,7 @@
36
36
  "vue-router": "^5.0.7",
37
37
  "vue3-emoji-picker": "^1.1.8",
38
38
  "zod": "^4.4.3",
39
- "@dev.smartpricing/message-composer-utils": "4.2.12"
39
+ "@dev.smartpricing/message-composer-utils": "4.2.13-ux.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@nuxt/eslint": "^1.15.2",