@webitel/ui-sdk 26.8.118 → 26.8.120
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/dist/{clients-DWcx-Mfk.js → clients-CwGTZ_pb.js} +1502 -170
- package/dist/{components-CeOlovz8.js → components-Bl2fDyms.js} +933 -948
- package/dist/ui-sdk.css +1 -1
- package/dist/ui-sdk.js +1 -1
- package/dist/ui-sdk.umd.cjs +2158 -2119
- package/dist/{wt-chat-emoji-v9gpIuHe.js → wt-chat-emoji-CQ-ZUYcm.js} +1 -1
- package/dist/{wt-display-chip-items-Bob7miL_.js → wt-display-chip-items-yuaxgXrU.js} +1 -1
- package/dist/wt-label-CiLpqe61.js +34 -0
- package/dist/wt-rich-text-editor-DyGnGabB.js +130 -0
- package/dist/{wt-send-message-popup-9O69XY0L.js → wt-send-message-popup-Ig3Uq6So.js} +2 -2
- package/dist/{wt-type-extension-value-input-55YmFcBp.js → wt-type-extension-value-input-B4fjVEic.js} +2 -2
- package/dist/{wt-vidstack-player-ETPSioGH.js → wt-vidstack-player-DitE8XQx.js} +1 -1
- package/package.json +3 -1
- package/src/components/index.js +14 -0
- package/src/components/on-demand/wt-rich-text-editor/__tests__/WtRichTextEditor.compat.spec.js +127 -0
- package/src/components/on-demand/wt-rich-text-editor/__tests__/WtRichTextEditor.spec.js +157 -0
- package/src/components/on-demand/wt-rich-text-editor/__tests__/loadTinyMce.spec.js +54 -0
- package/src/components/on-demand/wt-rich-text-editor/_internals/loadTinyMce.ts +31 -0
- package/src/components/on-demand/wt-rich-text-editor/_internals/wt-rich-text-editor-skeleton.vue +54 -0
- package/src/components/on-demand/wt-rich-text-editor/wt-rich-text-editor.vue +223 -0
- package/types/.tsbuildinfo +1 -1
- package/types/components/index.d.ts +7 -0
- package/types/components/on-demand/wt-call-media-action/wt-call-media-action.vue.d.ts +2 -2
- package/types/components/on-demand/wt-rich-text-editor/__tests__/WtRichTextEditor.compat.spec.d.ts +1 -0
- package/types/components/on-demand/wt-rich-text-editor/__tests__/WtRichTextEditor.spec.d.ts +1 -0
- package/types/components/on-demand/wt-rich-text-editor/__tests__/loadTinyMce.spec.d.ts +1 -0
- package/types/components/on-demand/wt-rich-text-editor/_internals/loadTinyMce.d.ts +1 -0
- package/types/components/on-demand/wt-rich-text-editor/_internals/wt-rich-text-editor-skeleton.vue.d.ts +3 -0
- package/types/components/on-demand/wt-rich-text-editor/wt-rich-text-editor.vue.d.ts +65 -0
- package/types/components/transitions/cases/wt-replace-transition.vue.d.ts +1 -1
- package/types/components/wt-datepicker/wt-datepicker.vue.d.ts +2 -2
- package/types/components/wt-input-text/wt-input-text.vue.d.ts +1 -1
- package/types/components/wt-player/wt-player.vue.d.ts +1 -1
- package/types/components/wt-search-bar/wt-search-bar.vue.d.ts +2 -2
- package/types/components/wt-table/wt-table.vue.d.ts +1 -1
- package/types/components/wt-textarea/wt-textarea.vue.d.ts +1 -1
- package/types/components/wt-time-input/wt-time-input.vue.d.ts +1 -1
package/src/components/on-demand/wt-rich-text-editor/_internals/wt-rich-text-editor-skeleton.vue
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- shown while the editor chunk (TinyMCE) downloads -->
|
|
3
|
+
<div
|
|
4
|
+
aria-busy="true"
|
|
5
|
+
class="wt-rich-text-editor-skeleton"
|
|
6
|
+
>
|
|
7
|
+
<div class="wt-rich-text-editor-skeleton__toolbar" />
|
|
8
|
+
<div class="wt-rich-text-editor-skeleton__body">
|
|
9
|
+
<div class="wt-rich-text-editor-skeleton__line" />
|
|
10
|
+
<div class="wt-rich-text-editor-skeleton__line" />
|
|
11
|
+
<div class="wt-rich-text-editor-skeleton__line wt-rich-text-editor-skeleton__line--short" />
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<style scoped>
|
|
17
|
+
.wt-rich-text-editor-skeleton {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
height: 200px;
|
|
21
|
+
cursor: progress;
|
|
22
|
+
border-radius: var(--border-radius);
|
|
23
|
+
box-shadow: var(--elevation-4);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.wt-rich-text-editor-skeleton__toolbar {
|
|
27
|
+
height: 40px;
|
|
28
|
+
box-shadow: var(--elevation-2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.wt-rich-text-editor-skeleton__body {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
gap: var(--spacing-xs);
|
|
35
|
+
padding: var(--spacing-sm);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.wt-rich-text-editor-skeleton__line {
|
|
39
|
+
height: 12px;
|
|
40
|
+
border-radius: var(--border-radius);
|
|
41
|
+
background: var(--skeleton-color, var(--dp-20-surface-color));
|
|
42
|
+
animation: wt-rich-text-editor-skeleton-pulse 1.2s ease-in-out infinite;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.wt-rich-text-editor-skeleton__line--short {
|
|
46
|
+
width: 60%;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@keyframes wt-rich-text-editor-skeleton-pulse {
|
|
50
|
+
50% {
|
|
51
|
+
opacity: 0.5;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="wt-rich-text-editor">
|
|
3
|
+
<wt-label
|
|
4
|
+
v-if="label || $slots.label"
|
|
5
|
+
:disabled="disabled"
|
|
6
|
+
:for="editorId"
|
|
7
|
+
v-bind="labelProps"
|
|
8
|
+
>
|
|
9
|
+
<!-- @slot Custom editor label -->
|
|
10
|
+
<slot
|
|
11
|
+
name="label"
|
|
12
|
+
v-bind="{ label }"
|
|
13
|
+
>
|
|
14
|
+
{{ label }}
|
|
15
|
+
</slot>
|
|
16
|
+
</wt-label>
|
|
17
|
+
<!-- tinymce-vue falls back to Tiny's cloud CDN when window.tinymce is
|
|
18
|
+
missing, so the editor only mounts once the self-hosted copy is in -->
|
|
19
|
+
<tiny-mce-editor
|
|
20
|
+
v-if="isTinyMceLoaded"
|
|
21
|
+
:id="editorId"
|
|
22
|
+
v-model="model"
|
|
23
|
+
:disabled="disabled"
|
|
24
|
+
:init="init"
|
|
25
|
+
:output-format="output"
|
|
26
|
+
:plugins="editorPlugins"
|
|
27
|
+
:toolbar="editorToolbar"
|
|
28
|
+
license-key="gpl"
|
|
29
|
+
@init="handleInit"
|
|
30
|
+
/>
|
|
31
|
+
<wt-rich-text-editor-skeleton v-else />
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
/**
|
|
37
|
+
* TinyMCE, self-hosted, loads with this component only: `components/index.js`
|
|
38
|
+
* registers it through `defineAsyncComponent`, and TinyMCE itself is loaded
|
|
39
|
+
* by `loadTinyMce` (in order) once the component is created.
|
|
40
|
+
*/
|
|
41
|
+
import TinyMceVueEditor from '@tinymce/tinymce-vue';
|
|
42
|
+
import type { Editor as TinyMceInstance } from 'tinymce';
|
|
43
|
+
import { computed, onBeforeUnmount, ref, useId } from 'vue';
|
|
44
|
+
|
|
45
|
+
import WtLabel from '../../wt-label/wt-label.vue';
|
|
46
|
+
import { loadTinyMce } from './_internals/loadTinyMce';
|
|
47
|
+
import WtRichTextEditorSkeleton from './_internals/wt-rich-text-editor-skeleton.vue';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* tinymce-vue binds the model only when it finds `onUpdate:modelValue` in its
|
|
51
|
+
* attrs. Apps on the @vue/compat runtime (cc-workspaces) rewrite `v-model` on
|
|
52
|
+
* any component not marked as Vue 3 into the Vue 2 `value` / `input` pair, so
|
|
53
|
+
* the listener never arrives and edits are lost (WTEL-4477). Marking the
|
|
54
|
+
* editor as Vue 3 keeps `v-model` intact; plain Vue 3 ignores the option.
|
|
55
|
+
*/
|
|
56
|
+
// A copy rather than `extends`: `extends` does not carry `setup()`, which is
|
|
57
|
+
// all tinymce-vue's Editor is.
|
|
58
|
+
const TinyMceEditor = {
|
|
59
|
+
...TinyMceVueEditor,
|
|
60
|
+
name: 'TinyMceEditor',
|
|
61
|
+
compatConfig: {
|
|
62
|
+
MODE: 3,
|
|
63
|
+
},
|
|
64
|
+
} as typeof TinyMceVueEditor;
|
|
65
|
+
|
|
66
|
+
type RichTextOutput = 'html' | 'text';
|
|
67
|
+
|
|
68
|
+
interface Props {
|
|
69
|
+
/**
|
|
70
|
+
* Editor label
|
|
71
|
+
*/
|
|
72
|
+
label?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Object with props, passed down to wt-label as props
|
|
75
|
+
*/
|
|
76
|
+
labelProps?: Record<string, unknown>;
|
|
77
|
+
/**
|
|
78
|
+
* `html` keeps the formatting toolbar; `text` edits (and emits) plain text
|
|
79
|
+
* @values html, text
|
|
80
|
+
*/
|
|
81
|
+
output?: RichTextOutput;
|
|
82
|
+
/**
|
|
83
|
+
* Editor height, in px
|
|
84
|
+
*/
|
|
85
|
+
height?: number | string;
|
|
86
|
+
disabled?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Placeholder shown while the editor is empty
|
|
89
|
+
*/
|
|
90
|
+
placeholder?: string;
|
|
91
|
+
/**
|
|
92
|
+
* TinyMCE toolbar, replacing the default for `output`
|
|
93
|
+
*/
|
|
94
|
+
toolbar?: string;
|
|
95
|
+
/**
|
|
96
|
+
* TinyMCE plugins, replacing the default for `output`. A plugin outside
|
|
97
|
+
* the bundled set (advlist, emoticons, fullscreen, image, link, lists,
|
|
98
|
+
* table) must be imported by the app.
|
|
99
|
+
*/
|
|
100
|
+
plugins?: string[];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Vue 3 semantics on the @vue/compat runtime too (cc-workspaces): otherwise
|
|
104
|
+
// compat rewrites this component's own `v-model` to `value` / `input` and
|
|
105
|
+
// defineModel below never sees it.
|
|
106
|
+
defineOptions({
|
|
107
|
+
compatConfig: {
|
|
108
|
+
MODE: 3,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
113
|
+
output: 'html',
|
|
114
|
+
height: 300,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @model modelValue — the content, as `output` formats it
|
|
119
|
+
*/
|
|
120
|
+
const model = defineModel<string>();
|
|
121
|
+
|
|
122
|
+
const editorId = useId();
|
|
123
|
+
|
|
124
|
+
const isTinyMceLoaded = ref(false);
|
|
125
|
+
loadTinyMce().then(() => {
|
|
126
|
+
isTinyMceLoaded.value = true;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const HTML_PLUGINS = [
|
|
130
|
+
'advlist',
|
|
131
|
+
'emoticons',
|
|
132
|
+
'fullscreen',
|
|
133
|
+
'image',
|
|
134
|
+
'link',
|
|
135
|
+
'lists',
|
|
136
|
+
'table',
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
const HTML_TOOLBAR = [
|
|
140
|
+
'undo redo',
|
|
141
|
+
'bold italic underline strikethrough',
|
|
142
|
+
'fullscreen',
|
|
143
|
+
'fontfamily fontsize',
|
|
144
|
+
'alignleft aligncenter alignright alignjustify',
|
|
145
|
+
'numlist bullist',
|
|
146
|
+
'forecolor backcolor removeformat',
|
|
147
|
+
'emoticons image link table',
|
|
148
|
+
].join(' | ');
|
|
149
|
+
|
|
150
|
+
const isHtml = computed(() => props.output === 'html');
|
|
151
|
+
|
|
152
|
+
const editorPlugins = computed(
|
|
153
|
+
() => props.plugins ?? (isHtml.value ? HTML_PLUGINS : []),
|
|
154
|
+
);
|
|
155
|
+
const editorToolbar = computed(
|
|
156
|
+
() => props.toolbar ?? (isHtml.value ? HTML_TOOLBAR : 'undo redo'),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// The editable area lives in an iframe, out of reach of the app's CSS
|
|
160
|
+
// variables, so its colours are read from the page and handed over.
|
|
161
|
+
function readColors() {
|
|
162
|
+
const styles = getComputedStyle(document.documentElement);
|
|
163
|
+
return {
|
|
164
|
+
backgroundColor: styles.getPropertyValue('--content-wrapper-color').trim(),
|
|
165
|
+
color: styles.getPropertyValue('--wt-text-field-text-color').trim(),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const init = computed(() => {
|
|
170
|
+
const { backgroundColor, color } = readColors();
|
|
171
|
+
return {
|
|
172
|
+
height: Number(props.height),
|
|
173
|
+
menubar: false,
|
|
174
|
+
statusbar: isHtml.value,
|
|
175
|
+
toolbar_mode: 'sliding' as const,
|
|
176
|
+
toolbar_sticky: true,
|
|
177
|
+
placeholder: props.placeholder,
|
|
178
|
+
// the skin CSS is imported above; content styles are set here
|
|
179
|
+
skin: false,
|
|
180
|
+
content_css: false,
|
|
181
|
+
content_style: `body { background-color: ${backgroundColor}; color: ${color}; }`,
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
let editor: TinyMceInstance | null = null;
|
|
186
|
+
|
|
187
|
+
// The app switches themes by toggling `theme--dark` on <html>; follow it.
|
|
188
|
+
const themeObserver = new MutationObserver(() => {
|
|
189
|
+
if (!editor?.getBody()) return;
|
|
190
|
+
editor.dom.setStyles(editor.getBody(), readColors());
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
function handleInit(_event: unknown, initialized: TinyMceInstance) {
|
|
194
|
+
editor = initialized;
|
|
195
|
+
themeObserver.observe(document.documentElement, {
|
|
196
|
+
attributes: true,
|
|
197
|
+
attributeFilter: [
|
|
198
|
+
'class',
|
|
199
|
+
],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
onBeforeUnmount(() => {
|
|
204
|
+
themeObserver.disconnect();
|
|
205
|
+
editor = null;
|
|
206
|
+
});
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<style scoped>
|
|
210
|
+
.wt-rich-text-editor :deep(.tox-toolbar__primary),
|
|
211
|
+
.wt-rich-text-editor :deep(.tox-editor-header) {
|
|
212
|
+
background-color: var(--content-wrapper-color);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.wt-rich-text-editor :deep(.tox-tbtn svg) {
|
|
216
|
+
fill: var(--icon-active-color);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.wt-rich-text-editor :deep(.tox-tbtn--disabled svg),
|
|
220
|
+
.wt-rich-text-editor :deep(.tox-tbtn--disabled:hover svg) {
|
|
221
|
+
fill: var(--icon-color);
|
|
222
|
+
}
|
|
223
|
+
</style>
|