@designcrowd/library.brand-page 5.10.9-data-id-2 → 5.10.9-sanitise-hyperlink-1

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 (72) hide show
  1. package/lib/src/brand-page/models/index.d.ts +5 -0
  2. package/lib/src/brand-page/models/index.js +4 -0
  3. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +1 -1
  4. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/content-settings-items.fixture.js +1 -0
  5. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/page-content-items.d.ts +1 -1
  6. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/page-content-items.js +2 -1
  7. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.d.ts +19 -0
  8. package/lib/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.js +31 -0
  9. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/AutoLink.d.ts +2 -0
  10. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/AutoLink.js +161 -0
  11. package/lib/src/brand-page-maker/composables/rich-text-editor/extensions/MaxLength.js +3 -3
  12. package/lib/src/brand-page-maker/composables/rich-text-editor/font.d.ts +1 -1
  13. package/lib/src/brand-page-maker/composables/rich-text-editor/font.js +0 -1
  14. package/lib/src/brand-page-maker/composables/rich-text-editor/formatting.d.ts +1 -1
  15. package/lib/src/brand-page-maker/composables/rich-text-editor/formatting.js +0 -1
  16. package/lib/src/brand-page-maker/composables/rich-text-editor/hyperlink.d.ts +19 -0
  17. package/lib/src/brand-page-maker/composables/rich-text-editor/hyperlink.js +87 -0
  18. package/lib/src/brand-page-maker/composables/rich-text-editor/spacing.d.ts +1 -1
  19. package/lib/src/brand-page-maker/composables/rich-text-editor/spacing.js +0 -1
  20. package/lib/src/brand-page-maker/composables/rich-text-editor/textAlign.d.ts +1 -1
  21. package/lib/src/brand-page-maker/composables/rich-text-editor/textAlign.js +0 -1
  22. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.d.ts +19 -3
  23. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +109 -22
  24. package/lib/src/brand-page-maker/models/button-group.model.d.ts +2 -0
  25. package/lib/src/brand-page-maker/utils/rich-text-editor.util.d.ts +15 -1
  26. package/lib/src/brand-page-maker/utils/rich-text-editor.util.js +282 -1
  27. package/lib/src/composables/useTranslate.js +2 -2
  28. package/lib/src/css/library.brand-page-brandCrowd.min.css +83 -57
  29. package/lib/src/css/library.brand-page-brandPage.min.css +83 -57
  30. package/lib/src/css/library.brand-page-designCom.min.css +83 -57
  31. package/lib/src/css/library.brand-page-preview.min.css +83 -57
  32. package/lib/src/index.d.ts +1 -0
  33. package/lib/src/index.mjs +24533 -22407
  34. package/lib/src/lib.js +1 -0
  35. package/lib/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.mixin.d.ts +11 -1
  36. package/lib/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.mixin.js +4 -0
  37. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +2 -0
  38. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.js +12 -7
  39. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +2 -0
  40. package/package.json +6 -3
  41. package/src/brand-page/components/banner-section/BannerSection.vue +6 -0
  42. package/src/brand-page/components/text/Text.vue +2 -0
  43. package/src/brand-page/components/text-wrapper/TextWrapper.vue +26 -8
  44. package/src/brand-page/models/index.ts +6 -0
  45. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +1 -1
  46. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/content-settings-items.fixture.ts +1 -0
  47. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/page-content-items.ts +2 -0
  48. package/src/brand-page-maker/components/rich-text-editor/RichTextEditor.vue +339 -46
  49. package/src/brand-page-maker/composables/rich-text-editor/bubbleMenu.ts +42 -0
  50. package/src/brand-page-maker/composables/rich-text-editor/extensions/AutoLink.ts +227 -0
  51. package/src/brand-page-maker/composables/rich-text-editor/extensions/MaxLength.test.ts +1 -1
  52. package/src/brand-page-maker/composables/rich-text-editor/extensions/MaxLength.ts +4 -15
  53. package/src/brand-page-maker/composables/rich-text-editor/font.ts +1 -1
  54. package/src/brand-page-maker/composables/rich-text-editor/formatting.ts +1 -1
  55. package/src/brand-page-maker/composables/rich-text-editor/hyperlink.ts +114 -0
  56. package/src/brand-page-maker/composables/rich-text-editor/spacing.ts +1 -1
  57. package/src/brand-page-maker/composables/rich-text-editor/textAlign.ts +1 -1
  58. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +160 -24
  59. package/src/brand-page-maker/models/button-group.model.ts +2 -0
  60. package/src/brand-page-maker/stores/brandpage-maker/__snapshots__/brandpage-maker-store.test.ts.snap +1 -0
  61. package/src/brand-page-maker/utils/rich-text-editor.util.test.ts +359 -0
  62. package/src/brand-page-maker/utils/rich-text-editor.util.ts +361 -1
  63. package/src/composables/useTranslate.ts +2 -2
  64. package/src/lib.ts +1 -0
  65. package/src/shared/common/SideBarSettingsHeader.vue +1 -1
  66. package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.mixin.ts +4 -0
  67. package/src/shared/content-settings/components/page-dropdown-field/PageDropdownField.vue +2 -2
  68. package/src/shared/content-settings/components/text-field/TextField.vue +1 -1
  69. package/src/shared/content-settings/components/text-items-field/TextItemsField.vue +2 -2
  70. package/src/shared/content-settings/components/textarea-field/TextareaField.vue +3 -3
  71. package/src/shared/content-settings/components/url-field/UrlField.mixin.ts +15 -10
  72. package/src/shared/content-settings/components/url-field/UrlField.vue +1 -0
@@ -207,6 +207,11 @@ export declare const BUTTON_LINK_TYPE: {
207
207
  readonly tel: "tel";
208
208
  };
209
209
  export type ButtonLinkType = keyof typeof BUTTON_LINK_TYPE;
210
+ export declare const HYPERLINK_TYPE: {
211
+ readonly url: "url";
212
+ readonly page: "page";
213
+ };
214
+ export type HyperlinkType = keyof typeof HYPERLINK_TYPE;
210
215
  export declare const URL_TYPE: {
211
216
  readonly url: "url";
212
217
  readonly email: "email";
@@ -136,6 +136,10 @@ export const BUTTON_LINK_TYPE = {
136
136
  mailTo: 'mailTo',
137
137
  tel: 'tel',
138
138
  };
139
+ export const HYPERLINK_TYPE = {
140
+ url: 'url',
141
+ page: 'page',
142
+ };
139
143
  export const URL_TYPE = {
140
144
  url: 'url',
141
145
  email: 'email',
@@ -171,7 +171,7 @@ export const BRAND_SITE = {
171
171
  color: 'logo-text-color',
172
172
  fontSize: '1.5rem',
173
173
  textAlign: 'center',
174
- fontFamily: 'Nunito',
174
+ fontFamily: 'Exo 2',
175
175
  lineHeight: '2rem',
176
176
  },
177
177
  colorReplacements: [
@@ -43,6 +43,7 @@ export const textContent = {
43
43
  textSize: {},
44
44
  textStyle: {
45
45
  textAlign: DEFAULT_TEXT_ALIGN,
46
+ fontSize: '2em',
46
47
  },
47
48
  textAlignment: {},
48
49
  textColor: {},
@@ -1,2 +1,2 @@
1
- export declare const brandPageContentsSampleData: (import("../../../../brand-page").ButtonContent | import("../../../../brand-page").ProfileContent | import("../../../../brand-page").LogoContent)[];
1
+ export declare const brandPageContentsSampleData: (import("../../../../brand-page").ButtonContent | import("../../../../brand-page").ProfileContent | import("../../../../brand-page").TextContent | import("../../../../brand-page").LogoContent)[];
2
2
  export declare const brandPageContentsSampleDataStarterKit: (import("../../../../brand-page").ButtonContent | import("../../../../brand-page").ProfileContent | import("../../../../brand-page").LogoContent)[];
@@ -1,7 +1,8 @@
1
- import { logoContent, profileContent, generalLink, facebookLink, websiteLink, emailLink, phoneLink, twitterLink, instagramLink, youtubeLink, linkedinLink, pinterestLink, twitchLink, tiktokLink, } from './content-settings-items.fixture';
1
+ import { logoContent, profileContent, generalLink, facebookLink, websiteLink, emailLink, phoneLink, twitterLink, instagramLink, youtubeLink, linkedinLink, pinterestLink, twitchLink, tiktokLink, textContent, } from './content-settings-items.fixture';
2
2
  export const brandPageContentsSampleData = [
3
3
  logoContent,
4
4
  profileContent,
5
+ textContent,
5
6
  generalLink,
6
7
  websiteLink,
7
8
  emailLink,
@@ -0,0 +1,19 @@
1
+ import type { Editor } from '@tiptap/vue-3';
2
+ import type { Ref } from 'vue';
3
+ export declare const useBubbleMenu: (editor: Editor, isHyperlinkPopupOpen?: Ref<boolean>) => {
4
+ shouldShow: ({ editor, state }: {
5
+ editor: any;
6
+ state: any;
7
+ }) => any;
8
+ tippyOptions: {
9
+ duration: number;
10
+ placement: "bottom";
11
+ interactive: boolean;
12
+ appendTo: "parent";
13
+ hideOnClick: boolean;
14
+ arrow: boolean;
15
+ offset: [number, number];
16
+ trigger: "manual";
17
+ zIndex: number;
18
+ };
19
+ };
@@ -0,0 +1,31 @@
1
+ export const useBubbleMenu = (editor, isHyperlinkPopupOpen) => {
2
+ const shouldShow = ({ editor, state }) => {
3
+ if (!editor || !state)
4
+ return false;
5
+ // Hide preview popup if the hyperlink popup is open
6
+ if (isHyperlinkPopupOpen?.value)
7
+ return false;
8
+ const { selection } = state;
9
+ // Only show bubble menu if there's a selection and it contains a link
10
+ if (!selection.empty) {
11
+ const linkAttrs = editor.getAttributes('link');
12
+ return linkAttrs.href && linkAttrs.href.trim() !== '';
13
+ }
14
+ return false;
15
+ };
16
+ const tippyOptions = {
17
+ duration: 100,
18
+ placement: 'bottom',
19
+ interactive: true,
20
+ appendTo: 'parent',
21
+ hideOnClick: false,
22
+ arrow: false,
23
+ offset: [0, 10],
24
+ trigger: 'manual',
25
+ zIndex: 10,
26
+ };
27
+ return {
28
+ shouldShow,
29
+ tippyOptions,
30
+ };
31
+ };
@@ -0,0 +1,2 @@
1
+ import { Extension } from '@tiptap/core';
2
+ export declare const AutoLink: Extension<any, any>;
@@ -0,0 +1,161 @@
1
+ import { Extension } from '@tiptap/core';
2
+ import { Plugin, PluginKey } from 'prosemirror-state';
3
+ const LINK_COLOR = '#0097d7';
4
+ const isLinkMark = (mark) => mark.type.name === 'link';
5
+ const isUnderlineMark = (mark) => mark.type.name === 'underline';
6
+ const isTextStyleMark = (mark) => mark.type.name === 'textStyle';
7
+ const isBlueColorMark = (mark) => isTextStyleMark(mark) && mark.attrs.color === LINK_COLOR;
8
+ const hasLinkMark = (marks) => marks.some(isLinkMark);
9
+ const hasUnderlineMark = (marks) => marks.some(isUnderlineMark);
10
+ const createStylesWithoutColor = (schema, textStyleMark) => {
11
+ const { color: _color, ...otherAttrs } = textStyleMark.attrs;
12
+ return Object.keys(otherAttrs).length > 0
13
+ ? schema.marks.textStyle?.create(otherAttrs)
14
+ : null;
15
+ };
16
+ const applyDefaultLinkStyling = (tr, schema, node, pos) => {
17
+ const range = { from: pos, to: pos + node.nodeSize };
18
+ let modified = false;
19
+ const textStyleMark = node.marks.find(isTextStyleMark);
20
+ // Always apply blue color to newly created links, regardless of existing colors
21
+ const attrs = { ...textStyleMark?.attrs, color: LINK_COLOR };
22
+ const colorMark = schema.marks.textStyle?.create(attrs);
23
+ if (colorMark) {
24
+ if (textStyleMark) {
25
+ tr.removeMark(range.from, range.to, textStyleMark);
26
+ }
27
+ tr.addMark(range.from, range.to, colorMark);
28
+ modified = true;
29
+ }
30
+ // Add underline to newly detected URLs (auto-links)
31
+ // This ensures typed URLs get the same styling as manually created links
32
+ if (!hasUnderlineMark(node.marks)) {
33
+ const underlineMark = schema.marks.underline?.create();
34
+ if (underlineMark) {
35
+ tr.addMark(range.from, range.to, underlineMark);
36
+ modified = true;
37
+ }
38
+ }
39
+ return modified;
40
+ };
41
+ const removeHyperlinkStyles = (tr, schema, node, pos) => {
42
+ const range = { from: pos, to: pos + node.nodeSize };
43
+ let modified = false;
44
+ // Only remove blue color that was added for link styling
45
+ const textStyleMark = node.marks.find(isBlueColorMark);
46
+ if (textStyleMark) {
47
+ tr.removeMark(range.from, range.to, textStyleMark);
48
+ const newTextStyleMark = createStylesWithoutColor(schema, textStyleMark);
49
+ if (newTextStyleMark) {
50
+ tr.addMark(range.from, range.to, newTextStyleMark);
51
+ }
52
+ modified = true;
53
+ }
54
+ // Don't automatically remove underlines from non-link text
55
+ // Users should be able to have underlined text that's not a link
56
+ return modified;
57
+ };
58
+ export const AutoLink = Extension.create({
59
+ name: 'autoLink',
60
+ addKeyboardShortcuts() {
61
+ return {
62
+ // Handle any character input at the end of links
63
+ Backspace: () => false, // Let default behavior handle backspace
64
+ };
65
+ },
66
+ addProseMirrorPlugins() {
67
+ return [
68
+ new Plugin({
69
+ key: new PluginKey('autoLinkStyling'),
70
+ props: {
71
+ handleTextInput(view, from, to, text) {
72
+ const { state } = view;
73
+ const { selection } = state;
74
+ const { $anchor } = selection;
75
+ // Only handle when typing at a cursor position (from === to)
76
+ if (from !== to) {
77
+ return false;
78
+ }
79
+ // Check if we have link marks at cursor position
80
+ const currentMarks = $anchor.marks();
81
+ const linkMark = currentMarks.find(isLinkMark);
82
+ if (linkMark) {
83
+ // Check if we're at the end of the link
84
+ const currentPos = $anchor.pos;
85
+ const nextPos = currentPos + 1;
86
+ const hasLinkAfter = nextPos <= state.doc.content.size &&
87
+ state.doc
88
+ .resolve(Math.min(nextPos, state.doc.content.size))
89
+ .marks()
90
+ .some((mark) => isLinkMark(mark) && mark.eq(linkMark));
91
+ if (!hasLinkAfter) {
92
+ // detach the link after the attached link mark
93
+ const preservedMarks = currentMarks
94
+ .filter((mark) => {
95
+ if (isLinkMark(mark))
96
+ return false;
97
+ // Only preserve underlines if there's no blue color (manually added underlines)
98
+ // Remove underlines that were part of link styling (with blue color)
99
+ if (isUnderlineMark(mark)) {
100
+ const hasBlueColor = currentMarks.some(isBlueColorMark);
101
+ return !hasBlueColor; // Keep underline only if no blue color
102
+ }
103
+ // Remove blue color styling but preserve other text styles
104
+ if (isBlueColorMark(mark)) {
105
+ return createStylesWithoutColor(state.schema, mark);
106
+ }
107
+ return true;
108
+ })
109
+ .filter(Boolean); // Insert text with only preserved marks
110
+ let tr = state.tr;
111
+ tr = tr.setStoredMarks(preservedMarks);
112
+ tr = tr.insertText(text, from, to);
113
+ tr = tr.setMeta('autoLinkDetached', true);
114
+ view.dispatch(tr);
115
+ return true;
116
+ }
117
+ }
118
+ return false;
119
+ },
120
+ },
121
+ appendTransaction(transactions, oldState, newState) {
122
+ const tr = newState.tr;
123
+ let modified = false;
124
+ const hasDocChanges = transactions.some((transaction) => transaction.docChanged);
125
+ if (!hasDocChanges)
126
+ return null;
127
+ const wasDetached = transactions.some((transaction) => transaction.getMeta('autoLinkDetached'));
128
+ // Apply default styling to links (unless just detached)
129
+ if (!wasDetached) {
130
+ newState.doc.descendants((node, pos) => {
131
+ if (!node.isText || !node.text || !hasLinkMark(node.marks))
132
+ return;
133
+ // Check if this is a newly created link by comparing with old state
134
+ const oldNode = pos < oldState.doc.nodeSize ? oldState.doc.nodeAt(pos) : null;
135
+ const wasAlreadyLink = oldNode && hasLinkMark(oldNode.marks);
136
+ // Only apply full styling (including underline) to newly created links
137
+ if (wasAlreadyLink === false) {
138
+ if (applyDefaultLinkStyling(tr, newState.schema, node, pos)) {
139
+ modified = true;
140
+ }
141
+ }
142
+ else {
143
+ // For existing links, preserve their current color and underline choices
144
+ // Don't override user customizations on existing links
145
+ }
146
+ });
147
+ }
148
+ // Clean up hyperlink styling from text without link marks
149
+ newState.doc.descendants((node, pos) => {
150
+ if (!node.isText || !node.text || hasLinkMark(node.marks))
151
+ return;
152
+ if (removeHyperlinkStyles(tr, newState.schema, node, pos)) {
153
+ modified = true;
154
+ }
155
+ });
156
+ return modified ? tr : null;
157
+ },
158
+ }),
159
+ ];
160
+ },
161
+ });
@@ -42,7 +42,7 @@ export const MaxLength = Extension.create({
42
42
  const text = node.text ?? '';
43
43
  const piece = text.slice(0, remaining);
44
44
  if (piece.length > 0) {
45
- nodes.push(node.type.create(node.attrs, node.type.schema.text(piece), node.marks));
45
+ nodes.push(node.type.schema.text(piece, node.marks));
46
46
  remaining -= piece.length;
47
47
  }
48
48
  }
@@ -64,7 +64,7 @@ export const MaxLength = Extension.create({
64
64
  const piece = (subNode.text ?? '').slice(0, remaining);
65
65
  if (piece.length === 0)
66
66
  return null;
67
- const newText = subNode.type.create(subNode.attrs, subNode.type.schema.text(piece), subNode.marks);
67
+ const newText = subNode.type.schema.text(piece, subNode.marks);
68
68
  remaining -= piece.length;
69
69
  return newText;
70
70
  }
@@ -77,7 +77,7 @@ export const MaxLength = Extension.create({
77
77
  });
78
78
  if (children.length === 0)
79
79
  return null;
80
- return subNode.type.create(subNode.attrs, Fragment.fromArray(children), subNode.marks);
80
+ return subNode.copy(Fragment.fromArray(children));
81
81
  }
82
82
  return null;
83
83
  };
@@ -1,5 +1,5 @@
1
1
  import { type Ref } from 'vue';
2
- import { Editor } from '@tiptap/vue-3';
2
+ import type { Editor } from '@tiptap/vue-3';
3
3
  interface DropdownRef {
4
4
  hideMenu: () => void;
5
5
  }
@@ -1,5 +1,4 @@
1
1
  import { ref } from 'vue';
2
- import { Editor } from '@tiptap/vue-3';
3
2
  import { emToPx, pxToEm, skipStyleDetection, } from '../../../brand-page-maker/utils';
4
3
  import debounce from 'lodash/debounce';
5
4
  export const useFont = (editor, fontSettings, dropdownRef) => {
@@ -1,4 +1,4 @@
1
- import { Editor } from '@tiptap/vue-3';
1
+ import type { Editor } from '@tiptap/vue-3';
2
2
  export declare const createFormattingActions: (editor: Editor) => {
3
3
  toggleBold: () => boolean;
4
4
  toggleItalic: () => boolean;
@@ -1,4 +1,3 @@
1
- import { Editor } from '@tiptap/vue-3';
2
1
  import { skipStyleDetection } from '../../../brand-page-maker/utils';
3
2
  export const createFormattingActions = (editor) => {
4
3
  return {
@@ -0,0 +1,19 @@
1
+ import type { Editor } from '@tiptap/vue-3';
2
+ export declare const useHyperlink: (editor: Editor) => {
3
+ hyperlinkValue: import("vue").WritableComputedRef<{
4
+ id: string;
5
+ name: string;
6
+ value: string;
7
+ }, {
8
+ id: string;
9
+ name: string;
10
+ value: string;
11
+ }>;
12
+ setHyperlinkUrl: (url: string) => void;
13
+ setHyperlinkUrlValue: (url: string) => void;
14
+ removeHyperlink: (clearUrlValue?: boolean) => void;
15
+ applyHyperlinkToEditor: (url: string, text?: string, selection?: {
16
+ from: number;
17
+ to: number;
18
+ }) => void;
19
+ };
@@ -0,0 +1,87 @@
1
+ import { ref, computed } from 'vue';
2
+ // Store original colors before applying hyperlinks
3
+ const originalColors = new WeakMap();
4
+ export const useHyperlink = (editor) => {
5
+ const hyperlinkUrl = ref('');
6
+ const storeOriginalColor = () => {
7
+ const currentTextStyle = editor.getAttributes('textStyle');
8
+ const originalColor = currentTextStyle.color || null;
9
+ // Store the original color using the editor instance as key
10
+ originalColors.set(editor, originalColor);
11
+ return originalColor;
12
+ };
13
+ const restoreOriginalColor = () => {
14
+ const originalColor = originalColors.get(editor);
15
+ if (originalColor) {
16
+ // Restore the original color
17
+ editor.chain().focus().setColor(originalColor).run();
18
+ }
19
+ else {
20
+ // No original color, remove color completely
21
+ editor.chain().focus().unsetColor().run();
22
+ }
23
+ // Clean up stored color
24
+ originalColors.delete(editor);
25
+ };
26
+ const setHyperlinkUrl = (url) => {
27
+ const safeUrl = typeof url === 'string' ? url : '';
28
+ hyperlinkUrl.value = safeUrl;
29
+ storeOriginalColor();
30
+ // Set the link, apply blue color, and add underline by default
31
+ editor
32
+ .chain()
33
+ .focus()
34
+ .setLink({ href: safeUrl })
35
+ .setColor('#0097d7')
36
+ .setUnderline()
37
+ .run();
38
+ };
39
+ const setHyperlinkUrlValue = (url) => {
40
+ hyperlinkUrl.value = typeof url === 'string' ? url : '';
41
+ };
42
+ const hyperlinkValue = computed({
43
+ get: () => ({
44
+ id: 'id',
45
+ name: 'url',
46
+ value: hyperlinkUrl.value,
47
+ }),
48
+ set: (v) => {
49
+ setHyperlinkUrl(v?.value || '');
50
+ },
51
+ });
52
+ const applyHyperlinkToEditor = (url, text, selection) => {
53
+ storeOriginalColor();
54
+ const selectedText = editor.chain().focus();
55
+ // Replace content with text if provided
56
+ if (text && text.trim()) {
57
+ const currentPos = selection?.from || editor.state.selection.from;
58
+ selectedText
59
+ .deleteSelection()
60
+ .insertContent(text)
61
+ .setTextSelection({
62
+ from: currentPos,
63
+ to: currentPos + text.length,
64
+ });
65
+ }
66
+ selectedText
67
+ .setLink({ href: url })
68
+ .setColor('#0097d7')
69
+ .setUnderline()
70
+ .run();
71
+ };
72
+ const removeHyperlink = (clearUrlValue = false) => {
73
+ editor.chain().focus().unsetLink().unsetUnderline().run();
74
+ // Restore original color (or remove color if there was none)
75
+ restoreOriginalColor();
76
+ if (clearUrlValue) {
77
+ setHyperlinkUrlValue('');
78
+ }
79
+ };
80
+ return {
81
+ hyperlinkValue,
82
+ setHyperlinkUrl,
83
+ setHyperlinkUrlValue,
84
+ removeHyperlink,
85
+ applyHyperlinkToEditor,
86
+ };
87
+ };
@@ -1,4 +1,4 @@
1
- import { Editor } from '@tiptap/vue-3';
1
+ import type { Editor } from '@tiptap/vue-3';
2
2
  export declare const useSpacing: (editor: Editor) => {
3
3
  letterSpacingValue: import("vue").WritableComputedRef<string, string>;
4
4
  lineHeightValue: import("vue").WritableComputedRef<string, string>;
@@ -1,5 +1,4 @@
1
1
  import { ref, computed } from 'vue';
2
- import { Editor } from '@tiptap/vue-3';
3
2
  import debounce from 'lodash/debounce';
4
3
  import { letterSpacingValueConvertor } from '../../../brand-page-maker/convertors';
5
4
  import { skipStyleDetection } from '../../../brand-page-maker/utils';
@@ -1,4 +1,4 @@
1
- import { Editor } from '@tiptap/vue-3';
1
+ import type { Editor } from '@tiptap/vue-3';
2
2
  export declare const useTextAlign: (editor: Editor) => {
3
3
  currentAlignment: import("vue").ComputedRef<{
4
4
  icon: string;
@@ -1,5 +1,4 @@
1
1
  import { ref, computed } from 'vue';
2
- import { Editor } from '@tiptap/vue-3';
3
2
  import { skipStyleDetection } from '../../../brand-page-maker/utils';
4
3
  export const useTextAlign = (editor) => {
5
4
  const alignmentSettings = {
@@ -1,7 +1,8 @@
1
1
  import { type ModelRef, type ComputedRef, type Ref, type WritableComputedRef } from 'vue';
2
2
  import { Editor } from '@tiptap/vue-3';
3
3
  import type { StyleTextFontFamilyOption } from '../../../shared/style-settings/models';
4
- import { TextStyles, type TextItem, type TextStyleDeclaration } from '../../../brand-page';
4
+ import type { TextStyles } from '../../../brand-page';
5
+ import { type TextItem, type TextStyleDeclaration } from '../../../brand-page';
5
6
  import type { ButtonGroupOption } from '../../../brand-page-maker/models/button-group.model';
6
7
  interface UseRichTextEditorReturn {
7
8
  editor: Editor;
@@ -28,12 +29,27 @@ interface UseRichTextEditorReturn {
28
29
  setInitialStyleSettings: (selectedTextStyle?: BrandPageCssStyle) => void;
29
30
  copyGlobalStyleSettings: (selectedTextStyle?: BrandPageCssStyle) => void;
30
31
  unsetAllMarks: () => void;
32
+ handleHyperlink: (action: 'toggle' | 'apply' | 'remove', data?: {
33
+ href?: string;
34
+ text?: string;
35
+ }) => void;
36
+ hyperlinkValue: ComputedRef<{
37
+ id: string;
38
+ name: string;
39
+ value: string;
40
+ }>;
41
+ setHyperlinkUrlValue: (url: string) => void;
42
+ shouldShow: ({ editor, state }: {
43
+ editor: any;
44
+ state: any;
45
+ }) => boolean;
46
+ tippyOptions: Record<string, any>;
31
47
  }
32
48
  interface DropdownInstance {
33
49
  hideMenu: () => void;
34
50
  }
35
51
  interface EditorOptionType {
36
- type: 'fontFamily' | 'fontSize' | 'fontWeight' | 'fontStyle' | 'textDecorationLine' | 'textDecorationStrike' | 'textColor' | 'backgroundColor' | 'lineHeight' | 'letterSpacing' | 'textAlign';
52
+ type: 'hyperlink' | 'fontFamily' | 'fontSize' | 'fontWeight' | 'fontStyle' | 'textDecorationLine' | 'textDecorationStrike' | 'textColor' | 'backgroundColor' | 'lineHeight' | 'letterSpacing' | 'textAlign';
37
53
  value: string | StyleTextFontFamilyOption;
38
54
  }
39
55
  export declare const useExternalRichTextEditor: () => {
@@ -56,5 +72,5 @@ export declare const useRichTextEditor: (model: ModelRef<any>, props: {
56
72
  selectedFontSizePx: number;
57
73
  selectedTextColor: string;
58
74
  selectedBackgroundColor: string;
59
- }, fontStyleDropdownRef: Ref<DropdownInstance | undefined, DropdownInstance | undefined>, selectedGlobalTextStyles: WritableComputedRef<TextStyles, string>, onSelectTextStyleType: (option: ButtonGroupOption) => void, updateRichTextEditor: Ref<boolean>, textStyleTypeOptions: ButtonGroupOption[]) => UseRichTextEditorReturn;
75
+ }, fontStyleDropdownRef: Ref<DropdownInstance | undefined, DropdownInstance | undefined>, selectedGlobalTextStyles: WritableComputedRef<TextStyles, string>, onSelectTextStyleType: (option: ButtonGroupOption) => void, updateRichTextEditor: Ref<boolean>, textStyleTypeOptions: ButtonGroupOption[], markedText: Ref<string>, isHyperlinkPopupOpen: Ref<boolean>) => UseRichTextEditorReturn;
60
76
  export {};