@dative-gpi/foundation-shared-components 0.0.7 → 0.0.9

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 (139) hide show
  1. package/{models/FSButtons.ts → aliases/FSButton.ts} +24 -21
  2. package/aliases/index.ts +1 -0
  3. package/components/FSAutocompleteField.vue +207 -0
  4. package/components/FSBadge.vue +38 -0
  5. package/components/FSBreadcrumbs.vue +49 -55
  6. package/components/FSButton.vue +116 -101
  7. package/components/FSCalendar.vue +184 -0
  8. package/components/FSCalendarTwin.vue +412 -0
  9. package/components/FSCard.vue +77 -0
  10. package/components/FSCarousel.vue +63 -0
  11. package/components/FSCheckbox.vue +111 -104
  12. package/components/FSChip.vue +140 -0
  13. package/components/FSClock.vue +172 -0
  14. package/components/FSCol.vue +104 -98
  15. package/components/FSColor.vue +61 -64
  16. package/components/FSColorIcon.vue +67 -0
  17. package/components/FSContainer.vue +64 -0
  18. package/components/FSDateField.vue +211 -0
  19. package/components/FSDateRangeField.vue +225 -0
  20. package/components/FSDateTimeField.vue +257 -0
  21. package/components/FSDateTimeRangeField.vue +286 -0
  22. package/components/FSDialog.vue +103 -0
  23. package/components/FSDivider.vue +39 -0
  24. package/components/FSFadeOut.vue +49 -59
  25. package/components/FSFileButton.vue +245 -0
  26. package/components/FSHeaderButton.vue +17 -0
  27. package/components/FSIcon.vue +23 -23
  28. package/components/FSIconField.vue +232 -0
  29. package/components/FSImage.vue +142 -0
  30. package/components/FSLoadTile.vue +93 -0
  31. package/components/FSNumberField.vue +51 -53
  32. package/components/FSPasswordField.vue +99 -99
  33. package/components/FSRadio.vue +107 -110
  34. package/components/FSRadioGroup.vue +55 -57
  35. package/components/FSRemoveDialog.vue +123 -0
  36. package/components/FSRichTextField.vue +551 -0
  37. package/components/FSRow.vue +110 -104
  38. package/components/FSSearchField.vue +114 -105
  39. package/components/FSSelectField.vue +188 -0
  40. package/components/FSSlideGroup.vue +45 -49
  41. package/components/FSSlider.vue +130 -0
  42. package/components/FSSpan.vue +53 -37
  43. package/components/FSSubmitDialog.vue +165 -0
  44. package/components/FSSwitch.vue +110 -109
  45. package/components/FSTab.vue +61 -61
  46. package/components/FSTabs.vue +53 -55
  47. package/components/FSTag.vue +88 -84
  48. package/components/FSTagField.vue +183 -128
  49. package/components/FSTagGroup.vue +38 -45
  50. package/components/FSText.vue +74 -64
  51. package/components/FSTextArea.vue +209 -0
  52. package/components/FSTextField.vue +152 -149
  53. package/components/FSTile.vue +90 -0
  54. package/components/FSToggleSet.vue +282 -0
  55. package/components/FSTooltip.vue +21 -0
  56. package/components/FSWindow.vue +26 -16
  57. package/components/FSWrapGroup.vue +44 -47
  58. package/components/deviceOrganisations/FSConnectivity.vue +114 -0
  59. package/components/deviceOrganisations/FSStatus.vue +117 -0
  60. package/components/deviceOrganisations/FSStatusesCarousel.vue +105 -0
  61. package/components/deviceOrganisations/FSStatusesRow.vue +66 -0
  62. package/components/deviceOrganisations/FSWorstAlert.vue +165 -0
  63. package/components/lists/FSDataIteratorGroup.vue +7 -0
  64. package/components/lists/FSDataIteratorItem.vue +103 -0
  65. package/components/lists/FSDataTable.vue +964 -0
  66. package/components/lists/FSFilterButton.vue +176 -0
  67. package/components/lists/FSHeaderButton.vue +99 -0
  68. package/components/lists/FSHiddenButton.vue +79 -0
  69. package/components/tiles/FSDeviceOrganisationTileUI.vue +232 -0
  70. package/components/tiles/FSGroupTileUI.vue +192 -0
  71. package/composables/index.ts +2 -1
  72. package/composables/useBreakpoints.ts +33 -0
  73. package/composables/useColors.ts +53 -23
  74. package/composables/useSlots.ts +43 -0
  75. package/index.ts +6 -0
  76. package/models/breadcrumbs.ts +8 -0
  77. package/models/colors.ts +17 -0
  78. package/models/deviceAlerts.ts +10 -0
  79. package/models/deviceConnectivities.ts +11 -0
  80. package/models/deviceStatuses.ts +16 -0
  81. package/models/dispositions.ts +33 -0
  82. package/models/index.ts +9 -0
  83. package/models/modelStatuses.ts +11 -0
  84. package/models/rules.ts +50 -0
  85. package/models/toggleSets.ts +7 -0
  86. package/package.json +13 -4
  87. package/plugins/colorPlugin.ts +2 -2
  88. package/shims-plugin.d.ts +1 -1
  89. package/styles/components/fs_autocomplete_field.scss +123 -0
  90. package/styles/components/fs_button.scss +4 -14
  91. package/styles/components/fs_calendar.scss +138 -0
  92. package/styles/components/fs_card.scss +4 -0
  93. package/styles/components/fs_carousel.scss +4 -0
  94. package/styles/components/fs_chip.scss +33 -0
  95. package/styles/components/fs_clock.scss +43 -0
  96. package/styles/components/fs_col.scss +2 -0
  97. package/styles/components/fs_color_icon.scss +37 -0
  98. package/styles/components/fs_container.scss +16 -0
  99. package/styles/components/fs_data_iterator_item.scss +19 -0
  100. package/styles/components/fs_data_table.scss +97 -0
  101. package/styles/components/fs_date_field.scss +8 -0
  102. package/styles/components/fs_dialog.scss +30 -0
  103. package/styles/components/fs_divider.scss +5 -0
  104. package/styles/components/fs_fade_out.scss +10 -2
  105. package/styles/components/fs_filter_button.scss +12 -0
  106. package/styles/components/fs_header_button.scss +4 -0
  107. package/styles/components/fs_icon.scss +14 -4
  108. package/styles/components/fs_icon_field.scss +12 -0
  109. package/styles/components/fs_image.scss +7 -0
  110. package/styles/components/fs_load_tile.scss +49 -0
  111. package/styles/components/fs_password_field.scss +2 -2
  112. package/styles/components/fs_rich_text_field.scss +67 -0
  113. package/styles/components/fs_row.scss +4 -1
  114. package/styles/components/fs_select_field.scss +71 -0
  115. package/styles/components/fs_slide_group.scss +6 -0
  116. package/styles/components/fs_slider.scss +40 -0
  117. package/styles/components/fs_span.scss +8 -0
  118. package/styles/components/fs_submit_dialog.scss +9 -0
  119. package/styles/components/fs_tabs.scss +4 -0
  120. package/styles/components/fs_tag_field.scss +6 -8
  121. package/styles/components/fs_text_area.scss +105 -0
  122. package/styles/components/fs_text_field.scss +23 -15
  123. package/styles/components/fs_tile.scss +33 -0
  124. package/styles/components/fs_tooltip.scss +5 -0
  125. package/styles/components/fs_wrap_group.scss +7 -8
  126. package/styles/components/index.scss +26 -0
  127. package/styles/globals/breakpoints.scss +7 -0
  128. package/styles/globals/overrides.scss +20 -7
  129. package/styles/globals/text_fonts.scss +8 -8
  130. package/themes/default.ts +1 -11
  131. package/utils/css.ts +11 -0
  132. package/utils/icons.ts +75416 -0
  133. package/utils/index.ts +5 -0
  134. package/utils/levenshtein.ts +97 -0
  135. package/utils/lexical.ts +27 -0
  136. package/utils/sort.ts +9 -0
  137. package/composables/useTouch.ts +0 -9
  138. package/models/FSTags.ts +0 -8
  139. package/models/FSTextFields.ts +0 -17
@@ -0,0 +1,551 @@
1
+ <template>
2
+ <FSCol>
3
+ <FSRow v-if="!readonly">
4
+ <slot name="label">
5
+ <FSRow :wrap="false">
6
+ <FSSpan
7
+ v-if="$props.label"
8
+ class="fs-rich-text-field-label"
9
+ font="text-overline"
10
+ :style="style"
11
+ >
12
+ {{ $props.label }}
13
+ </FSSpan>
14
+ <FSSpan
15
+ v-if="$props.label && $props.required"
16
+ class="fs-rich-text-field-label"
17
+ style="margin-left: -8px;"
18
+ font="text-overline"
19
+ :ellipsis="false"
20
+ :style="style"
21
+ >
22
+ *
23
+ </FSSpan>
24
+ </FSRow>
25
+ </slot>
26
+ <v-spacer />
27
+ <template v-if="$props.editable">
28
+ <FSIcon
29
+ class="fs-rich-text-field-icon"
30
+ :color="toolbarColors.undo"
31
+ :style="style"
32
+ @click="editor.dispatchCommand(UNDO_COMMAND)"
33
+ >
34
+ mdi-undo-variant
35
+ </FSIcon>
36
+ <v-divider vertical />
37
+ <FSIcon
38
+ class="fs-rich-text-field-icon"
39
+ :style="style"
40
+ @click="formatText('h1')"
41
+ >
42
+ mdi-format-header-1
43
+ </FSIcon>
44
+ <FSIcon
45
+ class="fs-rich-text-field-icon"
46
+ :style="style"
47
+ @click="formatText('h2')"
48
+ >
49
+ mdi-format-header-2
50
+ </FSIcon>
51
+ <FSIcon
52
+ class="fs-rich-text-field-icon"
53
+ :style="style"
54
+ @click="formatText('h3')"
55
+ >
56
+ mdi-format-header-3
57
+ </FSIcon>
58
+ <FSIcon
59
+ class="fs-rich-text-field-icon"
60
+ :style="style"
61
+ @click="formatParagraph()"
62
+ >
63
+ mdi-format-paragraph
64
+ </FSIcon>
65
+ <v-divider vertical />
66
+ <FSIcon
67
+ class="fs-rich-text-field-icon"
68
+ :color="toolbarColors.bold"
69
+ :style="style"
70
+ @click="editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold')"
71
+ >
72
+ mdi-format-bold
73
+ </FSIcon>
74
+ <FSIcon
75
+ class="fs-rich-text-field-icon"
76
+ :color="toolbarColors.italic"
77
+ :style="style"
78
+ @click="editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic')"
79
+ >
80
+ mdi-format-italic
81
+ </FSIcon>
82
+ <FSIcon
83
+ class="fs-rich-text-field-icon"
84
+ :color="toolbarColors.underline"
85
+ :style="style"
86
+ @click="editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline')"
87
+ >
88
+ mdi-format-underline
89
+ </FSIcon>
90
+ <FSIcon
91
+ class="fs-rich-text-field-icon"
92
+ :color="toolbarColors.strikethrough"
93
+ :style="style"
94
+ @click="editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'strikethrough')"
95
+ >
96
+ mdi-format-strikethrough
97
+ </FSIcon>
98
+ <FSIcon
99
+ class="fs-rich-text-field-icon"
100
+ :color="toolbarColors.link"
101
+ :style="style"
102
+ @click="openLink"
103
+ >
104
+ mdi-link
105
+ </FSIcon>
106
+ <v-divider vertical />
107
+ <FSIcon
108
+ class="fs-rich-text-field-icon"
109
+ :style="style"
110
+ @click="editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'left')"
111
+ >
112
+ mdi-format-align-left
113
+ </FSIcon>
114
+ <FSIcon
115
+ class="fs-rich-text-field-icon"
116
+ :style="style"
117
+ @click="editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'center')"
118
+ >
119
+ mdi-format-align-center
120
+ </FSIcon>
121
+ <FSIcon
122
+ class="fs-rich-text-field-icon"
123
+ :style="style"
124
+ @click="editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'right')"
125
+ >
126
+ mdi-format-align-right
127
+ </FSIcon>
128
+ <FSIcon
129
+ class="fs-rich-text-field-icon"
130
+ :style="style"
131
+ @click="editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'justify')"
132
+ >
133
+ mdi-format-align-justify
134
+ </FSIcon>
135
+ </template>
136
+ </FSRow>
137
+ <div
138
+ class="fs-rich-text-field"
139
+ :id="id"
140
+ :style="style"
141
+ :contenteditable="!readonly && $props.editable"
142
+ />
143
+ <FSTextField
144
+ v-if="isLink && $props.editable"
145
+ v-model="linkUrl"
146
+ :hideHeader="true"
147
+ @keypress.enter.stop="toggleLink"
148
+ />
149
+ <slot name="description">
150
+ <FSSpan
151
+ v-if="!readonly && $props.description"
152
+ class="fs-rich-text-field-description"
153
+ font="text-underline"
154
+ :style="style"
155
+ >
156
+ {{ $props.description }}
157
+ </FSSpan>
158
+ </slot>
159
+ </FSCol>
160
+ </template>
161
+
162
+ <script lang="ts">
163
+ import { $createParagraphNode, $getSelection, $isElementNode, $isRangeSelection, $setSelection, CAN_UNDO_COMMAND, createEditor, ElementNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, ParagraphNode, UNDO_COMMAND } from "lexical";
164
+ import { $createHeadingNode, HeadingNode, HeadingTagType, registerRichText } from "@lexical/rich-text";
165
+ import { createEmptyHistoryState, registerHistory } from "@lexical/history";
166
+ import { computed, defineComponent, onMounted, PropType, ref } from "vue";
167
+ import { $createLinkNode, $isLinkNode, LinkNode } from "@lexical/link";
168
+ import { $wrapNodes } from "@lexical/selection";
169
+
170
+ import { useBreakpoints, useColors } from "@dative-gpi/foundation-shared-components/composables";
171
+ import { getAncestor, getSelectedNode } from "@dative-gpi/foundation-shared-components/utils";
172
+ import { ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
173
+
174
+ import FSTextField from "./FSTextField.vue";
175
+ import FSIcon from "./FSIcon.vue";
176
+ import FSCol from "./FSCol.vue";
177
+ import FSRow from "./FSRow.vue";
178
+
179
+ export default defineComponent({
180
+ name: "FSRichTextField",
181
+ components: {
182
+ FSTextField,
183
+ FSIcon,
184
+ FSCol,
185
+ FSRow
186
+ },
187
+ props: {
188
+ label: {
189
+ type: String,
190
+ required: false,
191
+ default: null
192
+ },
193
+ description: {
194
+ type: String,
195
+ required: false,
196
+ default: null
197
+ },
198
+ modelValue: {
199
+ type: String,
200
+ required: false,
201
+ default: null
202
+ },
203
+ linkColor: {
204
+ type: String as PropType<ColorBase>,
205
+ required: false,
206
+ default: ColorEnum.Primary
207
+ },
208
+ required: {
209
+ type: Boolean,
210
+ required: false,
211
+ default: false
212
+ },
213
+ rows: {
214
+ type: Number,
215
+ required: false,
216
+ default: 5
217
+ },
218
+ variant: {
219
+ type: String as PropType<"standard" | "readonly">,
220
+ required: false,
221
+ default: "standard"
222
+ },
223
+ editable: {
224
+ type: Boolean,
225
+ required: false,
226
+ default: true
227
+ }
228
+ },
229
+ emits: ["update:modelValue"],
230
+ setup(props, { emit }) {
231
+ const { isMobileSized } = useBreakpoints();
232
+
233
+ const linkColors = computed(()=> useColors().getColors(props.linkColor));
234
+ const lights = useColors().getColors(ColorEnum.Light);
235
+ const darks = useColors().getColors(ColorEnum.Dark);
236
+
237
+ const canUndo = ref(false);
238
+ const isLink = ref(false);
239
+ const isBold = ref(false);
240
+ const isItalic = ref(false);
241
+ const isUnderline = ref(false);
242
+ const isStrikethrough = ref(false);
243
+
244
+ const id = `${Math.random()}-editor`;
245
+
246
+ const linkUrl = ref("https://");
247
+
248
+ const config = {
249
+ namespace: "MyEditor",
250
+ theme: {
251
+ paragraph: 'text-body',
252
+ heading: {
253
+ h1: 'text-h1',
254
+ h2: 'text-h2',
255
+ h3: 'text-h3'
256
+ },
257
+ link: 'editor-link',
258
+ text: {
259
+ bold: 'editor-text-bold',
260
+ italic: 'editor-text-italic',
261
+ underline: 'editor-text-underline',
262
+ strikethrough: 'editor-text-strikethrough',
263
+ underlineStrikethrough: 'editor-text-underline-strikethrough'
264
+ }
265
+ },
266
+ nodes: [
267
+ HeadingNode,
268
+ LinkNode,
269
+ ParagraphNode
270
+ ],
271
+ onError: console.error
272
+ }
273
+
274
+ const editor = createEditor(config);
275
+
276
+ onMounted((): void => {
277
+ const contentEditableElement = document.getElementById(id);
278
+ editor.setRootElement(contentEditableElement);
279
+ registerRichText(editor);
280
+ registerHistory(editor, createEmptyHistoryState(), 250);
281
+
282
+ if (props.modelValue != null) {
283
+ editor.update((): void => {
284
+ editor.setEditorState(editor.parseEditorState(props.modelValue));
285
+ });
286
+ }
287
+ });
288
+
289
+ const readonly = computed((): boolean => {
290
+ return props.variant === "readonly";
291
+ });
292
+
293
+ const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
294
+ let minHeight: string | undefined = undefined;
295
+ const base = isMobileSized.value ? 30 : 42;
296
+ const row = isMobileSized.value ? 16 : 20;
297
+ if (props.rows > 1) {
298
+ minHeight = `${base + (props.rows - 1) * row}px`;
299
+ }
300
+ else {
301
+ minHeight = `${base}px`;
302
+ }
303
+
304
+ switch (props.variant) {
305
+ case "standard": {
306
+ if (!props.editable) {
307
+ return {
308
+ "--fs-rich-text-field-undo-cursor" : "default",
309
+ "--fs-rich-text-field-icon-cursor" : "default",
310
+ "--fs-rich-text-field-border-color" : lights.base,
311
+ "--fs-rich-text-field-color" : lights.dark,
312
+ "--fs-rich-text-field-active-border-color": lights.base,
313
+ "--fs-rich-text-field-link-color" : linkColors.value.light,
314
+ "--fs-rich-text-field-min-height" : minHeight
315
+ };
316
+ }
317
+ else {
318
+ return {
319
+ "--fs-rich-text-field-undo-cursor" : canUndo ? "pointer" : "default",
320
+ "--fs-rich-text-field-icon-cursor" : "pointer",
321
+ "--fs-rich-text-field-border-color" : lights.dark,
322
+ "--fs-rich-text-field-color" : darks.base,
323
+ "--fs-rich-text-field-active-border-color": darks.dark,
324
+ "--fs-rich-text-field-link-color" : linkColors.value.dark,
325
+ "--fs-rich-text-field-min-height" : minHeight
326
+ };
327
+ }
328
+ };
329
+ case "readonly": return {
330
+ "--fs-rich-text-field-border-color" : "transparent",
331
+ "--fs-rich-text-field-color" : darks.base,
332
+ "--fs-rich-text-field-active-border-color": "transparent",
333
+ "--fs-rich-text-field-link-color" : linkColors.value.dark,
334
+ "--fs-rich-text-field-min-height" : minHeight
335
+ }
336
+ }
337
+ });
338
+
339
+ const toolbarColors = computed((): {[code: string]: string} => {
340
+ if (props.editable) {
341
+ return {
342
+ undo: canUndo.value ? darks.base : lights.base,
343
+ bold: isBold.value ? darks.base : lights.base,
344
+ italic: isItalic.value ? darks.base : lights.base,
345
+ underline: isUnderline.value ? darks.base : lights.base,
346
+ strikethrough: isStrikethrough.value ? darks.base : lights.base,
347
+ link: isLink.value ? darks.base : lights.base
348
+ };
349
+ }
350
+ else {
351
+ return {
352
+ undo: lights.base,
353
+ bold: lights.base,
354
+ italic: lights.base,
355
+ underline: lights.base,
356
+ strikethrough: lights.base,
357
+ link: lights.base
358
+ };
359
+ }
360
+ });
361
+
362
+ const updateToolbar = (): void => {
363
+ const selection = $getSelection();
364
+ if ($isRangeSelection(selection)) {
365
+ isBold.value = selection.hasFormat("bold");
366
+ isItalic.value = selection.hasFormat("italic");
367
+ isUnderline.value = selection.hasFormat("underline");
368
+ isStrikethrough.value = selection.hasFormat("strikethrough");
369
+ isLink.value = $isLinkNode(getSelectedNode(selection)) || $isLinkNode(getSelectedNode(selection).getParent());
370
+ }
371
+ };
372
+
373
+ editor.registerUpdateListener(({ editorState }) => {
374
+ editorState.read(() => {
375
+ updateToolbar();
376
+ emit("update:modelValue", JSON.stringify(editorState.toJSON()));
377
+ });
378
+ });
379
+
380
+ editor.registerCommand(CAN_UNDO_COMMAND, (payload) => {
381
+ canUndo.value = payload;
382
+ return false;
383
+ }, 1);
384
+
385
+ const formatText = (type: HeadingTagType) => {
386
+ editor.update(() => {
387
+ const selection = $getSelection();
388
+
389
+ if ($isRangeSelection(selection)) {
390
+ $wrapNodes(selection, () => $createHeadingNode(type));
391
+ }
392
+ });
393
+ };
394
+
395
+ const formatParagraph = (): void => {
396
+ editor.update(() => {
397
+ const selection = $getSelection();
398
+
399
+ if ($isRangeSelection(selection)) {
400
+ $wrapNodes(selection, () => $createParagraphNode());
401
+ }
402
+ });
403
+ };
404
+
405
+ const openLink = (): void => {
406
+ if (!isLink.value) {
407
+ isLink.value = true;
408
+ }
409
+ else {
410
+ editor.update(() => {
411
+ const selection = $getSelection();
412
+
413
+ if ($isRangeSelection(selection)) {
414
+ toggleLink();
415
+ }
416
+ });
417
+ }
418
+ };
419
+
420
+ const toggleLink = (): void => {
421
+ editor.update(() => {
422
+ const target = "_blank";
423
+ const title = "";
424
+ const rel = "noreferrer";
425
+ const selection = $getSelection();
426
+ $setSelection(null);
427
+
428
+ const nodes = selection.extract();
429
+
430
+ if (linkUrl.value === null) {
431
+ // Remove LinkNodes
432
+ nodes.forEach((node) => {
433
+ const parent = node.getParent();
434
+
435
+ if ($isLinkNode(parent)) {
436
+ const children = parent.getChildren();
437
+
438
+ for (let i = 0; i < children.length; i++) {
439
+ parent.insertBefore(children[i]);
440
+ }
441
+
442
+ parent.remove();
443
+ }
444
+ });
445
+ }
446
+ else {
447
+ if (nodes.length === 1) {
448
+ const firstNode = nodes[0];
449
+ const linkNode = getAncestor(firstNode, $isLinkNode);
450
+
451
+ if (linkNode !== null) {
452
+ linkNode.setURL(linkUrl.value);
453
+ if (target !== undefined) {
454
+ linkNode.setTarget(target);
455
+ }
456
+ if (rel !== null) {
457
+ linkNode.setRel(rel);
458
+ }
459
+ if (title !== undefined) {
460
+ linkNode.setTitle(title);
461
+ }
462
+ return;
463
+ }
464
+ }
465
+
466
+ let prevParent: ElementNode | LinkNode | null = null;
467
+ let linkNode: LinkNode | null = null;
468
+
469
+ nodes.forEach((node) => {
470
+ const parent = node.getParent();
471
+
472
+ if ( parent === linkNode || parent === null || ($isElementNode(node) && !node.isInline())) {
473
+ return;
474
+ }
475
+
476
+ if ($isLinkNode(parent)) {
477
+ linkNode = parent;
478
+ parent.setURL(linkUrl.value);
479
+ if (target !== undefined) {
480
+ parent.setTarget(target);
481
+ }
482
+ if (rel !== null) {
483
+ linkNode.setRel(rel);
484
+ }
485
+ if (title !== undefined) {
486
+ linkNode.setTitle(title);
487
+ }
488
+ return;
489
+ }
490
+
491
+ if (!parent.is(prevParent)) {
492
+ prevParent = parent;
493
+ linkNode = $createLinkNode(linkUrl.value, {rel, target, title});
494
+
495
+ if ($isLinkNode(parent)) {
496
+ if (node.getPreviousSibling() === null) {
497
+ parent.insertBefore(linkNode);
498
+ }
499
+ else {
500
+ parent.insertAfter(linkNode);
501
+ }
502
+ }
503
+ else {
504
+ node.insertBefore(linkNode);
505
+ }
506
+ }
507
+
508
+ if ($isLinkNode(node)) {
509
+ if (node.is(linkNode)) {
510
+ return;
511
+ }
512
+ if (linkNode !== null) {
513
+ const children = node.getChildren();
514
+
515
+ for (let i = 0; i < children.length; i++) {
516
+ linkNode.append(children[i]);
517
+ }
518
+ }
519
+
520
+ node.remove();
521
+ return;
522
+ }
523
+
524
+ if (linkNode !== null) {
525
+ linkNode.append(node);
526
+ }
527
+ });
528
+ }
529
+ });
530
+ isLink.value = false;
531
+ }
532
+
533
+ return {
534
+ readonly,
535
+ style,
536
+ id,
537
+ editor,
538
+ isLink,
539
+ linkUrl,
540
+ toolbarColors,
541
+ openLink,
542
+ toggleLink,
543
+ formatText,
544
+ formatParagraph,
545
+ UNDO_COMMAND,
546
+ FORMAT_TEXT_COMMAND,
547
+ FORMAT_ELEMENT_COMMAND,
548
+ }
549
+ }
550
+ });
551
+ </script>