@webitel/ui-sdk 26.8.119 → 26.8.121

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 (25) hide show
  1. package/dist/{clients-CjVXyqY8.js → clients-CwGTZ_pb.js} +1 -1
  2. package/dist/{components-jZ2GzWbv.js → components-Bl2fDyms.js} +709 -718
  3. package/dist/ui-sdk.css +1 -1
  4. package/dist/ui-sdk.js +1 -1
  5. package/dist/ui-sdk.umd.cjs +2117 -2117
  6. package/dist/{wt-chat-emoji-RHMx_k1y.js → wt-chat-emoji-CQ-ZUYcm.js} +1 -1
  7. package/dist/{wt-display-chip-items-D_iKQXSB.js → wt-display-chip-items-yuaxgXrU.js} +1 -1
  8. package/dist/wt-label-CiLpqe61.js +34 -0
  9. package/dist/wt-rich-text-editor-DyGnGabB.js +130 -0
  10. package/dist/{wt-send-message-popup-B2lqrtGF.js → wt-send-message-popup-Ig3Uq6So.js} +2 -2
  11. package/dist/{wt-type-extension-value-input-CJWynqhC.js → wt-type-extension-value-input-B4fjVEic.js} +2 -2
  12. package/dist/{wt-vidstack-player-BL6xL6yo.js → wt-vidstack-player-DitE8XQx.js} +1 -1
  13. package/package.json +1 -1
  14. package/src/components/on-demand/wt-rich-text-editor/__tests__/WtRichTextEditor.compat.spec.js +19 -13
  15. package/src/components/on-demand/wt-rich-text-editor/__tests__/WtRichTextEditor.spec.js +21 -18
  16. package/src/components/on-demand/wt-rich-text-editor/__tests__/loadTinyMce.spec.js +54 -0
  17. package/src/components/on-demand/wt-rich-text-editor/_internals/loadTinyMce.ts +31 -0
  18. package/src/components/on-demand/wt-rich-text-editor/wt-rich-text-editor.vue +15 -17
  19. package/src/modules/AgentPdfs/components/agent-pdfs-tab.vue +6 -0
  20. package/types/.tsbuildinfo +1 -1
  21. package/types/components/on-demand/wt-rich-text-editor/__tests__/loadTinyMce.spec.d.ts +1 -0
  22. package/types/components/on-demand/wt-rich-text-editor/_internals/loadTinyMce.d.ts +1 -0
  23. package/types/components/on-demand/wt-rich-text-editor/wt-rich-text-editor.vue.d.ts +0 -13
  24. package/dist/wt-label-unpxAKnu.js +0 -28
  25. package/dist/wt-rich-text-editor-B-iHNpqD.js +0 -119
@@ -1,4 +1,4 @@
1
- import "./components-jZ2GzWbv.js";
1
+ import "./components-Bl2fDyms.js";
2
2
  import { t as e } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
3
  import { m as t } from "./enums-CsINFPBu.js";
4
4
  import { t as n } from "./wt-button-rUZKbswL.js";
@@ -1,4 +1,4 @@
1
- import { C as e, i as t } from "./components-jZ2GzWbv.js";
1
+ import { C as e, i as t } from "./components-Bl2fDyms.js";
2
2
  import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
3
  import { t as r } from "./utils-DHSDTqJG.js";
4
4
  import { Fragment as i, computed as a, createCommentVNode as o, createElementBlock as s, createTextVNode as c, createVNode as l, defineComponent as u, openBlock as d, renderList as f, renderSlot as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
@@ -0,0 +1,34 @@
1
+ import { t as e } from "./_plugin-vue_export-helper-B3ysoDQm.js";
2
+ import { createBlock as t, createCommentVNode as n, createElementBlock as r, createStaticVNode as i, createTextVNode as a, defineComponent as o, normalizeClass as s, openBlock as c, renderSlot as l, resolveComponent as u, toDisplayString as d, withCtx as f } from "vue";
3
+ //#region src/components/on-demand/wt-rich-text-editor/_internals/wt-rich-text-editor-skeleton.vue
4
+ var p = {}, m = {
5
+ "aria-busy": "true",
6
+ class: "wt-rich-text-editor-skeleton"
7
+ };
8
+ function h(e, t) {
9
+ return c(), r("div", m, [...t[0] ||= [i("<div class=\"wt-rich-text-editor-skeleton__toolbar\" data-v-0802d94d></div><div class=\"wt-rich-text-editor-skeleton__body\" data-v-0802d94d><div class=\"wt-rich-text-editor-skeleton__line\" data-v-0802d94d></div><div class=\"wt-rich-text-editor-skeleton__line\" data-v-0802d94d></div><div class=\"wt-rich-text-editor-skeleton__line wt-rich-text-editor-skeleton__line--short\" data-v-0802d94d></div></div>", 2)]]);
10
+ }
11
+ var g = /*#__PURE__*/ e(p, [["render", h], ["__scopeId", "data-v-0802d94d"]]), _ = /*#__PURE__*/ e(/* @__PURE__ */ o({
12
+ __name: "wt-label",
13
+ props: {
14
+ disabled: { type: Boolean },
15
+ invalid: { type: Boolean },
16
+ required: { type: Boolean },
17
+ hint: {}
18
+ },
19
+ setup(e) {
20
+ return (i, o) => {
21
+ let p = u("wt-hint");
22
+ return c(), r("label", { class: s([{
23
+ "wt-label--invalid": e.invalid,
24
+ "wt-label--disabled": e.disabled,
25
+ "wt-label--required": e.required
26
+ }, "wt-label typo-subtitle-2"]) }, [l(i.$slots, "default", {}, void 0, !0), e.hint ? (c(), t(p, { key: 0 }, {
27
+ default: f(() => [a(d(e.hint), 1)], void 0, !0),
28
+ _: 1
29
+ })) : n("", !0)], 2);
30
+ };
31
+ }
32
+ }), [["__scopeId", "data-v-7d80b998"]]);
33
+ //#endregion
34
+ export { g as n, _ as t };
@@ -0,0 +1,130 @@
1
+ import { n as e, t } from "./wt-label-CiLpqe61.js";
2
+ import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
+ import { computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createTextVNode as s, defineComponent as c, guardReactiveProps as l, mergeModels as u, mergeProps as d, normalizeProps as f, onBeforeUnmount as p, openBlock as m, ref as h, renderSlot as g, toDisplayString as _, unref as v, useId as y, useModel as b, withCtx as x } from "vue";
4
+ import S from "@tinymce/tinymce-vue";
5
+ //#region src/components/on-demand/wt-rich-text-editor/_internals/loadTinyMce.ts
6
+ var C = null;
7
+ function w() {
8
+ return C ??= import("tinymce/tinymce").then(() => Promise.all([
9
+ import("tinymce/icons/default"),
10
+ import("tinymce/models/dom"),
11
+ import("tinymce/themes/silver"),
12
+ import("tinymce/plugins/advlist"),
13
+ import("tinymce/plugins/emoticons"),
14
+ import("tinymce/plugins/emoticons/js/emojis"),
15
+ import("tinymce/plugins/fullscreen"),
16
+ import("tinymce/plugins/image"),
17
+ import("tinymce/plugins/link"),
18
+ import("tinymce/plugins/lists"),
19
+ import("tinymce/plugins/table"),
20
+ import("tinymce/skins/ui/oxide/skin.css")
21
+ ]).then(() => void 0)), C;
22
+ }
23
+ //#endregion
24
+ //#region src/components/on-demand/wt-rich-text-editor/wt-rich-text-editor.vue?vue&type=script&setup=true&lang.ts
25
+ var T = { class: "wt-rich-text-editor" }, E = /*#__PURE__*/ n(/* @__PURE__ */ c({
26
+ compatConfig: { MODE: 3 },
27
+ __name: "wt-rich-text-editor",
28
+ props: /*@__PURE__*/ u({
29
+ label: {},
30
+ labelProps: {},
31
+ output: { default: "html" },
32
+ height: { default: 300 },
33
+ disabled: { type: Boolean },
34
+ placeholder: {},
35
+ toolbar: {},
36
+ plugins: {}
37
+ }, {
38
+ modelValue: {},
39
+ modelModifiers: {}
40
+ }),
41
+ emits: ["update:modelValue"],
42
+ setup(n) {
43
+ let c = {
44
+ ...S,
45
+ name: "TinyMceEditor",
46
+ compatConfig: { MODE: 3 }
47
+ }, u = n, C = b(n, "modelValue"), E = y(), D = h(!1);
48
+ w().then(() => {
49
+ D.value = !0;
50
+ });
51
+ let O = [
52
+ "advlist",
53
+ "emoticons",
54
+ "fullscreen",
55
+ "image",
56
+ "link",
57
+ "lists",
58
+ "table"
59
+ ], k = [
60
+ "undo redo",
61
+ "bold italic underline strikethrough",
62
+ "fullscreen",
63
+ "fontfamily fontsize",
64
+ "alignleft aligncenter alignright alignjustify",
65
+ "numlist bullist",
66
+ "forecolor backcolor removeformat",
67
+ "emoticons image link table"
68
+ ].join(" | "), A = r(() => u.output === "html"), j = r(() => u.plugins ?? (A.value ? O : [])), M = r(() => u.toolbar ?? (A.value ? k : "undo redo"));
69
+ function N() {
70
+ let e = getComputedStyle(document.documentElement);
71
+ return {
72
+ backgroundColor: e.getPropertyValue("--content-wrapper-color").trim(),
73
+ color: e.getPropertyValue("--wt-text-field-text-color").trim()
74
+ };
75
+ }
76
+ let P = r(() => {
77
+ let { backgroundColor: e, color: t } = N();
78
+ return {
79
+ height: Number(u.height),
80
+ menubar: !1,
81
+ statusbar: A.value,
82
+ toolbar_mode: "sliding",
83
+ toolbar_sticky: !0,
84
+ placeholder: u.placeholder,
85
+ skin: !1,
86
+ content_css: !1,
87
+ content_style: `body { background-color: ${e}; color: ${t}; }`
88
+ };
89
+ }), F = null, I = new MutationObserver(() => {
90
+ F?.getBody() && F.dom.setStyles(F.getBody(), N());
91
+ });
92
+ function L(e, t) {
93
+ F = t, I.observe(document.documentElement, {
94
+ attributes: !0,
95
+ attributeFilter: ["class"]
96
+ });
97
+ }
98
+ return p(() => {
99
+ I.disconnect(), F = null;
100
+ }), (r, u) => (m(), o("div", T, [n.label || r.$slots.label ? (m(), i(t, d({ key: 0 }, n.labelProps, {
101
+ disabled: n.disabled,
102
+ for: v(E)
103
+ }), {
104
+ default: x(() => [g(r.$slots, "label", f(l({ label: n.label })), () => [s(_(n.label), 1)], !0)], void 0, !0),
105
+ _: 3
106
+ }, 16, ["disabled", "for"])) : a("", !0), D.value ? (m(), i(c, {
107
+ key: 1,
108
+ id: v(E),
109
+ modelValue: C.value,
110
+ "onUpdate:modelValue": u[0] ||= (e) => C.value = e,
111
+ disabled: n.disabled,
112
+ init: P.value,
113
+ "output-format": n.output,
114
+ plugins: j.value,
115
+ toolbar: M.value,
116
+ "license-key": "gpl",
117
+ onInit: L
118
+ }, null, 8, [
119
+ "id",
120
+ "modelValue",
121
+ "disabled",
122
+ "init",
123
+ "output-format",
124
+ "plugins",
125
+ "toolbar"
126
+ ])) : (m(), i(e, { key: 2 }))]));
127
+ }
128
+ }), [["__scopeId", "data-v-47d8ed9a"]]);
129
+ //#endregion
130
+ export { E as default };
@@ -1,6 +1,6 @@
1
- import { n as e } from "./components-jZ2GzWbv.js";
1
+ import { n as e } from "./components-Bl2fDyms.js";
2
2
  import "./convertDuration-CW5qzlwH.js";
3
- import { i as t, r as n } from "./clients-CjVXyqY8.js";
3
+ import { i as t, r as n } from "./clients-CwGTZ_pb.js";
4
4
  import { n as r } from "./vue-i18n-VBiROMZ4.js";
5
5
  import { n as i } from "./useEventBus-B-ZeklLu.js";
6
6
  import { createBlock as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeProps as l, onMounted as u, openBlock as d, ref as f, resolveComponent as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
@@ -1,8 +1,8 @@
1
1
  import { a as e, c as t, i as n, n as r, r as i, s as a, t as o } from "./dist-EXdgfpvN.js";
2
2
  import { n as s } from "./genClient-CvWlQ0TZ.js";
3
3
  import { a as c } from "./caseConverters-DGri4uJo.js";
4
- import { a as l, o as u, r as d, s as f } from "./components-jZ2GzWbv.js";
5
- import { a as p, n as m, t as h } from "./clients-CjVXyqY8.js";
4
+ import { a as l, o as u, r as d, s as f } from "./components-Bl2fDyms.js";
5
+ import { a as p, n as m, t as h } from "./clients-CwGTZ_pb.js";
6
6
  import { a as g, b as _, d as v, f as y, g as b, i as x, n as S, o as C, r as w, s as T, t as E, y as D } from "./enums-CsINFPBu.js";
7
7
  import { n as ee, t as te } from "./vue-i18n-VBiROMZ4.js";
8
8
  import "./wt-toast-pXV44Y3v.js";
@@ -1,4 +1,4 @@
1
- import { w as e } from "./components-jZ2GzWbv.js";
1
+ import { w as e } from "./components-Bl2fDyms.js";
2
2
  import "./convertDuration-CW5qzlwH.js";
3
3
  import { t } from "./_plugin-vue_export-helper-B3ysoDQm.js";
4
4
  import { m as n } from "./enums-CsINFPBu.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webitel/ui-sdk",
3
- "version": "26.8.119",
3
+ "version": "26.8.121",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "dev": "npm run docs:dev",
@@ -59,7 +59,7 @@ const { default: WtRichTextEditor } = await import(
59
59
  '../wt-rich-text-editor.vue'
60
60
  );
61
61
 
62
- function render(component, props) {
62
+ async function render(component, props) {
63
63
  const app = createApp({
64
64
  render: () => h(component, props),
65
65
  });
@@ -68,6 +68,8 @@ function render(component, props) {
68
68
  render: () => null,
69
69
  });
70
70
  app.mount(document.createElement('div'));
71
+ // the editor mounts once TinyMCE has loaded (mocked here: at once)
72
+ await new Promise((resolve) => setTimeout(resolve));
71
73
  return app;
72
74
  }
73
75
 
@@ -89,27 +91,31 @@ describe('WtRichTextEditor on @vue/compat', () => {
89
91
  seenModel.length = 0;
90
92
  });
91
93
 
92
- it('control: compat takes the model listener away from plain tinymce-vue', () => {
93
- render(TinyMceVueEditor, {
94
- modelValue: '',
95
- 'onUpdate:modelValue': () => {},
96
- }).unmount();
94
+ it('control: compat takes the model listener away from plain tinymce-vue', async () => {
95
+ (
96
+ await render(TinyMceVueEditor, {
97
+ modelValue: '',
98
+ 'onUpdate:modelValue': () => {},
99
+ })
100
+ ).unmount();
97
101
 
98
102
  expect(seenAttrs[0]).not.toContain('onUpdate:modelValue');
99
103
  });
100
104
 
101
- it('keeps the model listener on its editor', () => {
102
- render(WtRichTextEditor, {
103
- modelValue: '',
104
- 'onUpdate:modelValue': () => {},
105
- }).unmount();
105
+ it('keeps the model listener on its editor', async () => {
106
+ (
107
+ await render(WtRichTextEditor, {
108
+ modelValue: '',
109
+ 'onUpdate:modelValue': () => {},
110
+ })
111
+ ).unmount();
106
112
 
107
113
  expect(seenAttrs[0]).toContain('onUpdate:modelValue');
108
114
  });
109
115
 
110
- it('takes its own v-model on compat: the value in, edits out', () => {
116
+ it('takes its own v-model on compat: the value in, edits out', async () => {
111
117
  const onUpdate = vi.fn();
112
- render(WtRichTextEditor, {
118
+ await render(WtRichTextEditor, {
113
119
  modelValue: '<p>Seed</p>',
114
120
  'onUpdate:modelValue': onUpdate,
115
121
  });
@@ -1,4 +1,4 @@
1
- import { mount } from '@vue/test-utils';
1
+ import { flushPromises, mount } from '@vue/test-utils';
2
2
  import { nextTick } from 'vue';
3
3
 
4
4
  // TinyMCE cannot run in jsdom: its side-effect imports are stubbed, and the
@@ -48,10 +48,14 @@ const { default: WtRichTextEditor } = await import(
48
48
  '../wt-rich-text-editor.vue'
49
49
  );
50
50
 
51
- const mountEditor = (props = {}) =>
52
- mount(WtRichTextEditor, {
51
+ // the editor mounts once TinyMCE has loaded (mocked here: at once)
52
+ const mountEditor = async (props = {}) => {
53
+ const wrapper = mount(WtRichTextEditor, {
53
54
  props,
54
55
  });
56
+ await flushPromises();
57
+ return wrapper;
58
+ };
55
59
 
56
60
  const editorStub = (wrapper) =>
57
61
  wrapper.findComponent({
@@ -63,17 +67,16 @@ describe('WtRichTextEditor', () => {
63
67
  document.documentElement.classList.remove('theme--dark');
64
68
  });
65
69
 
66
- it('shows a label only when given one', () => {
67
- expect(mountEditor().find('.wt-label').exists()).toBe(false);
68
- expect(
69
- mountEditor({
70
- label: 'Reply',
71
- }).text(),
72
- ).toContain('Reply');
70
+ it('shows a label only when given one', async () => {
71
+ expect((await mountEditor()).find('.wt-label').exists()).toBe(false);
72
+ const labelled = await mountEditor({
73
+ label: 'Reply',
74
+ });
75
+ expect(labelled.text()).toContain('Reply');
73
76
  });
74
77
 
75
- it('edits html with the full toolbar by default', () => {
76
- const editor = editorStub(mountEditor());
78
+ it('edits html with the full toolbar by default', async () => {
79
+ const editor = editorStub(await mountEditor());
77
80
 
78
81
  expect(editor.props('outputFormat')).toBe('html');
79
82
  expect(editor.props('plugins')).toContain('table');
@@ -82,9 +85,9 @@ describe('WtRichTextEditor', () => {
82
85
  expect(editor.props('licenseKey')).toBe('gpl');
83
86
  });
84
87
 
85
- it('drops the formatting tools for plain text', () => {
88
+ it('drops the formatting tools for plain text', async () => {
86
89
  const editor = editorStub(
87
- mountEditor({
90
+ await mountEditor({
88
91
  output: 'text',
89
92
  }),
90
93
  );
@@ -94,9 +97,9 @@ describe('WtRichTextEditor', () => {
94
97
  expect(editor.props('init').statusbar).toBe(false);
95
98
  });
96
99
 
97
- it('takes toolbar and plugin overrides', () => {
100
+ it('takes toolbar and plugin overrides', async () => {
98
101
  const editor = editorStub(
99
- mountEditor({
102
+ await mountEditor({
100
103
  toolbar: 'bold italic | link',
101
104
  plugins: [
102
105
  'link',
@@ -111,7 +114,7 @@ describe('WtRichTextEditor', () => {
111
114
  });
112
115
 
113
116
  it('passes the model through both ways', async () => {
114
- const wrapper = mountEditor({
117
+ const wrapper = await mountEditor({
115
118
  modelValue: '<p>Hi</p>',
116
119
  });
117
120
  expect(editorStub(wrapper).props('modelValue')).toBe('<p>Hi</p>');
@@ -126,7 +129,7 @@ describe('WtRichTextEditor', () => {
126
129
  it('restyles the editable body when the app switches theme', async () => {
127
130
  const body = {};
128
131
  const setStyles = vi.fn();
129
- const wrapper = mountEditor();
132
+ const wrapper = await mountEditor();
130
133
 
131
134
  editorStub(wrapper).vm.$emit(
132
135
  'init',
@@ -0,0 +1,54 @@
1
+ const { loaded, track } = vi.hoisted(() => {
2
+ const loaded = [];
3
+ return {
4
+ loaded,
5
+ track: (name) => () => {
6
+ loaded.push(name);
7
+ return {};
8
+ },
9
+ };
10
+ });
11
+
12
+ vi.mock('tinymce/tinymce', track('core'));
13
+ vi.mock('tinymce/icons/default', track('icons'));
14
+ vi.mock('tinymce/models/dom', track('model'));
15
+ vi.mock('tinymce/themes/silver', track('theme'));
16
+ vi.mock('tinymce/plugins/advlist', track('advlist'));
17
+ vi.mock('tinymce/plugins/emoticons', track('emoticons'));
18
+ vi.mock('tinymce/plugins/emoticons/js/emojis', track('emojis'));
19
+ vi.mock('tinymce/plugins/fullscreen', track('fullscreen'));
20
+ vi.mock('tinymce/plugins/image', track('image'));
21
+ vi.mock('tinymce/plugins/link', track('link'));
22
+ vi.mock('tinymce/plugins/lists', track('lists'));
23
+ vi.mock('tinymce/plugins/table', track('table'));
24
+ vi.mock('tinymce/skins/ui/oxide/skin.css', track('skin'));
25
+
26
+ const { loadTinyMce } = await import('../_internals/loadTinyMce');
27
+
28
+ describe('loadTinyMce', () => {
29
+ it('evaluates the core before any add-on that registers on it', async () => {
30
+ await loadTinyMce();
31
+
32
+ expect(loaded[0]).toBe('core');
33
+ expect(loaded).toEqual(
34
+ expect.arrayContaining([
35
+ 'icons',
36
+ 'model',
37
+ 'theme',
38
+ 'table',
39
+ 'skin',
40
+ ]),
41
+ );
42
+ });
43
+
44
+ it('loads once, however many editors ask', async () => {
45
+ const count = loaded.length;
46
+
47
+ await Promise.all([
48
+ loadTinyMce(),
49
+ loadTinyMce(),
50
+ ]);
51
+
52
+ expect(loaded).toHaveLength(count);
53
+ });
54
+ });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Loads TinyMCE (self-hosted) once, core first.
3
+ *
4
+ * The icons, model, theme and plugins register themselves on the global
5
+ * `tinymce` the core sets, so the core must run before any of them. Plain
6
+ * side-effect imports do not guarantee that: in an app's production build
7
+ * the core — it touches `module` — ends up behind a lazy CommonJS wrapper,
8
+ * while the add-ons, plain IIFEs, are hoisted and run first, failing with
9
+ * "tinymce is not defined". Awaiting the core's import pins the order.
10
+ */
11
+ let loading: Promise<void> | null = null;
12
+
13
+ export function loadTinyMce(): Promise<void> {
14
+ loading ??= import('tinymce/tinymce').then(() =>
15
+ Promise.all([
16
+ import('tinymce/icons/default'),
17
+ import('tinymce/models/dom'),
18
+ import('tinymce/themes/silver'),
19
+ import('tinymce/plugins/advlist'),
20
+ import('tinymce/plugins/emoticons'),
21
+ import('tinymce/plugins/emoticons/js/emojis'),
22
+ import('tinymce/plugins/fullscreen'),
23
+ import('tinymce/plugins/image'),
24
+ import('tinymce/plugins/link'),
25
+ import('tinymce/plugins/lists'),
26
+ import('tinymce/plugins/table'),
27
+ import('tinymce/skins/ui/oxide/skin.css'),
28
+ ]).then(() => undefined),
29
+ );
30
+ return loading;
31
+ }
@@ -14,7 +14,10 @@
14
14
  {{ label }}
15
15
  </slot>
16
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 -->
17
19
  <tiny-mce-editor
20
+ v-if="isTinyMceLoaded"
18
21
  :id="editorId"
19
22
  v-model="model"
20
23
  :disabled="disabled"
@@ -25,33 +28,23 @@
25
28
  license-key="gpl"
26
29
  @init="handleInit"
27
30
  />
31
+ <wt-rich-text-editor-skeleton v-else />
28
32
  </div>
29
33
  </template>
30
34
 
31
35
  <script setup lang="ts">
32
36
  /**
33
- * TinyMCE, self-hosted. Everything below loads with this component only:
34
- * `components/index.js` registers it through `defineAsyncComponent`, so an
35
- * app that never renders an editor never downloads TinyMCE.
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.
36
40
  */
37
41
  import TinyMceVueEditor from '@tinymce/tinymce-vue';
38
42
  import type { Editor as TinyMceInstance } from 'tinymce';
39
- import 'tinymce/tinymce';
40
- import 'tinymce/icons/default';
41
- import 'tinymce/models/dom';
42
- import 'tinymce/themes/silver';
43
- import 'tinymce/plugins/advlist';
44
- import 'tinymce/plugins/emoticons';
45
- import 'tinymce/plugins/emoticons/js/emojis';
46
- import 'tinymce/plugins/fullscreen';
47
- import 'tinymce/plugins/image';
48
- import 'tinymce/plugins/link';
49
- import 'tinymce/plugins/lists';
50
- import 'tinymce/plugins/table';
51
- import 'tinymce/skins/ui/oxide/skin.css';
52
- import { computed, onBeforeUnmount, useId } from 'vue';
43
+ import { computed, onBeforeUnmount, ref, useId } from 'vue';
53
44
 
54
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';
55
48
 
56
49
  /**
57
50
  * tinymce-vue binds the model only when it finds `onUpdate:modelValue` in its
@@ -128,6 +121,11 @@ const model = defineModel<string>();
128
121
 
129
122
  const editorId = useId();
130
123
 
124
+ const isTinyMceLoaded = ref(false);
125
+ loadTinyMce().then(() => {
126
+ isTinyMceLoaded.value = true;
127
+ });
128
+
131
129
  const HTML_PLUGINS = [
132
130
  'advlist',
133
131
  'emoticons',
@@ -35,8 +35,12 @@
35
35
  :headers="shownHeaders"
36
36
  :selected="selected"
37
37
  sortable
38
+ resizable-columns
39
+ reorderable-columns
38
40
  @sort="updateSort"
39
41
  @update:selected="updateSelected"
42
+ @column-resize="columnResize"
43
+ @column-reorder="columnReorder"
40
44
  >
41
45
  <template #preview="{ item }">
42
46
  <pdf-status-preview
@@ -175,6 +179,8 @@ const {
175
179
  updateSort,
176
180
  hasFilter,
177
181
  addFilter,
182
+ columnResize,
183
+ columnReorder,
178
184
  } = tableStore;
179
185
 
180
186
  initialize();