@juit/vue-tiny-editor 0.0.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.
package/LICENSE.md ADDED
@@ -0,0 +1,211 @@
1
+ # Apache License
2
+
3
+ Version 2.0, January 2004
4
+
5
+ [http://www.apache.org/licenses/](http://www.apache.org/licenses/)
6
+
7
+ ##### TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ ### 1. Definitions.
10
+
11
+ "**License**" shall mean the terms and conditions for use, reproduction,
12
+ and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "**Licensor**" shall mean the copyright owner or entity authorized by
15
+ the copyright owner that is granting the License.
16
+
17
+ "**Legal Entity**" shall mean the union of the acting entity and all
18
+ other entities that control, are controlled by, or are under common
19
+ control with that entity. For the purposes of this definition,
20
+ "**control**" means **(i)** the power, direct or indirect, to cause the
21
+ direction or management of such entity, whether by contract or
22
+ otherwise, _or_ **(ii)** ownership of fifty percent (50%) or more of
23
+ the outstanding shares, _or_ **(iii)** beneficial ownership of such
24
+ entity.
25
+
26
+ "**You**" (or "**Your**") shall mean an individual or Legal Entity
27
+ exercising permissions granted by this License.
28
+
29
+ "**Source**" form shall mean the preferred form for making
30
+ modifications, including but not limited to software source code,
31
+ documentation source, and configuration files.
32
+
33
+ "**Object**" form shall mean any form resulting from mechanical
34
+ transformation or translation of a Source form, including but not
35
+ limited to compiled object code, generated documentation, and
36
+ conversions to other media types.
37
+
38
+ "**Work**" shall mean the work of authorship, whether in Source or
39
+ Object form, made available under the License, as indicated by a
40
+ copyright notice that is included in or attached to the work (an example
41
+ is provided in the Appendix below).
42
+
43
+ "**Derivative Works**" shall mean any work, whether in Source or Object
44
+ form, that is based on (or derived from) the Work and for which the
45
+ editorial revisions, annotations, elaborations, or other modifications
46
+ represent, as a whole, an original work of authorship. For the purposes
47
+ of this License, Derivative Works shall not include works that remain
48
+ separable from, or merely link (or bind by name) to the interfaces of,
49
+ the Work and Derivative Works thereof.
50
+
51
+ "**Contribution**" shall mean any work of authorship, including the
52
+ original version of the Work and any modifications or additions to that
53
+ Work or Derivative Works thereof, that is intentionally submitted to
54
+ Licensor for inclusion in the Work by the copyright owner or by an
55
+ individual or Legal Entity authorized to submit on behalf of the
56
+ copyright owner. For the purposes of this definition, "**submitted**"
57
+ means any form of electronic, verbal, or written communication sent to
58
+ the Licensor or its representatives, including but not limited to
59
+ communication on electronic mailing lists, source code control systems,
60
+ and issue tracking systems that are managed by, or on behalf of, the
61
+ Licensor for the purpose of discussing and improving the Work, but
62
+ excluding communication that is conspicuously marked or otherwise
63
+ designated in writing by the copyright owner as "**Not a
64
+ Contribution**".
65
+
66
+ "**Contributor**" shall mean Licensor and any individual or Legal Entity
67
+ on behalf of whom a Contribution has been received by Licensor and
68
+ subsequently incorporated within the Work.
69
+
70
+ ### 2. Grant of Copyright License.
71
+
72
+ Subject to the terms and conditions of this License, each Contributor
73
+ hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
74
+ royalty-free, irrevocable copyright license to reproduce, prepare
75
+ Derivative Works of, publicly display, publicly perform, sublicense,
76
+ and distribute the Work and such Derivative Works in Source or Object
77
+ form.
78
+
79
+ ### 3. Grant of Patent License.
80
+
81
+ Subject to the terms and conditions of this License, each Contributor
82
+ hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
83
+ royalty-free, irrevocable (except as stated in this section) patent
84
+ license to make, have made, use, offer to sell, sell, import, and
85
+ otherwise transfer the Work, where such license applies only to those
86
+ patent claims licensable by such Contributor that are necessarily
87
+ infringed by their Contribution(s) alone or by combination of their
88
+ Contribution(s) with the Work to which such Contribution(s) was
89
+ submitted. If You institute patent litigation against any entity
90
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
91
+ the Work or a Contribution incorporated within the Work constitutes
92
+ direct or contributory patent infringement, then any patent licenses
93
+ granted to You under this License for that Work shall terminate as of
94
+ the date such litigation is filed.
95
+
96
+ ### 4. Redistribution.
97
+
98
+ You may reproduce and distribute copies of the Work or Derivative Works
99
+ thereof in any medium, with or without modifications, and in Source or
100
+ Object form, provided that You meet the following conditions:
101
+
102
+ <ol type="a">
103
+ <li>You must give any other recipients of the Work or Derivative Works
104
+ a copy of this License; and
105
+ <li>You must cause any modified files to carry prominent notices
106
+ stating that You changed the files; and
107
+ <li>You must retain, in the Source form of any Derivative Works that
108
+ You distribute, all copyright, patent, trademark, and attribution
109
+ notices from the Source form of the Work, excluding those notices
110
+ that do not pertain to any part of the Derivative Works; and
111
+ <li>If the Work includes a "NOTICE" text file as part of its
112
+ distribution, then any Derivative Works that You distribute must
113
+ include a readable copy of the attribution notices contained within
114
+ such NOTICE file, excluding those notices that do not pertain to any
115
+ part of the Derivative Works, in at least one of the following
116
+ places: within a NOTICE text file distributed as part of the
117
+ Derivative Works; within the Source form or documentation, if
118
+ provided along with the Derivative Works; or, within a display
119
+ generated by the Derivative Works, if and wherever such third-party
120
+ notices normally appear. The contents of the NOTICE file are for
121
+ informational purposes only and do not modify the License. You may
122
+ add Your own attribution notices within Derivative Works that You
123
+ distribute, alongside or as an addendum to the NOTICE text from the
124
+ Work, provided that such additional attribution notices cannot be
125
+ construed as modifying the License.
126
+ </ol>
127
+
128
+ You may add Your own copyright statement to Your modifications and may
129
+ provide additional or different license terms and conditions for use,
130
+ reproduction, or distribution of Your modifications, or for any such
131
+ Derivative Works as a whole, provided Your use, reproduction, and
132
+ distribution of the Work otherwise complies with the conditions stated
133
+ in this License.
134
+
135
+ ### 5. Submission of Contributions.
136
+
137
+ Unless You explicitly state otherwise, any Contribution intentionally
138
+ submitted for inclusion in the Work by You to the Licensor shall be
139
+ under the terms and conditions of this License, without any additional
140
+ terms or conditions. Notwithstanding the above, nothing herein shall
141
+ supersede or modify the terms of any separate license agreement you may
142
+ have executed with Licensor regarding such Contributions.
143
+
144
+ ### 6. Trademarks.
145
+
146
+ This License does not grant permission to use the trade names,
147
+ trademarks, service marks, or product names of the Licensor, except as
148
+ required for reasonable and customary use in describing the origin of
149
+ the Work and reproducing the content of the NOTICE file.
150
+
151
+ ### 7. Disclaimer of Warranty.
152
+
153
+ Unless required by applicable law or agreed to in writing, Licensor
154
+ provides the Work (and each Contributor provides its Contributions) on
155
+ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
156
+ express or implied, including, without limitation, any warranties or
157
+ conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
158
+ A PARTICULAR PURPOSE. You are solely responsible for determining the
159
+ appropriateness of using or redistributing the Work and assume any risks
160
+ associated with Your exercise of permissions under this License.
161
+
162
+ ### 8. Limitation of Liability.
163
+
164
+ In no event and under no legal theory, whether in tort (including
165
+ negligence), contract, or otherwise, unless required by applicable law
166
+ (such as deliberate and grossly negligent acts) or agreed to in writing,
167
+ shall any Contributor be liable to You for damages, including any
168
+ direct, indirect, special, incidental, or consequential damages of any
169
+ character arising as a result of this License or out of the use or
170
+ inability to use the Work (including but not limited to damages for loss
171
+ of goodwill, work stoppage, computer failure or malfunction, or any and
172
+ all other commercial damages or losses), even if such Contributor has
173
+ been advised of the possibility of such damages.
174
+
175
+ ### 9. Accepting Warranty or Additional Liability.
176
+
177
+ While redistributing the Work or Derivative Works thereof, You may
178
+ choose to offer, and charge a fee for, acceptance of support, warranty,
179
+ indemnity, or other liability obligations and/or rights consistent with
180
+ this License. However, in accepting such obligations, You may act only
181
+ on Your own behalf and on Your sole responsibility, not on behalf of any
182
+ other Contributor, and only if You agree to indemnify, defend, and hold
183
+ each Contributor harmless for any liability incurred by, or claims
184
+ asserted against, such Contributor by reason of your accepting any
185
+ such warranty or additional liability.
186
+
187
+ ##### END OF TERMS AND CONDITIONS
188
+
189
+ ## APPENDIX: How to apply the Apache License to your work.
190
+
191
+ To apply the Apache License to your work, attach the following
192
+ boilerplate notice, with the fields enclosed by brackets "[]"
193
+ replaced with your own identifying information. (Don't include the
194
+ brackets!) The text should be enclosed in the appropriate comment
195
+ syntax for the file format. We also recommend that a file or class name
196
+ and description of purpose be included on the same "printed page" as the
197
+ copyright notice for easier identification within third-party archives.
198
+
199
+ > #### Copyright [yyyy] [name of copyright owner]
200
+ >
201
+ > Licensed under the Apache License, Version 2.0 (the "License");
202
+ > you may not use this file except in compliance with the License.
203
+ > You may obtain a copy of the License at
204
+ >
205
+ > [http://www.apache.org/licenses/](http://www.apache.org/licenses/)
206
+ >
207
+ > Unless required by applicable law or agreed to in writing, software
208
+ > distributed under the License is distributed on an "AS IS" BASIS,
209
+ > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
210
+ > See the License for the specific language governing permissions and
211
+ > limitations under the License.
package/README.md ADDED
@@ -0,0 +1,8 @@
1
+ Juit Tiny Editor (for mentions!)
2
+ ================================
3
+
4
+ A simple Vue component providing an editable text area supporting bold, italic,
5
+ links and mentions.
6
+
7
+ * [Copyright Notice](NOTICE.md)
8
+ * [License](LICENSE.md)
@@ -0,0 +1,80 @@
1
+ import { ComponentOptionsMixin } from 'vue';
2
+ import { ComponentProvideOptions } from 'vue';
3
+ import { DefineComponent } from 'vue';
4
+ import { ExtractPropTypes } from 'vue';
5
+ import { PropType } from 'vue';
6
+ import { PublicProps } from 'vue';
7
+
8
+ declare const _default: DefineComponent<ExtractPropTypes< {
9
+ /** A flag indicating if content can be edited (default: `true`) */
10
+ editable: {
11
+ type: BooleanConstructor;
12
+ required: false;
13
+ default: boolean;
14
+ };
15
+ /** A flag indicating whether dark mode is enabled or not */
16
+ dark: {
17
+ type: BooleanConstructor;
18
+ required: false;
19
+ default: boolean;
20
+ };
21
+ /** A placeholder for when no text is available */
22
+ placeholder: {
23
+ type: StringConstructor;
24
+ required: false;
25
+ default: string;
26
+ };
27
+ /** A list of mentions to be used in the editor */
28
+ mentions: {
29
+ type: PropType<Record<string, string> | null>;
30
+ required: false;
31
+ default: () => {};
32
+ };
33
+ modelValue: {
34
+ type: PropType<string>;
35
+ };
36
+ }>, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
37
+ mention: (args_0: string) => any;
38
+ submit: (args_0: string) => any;
39
+ "update:modelValue": (modelValue: string) => any;
40
+ }, string, PublicProps, Readonly<ExtractPropTypes< {
41
+ /** A flag indicating if content can be edited (default: `true`) */
42
+ editable: {
43
+ type: BooleanConstructor;
44
+ required: false;
45
+ default: boolean;
46
+ };
47
+ /** A flag indicating whether dark mode is enabled or not */
48
+ dark: {
49
+ type: BooleanConstructor;
50
+ required: false;
51
+ default: boolean;
52
+ };
53
+ /** A placeholder for when no text is available */
54
+ placeholder: {
55
+ type: StringConstructor;
56
+ required: false;
57
+ default: string;
58
+ };
59
+ /** A list of mentions to be used in the editor */
60
+ mentions: {
61
+ type: PropType<Record<string, string> | null>;
62
+ required: false;
63
+ default: () => {};
64
+ };
65
+ modelValue: {
66
+ type: PropType<string>;
67
+ };
68
+ }>> & Readonly<{
69
+ onMention?: ((args_0: string) => any) | undefined;
70
+ onSubmit?: ((args_0: string) => any) | undefined;
71
+ "onUpdate:modelValue"?: ((modelValue: string) => any) | undefined;
72
+ }>, {
73
+ editable: boolean;
74
+ dark: boolean;
75
+ placeholder: string;
76
+ mentions: Record<string, string> | null;
77
+ }, {}, {}, {}, string, ComponentProvideOptions, true, {}, any>;
78
+ export default _default;
79
+
80
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,670 @@
1
+ import { openBlock as w, createElementBlock as y, createElementVNode as x, defineComponent as Qe, mergeModels as je, useModel as Ye, watch as T, shallowRef as q, ref as E, computed as H, onMounted as Ze, nextTick as Le, onUnmounted as et, normalizeClass as M, Fragment as tt, renderList as nt, createTextVNode as ot, toDisplayString as V, createCommentVNode as Me, createVNode as P, unref as A, withDirectives as De, vModelText as Be } from "vue";
2
+ function ie(n, e) {
3
+ return n instanceof Node && (n = I(n)), n.isPointInRange(e.startContainer, e.startOffset) && n.isPointInRange(e.endContainer, e.endOffset) ? e : null;
4
+ }
5
+ function rt(n) {
6
+ const e = document.createRange();
7
+ return e.selectNode(n), e;
8
+ }
9
+ function I(n) {
10
+ const e = document.createRange();
11
+ return e.selectNodeContents(n), e;
12
+ }
13
+ function be(n, e, a) {
14
+ if (!I(n).isPointInRange(e, a))
15
+ throw new Error("Range out of bounds");
16
+ const i = document.createRange();
17
+ return i.setStart(n, 0), i.setEnd(e, a), i.toString();
18
+ }
19
+ function W(n, e) {
20
+ const a = { start: 0, end: 0, backwards: e.backwards };
21
+ return a.start = a.end = be(n, e.startContainer, e.startOffset).length, e.collapsed || (a.end = be(n, e.endContainer, e.endOffset).length), a;
22
+ }
23
+ function U(n, e) {
24
+ const a = I(n);
25
+ e.backwards && (a.backwards = !0);
26
+ let { start: s, end: i } = e, l = null, r = null, f = !1, m = !1;
27
+ const c = document.createNodeIterator(n, NodeFilter.SHOW_TEXT);
28
+ for (let g = c.nextNode(); g && (s >= 0 || i > 0); g = c.nextNode()) {
29
+ l || (l = g), r = g;
30
+ const v = g.nodeValue.length;
31
+ if (s >= 0 && s < v && (f = !0, a.setStart(g, s), s === i))
32
+ return m = "extra", a.setEnd(g, i), a;
33
+ if (i > 0 && i <= v && (m = !0, a.setEnd(g, i), i === s))
34
+ return f = "extra", a.setStart(g, s), a;
35
+ s -= v, i -= v;
36
+ }
37
+ return !f && l && a.setStart(l, 0), !m && r && a.setEnd(r, r.length), a;
38
+ }
39
+ const st = /* @__PURE__ */ new Set([
40
+ "button",
41
+ "embed",
42
+ "form",
43
+ "iframe",
44
+ "input",
45
+ "object",
46
+ "script",
47
+ "select",
48
+ "style",
49
+ "textarea"
50
+ ]);
51
+ function $e(n) {
52
+ if (!n || !n.match(/^https?:\/\/[^\s]+$/)) return null;
53
+ try {
54
+ return new URL(n).href;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function qe(n, e, a = !1) {
60
+ if (a) return n;
61
+ const s = document.createElement(e);
62
+ return s.append(n), s;
63
+ }
64
+ function Z(n, e = {}) {
65
+ var s;
66
+ const a = document.createDocumentFragment();
67
+ for (const i of n.childNodes)
68
+ if (i.nodeType === Node.TEXT_NODE)
69
+ i.nodeValue && a.append(i.cloneNode());
70
+ else if (i.nodeType === Node.ELEMENT_NODE) {
71
+ const l = i, r = l.tagName.toLowerCase();
72
+ if (st.has(r)) continue;
73
+ const f = ((s = a.lastChild) == null ? void 0 : s.nodeType) === Node.ELEMENT_NODE ? {
74
+ element: a.lastChild,
75
+ tagName: a.lastChild.tagName.toLowerCase()
76
+ } : null, m = document.createDocumentFragment();
77
+ if (r === "b" || r === "strong") {
78
+ const c = Z(l, { ...e, bold: !0 });
79
+ if (!c.hasChildNodes()) continue;
80
+ (f == null ? void 0 : f.tagName) === "b" ? f.element.append(c) : m.append(qe(c, "b", e.bold));
81
+ } else if (r === "i" || r === "em") {
82
+ const c = Z(l, { ...e, italic: !0 });
83
+ if (!c.hasChildNodes()) continue;
84
+ (f == null ? void 0 : f.tagName) === "i" ? f.element.append(c) : m.append(qe(c, "i", e.italic));
85
+ } else if (r === "br")
86
+ m.append(document.createElement("br"));
87
+ else if (r === "div" || r === "p" || r === "pre" || r === "h1" || r === "h2" || r === "h3" || r === "h4" || r === "h5" || r === "h6" || r === "ul" || r === "ol" || r === "li") {
88
+ m.append(document.createElement("br"));
89
+ const c = Z(l, e);
90
+ c.hasChildNodes() && (m.append(c), m.append(document.createElement("br")));
91
+ } else if (r === "link") {
92
+ if (l.getAttribute("rel") !== "mention") continue;
93
+ const g = l.getAttribute("name"), v = l.getAttribute("title");
94
+ if (!(g && v)) continue;
95
+ const h = document.createElement("link");
96
+ h.setAttribute("rel", "mention"), h.setAttribute("title", v), h.setAttribute("name", g), m.append(h);
97
+ } else
98
+ m.append(Z(l, e));
99
+ m.hasChildNodes() && a.append(m);
100
+ }
101
+ return a;
102
+ }
103
+ function lt(n) {
104
+ const e = n.map((s, i) => ({ ...s, index: i }));
105
+ e.sort((s, i) => s.start < i.start ? -1 : s.start > i.start || s.end < i.end ? 1 : s.end > i.end || /* v8 ignore next 3 */
106
+ s.index < i.index ? -1 : s.index > i.index ? 1 : 0);
107
+ const a = /* @__PURE__ */ new Set();
108
+ for (let s = 0; s < e.length; s++) {
109
+ const i = e[s];
110
+ for (let l = s + 1; l < e.length; l++) {
111
+ const r = e[l];
112
+ a.has(r) || (r.start === r.end || r.start >= i.start && r.start < i.end || r.end >= i.start && r.end < i.end) && a.add(r);
113
+ }
114
+ }
115
+ return e.filter((s) => !a.has(s)).filter((s, i, l) => {
116
+ const r = l[i - 1];
117
+ return (r == null ? void 0 : r.end) === s.start && r.href === s.href ? (r.end = s.end, !1) : !0;
118
+ });
119
+ }
120
+ function at(n) {
121
+ const e = [], a = /(https?:\/\/[^\s]+)/gm, s = n.textContent;
122
+ for (let r = a.exec(s); r; r = a.exec(s)) {
123
+ const f = r[1], m = $e(f);
124
+ m && e.push({ start: r.index, end: r.index + f.length, href: m });
125
+ }
126
+ const i = [], l = document.createNodeIterator(n, NodeFilter.SHOW_ELEMENT, (r) => r.nodeName.toLowerCase() === "a" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP);
127
+ for (let r = l.nextNode(); r; r = l.nextNode()) {
128
+ const f = r, m = $e(f.getAttribute("href"));
129
+ if (m) {
130
+ const c = I(f);
131
+ i.push({ ...W(n, c), href: m });
132
+ }
133
+ }
134
+ return lt([...e, ...i]);
135
+ }
136
+ function it(n) {
137
+ var a;
138
+ n.normalize();
139
+ const e = document.createNodeIterator(n, NodeFilter.SHOW_ELEMENT);
140
+ for (let s = e.nextNode(); s; s = e.nextNode())
141
+ s !== n && s.nodeName.toLowerCase() !== "link" && (s.hasChildNodes() || (s.nodeName.toLowerCase() === "br" && s.parentNode.replaceChild(document.createTextNode(`
142
+ `), s), (a = s.parentNode) == null || a.removeChild(s)));
143
+ n.normalize(), n.hasChildNodes() || n.append(document.createElement("br"));
144
+ }
145
+ function ee(n) {
146
+ var s;
147
+ if (n.innerHTML === "<br>") return n;
148
+ n.normalize();
149
+ const e = at(n), a = Z(n);
150
+ return ((s = a.lastChild) == null ? void 0 : s.nodeName.toLowerCase()) === "link" && a.append(document.createTextNode(" ")), n.replaceChildren(a), e.forEach((i) => {
151
+ const l = U(n, i), r = l.extractContents(), f = document.createElement("a");
152
+ f.setAttribute("href", i.href), f.append(r), l.insertNode(f);
153
+ }), it(n), n;
154
+ }
155
+ function me(n, e, a) {
156
+ var v, h;
157
+ if (typeof a == "string") {
158
+ if (!ie(n, e)) throw new Error("Range out of bounds");
159
+ const S = new DOMParser().parseFromString(a, "text/html").body;
160
+ a = document.createDocumentFragment(), a.append(...S.childNodes);
161
+ }
162
+ const s = W(n, e), i = ((v = n.textContent) == null ? void 0 : v.length) || 0, l = I(n), r = document.createRange();
163
+ r.setStart(l.startContainer, l.startOffset), r.setEnd(e.startContainer, e.startOffset);
164
+ const f = document.createRange();
165
+ f.setStart(e.endContainer, e.endOffset), f.setEnd(l.endContainer, l.endOffset);
166
+ const m = r.extractContents(), c = f.extractContents();
167
+ n.replaceChildren(), n.append(m, a, c), ee(n);
168
+ const g = ((h = n.textContent) == null ? void 0 : h.length) || 0;
169
+ return { ...s, end: s.end + g - i };
170
+ }
171
+ function Ce(n) {
172
+ const e = document.getSelection();
173
+ if (!(e != null && e.rangeCount)) return null;
174
+ const a = e.getRangeAt(0);
175
+ if (!ie(n, a)) return null;
176
+ const s = e.isCollapsed ? !1 : e.anchorNode === e.focusNode ? e.anchorOffset > e.focusOffset : e.anchorNode.compareDocumentPosition(e.focusNode) === Node.DOCUMENT_POSITION_PRECEDING;
177
+ return Object.assign(a, { backwards: s });
178
+ }
179
+ function he(n) {
180
+ const e = Ce(n);
181
+ return e ? W(n, e) : null;
182
+ }
183
+ function ge(n, e, a = !1) {
184
+ a && (e = { start: e.end, end: e.end });
185
+ const s = U(n, e), { startContainer: i, startOffset: l, endContainer: r, endOffset: f, backwards: m } = s, c = document.getSelection();
186
+ return c && (m ? c.setBaseAndExtent(r, f, i, l) : c.setBaseAndExtent(i, l, r, f)), s;
187
+ }
188
+ function ae(n, e, a) {
189
+ const s = I(n);
190
+ if (!ie(n, e)) return null;
191
+ let i = e.commonAncestorContainer;
192
+ for (; i && s.intersectsNode(i); ) {
193
+ if (i.nodeType === Node.ELEMENT_NODE && i.nodeName.toLowerCase() === a)
194
+ return i;
195
+ i = i.parentNode;
196
+ }
197
+ return null;
198
+ }
199
+ function Pe(n, e, a, s = {}) {
200
+ if (e.collapsed || !ie(n, e)) return;
201
+ const i = ae(n, e, a), l = i ? rt(i) : I(e.commonAncestorContainer), r = document.createRange();
202
+ r.setStart(l.startContainer, l.startOffset), r.setEnd(e.startContainer, e.startOffset);
203
+ const f = document.createRange();
204
+ f.setStart(e.endContainer, e.endOffset), f.setEnd(l.endContainer, l.endOffset);
205
+ const m = f.extractContents(), c = e.extractContents(), g = r.extractContents();
206
+ l.deleteContents();
207
+ const v = document.createDocumentFragment();
208
+ if (v.append(g), i)
209
+ v.append(c);
210
+ else {
211
+ const h = document.createElement(a);
212
+ Object.entries(s).forEach((S) => h.setAttribute(...S)), h.append(c), v.append(h);
213
+ }
214
+ v.append(m), l.insertNode(v), ee(n);
215
+ }
216
+ const ut = {
217
+ xmlns: "http://www.w3.org/2000/svg",
218
+ fill: "currentColor",
219
+ viewBox: "0 -960 960 960"
220
+ };
221
+ function ct(n, e) {
222
+ return w(), y("svg", ut, e[0] || (e[0] = [
223
+ x("path", { d: "M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200zm121-112h104q48 0 58.5-24.5T566-372t-10.5-35.5T494-432H393zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95z" }, null, -1)
224
+ ]));
225
+ }
226
+ const dt = { render: ct }, ft = {
227
+ xmlns: "http://www.w3.org/2000/svg",
228
+ fill: "currentColor",
229
+ viewBox: "0 -960 960 960"
230
+ };
231
+ function pt(n, e) {
232
+ return w(), y("svg", ft, e[0] || (e[0] = [
233
+ x("path", { d: "M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100z" }, null, -1)
234
+ ]));
235
+ }
236
+ const vt = { render: pt }, mt = {
237
+ xmlns: "http://www.w3.org/2000/svg",
238
+ fill: "currentColor",
239
+ viewBox: "0 -960 960 960"
240
+ };
241
+ function ht(n, e) {
242
+ return w(), y("svg", mt, e[0] || (e[0] = [
243
+ x("path", { d: "M440-280H280q-83 0-141.5-58.5T80-480t58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85 35 85 85 35h160zM320-440v-80h320v80zm200 160v-80h160q50 0 85-35t35-85-35-85-85-35H520v-80h160q83 0 141.5 58.5T880-480t-58.5 141.5T680-280z" }, null, -1)
244
+ ]));
245
+ }
246
+ const Ae = { render: ht }, gt = {
247
+ xmlns: "http://www.w3.org/2000/svg",
248
+ fill: "currentColor",
249
+ viewBox: "0 -960 960 960"
250
+ };
251
+ function bt(n, e) {
252
+ return w(), y("svg", gt, e[0] || (e[0] = [
253
+ x("path", { d: "M480-80q-83 0-156-31.5T197-197t-85.5-127T80-480t31.5-156T197-763t127-85.5T480-880t156 31.5T763-763t85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480t58.5-141.5T480-680t141.5 58.5T680-480v58q0 26 17 44t43 18 43-18 17-44v-58q0-134-93-227t-227-93-227 93-93 227 93 227 227 93h200v80zm0-280q50 0 85-35t35-85-35-85-85-35-85 35-35 85 35 85 85 35" }, null, -1)
254
+ ]));
255
+ }
256
+ const Ct = { render: bt }, xt = {
257
+ xmlns: "http://www.w3.org/2000/svg",
258
+ fill: "currentColor",
259
+ viewBox: "0 -960 960 960"
260
+ };
261
+ function wt(n, e) {
262
+ return w(), y("svg", xt, e[0] || (e[0] = [
263
+ x("path", { d: "M792-443 176-183q-20 8-38-3.5T120-220v-520q0-22 18-33.5t38-3.5l616 260q25 11 25 37t-25 37M200-280l474-200-474-200v140l240 60-240 60zm0 0v-400z" }, null, -1)
264
+ ]));
265
+ }
266
+ const yt = { render: wt }, Nt = {
267
+ xmlns: "http://www.w3.org/2000/svg",
268
+ fill: "currentColor",
269
+ viewBox: "0 -960 960 960"
270
+ };
271
+ function Tt(n, e) {
272
+ return w(), y("svg", Nt, e[0] || (e[0] = [
273
+ x("path", { d: "M280-160v-520H80v-120h520v120H400v520zm360 0v-320H520v-120h360v120H760v320z" }, null, -1)
274
+ ]));
275
+ }
276
+ const Et = { render: Tt }, Rt = ["onMousedown"], kt = { class: "-jte-mentions-entry" }, St = { class: "-jte-mentions-ref" }, Ot = {
277
+ key: 0,
278
+ class: "-jte-mentions-waiting"
279
+ }, jt = {
280
+ key: 1,
281
+ class: "-jte-mentions-none"
282
+ }, Lt = {
283
+ key: 0,
284
+ class: "-jte-placeholder"
285
+ }, Mt = ["contenteditable"], Dt = { class: "-jte-toolbox" }, $t = /* @__PURE__ */ Qe({
286
+ name: "JuitTinyEditor",
287
+ __name: "editor",
288
+ props: /* @__PURE__ */ je({
289
+ /** A flag indicating if content can be edited (default: `true`) */
290
+ editable: {
291
+ type: Boolean,
292
+ required: !1,
293
+ default: !0
294
+ },
295
+ /** A flag indicating whether dark mode is enabled or not */
296
+ dark: {
297
+ type: Boolean,
298
+ required: !1,
299
+ default: !1
300
+ },
301
+ /** A placeholder for when no text is available */
302
+ placeholder: {
303
+ type: String,
304
+ required: !1,
305
+ default: ""
306
+ },
307
+ /** A list of mentions to be used in the editor */
308
+ mentions: {
309
+ type: Object,
310
+ required: !1,
311
+ default: () => ({})
312
+ }
313
+ }, {
314
+ modelValue: {
315
+ type: String,
316
+ required: !1,
317
+ default: ""
318
+ },
319
+ modelModifiers: {}
320
+ }),
321
+ emits: /* @__PURE__ */ je(["mention", "submit"], ["update:modelValue"]),
322
+ setup(n, { emit: e }) {
323
+ const a = Ye(n, "modelValue"), s = n;
324
+ T(() => s.dark, (o) => {
325
+ var t;
326
+ (t = i.value) == null || t.style.setProperty("color-scheme", o ? "dark light" : "light dark");
327
+ });
328
+ const i = q(null), l = q(null), r = q(null), f = q(null), m = q(null), c = q(null), g = E(""), v = E(0), h = q(null), S = H(() => {
329
+ var o;
330
+ return ((o = h.value) == null ? void 0 : o.toString()) || "";
331
+ }), xe = H(() => !!s.mentions), D = H(() => {
332
+ const o = S.value.substring(1).toLowerCase();
333
+ return s.mentions ? Object.entries(s.mentions).filter(([t, p]) => p.toLowerCase().startsWith(o)).sort(([t, p], [u, b]) => p.localeCompare(b)).map(([t, p]) => ({ name: t, value: p })) : [];
334
+ }), K = E(null), G = E(null), te = E(null), _ = H(() => K.value || G.value);
335
+ T(K, (o) => o && (te.value = o));
336
+ const ne = E(""), O = E(""), Ie = H(() => {
337
+ if (!O.value) return !0;
338
+ try {
339
+ return new URL(O.value), !0;
340
+ } catch {
341
+ return !1;
342
+ }
343
+ });
344
+ T([ne, O], ([o, t]) => {
345
+ _.value && (_.value.textContent = o, _.value.setAttribute("href", t), $());
346
+ });
347
+ const ue = E(null), ce = E(null), oe = E(0), _e = H(() => "." + ".".repeat(oe.value % 5));
348
+ let X = null;
349
+ const B = E("");
350
+ T(c, (o, t) => {
351
+ t !== null && o !== null && (t.startContainer == o.startContainer && t.startOffset == o.startOffset && t.endContainer == o.endContainer && t.endOffset == o.endOffset && (t == null ? void 0 : t.backwards) == (o == null ? void 0 : o.backwards) || (B.value = ""));
352
+ });
353
+ const F = [];
354
+ function de(o, t) {
355
+ return c.value ? t ? o && B.value === t ? "" : o || B.value === t ? "-jte-active" : "" : o ? "-jte-active" : c.value.collapsed ? "-jte-disabled" : "" : "-jte-disabled";
356
+ }
357
+ function $() {
358
+ if (!l.value) return void (g.value = "");
359
+ const o = l.value.innerHTML;
360
+ g.value = o === "<br>" ? "" : (
361
+ // edge case: empty editor
362
+ o.replaceAll("&nbsp;", " ").trimEnd()
363
+ );
364
+ }
365
+ function fe() {
366
+ var o;
367
+ if (l.value && ((o = F[F.length - 1]) == null ? void 0 : o.content) !== g.value) {
368
+ const t = he(l.value) || { start: 0, end: 0 };
369
+ F.push({ ...t, content: g.value }), F.length > 100 && F.shift();
370
+ }
371
+ }
372
+ function Fe() {
373
+ if (!l.value || !c.value) return;
374
+ const o = F.pop();
375
+ if (!o) return;
376
+ const t = new DOMParser().parseFromString(o.content, "text/html").body;
377
+ l.value.replaceChildren(...t.childNodes), ge(l.value, o);
378
+ }
379
+ function we() {
380
+ var o;
381
+ if (h.value && ((o = c.value) != null && o.collapsed)) {
382
+ const t = h.value.cloneRange();
383
+ if (t.setEnd(c.value.startContainer, c.value.startOffset), t.toString().match(/^@[^\r\n]*$/)) {
384
+ h.value = t;
385
+ return;
386
+ }
387
+ }
388
+ h.value = null;
389
+ }
390
+ function ye(o, t) {
391
+ if (!l.value || !h.value) return;
392
+ const p = l.value;
393
+ fe();
394
+ const u = document.createDocumentFragment(), b = document.createElement("link");
395
+ b.setAttribute("rel", "mention"), b.setAttribute("name", o), b.setAttribute("title", t), u.append(b, " ");
396
+ const d = me(p, h.value, u);
397
+ z(d, !0), h.value = null, $();
398
+ }
399
+ function J(o, t = {}) {
400
+ if (!l.value || !c.value) return;
401
+ const p = l.value;
402
+ if (c.value.collapsed) {
403
+ B.value = o, p.focus();
404
+ return;
405
+ }
406
+ fe();
407
+ const u = W(p, c.value);
408
+ Pe(p, c.value, o, t), z(u), $();
409
+ }
410
+ function ze() {
411
+ var u;
412
+ if (!l.value || !((u = c.value) != null && u.collapsed)) return;
413
+ const o = l.value, t = W(o, c.value), p = document.createTextNode("@");
414
+ c.value.insertNode(p), t.end += 1, h.value = U(o, t), t.start += 1, z(t);
415
+ }
416
+ function z(o, t) {
417
+ if (!l.value) return;
418
+ const p = l.value;
419
+ c.value = ge(p, o, t), p.focus();
420
+ }
421
+ function He() {
422
+ g.value && Te("submit", g.value);
423
+ }
424
+ function Ve(o) {
425
+ if (o.metaKey || o.ctrlKey)
426
+ switch (o.key) {
427
+ case "b":
428
+ J("b"), o.preventDefault();
429
+ break;
430
+ case "i":
431
+ J("i"), o.preventDefault();
432
+ break;
433
+ case "z":
434
+ Fe(), o.preventDefault();
435
+ break;
436
+ }
437
+ else if (h.value)
438
+ switch (o.key) {
439
+ case "Home":
440
+ v.value = 0, o.preventDefault();
441
+ break;
442
+ case "ArrowUp":
443
+ v.value = Math.max(v.value - 1, 0), o.preventDefault();
444
+ break;
445
+ case "PageUp":
446
+ v.value = Math.max(v.value - 4, 0), o.preventDefault();
447
+ break;
448
+ case "ArrowDown":
449
+ v.value = Math.min(v.value + 1, D.value.length - 1), o.preventDefault();
450
+ break;
451
+ case "PageDown":
452
+ v.value = Math.min(v.value + 4, D.value.length - 1), o.preventDefault();
453
+ break;
454
+ case "End":
455
+ v.value = D.value.length - 1, o.preventDefault();
456
+ break;
457
+ case "Escape":
458
+ h.value = null, o.preventDefault();
459
+ break;
460
+ case "Enter": {
461
+ const t = D.value[v.value];
462
+ t && ye(t.name, t.value), o.preventDefault();
463
+ break;
464
+ }
465
+ }
466
+ }
467
+ function Ue(o) {
468
+ if (o.preventDefault(), !l.value || !c.value) return;
469
+ const t = o.clipboardData;
470
+ if (!t) return;
471
+ const p = t.getData("text/html") || t.getData("text/plain");
472
+ if (!p) return;
473
+ const u = me(l.value, c.value, p);
474
+ z(u, !0), $();
475
+ }
476
+ function We(o) {
477
+ if (l.value && c.value && (fe(), !c.value.collapsed && (o.inputType === "deleteContentBackward" || o.inputType === "deleteContentForward"))) {
478
+ o.preventDefault(), o.stopPropagation();
479
+ const t = me(l.value, c.value, "");
480
+ z(t, !0), $();
481
+ }
482
+ }
483
+ function Ke(o) {
484
+ var b;
485
+ const t = l.value;
486
+ if (!t) return;
487
+ const p = h.value ? W(t, h.value) : null, u = he(t);
488
+ if (B.value && u && u.start === u.end && o.inputType === "insertText" && o.data !== "@" && !h.value) {
489
+ const d = U(t, { start: u.start - 1, end: u.end });
490
+ Pe(t, d, B.value);
491
+ }
492
+ if (B.value = "", ee(t), p && (h.value = U(t, p)), u && (t.innerHTML === "<br>" ? ((b = document.getSelection()) == null || b.setBaseAndExtent(t, 0, t, 0), t.focus()) : z(u)), !_.value && !h.value && o.data === "@" && o.inputType === "insertText") {
493
+ const d = document.getSelection();
494
+ if (!(d != null && d.anchorNode)) return;
495
+ const C = be(t, d.anchorNode, d.anchorOffset);
496
+ if (C.match(/(\s|^)@$/)) {
497
+ const N = C.length === 1 ? 0 : C.length - 1, j = C.length, R = { start: N, end: j };
498
+ h.value = U(t, R);
499
+ }
500
+ } else
501
+ we();
502
+ $();
503
+ }
504
+ function Ne(o) {
505
+ i.value && Ce(i.value) || (K.value = null, te.value = null, G.value = null), c.value = l.value ? Ce(l.value) : null, we();
506
+ }
507
+ Ze(() => {
508
+ if (document.addEventListener("selectionchange", Ne), !i.value) throw new Error("Root not referenced");
509
+ if (!l.value) throw new Error("Editor not referenced");
510
+ if (!r.value) throw new Error("Popup not referenced");
511
+ if (!f.value) throw new Error("List not referenced");
512
+ if (!m.value) throw new Error("Popup not referenced");
513
+ const o = i.value, t = l.value, p = r.value, u = f.value, b = m.value;
514
+ T(g, (d) => {
515
+ d !== a.value && (a.value = d);
516
+ }), T(a, (d) => {
517
+ if (d === g.value) return;
518
+ const C = new DOMParser().parseFromString(d, "text/html").body;
519
+ ee(C), t.replaceChildren(...C.childNodes), $();
520
+ }, { immediate: !0 }), T(xe, (d) => {
521
+ oe.value = 0, d ? X && clearInterval(X) : X = setInterval(() => oe.value = oe.value + 1, 200);
522
+ }, { immediate: !0 }), T(_, (d) => {
523
+ if (ne.value = (d == null ? void 0 : d.textContent) || "", O.value = (d == null ? void 0 : d.getAttribute("href")) || "", O.value === "http://__placeholder__/" && (d == null || d.setAttribute("href", ""), O.value = ""), !d) {
524
+ const C = he(t);
525
+ ee(t), C && ge(t, C), b.style.display = "none", b.style.top = "0", b.style.left = "0";
526
+ return;
527
+ }
528
+ Le(() => {
529
+ b.style.display = "flex";
530
+ const C = o.getBoundingClientRect(), N = d.getBoundingClientRect(), j = t.getBoundingClientRect(), R = getComputedStyle(b), k = parseInt(R.width), L = parseInt(R.height), re = N.top - L - 6 > j.top, se = N.bottom + L + 3 < j.bottom, Q = N.left + k < j.right;
531
+ b.style.top = !se && re ? `${N.top - L - C.top - 6}px` : (
532
+ // over the link
533
+ `${N.bottom - C.top + 3}px`
534
+ ), b.style.left = Q ? `${N.left - C.left}px` : (
535
+ // right of the link
536
+ `${j.right - k - C.left}px`
537
+ );
538
+ });
539
+ }, { immediate: !0 }), T([v, h, D], ([d, C, N], [j = -1]) => {
540
+ if (d >= N.length && (d = Math.max(N.length - 1, 0)), v.value = d, !!p && u) {
541
+ if (!C) {
542
+ v.value = 0, p.style.display = "none", p.style.top = "0", p.style.left = "0", u.scrollTop = 0;
543
+ return;
544
+ }
545
+ Le(() => {
546
+ p.style.display = "block";
547
+ const R = o.getBoundingClientRect(), k = C.getBoundingClientRect(), L = t.getBoundingClientRect(), re = getComputedStyle(p), se = parseInt(re.width), Q = parseInt(re.height), Ge = k.top - Q - 6 > L.top, Xe = k.bottom + Q + 3 < L.bottom, Je = k.left + se < L.right;
548
+ p.style.top = !Xe && Ge ? `${k.top - Q - R.top - 6}px` : (
549
+ // over the link
550
+ `${k.bottom - R.top + 3}px`
551
+ ), p.style.left = Je ? `${k.left - R.left}px` : (
552
+ // right of the link
553
+ `${L.right - se - R.left}px`
554
+ );
555
+ const pe = u.querySelectorAll("li")[v.value];
556
+ if (!pe) return;
557
+ const Ee = getComputedStyle(pe), Re = parseFloat(Ee.getPropertyValue("border-top-width")) || 0, ke = parseFloat(Ee.getPropertyValue("border-bottom-width")) || 0, Y = u.getBoundingClientRect(), ve = pe.getBoundingClientRect(), le = ve.top - Y.top, Se = ve.bottom - Y.bottom, Oe = ve.bottom - Y.top;
558
+ d > j ? Oe > Y.height ? u.scrollTop += Se - ke : le < 0 && (u.scrollTop += le + Re) : le < 0 ? u.scrollTop += le + Re : Se > 0 && (u.scrollTop += Oe - Y.height - ke);
559
+ });
560
+ }
561
+ }, { immediate: !0 }), T(c, (d) => {
562
+ d ? (K.value = ae(t, d, "a"), ue.value = ae(t, d, "b"), ce.value = ae(t, d, "i")) : (K.value = null, ue.value = null, ce.value = null);
563
+ }, { immediate: !0 });
564
+ }), et(() => {
565
+ document.removeEventListener("selectionchange", Ne), X && clearInterval(X);
566
+ });
567
+ const Te = e;
568
+ return T(S, (o) => Te("mention", o.substring(1)), { immediate: !0 }), (o, t) => {
569
+ var p;
570
+ return w(), y("div", {
571
+ class: M(["juit-tiny-edit", { "-jte-editable": n.editable, "-jte-dark": n.dark }])
572
+ }, [
573
+ x("div", {
574
+ ref_key: "rootRef",
575
+ ref: i,
576
+ class: "-jte-root"
577
+ }, [
578
+ x("div", {
579
+ ref_key: "mentionsPopupRef",
580
+ ref: r,
581
+ class: "-jte-mentions-popup"
582
+ }, [
583
+ x("ul", {
584
+ ref_key: "mentionsListRef",
585
+ ref: f
586
+ }, [
587
+ (w(!0), y(tt, null, nt(D.value, (u, b) => (w(), y("li", {
588
+ key: b,
589
+ class: M({ "-jte-mentions-selected": v.value === b }),
590
+ onMousedown: (d) => ye(u.name, u.value)
591
+ }, [
592
+ x("div", kt, [
593
+ ot(V(u.value) + " ", 1),
594
+ x("span", St, V(u.name), 1)
595
+ ])
596
+ ], 42, Rt))), 128))
597
+ ], 512),
598
+ xe.value ? D.value.length ? Me("", !0) : (w(), y("div", jt, V(S.value), 1)) : (w(), y("div", Ot, V(S.value) + V(_e.value), 1))
599
+ ], 512),
600
+ x("div", {
601
+ ref_key: "linksPopupRef",
602
+ ref: m,
603
+ class: "-jte-links-popup",
604
+ onClick: t[2] || (t[2] = (u) => G.value = te.value),
605
+ onFocusin: t[3] || (t[3] = (u) => G.value = te.value)
606
+ }, [
607
+ x("div", null, [
608
+ P(A(Et), { class: "-jte-icon" }),
609
+ De(x("input", {
610
+ "onUpdate:modelValue": t[0] || (t[0] = (u) => ne.value = u),
611
+ type: "text"
612
+ }, null, 512), [
613
+ [Be, ne.value]
614
+ ])
615
+ ]),
616
+ x("div", {
617
+ class: M({ "-jte-error": !Ie.value })
618
+ }, [
619
+ P(A(Ae), { class: "-jte-icon" }),
620
+ De(x("input", {
621
+ "onUpdate:modelValue": t[1] || (t[1] = (u) => O.value = u),
622
+ type: "text"
623
+ }, null, 512), [
624
+ [Be, O.value]
625
+ ])
626
+ ], 2)
627
+ ], 544),
628
+ n.placeholder && !g.value ? (w(), y("div", Lt, V(n.placeholder), 1)) : Me("", !0),
629
+ x("div", {
630
+ ref_key: "editorRef",
631
+ ref: l,
632
+ class: "-jte-editor",
633
+ contenteditable: n.editable ? "plaintext-only" : "false",
634
+ onBeforeinput: t[4] || (t[4] = (u) => We(u)),
635
+ onInput: t[5] || (t[5] = (u) => Ke(u)),
636
+ onKeydown: t[6] || (t[6] = (u) => Ve(u)),
637
+ onPaste: t[7] || (t[7] = (u) => Ue(u)),
638
+ onFocusin: t[8] || (t[8] = (u) => G.value = null)
639
+ }, null, 40, Mt),
640
+ x("div", Dt, [
641
+ P(A(Ct), {
642
+ class: M(["-jte-icon -jte-icon-mention", { "-jte-disabled": !((p = c.value) != null && p.collapsed) }]),
643
+ onClick: ze
644
+ }, null, 8, ["class"]),
645
+ P(A(dt), {
646
+ class: M(["-jte-icon -jte-icon-bold", de(ue.value, "b")]),
647
+ onClick: t[9] || (t[9] = (u) => J("b"))
648
+ }, null, 8, ["class"]),
649
+ P(A(vt), {
650
+ class: M(["-jte-icon -jte-icon-italic", de(ce.value, "i")]),
651
+ onClick: t[10] || (t[10] = (u) => J("i"))
652
+ }, null, 8, ["class"]),
653
+ P(A(Ae), {
654
+ class: M(["-jte-icon -jte-icon-href", de(_.value)]),
655
+ onClick: t[11] || (t[11] = (u) => J("a", { href: "http://__placeholder__/" }))
656
+ }, null, 8, ["class"]),
657
+ P(A(yt), {
658
+ class: M(["-jte-icon -jte-icon-send", { "-jte-disabled": !g.value, "-jte-active": !!g.value }]),
659
+ onClick: He
660
+ }, null, 8, ["class"])
661
+ ])
662
+ ], 512)
663
+ ], 2);
664
+ };
665
+ }
666
+ });
667
+ export {
668
+ $t as default
669
+ };
670
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../lib/range.ts","../lib/sanitize.ts","../lib/replace.ts","../lib/selection.ts","../lib/tags.ts","../lib/svg/bold.svg","../lib/svg/italic.svg","../lib/svg/link.svg","../lib/svg/mention.svg","../lib/svg/send.svg","../lib/svg/text.svg","../lib/editor.vue"],"sourcesContent":["/** A DOM {@link Range} with an optional flag indicating its direction */\nexport interface DirectedRange extends Range {\n /** If `end` should come before `start` (for user selection) */\n backwards?: boolean\n}\n\n/** Offsets for a selected region (in characters) */\nexport interface Offsets {\n /** The start character position */\n start: number\n /** The end character position */\n end: number\n /** If the selection is backwards */\n backwards?: boolean\n}\n\n/**\n * Figure out if the specified `needle` {@link Range} is entirely enclosed in\n * the `haystack` {@link Range} or {@link Node} and return the `haystack` if\n * true.\n *\n * @param haystack The outer range\n * @param needle The inner range\n * @returns The `needle` if it is entirely enclosed in the `haystack`\n */\nexport function containsRange(\n haystack: Node | Range,\n needle: Range,\n): Range | null {\n if (haystack instanceof Node) haystack = rangeFromContents(haystack)\n\n // Check if the selection is entirely within the tag\n const inRange =\n haystack.isPointInRange(needle.startContainer, needle.startOffset) &&\n haystack.isPointInRange(needle.endContainer, needle.endOffset)\n\n // Return the needle if it is within the haystack\n return inRange ? needle : null\n}\n\n/**\n * Create a new {@link Range} object that selects the specified node\n *\n * @param node The node to select\n * @returns A new {@link Range} created with `selectNode(...)`\n */\nexport function rangeFromNode(node: Node): Range {\n const range = document.createRange()\n range.selectNode(node)\n return range\n}\n\n/**\n * Create a new {@link Range} object that selects the _contents_ of the\n * specified node\n *\n * @param node The node to select\n * @returns A new {@link Range} created with `selectNodeContents(...)`\n */\nexport function rangeFromContents(node: Node): Range {\n const range = document.createRange()\n range.selectNodeContents(node)\n return range\n}\n\n/**\n * Retrieve the characters contained in a parent {@link Node} up to the\n * specified {@link Node} and offset.\n */\nexport function getCharacters(\n parent: Node,\n child: Node,\n offset: number,\n): string {\n const parentRange = rangeFromContents(parent)\n\n if (! parentRange.isPointInRange(child, offset)) {\n throw new Error('Range out of bounds')\n }\n\n const range = document.createRange()\n range.setStart(parent, 0)\n range.setEnd(child, offset)\n\n return range.toString()\n}\n\n/**\n * Retrieve the character {@link Offsets} for the specified {@link Range}.\n *\n * @param parent The container {@link Node} for the {@link Range}\n * @param range The {@link Range} to calculate offsets for\n * @returns The {@link Offsets} for the specified {@link Range}\n */\nexport function getRangeOffsets(\n parent: Node,\n range: DirectedRange,\n): Offsets {\n const result = { start: 0, end: 0, backwards: range.backwards }\n\n result.start = result.end = getCharacters(parent, range.startContainer, range.startOffset).length\n if (range.collapsed) return result\n\n result.end = getCharacters(parent, range.endContainer, range.endOffset).length\n return result\n}\n\n/**\n * Return a {@link Range} for the specified character {@link Offsets}.\n *\n * @param parent The container {@link Node} for the {@link Range}\n * @returns The {@link Range} for the specified {@link Offsets}\n */\nexport function getOffsetsRange(\n parent: Node,\n offsets: Offsets,\n): DirectedRange {\n const range: DirectedRange = rangeFromContents(parent)\n if (offsets.backwards) range.backwards = true\n let { start, end } = offsets\n\n let firstNode: Text | null = null\n let lastNode: Text | null = null\n let foundStart: boolean | string = false\n let foundEnd: boolean | string = false\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_TEXT)\n for (\n let text = iterator.nextNode() as Text | null;\n text && ((start >= 0) || (end > 0));\n text = iterator.nextNode() as Text | null\n ) {\n if (! firstNode) firstNode = text\n lastNode = text\n\n const length = text.nodeValue!.length\n\n // start is always calculated at the *start* of the text node\n if ((start >= 0) && (start < length)) {\n foundStart = true\n range.setStart(text, start)\n if (start === end) {\n foundEnd = 'extra'\n range.setEnd(text, end)\n return range\n }\n }\n\n // end is always calculated at the *end* of the text node\n if ((end > 0) && (end <= length)) {\n foundEnd = true\n range.setEnd(text, end)\n if (end === start) {\n foundStart = 'extra'\n range.setStart(text, start)\n return range\n }\n }\n\n start -= length\n end -= length\n }\n\n if ((! foundStart) && (firstNode)) range.setStart(firstNode, 0)\n if ((! foundEnd) && (lastNode)) range.setEnd(lastNode, lastNode.length)\n\n return range\n}\n","import { getOffsetsRange, getRangeOffsets, rangeFromContents } from './range'\n\nimport type { Offsets } from './range'\n\n/** Tags that will be stripped from output */\nconst STRIP_TAGS = new Set([\n 'button',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'object',\n 'script',\n 'select',\n 'style',\n 'textarea',\n])\n\n/** Parse an URL and return the normalized HREF if it's a valid HTTP url */\nfunction parseURL(string?: string | null): string | null {\n if (! string) return null\n if (! string.match(/^https?:\\/\\/[^\\s]+$/)) return null\n try {\n return new URL(string).href\n } catch {\n return null\n }\n}\n\n/**\n * Wrap a document fragment in a tag unless `noWrap` is `true`\n *\n * @param fragment The fragment to wrap\n * @param tagName The tag to wrap the fragment in\n * @param noWrap If `true`, the fragment will not be wrapped\n * @param attribues Additional attributes to add to the tag\n * @returns The wrapped fragment or the fragment itself\n */\nfunction maybeWrap(\n fragment: DocumentFragment,\n tagName: string,\n noWrap: boolean = false,\n): DocumentFragment | Element {\n if (noWrap) return fragment\n\n const tag = document.createElement(tagName)\n tag.append(fragment)\n return tag\n}\n\n/**\n * Sanitize a {@link Node}'s content for styles (bold, italic, ...)\n *\n * The `state` object is used to track the current state of the sanitizer: if\n * `bold` is `true`, then all nested `<b>` and `<strong>` tags will be stripped,\n * the same works for `italic` with `<i>` and `<em>`.\n *\n * @param fragment The fragment to sanitize\n * @param state The current state of the sanitizer\n * @returns The sanitized fragment\n */\nfunction sanitizeStyles(\n parent: Element,\n state: {\n bold?: boolean,\n italic?: boolean,\n } = {},\n): DocumentFragment {\n const result = document.createDocumentFragment()\n\n for (const node of parent.childNodes) {\n // Text nodes are cloned if they have contents\n if (node.nodeType === Node.TEXT_NODE) {\n if ((node as Text).nodeValue) result.append(node.cloneNode())\n\n // Elements are processed recursively\n } else if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element\n const tagName = element.tagName.toLowerCase()\n\n // Strip unwanted tags\n if (STRIP_TAGS.has(tagName)) continue\n\n const previous = result.lastChild?.nodeType === Node.ELEMENT_NODE ? {\n element: result.lastChild as Element,\n tagName: (result.lastChild as Element).tagName.toLowerCase(),\n } : null\n\n const child = document.createDocumentFragment()\n\n // Bold / strong\n if ((tagName === 'b') || (tagName === 'strong')) {\n const fragment = sanitizeStyles(element, { ...state, bold: true })\n if (! fragment.hasChildNodes()) continue\n\n if (previous?.tagName === 'b') previous.element.append(fragment)\n else child.append(maybeWrap(fragment, 'b', state.bold))\n\n // Italic / emphasis\n } else if ((tagName === 'i') || (tagName === 'em')) {\n const fragment = sanitizeStyles(element, { ...state, italic: true })\n if (! fragment.hasChildNodes()) continue\n\n if (previous?.tagName === 'i') previous.element.append(fragment)\n else child.append(maybeWrap(fragment, 'i', state.italic))\n\n // Paragraphs, divs, line breaks\n } else if (tagName === 'br') {\n child.append(document.createElement('br'))\n } else if ((tagName === 'div') || (tagName === 'p') || (tagName === 'pre') ||\n (tagName === 'h1') || (tagName === 'h2') || (tagName === 'h3') ||\n (tagName === 'h4') || (tagName === 'h5') || (tagName === 'h6') ||\n (tagName === 'ul') || (tagName === 'ol') || (tagName === 'li')) {\n child.append(document.createElement('br'))\n const fragment = sanitizeStyles(element, state)\n if (fragment.hasChildNodes()) {\n child.append(fragment)\n child.append(document.createElement('br'))\n }\n\n // Mentions are implemented as <link rel=\"mention\" name=\"...\" title=\"...\">\n } else if (tagName === 'link') {\n const rel = element.getAttribute('rel')\n if (rel !== 'mention') continue\n\n const name = element.getAttribute('name')\n const title = element.getAttribute('title')\n if (!(name && title)) continue\n\n const link = document.createElement('link')\n link.setAttribute('rel', 'mention')\n link.setAttribute('title', title)\n link.setAttribute('name', name)\n child.append(link)\n\n // Anything else is processed recursively (strip tag, keep contents)\n } else {\n child.append(sanitizeStyles(element, state))\n }\n\n if (child.hasChildNodes()) result.append(child)\n }\n }\n\n return result\n}\n\n/** Merge offsets with HREF (exported for tests) */\nexport function mergeOffsets(list: (Offsets & { href: string })[]): (Offsets & { href: string })[] {\n // Map all our offsets to an array of objects with an index\n const offsets = list.map((offset, i) => ({ ...offset, index: i }))\n\n // Sort by start, keep longest first, if seame, the first one has priority\n offsets.sort((a, b) => {\n return a.start < b.start ? -1 :\n a.start > b.start ? 1 :\n a.end < b.end ? 1 :\n a.end > b.end ? -1 :\n /* v8 ignore next 3 */\n a.index < b.index ? -1 :\n a.index > b.index ? 1 :\n 0\n })\n\n // Find all ranges that are nested/overlapping within another range\n const nested = new Set<Offsets>()\n\n for (let i = 0; i < offsets.length; i++) {\n const outer = offsets[i]!\n for (let j = i + 1; j < offsets.length; j++) {\n const inner = offsets[j]!\n if (nested.has(inner)) continue\n if (inner.start === inner.end) nested.add(inner)\n else if ((inner.start >= outer.start) && (inner.start < outer.end)) nested.add(inner)\n else if ((inner.end >= outer.start) && (inner.end < outer.end)) nested.add(inner)\n }\n }\n\n // All our anchor offsets\n return offsets\n // Filter out any nested/overlapping offsets\n .filter((offset) => ! nested.has(offset))\n // Join consecutive offsets with the same href\n .filter((offset, i, offsets) => {\n const previous = offsets[i - 1]\n if ((previous?.end === offset.start) && (previous.href === offset.href)) {\n previous.end = offset.end\n return false\n } else {\n return true\n }\n })\n}\n\n/**\n * Find the various links in a {@link Node}, and return their offsets and hrefs.\n *\n * @param parent The {@link Node} for which links will be sanitized\n * @returns An array of offsets and related hrefs of all links\n */\nfunction sanitizeLinks(parent: Element): (Offsets & { href: string })[] {\n // Process links parsed directly from the text ()\n const textRanges: (Offsets & { href: string })[] = []\n\n const re = /(https?:\\/\\/[^\\s]+)/gm\n const plain = parent.textContent!\n for (let match = re.exec(plain); match; match = re.exec(plain)) {\n const text = match[1]!\n const href = parseURL(text)\n if (href) textRanges.push({ start: match.index, end: match.index + text.length, href })\n }\n\n // Process links parsed from <a> elements\n const linkRanges: (Offsets & { href: string })[] = []\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT, (node) => {\n return node.nodeName.toLowerCase() === 'a' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP\n })\n\n for (let node = iterator.nextNode(); node; node = iterator.nextNode()) {\n const element = node as Element\n\n const href = parseURL(element.getAttribute('href'))\n if (href) {\n const range = rangeFromContents(element)\n linkRanges.push({ ...getRangeOffsets(parent, range), href })\n }\n }\n\n return mergeOffsets([ ...textRanges, ...linkRanges ])\n}\n\n/**\n * Strip all empty elements out from the children of a specified {@link Node}.\n *\n * @param parent The {@link Element} to sanitize\n */\nfunction sanitizeEmpty(parent: Element): void {\n parent.normalize()\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT)\n for (let node = iterator.nextNode(); node; node = iterator.nextNode()) {\n if (node === parent) continue // obviously skip the parent node\n if (node.nodeName.toLowerCase() === 'link') continue // mentions\n if (node.hasChildNodes()) continue // non-empty elements\n if (node.nodeName.toLowerCase() === 'br') { // keep line breaks\n node.parentNode!.replaceChild(document.createTextNode('\\n'), node)\n }\n // remove empty elements\n node.parentNode?.removeChild(node)\n }\n\n parent.normalize()\n // Edge case, empty editor\n if (! parent.hasChildNodes()) {\n parent.append(document.createElement('br'))\n }\n}\n\n/**\n * Sanitize the contents of a {@link Element} normalizing styles (bold,\n * italic, ...) and links.\n *\n * @param parent The {@link Element} to sanitize\n * @returns The same `parent` specified as an argument\n */\nexport function sanitize(parent: Element): Element {\n // Edge case: empty editor\n if (parent.innerHTML === '<br>') return parent\n\n // Cleanup before we start\n parent.normalize()\n\n // Save any and all links\n const offsets = sanitizeLinks(parent)\n\n // Sanitize the content\n const sanitized = sanitizeStyles(parent)\n // Append a space if the last child was a link (mention)\n if (sanitized.lastChild?.nodeName.toLowerCase() === 'link') {\n sanitized.append(document.createTextNode(' '))\n }\n\n // Replace our contents\n parent.replaceChildren(sanitized)\n\n // Restore the links\n offsets.forEach((offsets) => {\n const range = getOffsetsRange(parent, offsets)\n const wrapped = range.extractContents()\n const link = document.createElement('a')\n link.setAttribute('href', offsets.href)\n link.append(wrapped)\n range.insertNode(link)\n })\n\n // Cleanup any empty elements which might have been introduced\n // when restoring the links above\n sanitizeEmpty(parent)\n\n // Return the same element we were given\n return parent\n}\n","import { containsRange, getRangeOffsets, rangeFromContents } from './range'\nimport { sanitize } from './sanitize'\n\nimport type { Offsets } from './range'\n\nexport function replaceRange(\n parent: Element,\n range: Range,\n html: DocumentFragment | string,\n): Offsets {\n if (typeof html === 'string') {\n if (! containsRange(parent, range)) throw new Error('Range out of bounds')\n\n const body = new DOMParser().parseFromString(html, 'text/html').body\n html = document.createDocumentFragment()\n html.append(...body.childNodes)\n }\n\n // Save our selection offsets\n const offsets = getRangeOffsets(parent, range)\n const beforeLength = parent.textContent?.length || 0\n\n // Split into ranges (before and after)\n const parentRange = rangeFromContents(parent)\n\n const beforeRange = document.createRange()\n beforeRange.setStart(parentRange.startContainer, parentRange.startOffset)\n beforeRange.setEnd(range.startContainer, range.startOffset)\n\n const afterRange = document.createRange()\n afterRange.setStart(range.endContainer, range.endOffset)\n afterRange.setEnd(parentRange.endContainer, parentRange.endOffset)\n\n // Extract the content to keep\n const before = beforeRange.extractContents()\n const after = afterRange.extractContents()\n\n // Re-add all our content and sanitize\n parent.replaceChildren() // wipe everything\n parent.append(before, html, after)\n sanitize(parent)\n\n // Adjust our selection offsets depending on pasted data\n const afterLength = parent.textContent?.length || 0\n return { ...offsets, end: offsets.end + afterLength - beforeLength }\n}\n","import { containsRange, getOffsetsRange, getRangeOffsets } from './range'\n\nimport type { DirectedRange, Offsets } from './range'\n\n/**\n * Retrieve the current selection as a {@link DirectedRange} within a parent\n * {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @returns The current selection as a {@link DirectedRange} if within bounds\n */\nexport function getSelectionRange(\n parent: Element,\n): DirectedRange | null {\n const selection = document.getSelection()\n if (! selection?.rangeCount) return null\n\n const selectionRange = selection.getRangeAt(0)\n if (! containsRange(parent, selectionRange)) return null\n\n const backwards =\n selection.isCollapsed ? false :\n selection.anchorNode === selection.focusNode ? selection.anchorOffset > selection.focusOffset :\n selection.anchorNode!.compareDocumentPosition(selection.focusNode!) === Node.DOCUMENT_POSITION_PRECEDING\n return Object.assign(selectionRange, { backwards })\n}\n\n/**\n * Retrieve the current selection as character {@link Offsets} within a parent\n * {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @returns The current selection as a {@link DirectedRange} if within bounds\n */\nexport function getSelectionOffsets(\n parent: Element,\n): Offsets | null {\n const range = getSelectionRange(parent)\n return range ? getRangeOffsets(parent, range) : null\n}\n\n/**\n * Restore the current selection from character {@link Offsets} in the\n * specified {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @param offsets The character offsets to restore\n */\nexport function restoreSelection(\n parent: Element,\n offsets: Offsets,\n collapse = false,\n): DirectedRange {\n if (collapse) offsets = { start: offsets.end, end: offsets.end }\n const range = getOffsetsRange(parent, offsets)\n const { startContainer, startOffset, endContainer, endOffset, backwards } = range\n\n // Selection can be forwards or backwards... restore correctly\n const selection = document.getSelection()\n\n if (selection) {\n if (backwards) {\n selection.setBaseAndExtent(endContainer, endOffset, startContainer, startOffset)\n } else {\n selection.setBaseAndExtent(startContainer, startOffset, endContainer, endOffset)\n }\n }\n\n return range\n}\n","import { containsRange, rangeFromContents, rangeFromNode } from './range'\nimport { sanitize } from './sanitize'\n\n/**\n * Figure out if the current selection entirely enclosed in an element\n * with the specified tag name (normally bold, italic, ...)\n *\n * @param parent The element whose contents we are searching\n * @param range The current selection (if any)\n * @param tagName The tag name to check for (e.g. 'b', 'i', ...)\n * @returns The parent node with the specified tag name if found\n */\nexport function isTagged(\n parent: Node,\n range: Range,\n tagName: string,\n): Element | null {\n // Get the parent range, and make sure the selection is within it\n const parentRange = rangeFromContents(parent)\n if (! containsRange(parent, range)) return null\n\n // Walk up from our common ancestor to to the parent to see if we find\n // an element with the specified tag name\n let ancestor: Node | null = range.commonAncestorContainer\n while (ancestor && parentRange.intersectsNode(ancestor)) {\n if ((ancestor.nodeType === Node.ELEMENT_NODE) &&\n (ancestor.nodeName.toLowerCase() === tagName)) {\n return ancestor as Element\n }\n ancestor = ancestor.parentNode\n }\n\n // Nothing found!\n return null\n}\n\n/**\n * Toggle a tag within the specified {@link Element}, around the specified\n * selection.\n *\n * @param parent The element whose contents we are modifying\n * @param range The range to toggle the tag around\n * @param tagName The tag name (e.g. 'b', 'i', ...) to toggle\n * @param attributes Any attributes to set on the tag (will be sanitized)\n */\nexport function toggleTag(\n parent: Element,\n range: Range,\n tagName: string,\n attributes: Record<string, string> = {},\n): void {\n if (range.collapsed) return\n if (! containsRange(parent, range)) return\n\n const ancestor = isTagged(parent, range, tagName)\n\n const ancestorRange = ancestor ?\n rangeFromNode(ancestor) :\n rangeFromContents(range.commonAncestorContainer)\n\n const before = document.createRange()\n before.setStart(ancestorRange.startContainer, ancestorRange.startOffset)\n before.setEnd(range.startContainer, range.startOffset)\n\n const after = document.createRange()\n after.setStart(range.endContainer, range.endOffset)\n after.setEnd(ancestorRange.endContainer, ancestorRange.endOffset)\n\n // Remove back to front...\n const afterFragment = after.extractContents()\n const selectedFragment = range.extractContents()\n const beforeFragment = before.extractContents()\n ancestorRange.deleteContents() // wipe everything, just in case\n\n // Create our fragment\n const fragment = document.createDocumentFragment()\n fragment.append(beforeFragment)\n\n // Append our selected fragment, possibly wrapped in a tag\n if (ancestor) {\n fragment.append(selectedFragment)\n } else {\n const tag = document.createElement(tagName)\n Object.entries(attributes).forEach((attr) => tag.setAttribute(...attr))\n tag.append(selectedFragment)\n fragment.append(tag)\n }\n\n // Surround with before and after fragments\n // fragment.insertBefore(beforeFragment, fragment.firstChild)\n fragment.append(afterFragment)\n\n // Insert and sanitize\n ancestorRange.insertNode(fragment)\n sanitize(parent)\n}\n","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200zm121-112h104q48 0 58.5-24.5T566-372t-10.5-35.5T494-432H393zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M440-280H280q-83 0-141.5-58.5T80-480t58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85 35 85 85 35h160zM320-440v-80h320v80zm200 160v-80h160q50 0 85-35t35-85-35-85-85-35H520v-80h160q83 0 141.5 58.5T880-480t-58.5 141.5T680-280z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M480-80q-83 0-156-31.5T197-197t-85.5-127T80-480t31.5-156T197-763t127-85.5T480-880t156 31.5T763-763t85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480t58.5-141.5T480-680t141.5 58.5T680-480v58q0 26 17 44t43 18 43-18 17-44v-58q0-134-93-227t-227-93-227 93-93 227 93 227 227 93h200v80zm0-280q50 0 85-35t35-85-35-85-85-35-85 35-35 85 35 85 85 35\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M792-443 176-183q-20 8-38-3.5T120-220v-520q0-22 18-33.5t38-3.5l616 260q25 11 25 37t-25 37M200-280l474-200-474-200v140l240 60-240 60zm0 0v-400z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M280-160v-520H80v-120h520v120H400v520zm360 0v-320H520v-120h360v120H760v320z\" }, null, -1)\n ])))\n}\nexport default { render: render }","<template>\n <div class=\"juit-tiny-edit\" :class=\"{ '-jte-editable': editable, '-jte-dark': dark }\">\n <div ref=\"rootRef\" class=\"-jte-root\">\n <!-- our mentions popup -->\n <div ref=\"mentionsPopupRef\" class=\"-jte-mentions-popup\">\n <ul ref=\"mentionsListRef\">\n <li\n v-for=\"(mention, i) in mentionsEntries\"\n :key=\"i\"\n :class=\"{ '-jte-mentions-selected': mentionsIndex === i }\"\n @mousedown=\"applyMention(mention.name, mention.value)\"\n >\n <div class=\"-jte-mentions-entry\">\n {{ mention.value }}\n <span class=\"-jte-mentions-ref\">{{ mention.name }}</span>\n </div>\n </li>\n </ul>\n <div v-if=\"! mentionsReady\" class=\"-jte-mentions-waiting\">\n {{ mentionsText }}{{ dotsString }}\n </div>\n <div v-else-if=\"! mentionsEntries.length\" class=\"-jte-mentions-none\">\n {{ mentionsText }}\n </div>\n </div>\n\n <!-- our links popup -->\n <div\n ref=\"linksPopupRef\"\n class=\"-jte-links-popup\"\n @click=\"activeLink = previousLink\"\n @focusin=\"activeLink = previousLink\"\n >\n <div>\n <iconText class=\"-jte-icon\" />\n <input v-model=\"linkText\" type=\"text\">\n </div>\n <div :class=\"{ '-jte-error': !linkValid }\">\n <iconLink class=\"-jte-icon\" />\n <input v-model=\"linkHref\" type=\"text\">\n </div>\n </div>\n\n <div v-if=\"placeholder && (! html)\" class=\"-jte-placeholder\">\n {{ placeholder }}\n </div>\n\n <!-- our editor -->\n <div\n ref=\"editorRef\"\n class=\"-jte-editor\"\n :contenteditable=\"editable ? 'plaintext-only' : 'false'\"\n @beforeinput=\"onBeforeInput($event as InputEvent)\"\n @input=\"onInput($event as InputEvent)\"\n @keydown=\"onKeydown($event)\"\n @paste=\"onPaste($event)\"\n @focusin=\"activeLink = null\"\n />\n\n <div class=\"-jte-toolbox\">\n <iconMention class=\"-jte-icon -jte-icon-mention\" :class=\"{ '-jte-disabled': ! selected?.collapsed }\" @click=\"insertMention\" />\n <iconBold class=\"-jte-icon -jte-icon-bold\" :class=\"iconClass(isBold, 'b')\" @click=\"applyTag('b')\" />\n <iconItalic class=\"-jte-icon -jte-icon-italic\" :class=\"iconClass(isItalic, 'i')\" @click=\"applyTag('i')\" />\n <iconLink class=\"-jte-icon -jte-icon-href\" :class=\"iconClass(isLink)\" @click=\"applyTag('a', { href: 'http://__placeholder__/' })\" />\n <iconSend class=\"-jte-icon -jte-icon-send\" :class=\"{ '-jte-disabled': !html, '-jte-active': !!html }\" @click=\"submit\" />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'\n\nimport { getCharacters, getOffsetsRange, getRangeOffsets } from './range'\nimport { replaceRange } from './replace'\nimport { sanitize } from './sanitize'\nimport { getSelectionOffsets, getSelectionRange, restoreSelection } from './selection'\nimport { isTagged, toggleTag } from './tags'\n// SVGs\nimport iconBold from './svg/bold.svg'\nimport iconItalic from './svg/italic.svg'\nimport iconLink from './svg/link.svg'\nimport iconMention from './svg/mention.svg'\nimport iconSend from './svg/send.svg'\nimport iconText from './svg/text.svg'\n\nimport type { PropType } from 'vue'\nimport type { Offsets } from './range'\n\n/* ==== DEFS ================================================================ */\n\ndefineOptions({ name: 'JuitTinyEditor' })\n\nconst model = defineModel({\n type: String,\n required: false,\n default: '',\n})\n\nconst props = defineProps({\n /** A flag indicating if content can be edited (default: `true`) */\n editable: {\n type: Boolean,\n required: false,\n default: true,\n },\n /** A flag indicating whether dark mode is enabled or not */\n dark: {\n type: Boolean,\n required: false,\n default: false,\n },\n /** A placeholder for when no text is available */\n placeholder: {\n type: String,\n required: false,\n default: '',\n },\n /** A list of mentions to be used in the editor */\n mentions: {\n type: Object as PropType<Record<string, string> | null>,\n required: false,\n default: () => ({}),\n },\n})\n\nwatch(() => props.dark, (dark) => {\n rootRef.value?.style.setProperty('color-scheme', dark ? 'dark light' : 'light dark')\n // document.body.classList.toggle('dark', dark)\n})\n\n/* ==== REFS ================================================================ */\n\n/** Reference to our root container */\nconst rootRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our content editable DIV */\nconst editorRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our popup */\nconst mentionsPopupRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our mentions list (in the popup) DIV */\nconst mentionsListRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our links popup */\nconst linksPopupRef = shallowRef<HTMLDivElement | null>(null)\n\n/** The `Range` of the current selection, if within our editor */\nconst selected = shallowRef<Range & { backwards?: boolean } | null>(null)\n\n/** The HTML string contained by the editor */\nconst html = ref('')\n\n/** Index of the current mention */\nconst mentionsIndex = ref(0)\n/** Range of the current mention (while being inserted) */\nconst mentionsRange = shallowRef<Range | null>(null)\n/** Text of the current mention (including the leading \"@\") */\nconst mentionsText = computed(() => mentionsRange.value?.toString() || '')\n/** Computed to see if mentions (from our prop) are ready */\nconst mentionsReady = computed(() => !! props.mentions)\n/** Computed list of mentions that match the current text */\nconst mentionsEntries = computed(() => {\n const text = mentionsText.value.substring(1).toLowerCase()\n\n if (! props.mentions) return []\n return Object.entries(props.mentions)\n .filter(([ _, name ]) => name.toLowerCase().startsWith(text))\n .sort(([ _a, a ], [ _b, b ]) => a.localeCompare(b))\n .map(([ name, value ]) => ({ name, value }))\n})\n\n/** The link associated with the *current* selection in the editor */\nconst selectedLink = ref<Element | null>(null)\n/** The link currently being handled by the links popup */\nconst activeLink = ref(null as any)\n/** The last link highlighted by the editor (updated by the watch below) */\nconst previousLink = ref<Element | null>(null)\n/** The current link, either in the editor or in the links popup */\nconst isLink = computed<Element | null>(() => selectedLink.value || activeLink.value)\n/* Watch the selected link and remember the last viable one */\nwatch(selectedLink, (link) => link && (previousLink.value = link))\n/** The text of the current link */\nconst linkText = ref('')\n/** The href of the current link */\nconst linkHref = ref('')\n/** Validate the link's own href (must be empty or an URL) */\nconst linkValid = computed(() => {\n if (! linkHref.value) return true\n try {\n new URL(linkHref.value)\n return true\n } catch {\n return false\n }\n})\n/** Update the content and href of the current link from the popup */\nwatch([ linkText, linkHref ], ([ text, href ]) => {\n if (! isLink.value) return\n isLink.value.textContent = text\n isLink.value.setAttribute('href', href)\n commit()\n})\n\n\n/** Whether the current selection is all bold */\nconst isBold = ref<Element | null>(null)\n/** Whether the current selection is all italic */\nconst isItalic = ref<Element | null>(null)\n\n/** How many dots are we displaying (in \"waiting for mentions\" animation) */\nconst dots = ref(0)\n/** String of dots to display (in \"waiting for mentions\" animation) */\nconst dotsString = computed(() => '.' + '.'.repeat(dots.value % 5))\n/** Interval animating our \"waiting for mentions\" text */\nlet dotsInterval: ReturnType<typeof setInterval> | null = null\n/** The _next_ tag to wrap our input into (when enabling bold on collapsed) */\nconst nextTag = ref('')\n/** When the selected range changes, reset the next tag */\nwatch((selected), (newRange, oldRange) => {\n if (oldRange === null) return\n if (newRange === null) return\n\n if ((oldRange.startContainer == newRange.startContainer) &&\n (oldRange.startOffset == newRange.startOffset) &&\n (oldRange.endContainer == newRange.endContainer) &&\n (oldRange.endOffset == newRange.endOffset) &&\n (oldRange?.backwards == newRange?.backwards)) return\n nextTag.value = ''\n})\n\n/** Our history (for undo) */\nconst history: (Offsets & { content: string })[] = []\n\n/* ==== FUNCTIONS =========================================================== */\n\n/** Compute the class for an icon (bold or italic) */\nfunction iconClass(element: Element | null, tag?: string): string {\n if (! selected.value) return '-jte-disabled'\n\n // If we don't have a tag, we're not processing zero-width selections\n if (! tag) {\n if (element) return '-jte-active'\n if (selected.value.collapsed) return '-jte-disabled'\n return ''\n }\n\n // styled, but next input will remove the style\n if (element && (nextTag.value === tag)) return ''\n // either styled *or* next input will add the style\n if (element || nextTag.value === tag) return '-jte-active'\n // not styled, but can be styled\n return ''\n}\n\n/** Commit changes to the model converting our editor into HTML */\nfunction commit(): void {\n if (! editorRef.value) return void (html.value = '')\n\n // Clean our HTML, then update the model\n const string = editorRef.value.innerHTML\n html.value = string === '<br>' ? '' : // edge case: empty editor\n string.replaceAll('&nbsp;', ' ').trimEnd() // remove trailing whitespace\n}\n\n/** Mark a new entry in our history */\nfunction historySave(): void {\n if (! editorRef.value) return\n\n // Save our history\n if (history[history.length - 1]?.content !== html.value) {\n const offsets = getSelectionOffsets(editorRef.value) || { start: 0, end: 0 }\n history.push({ ...offsets, content: html.value })\n if (history.length > 100) history.shift()\n }\n}\n\n/** Undo the last change */\nfunction historyUndo(): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n const entry = history.pop()\n if (! entry) return\n\n const body = new DOMParser().parseFromString(entry.content, 'text/html').body\n editorRef.value.replaceChildren(...body.childNodes)\n restoreSelection(editorRef.value, entry)\n}\n\n/** Compute the mention range according to changes th the selection */\nfunction computeMention(): void {\n if (mentionsRange.value && selected.value?.collapsed) {\n const range = mentionsRange.value.cloneRange()\n range.setEnd(selected.value.startContainer, selected.value.startOffset)\n if (range.toString().match(/^@[^\\r\\n]*$/)) {\n mentionsRange.value = range\n return\n }\n }\n mentionsRange.value = null\n}\n\n/** Replace the current mentions range with a proper mention */\nfunction applyMention(name: string, value: string): void {\n if (! editorRef.value) return\n if (! mentionsRange.value) return\n\n const editor = editorRef.value\n\n historySave()\n const fragment = document.createDocumentFragment()\n const element = document.createElement('link')\n element.setAttribute('rel', 'mention')\n element.setAttribute('name', name)\n element.setAttribute('title', value)\n fragment.append(element, ' ')\n\n const offsets = replaceRange(editor, mentionsRange.value, fragment)\n selectWithOffsets(offsets, true)\n mentionsRange.value = null\n commit()\n}\n\n/** Apply a \"tag\" (bold, italic, ...) */\nfunction applyTag(tagName: string, attributes: Record<string, string> = {}): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n const editor = editorRef.value\n\n // If nothing is selected, this is reserved for the next tag\n if (selected.value.collapsed) {\n nextTag.value = tagName\n editor.focus()\n return\n }\n\n historySave()\n const offsets = getRangeOffsets(editor, selected.value)\n toggleTag(editor, selected.value, tagName, attributes)\n selectWithOffsets(offsets)\n commit()\n}\n\nfunction insertMention(): void {\n if (! editorRef.value) return\n if (! selected.value?.collapsed) return\n\n const editor = editorRef.value\n\n const offsets = getRangeOffsets(editor, selected.value)\n\n const at = document.createTextNode('@')\n selected.value.insertNode(at)\n\n offsets.end += 1\n mentionsRange.value = getOffsetsRange(editor, offsets)\n offsets.start += 1\n selectWithOffsets(offsets)\n}\n\nfunction selectWithOffsets(offsets: Offsets, collapse?: boolean): void {\n if (! editorRef.value) return\n\n const editor = editorRef.value\n selected.value = restoreSelection(editor, offsets, collapse)\n editor.focus()\n}\n\nfunction submit(): void {\n if (! html.value) return\n emit('submit', html.value)\n}\n\n/* ==== DOM EVENTS ========================================================== */\n\n/** Trigger shortcuts for applying bold, italic, ... */\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.metaKey || event.ctrlKey) {\n switch (event.key) {\n case 'b':\n applyTag('b')\n event.preventDefault()\n break\n case 'i':\n applyTag('i')\n event.preventDefault()\n break\n case 'z':\n historyUndo()\n event.preventDefault()\n break\n }\n } else if (mentionsRange.value) {\n switch (event.key) {\n case 'Home':\n mentionsIndex.value = 0\n event.preventDefault()\n break\n case 'ArrowUp':\n mentionsIndex.value = Math.max(mentionsIndex.value - 1, 0)\n event.preventDefault()\n break\n case 'PageUp':\n mentionsIndex.value = Math.max(mentionsIndex.value - 4, 0)\n event.preventDefault()\n break\n case 'ArrowDown':\n mentionsIndex.value = Math.min(mentionsIndex.value + 1, mentionsEntries.value.length - 1)\n event.preventDefault()\n break\n case 'PageDown':\n mentionsIndex.value = Math.min(mentionsIndex.value + 4, mentionsEntries.value.length - 1)\n event.preventDefault()\n break\n case 'End':\n mentionsIndex.value = mentionsEntries.value.length - 1\n event.preventDefault()\n break\n case 'Escape':\n mentionsRange.value = null\n event.preventDefault()\n break\n case 'Enter': {\n const mention = mentionsEntries.value[mentionsIndex.value]\n if (mention) applyMention(mention.name, mention.value)\n event.preventDefault()\n break\n }\n }\n }\n}\n\n/** Replace the current selection with the (sanitized) content from clipboard */\nfunction onPaste(event: ClipboardEvent): void {\n event.preventDefault()\n if (! editorRef.value) return\n if (! selected.value) return\n\n const data = event.clipboardData\n if (! data) return\n\n const text = data.getData('text/html') || data.getData('text/plain')\n if (! text) return\n\n const offsets = replaceRange(editorRef.value, selected.value, text)\n\n selectWithOffsets(offsets, true)\n commit()\n}\n\n/** If deleting a bunch of content, ensure links are removed, too... */\nfunction onBeforeInput(event: InputEvent): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n historySave()\n\n if ((!selected.value.collapsed) &&\n ((event.inputType === 'deleteContentBackward') ||\n (event.inputType === 'deleteContentForward'))) {\n event.preventDefault()\n event.stopPropagation()\n const offsets = replaceRange(editorRef.value, selected.value, '')\n selectWithOffsets(offsets, true)\n commit()\n }\n}\n\n/** Someone typed something in our editor, let's process it */\nfunction onInput(event: InputEvent): void {\n const editor = editorRef.value\n if (! editor) return\n\n // Save the current selection and mentions\n const mentions = mentionsRange.value ? getRangeOffsets(editor, mentionsRange.value) : null\n const selection = getSelectionOffsets(editor)\n\n // See if we need to process this event with style (bold, italic, ...)\n if (nextTag.value &&\n selection &&\n (selection.start === selection.end) &&\n (event.inputType === 'insertText') &&\n (event.data !== '@') &&\n (! mentionsRange.value)) {\n const range = getOffsetsRange(editor, { start: selection.start - 1, end: selection.end })\n toggleTag(editor, range, nextTag.value)\n }\n nextTag.value = ''\n\n // First thing first: sanitize the content (this will also take care of links)\n sanitize(editor)\n\n // Restore mentions range *after* sanitization\n if (mentions) mentionsRange.value = getOffsetsRange(editor, mentions)\n\n // Restore the selection range\n if (selection) {\n // Edge case when all content is deleted: insert a single \"<br>\"\n if (editor.innerHTML === '<br>') {\n document.getSelection()?.setBaseAndExtent(editor, 0, editor, 0)\n editor.focus()\n } else {\n selectWithOffsets(selection)\n }\n }\n\n // When the input is \"@\" we might want to trigger a mention\n if ((! isLink.value) &&\n (! mentionsRange.value) &&\n (event.data === '@') &&\n (event.inputType === 'insertText')) {\n const selection = document.getSelection()\n if (! selection?.anchorNode) return\n const text = getCharacters(editor, selection.anchorNode, selection.anchorOffset)\n if (text.match(/(\\s|^)@$/)) {\n const start = text.length === 1 ? 0 : text.length - 1\n const end = text.length\n const offsets: Offsets = { start, end }\n mentionsRange.value = getOffsetsRange(editor, offsets)\n }\n } else {\n computeMention()\n }\n\n // Commit the changes\n commit()\n}\n\n\n/* Update the `selected` Range when the document selection changes */\nfunction onSelectionChange(_event: Event): void {\n // Wipe all link information if we leave our component\n if (!(rootRef.value && getSelectionRange(rootRef.value))) {\n selectedLink.value = null\n previousLink.value = null\n activeLink.value = null\n }\n\n // Handle the current editor selection and remember its range (if any)\n selected.value = editorRef.value ? getSelectionRange(editorRef.value) : null\n\n // Process any changes to mentions\n computeMention()\n}\n\n/* ===== WATCHES AND COMPONENT LIFECYCLE ==================================== */\n\nonMounted(() => {\n // Bind DOM events\n document.addEventListener('selectionchange', onSelectionChange)\n\n // References to our editor, popup, and list\n if (! rootRef.value) throw new Error('Root not referenced')\n if (! editorRef.value) throw new Error('Editor not referenced')\n if (! mentionsPopupRef.value) throw new Error('Popup not referenced')\n if (! mentionsListRef.value) throw new Error('List not referenced')\n if (! linksPopupRef.value) throw new Error('Popup not referenced')\n const root = rootRef.value\n const editor = editorRef.value\n const mentionsPopup = mentionsPopupRef.value\n const mentionsList = mentionsListRef.value\n const linksPopup = linksPopupRef.value\n\n /* On changes to the _local_ HTML, trigger an emit on our model value */\n watch(html, (html) => {\n if (html !== model.value) model.value = html // no changes, ignore!\n })\n\n /* Watch our model, when the value is different from the local HTML, parse */\n watch(model, (model) => {\n if (model === html.value) return // no changes, ignore!\n\n const body = new DOMParser().parseFromString(model, 'text/html').body\n sanitize(body)\n editor.replaceChildren(...body.childNodes)\n commit()\n }, { immediate: true })\n\n /* Watch our mentions availability and trigger a waiting animation */\n watch(mentionsReady, (available) => {\n dots.value = 0\n if (! available) dotsInterval = setInterval(() => dots.value = dots.value + 1, 200)\n else if (dotsInterval) clearInterval(dotsInterval)\n }, { immediate: true })\n\n /* Watch our links and update the popup */\n watch(isLink, (link) => {\n // Always update (or reset?) the link text and href of a link\n linkText.value = link?.textContent || ''\n linkHref.value = link?.getAttribute('href') || ''\n\n // If the link is a placeholder, consider this to be an empty link\n if (linkHref.value === 'http://__placeholder__/') {\n link?.setAttribute('href', '')\n linkHref.value = ''\n }\n\n // If we are no longer editing a link, sanitize and hide the popup\n if (! link) {\n const offsets = getSelectionOffsets(editor)\n sanitize(editor)\n if (offsets) restoreSelection(editor, offsets)\n\n linksPopup.style.display = 'none'\n linksPopup.style.top = '0'\n linksPopup.style.left = '0'\n return\n }\n\n // Wait for the next tick to make sure the DOM is updated\n nextTick(() => {\n // Position and show the popup\n linksPopup.style.display = 'flex'\n\n const rootRect = root.getBoundingClientRect()\n const rangeRect = link.getBoundingClientRect()\n const editorRect = editor.getBoundingClientRect()\n\n const popupStyle = getComputedStyle(linksPopup)\n const popupWidth = parseInt(popupStyle.width)\n const popupHeight = parseInt(popupStyle.height)\n\n const fitsTop = (rangeRect.top - popupHeight) - 6 > editorRect.top\n const fitsBottom = (rangeRect.bottom + popupHeight + 3) < editorRect.bottom\n const fitsRight = rangeRect.left + popupWidth < editorRect.right\n\n linksPopup.style.top = (! fitsBottom && fitsTop) ?\n `${rangeRect.top - popupHeight - rootRect.top - 6}px` : // over the link\n `${rangeRect.bottom - rootRect.top + 3}px` // below the link\n linksPopup.style.left = fitsRight ?\n `${rangeRect.left - rootRect.left}px` : // right of the link\n `${editorRect.right - popupWidth - rootRect.left}px` // aligned to the right of the editor\n })\n }, { immediate: true })\n\n /* Watch our mentions index and update the popup */\n watch([ mentionsIndex, mentionsRange, mentionsEntries ], ([ index, range, entries ], [ oldIndex = -1 ]) => {\n // Make sure that our index is within acceptable bounds\n if (index >= entries.length) index = Math.max(entries.length - 1, 0)\n mentionsIndex.value = index\n\n // No list or no popup, nothing to do\n if (! mentionsPopup) return\n if (! mentionsList) return\n\n // No range, hide and reset the popup\n if (! range) {\n mentionsIndex.value = 0\n mentionsPopup.style.display = 'none'\n mentionsPopup.style.top = '0'\n mentionsPopup.style.left = '0'\n mentionsList.scrollTop = 0\n return\n }\n\n // Wait for the next tick to make sure the DOM is updated\n nextTick(() => {\n // Position and show the popup\n mentionsPopup.style.display = 'block'\n\n const rootRect = root.getBoundingClientRect()\n const rangeRect = range.getBoundingClientRect()\n const editorRect = editor.getBoundingClientRect()\n\n const popupStyle = getComputedStyle(mentionsPopup)\n const popupWidth = parseInt(popupStyle.width)\n const popupHeight = parseInt(popupStyle.height)\n\n const fitsTop = (rangeRect.top - popupHeight) - 6 > editorRect.top\n const fitsBottom = (rangeRect.bottom + popupHeight + 3) < editorRect.bottom\n const fitsRight = rangeRect.left + popupWidth < editorRect.right\n\n mentionsPopup.style.top = (! fitsBottom && fitsTop) ?\n `${rangeRect.top - popupHeight - rootRect.top - 6}px` : // over the link\n `${rangeRect.bottom - rootRect.top + 3}px` // below the link\n mentionsPopup.style.left = fitsRight ?\n `${rangeRect.left - rootRect.left}px` : // right of the link\n `${editorRect.right - popupWidth - rootRect.left}px` // aligned to the right of the editor\n\n // Scroll the list to make sure the selected item is visible\n const item = mentionsList.querySelectorAll('li')[mentionsIndex.value]\n if (! item) return\n\n const style = getComputedStyle(item)\n const topBorder = parseFloat(style.getPropertyValue('border-top-width')) || 0\n const bottomBorder= parseFloat(style.getPropertyValue('border-bottom-width')) || 0\n\n // Get the bounding rectangles for the list and the selected item\n const listRect = mentionsList.getBoundingClientRect()\n const itemRect = item.getBoundingClientRect()\n\n // Some CSS magic to scroll the list\n const topTop = itemRect.top - listRect.top\n const btmBtm = itemRect.bottom - listRect.bottom\n const relativeBtm = itemRect.bottom - listRect.top\n\n // Going down\n if (index > oldIndex) {\n // Element is hidden below the visible list\n if (relativeBtm > listRect.height) {\n mentionsList.scrollTop += btmBtm - bottomBorder\n // Element is hidden above the visible list\n } else if (topTop < 0) {\n mentionsList.scrollTop += topTop + topBorder\n }\n // Going up\n } else {\n // Element is hidden above the visible list\n if (topTop < 0) {\n mentionsList.scrollTop += topTop + topBorder\n // Element is hidden below the visible list\n } else if (btmBtm > 0) {\n mentionsList.scrollTop += relativeBtm - listRect.height - bottomBorder\n }\n }\n })\n }, { immediate: true })\n\n /* Watch the selected range and infer state for bold, italic, ... */\n watch(selected, (selected) => {\n if (selected) {\n selectedLink.value = isTagged(editor, selected, 'a')\n isBold.value = isTagged(editor, selected, 'b')\n isItalic.value = isTagged(editor, selected, 'i')\n } else {\n selectedLink.value = null\n isBold.value = null\n isItalic.value = null\n }\n }, { immediate: true })\n})\n\nonUnmounted(() => {\n document.removeEventListener('selectionchange', onSelectionChange)\n if (dotsInterval) clearInterval(dotsInterval)\n})\n\n/* ===== EMITS AND EXPOSED METHODS ========================================== */\n\nconst emit = defineEmits<{\n mention: [ string ],\n submit: [ string ],\n}>()\n\n/* Watch our local refs and emit */\nwatch(mentionsText, (text) => emit('mention', text.substring(1)), { immediate: true })\n</script>\n\n<style lang=\"pcss\">\n:root {\n --jte-border-radius: 4px;\n\n /* ===== LIGHT THEME ====================================================== */\n\n /* main colors */\n --jte-text: currentColor;\n --jte-background: transparent;\n --jte-active: #009;\n --jte-mention: #99f;\n --jte-popup: #fff;\n --jte-link: #009;\n --jte-error: #933;\n\n /* derived colors */\n --jte-inactive: color-mix(in srgb, var(--jte-text) 50%, transparent);\n --jte-border: color-mix(in srgb, var(--jte-text) 12.5%, transparent);\n --jte-shade: color-mix(in srgb, var(--jte-text) 6.25%, transparent);\n --jte-active-shade: color-mix(in srgb, var(--jte-active) 6.25%, transparent);\n\n /* button colors */\n --jte-button-text: var(--jte-text);\n --jte-button-border: var(--jte-border);\n --jte-button-background: var(--jte-background);\n\n --jte-button-hovered-text: var(--jte-text);\n --jte-button-hovered-border: var(--jte-border);\n --jte-button-hovered-background: var(--jte-shade);\n\n --jte-button-active-text: var(--jte-active);\n --jte-button-active-border: var(--jte-border);\n --jte-button-active-background: var(--jte-active-shade);\n\n --jte-button-disabled-text: var(--jte-inactive);\n --jte-button-disabled-border: var(--jte-border);\n --jte-button-disabled-background: var(--jte-shade);\n\n /* inputs within popup */\n --jte-input-text: var(--jte-text);\n --jte-input-background: transparent;\n --jte-input-border: var(--jte-border);\n --jte-input-shade: var(--jte-shade);\n\n /* ===== DARK THEME ======================================================= */\n\n /* main colors */\n --jte-dark-text: currentColor;\n --jte-dark-background: transparent;\n --jte-dark-active: #99f;\n --jte-dark-mention: #669;\n --jte-dark-popup: #111;\n --jte-dark-link: #99f;\n --jte-dark-error: #f99;\n --jte-dark-input: transparent;\n\n /* derived colors */\n --jte-dark-inactive: color-mix(in srgb, var(--jte-dark-text) 50%, transparent);\n --jte-dark-border: color-mix(in srgb, var(--jte-dark-text) 12.5%, transparent);\n --jte-dark-shade: color-mix(in srgb, var(--jte-dark-text) 12.5%, transparent);\n --jte-dark-active-shade: color-mix(in srgb, var(--jte-dark-active) 6.25%, transparent);\n\n /* button colors */\n --jte-dark-button-text: var(--jte-dark-text);\n --jte-dark-button-border: var(--jte-dark-border);\n --jte-dark-button-background: var(--jte-dark-background);\n\n --jte-dark-button-hovered-text: var(--jte-dark-text);\n --jte-dark-button-hovered-border: var(--jte-dark-border);\n --jte-dark-button-hovered-background: var(--jte-dark-shade);\n\n --jte-dark-button-active-text: var(--jte-dark-active);\n --jte-dark-button-active-border: var(--jte-dark-border);\n --jte-dark-button-active-background: var(--jte-dark-active-shade);\n\n --jte-dark-button-disabled-text: var(--jte-dark-inactive);\n --jte-dark-button-disabled-border: var(--jte-dark-border);\n --jte-dark-button-disabled-background: var(--jte-dark-shade);\n\n /* inputs within popup */\n --jte-dark-input-text: var(--jte-dark-text);\n --jte-dark-input-background: transparent;\n --jte-dark-input-border: var(--jte-dark-border);\n --jte-dark-input-shade: var(--jte-dark-shade);\n}\n\n/* ========================================================================== *\n * EDITOR STYLING *\n * ========================================================================== */\n\n.juit-tiny-edit {\n\n /* ===== APPLY DARK THEME ================================================= */\n\n &.-jte-dark .-jte-root {\n /* main colors */\n --jte-text: var(--jte-dark-text);\n --jte-background: var(--jte-dark-background);\n --jte-active: var(--jte-dark-active);\n --jte-mention: var(--jte-dark-mention);\n --jte-popup: var(--jte-dark-popup);\n --jte-link: var(--jte-dark-link);\n --jte-error: var(--jte-dark-error);\n\n /* derived colors */\n --jte-inactive: var(--jte-dark-inactive);\n --jte-border: var(--jte-dark-border);\n --jte-shade: var(--jte-dark-shade);\n --jte-active-shade: var(--jte-dark-active-shade);\n\n /* button colors */\n --jte-button-text: var(--jte-dark-button-text);\n --jte-button-border: var(--jte-dark-button-border);\n --jte-button-background: var(--jte-dark-button-background);\n\n --jte-button-hovered-text: var(--jte-dark-button-hovered-text);\n --jte-button-hovered-border: var(--jte-dark-button-hovered-border);\n --jte-button-hovered-background: var(--jte-dark-button-hovered-background);\n\n --jte-button-active-text: var(--jte-dark-button-active-text);\n --jte-button-active-border: var(--jte-dark-button-active-border);\n --jte-button-active-background: var(--jte-dark-button-active-background);\n\n --jte-button-disabled-text: var(--jte-dark-button-disabled-text);\n --jte-button-disabled-border: var(--jte-dark-button-disabled-border);\n --jte-button-disabled-background: var(--jte-dark-button-disabled-background);\n\n /* inputs within popup */\n --jte-input-text: var(--jte-dark-input-text);\n --jte-input-background: var(--jte-dark-input-background);\n --jte-input-border: var(--jte-dark-input-border);\n --jte-input-shade: var(--jte-dark-input-shade);\n }\n\n /* ===== STYLE OUR EDITOR ================================================= */\n\n .-jte-root {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n position: relative;\n box-sizing: content-box;\n\n color: var(--jte-text);\n background-color: var(--jte-background);\n\n /* ===== TOOLBAR ======================================================== */\n\n .-jte-toolbox {\n display: none;\n user-select: none;\n position: relative;\n height: 1.75em;\n overflow: hidden;\n margin-top: 0.75em;\n padding: 0 0.25em 0 0.25em;\n\n .-jte-icon {\n width: 1em;\n height: 1em;\n padding: 0.125em 0.25em;\n margin: 0 0.25em;\n vertical-align: top;\n\n color: var(--jte-button-text);\n background-color: var(--jte-button-background);\n border: 1px solid var(--jte-button-border);\n border-radius: var(--jte-border-radius);\n\n &.-jte-icon-mention {\n margin-left: 0.125em;\n }\n\n &.-jte-icon-send {\n margin-left: auto;\n margin-right: 0.125em;\n padding-left: 1em;\n padding-right: 1em;\n }\n\n &.-jte-disabled {\n color: var(--jte-button-disabled-text);\n border-color: var(--jte-button-disabled-border);\n background-color: var(--jte-button-disabled-background);\n }\n\n &.-jte-active {\n color: var(--jte-button-active-text);\n border-color: var(--jte-button-active-border);\n background-color: var(--jte-button-active-background);\n }\n\n &:active:not(.-jte-disabled) {\n color: var(--jte-button-active-text);\n border-color: var(--jte-button-active-border);\n background-color: var(--jte-button-active-background);\n }\n\n &:hover:not(.-jte-disabled):not(.-jte-active):not(:active) {\n color: var(--jte-button-hovered-text);\n border-color: var(--jte-button-hovered-border);\n background-color: var(--jte-button-hovered-background);\n }\n }\n }\n\n /* ===== PLACEHOLDER ==================================================== */\n\n .-jte-placeholder {\n position: absolute;\n font-style: italic;\n padding: 0.25em;\n pointer-events: none;\n color: var(--jte-inactive);\n }\n\n /* ===== EDITOR ========================================================= */\n\n .-jte-editor {\n width: 100%;\n box-sizing: border-box;\n padding: 0.25em;\n white-space: pre-wrap;\n\n border-radius: var(--jte-border-radius);\n border: 1px solid var(--jte-border);\n\n &:focus {\n outline: 1px solid var(--jte-active);\n border: 1px solid var(--jte-active);\n }\n\n /** Links */\n a {\n color: var(--jte-link);\n }\n\n /** Our mentions (this needs to be \"inline\" for contenteditable to work) */\n link[rel=\"mention\"] {\n display: inline;\n\n cursor: default;\n user-select: none;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n\n font-size: 0.85em;\n overflow: hidden;\n\n &:before {\n padding: 0 0.25em;\n content: '\\200b'; /* zero width space */\n\n background-color: var(--jte-mention);\n border: 1px solid var(--jte-border);\n border-radius: var(--jte-border-radius);\n border-right: none;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n\n &:after {\n content: attr(title);\n padding: 0 0.5em 0 0.25em;\n\n background-color: var(--jte-shade);\n border: 1px solid var(--jte-border);\n border-radius: var(--jte-border-radius);\n border-left: none;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n }\n }\n\n /* ===== POPUPS ========================================================= */\n\n /** Popups basics */\n .-jte-mentions-popup, .-jte-links-popup {\n display: none;\n position: absolute;\n z-index: 1000;\n\n font-size: 0.85em;\n background-color: var(--jte-popup);\n border-radius: var(--jte-border-radius);\n border: 1px solid var(--jte-border);\n }\n\n /* ===== MENTIONS POPUP ================================================= */\n\n /** Our mentions popup */\n .-jte-mentions-popup {\n overflow: hidden;\n cursor: pointer;\n min-width: 8em;\n\n /** Our list of mentions */\n ul {\n overflow: scroll;\n max-height: calc(7.5em + 3px);\n white-space: nowrap;\n margin: 0;\n padding: 0;\n }\n\n /** Each mention entry */\n li {\n list-style-type: none;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n color: var(--jte-inactive);\n\n /** Inside of our mentions entry */\n .-jte-mentions-entry {\n padding-block: 0.125em;\n padding-left: 0.25em;\n padding-right: 1.5em;\n border-left-width: 0.5em;\n border-left-style: solid;\n border-left-color: transparent;\n\n /** Refs want a smaller font */\n .-jte-mentions-ref {\n margin-left: 0.5em;\n font-size: 0.85em;\n font-style: italic;\n }\n }\n\n /** Selected list item */\n &.-jte-mentions-selected {\n color: var(--jte-input-text);\n background-color: var(--jte-shade);\n border-color: var(--jte-input-border);\n\n /** Apply our \"tag\" to the slected mention */\n .-jte-mentions-entry {\n border-color: var(--jte-mention);\n }\n }\n\n /** No borders for first and last item */\n &:first-child { border-top: none; }\n &:last-child { border-bottom: none; }\n }\n\n /** Basic style for waiting / no item found */\n .-jte-mentions-waiting, .-jte-mentions-none {\n font-size: 0.85em;\n color: var(--jte-inactive);\n border-left-width: 0.5em;\n border-left-style: solid;\n border-color: transparent;\n padding-block: 0.125em;\n padding-inline: 0.25em;\n }\n\n /** Waiting is in italic */\n .-jte-mentions-waiting {\n font-style: italic;\n }\n\n /** None has a grey tab */\n .-jte-mentions-none {\n border-color: var(--jte-shade);\n }\n }\n\n /* ===== LINKS POPUP ==================================================== */\n\n .-jte-links-popup {\n user-select: none;\n padding: 0.125em;\n width: 20em;\n flex-direction: column;\n\n div {\n display: flex;\n flex-direction: row;\n margin: 0.25em;\n position: relative;\n background-color: var(--jte-input-background);\n border-radius: var(--jte-border-radius);\n }\n\n .-jte-icon {\n display: block;\n position: absolute;\n\n top: 1px;\n left: 1px;\n\n width: 1.25em;\n height: 1.25em;\n padding: 0.125em 0.25em;\n margin: 0;\n\n color: var(--jte-input-text);\n background-color: var(--jte-input-shade);\n border: none;\n border-radius: calc(var(--jte-border-radius) - 1px);\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n border-right: none;\n }\n\n input {\n display: block;\n flex-grow: 1;\n\n font-size: 1em;\n height: 1.25em;\n padding: 0.125em 0.25em 0.125em 2.25em;\n margin: 0;\n\n color: var(--jte-input-text);\n border: 1px solid var(--jte-input-border);\n border-radius: var(--jte-border-radius);\n background-color: transparent;\n\n &:focus {\n outline: 1px solid var(--jte-active);\n border: 1px solid var(--jte-active);\n }\n }\n\n .-jte-error {\n .-jte-icon, input {\n color: var(--jte-error);\n }\n }\n }\n }\n\n /* ===== TOOLBOX VISIBLE ONLY ON EDITABLE ================================= */\n\n &.-jte-editable {\n .-jte-editor {\n padding-bottom: 2.5em;\n margin-bottom: -2.5em;\n }\n\n .-jte-toolbox {\n display: flex;\n }\n }\n}\n</style>\n"],"names":["containsRange","haystack","needle","rangeFromContents","rangeFromNode","node","range","getCharacters","parent","child","offset","getRangeOffsets","result","getOffsetsRange","offsets","start","end","firstNode","lastNode","foundStart","foundEnd","iterator","text","length","STRIP_TAGS","parseURL","string","maybeWrap","fragment","tagName","noWrap","tag","sanitizeStyles","state","element","previous","_a","name","title","link","mergeOffsets","list","a","b","nested","i","outer","j","inner","sanitizeLinks","textRanges","re","plain","match","href","linkRanges","sanitizeEmpty","sanitize","sanitized","wrapped","replaceRange","html","body","beforeLength","parentRange","beforeRange","afterRange","before","after","afterLength","_b","getSelectionRange","selection","selectionRange","backwards","getSelectionOffsets","restoreSelection","collapse","startContainer","startOffset","endContainer","endOffset","isTagged","ancestor","toggleTag","attributes","ancestorRange","afterFragment","selectedFragment","beforeFragment","attr","_hoisted_1","render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","iconBold","iconItalic","iconLink","iconMention","iconSend","iconText","model","_useModel","__props","props","watch","dark","rootRef","shallowRef","editorRef","mentionsPopupRef","mentionsListRef","linksPopupRef","selected","ref","mentionsIndex","mentionsRange","mentionsText","computed","mentionsReady","mentionsEntries","_","value","selectedLink","activeLink","previousLink","isLink","linkText","linkHref","linkValid","commit","isBold","isItalic","dots","dotsString","dotsInterval","nextTag","newRange","oldRange","history","iconClass","historySave","historyUndo","entry","computeMention","applyMention","editor","selectWithOffsets","applyTag","insertMention","at","submit","emit","onKeydown","event","mention","onPaste","data","onBeforeInput","onInput","mentions","onSelectionChange","_event","onMounted","root","mentionsPopup","mentionsList","linksPopup","available","nextTick","rootRect","rangeRect","editorRect","popupStyle","popupWidth","popupHeight","fitsTop","fitsBottom","fitsRight","index","entries","oldIndex","item","style","topBorder","bottomBorder","listRect","itemRect","topTop","btmBtm","relativeBtm","onUnmounted","__emit"],"mappings":"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"}
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ :root{--jte-border-radius: 4px;--jte-text: currentColor;--jte-background: transparent;--jte-active: #009;--jte-mention: #99f;--jte-popup: #fff;--jte-link: #009;--jte-error: #933;--jte-inactive: color-mix(in srgb, var(--jte-text) 50%, transparent);--jte-border: color-mix(in srgb, var(--jte-text) 12.5%, transparent);--jte-shade: color-mix(in srgb, var(--jte-text) 6.25%, transparent);--jte-active-shade: color-mix(in srgb, var(--jte-active) 6.25%, transparent);--jte-button-text: var(--jte-text);--jte-button-border: var(--jte-border);--jte-button-background: var(--jte-background);--jte-button-hovered-text: var(--jte-text);--jte-button-hovered-border: var(--jte-border);--jte-button-hovered-background: var(--jte-shade);--jte-button-active-text: var(--jte-active);--jte-button-active-border: var(--jte-border);--jte-button-active-background: var(--jte-active-shade);--jte-button-disabled-text: var(--jte-inactive);--jte-button-disabled-border: var(--jte-border);--jte-button-disabled-background: var(--jte-shade);--jte-input-text: var(--jte-text);--jte-input-background: transparent;--jte-input-border: var(--jte-border);--jte-input-shade: var(--jte-shade);--jte-dark-text: currentColor;--jte-dark-background: transparent;--jte-dark-active: #99f;--jte-dark-mention: #669;--jte-dark-popup: #111;--jte-dark-link: #99f;--jte-dark-error: #f99;--jte-dark-input: transparent;--jte-dark-inactive: color-mix(in srgb, var(--jte-dark-text) 50%, transparent);--jte-dark-border: color-mix(in srgb, var(--jte-dark-text) 12.5%, transparent);--jte-dark-shade: color-mix(in srgb, var(--jte-dark-text) 12.5%, transparent);--jte-dark-active-shade: color-mix(in srgb, var(--jte-dark-active) 6.25%, transparent);--jte-dark-button-text: var(--jte-dark-text);--jte-dark-button-border: var(--jte-dark-border);--jte-dark-button-background: var(--jte-dark-background);--jte-dark-button-hovered-text: var(--jte-dark-text);--jte-dark-button-hovered-border: var(--jte-dark-border);--jte-dark-button-hovered-background: var(--jte-dark-shade);--jte-dark-button-active-text: var(--jte-dark-active);--jte-dark-button-active-border: var(--jte-dark-border);--jte-dark-button-active-background: var(--jte-dark-active-shade);--jte-dark-button-disabled-text: var(--jte-dark-inactive);--jte-dark-button-disabled-border: var(--jte-dark-border);--jte-dark-button-disabled-background: var(--jte-dark-shade);--jte-dark-input-text: var(--jte-dark-text);--jte-dark-input-background: transparent;--jte-dark-input-border: var(--jte-dark-border);--jte-dark-input-shade: var(--jte-dark-shade)}.juit-tiny-edit.-jte-dark .-jte-root{--jte-text: var(--jte-dark-text);--jte-background: var(--jte-dark-background);--jte-active: var(--jte-dark-active);--jte-mention: var(--jte-dark-mention);--jte-popup: var(--jte-dark-popup);--jte-link: var(--jte-dark-link);--jte-error: var(--jte-dark-error);--jte-inactive: var(--jte-dark-inactive);--jte-border: var(--jte-dark-border);--jte-shade: var(--jte-dark-shade);--jte-active-shade: var(--jte-dark-active-shade);--jte-button-text: var(--jte-dark-button-text);--jte-button-border: var(--jte-dark-button-border);--jte-button-background: var(--jte-dark-button-background);--jte-button-hovered-text: var(--jte-dark-button-hovered-text);--jte-button-hovered-border: var(--jte-dark-button-hovered-border);--jte-button-hovered-background: var(--jte-dark-button-hovered-background);--jte-button-active-text: var(--jte-dark-button-active-text);--jte-button-active-border: var(--jte-dark-button-active-border);--jte-button-active-background: var(--jte-dark-button-active-background);--jte-button-disabled-text: var(--jte-dark-button-disabled-text);--jte-button-disabled-border: var(--jte-dark-button-disabled-border);--jte-button-disabled-background: var(--jte-dark-button-disabled-background);--jte-input-text: var(--jte-dark-input-text);--jte-input-background: var(--jte-dark-input-background);--jte-input-border: var(--jte-dark-input-border);--jte-input-shade: var(--jte-dark-input-shade)}.juit-tiny-edit .-jte-root{display:flex;flex-direction:column;align-items:stretch;position:relative;box-sizing:content-box;color:var(--jte-text);background-color:var(--jte-background)}.juit-tiny-edit .-jte-root .-jte-toolbox{display:none;-webkit-user-select:none;user-select:none;position:relative;height:1.75em;overflow:hidden;margin-top:.75em;padding:0 .25em}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon{width:1em;height:1em;padding:.125em .25em;margin:0 .25em;vertical-align:top;color:var(--jte-button-text);background-color:var(--jte-button-background);border:1px solid var(--jte-button-border);border-radius:var(--jte-border-radius)}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon.-jte-icon-mention{margin-left:.125em}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon.-jte-icon-send{margin-left:auto;margin-right:.125em;padding-left:1em;padding-right:1em}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon.-jte-disabled{color:var(--jte-button-disabled-text);border-color:var(--jte-button-disabled-border);background-color:var(--jte-button-disabled-background)}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon.-jte-active{color:var(--jte-button-active-text);border-color:var(--jte-button-active-border);background-color:var(--jte-button-active-background)}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon:active:not(.-jte-disabled){color:var(--jte-button-active-text);border-color:var(--jte-button-active-border);background-color:var(--jte-button-active-background)}.juit-tiny-edit .-jte-root .-jte-toolbox .-jte-icon:hover:not(.-jte-disabled):not(.-jte-active):not(:active){color:var(--jte-button-hovered-text);border-color:var(--jte-button-hovered-border);background-color:var(--jte-button-hovered-background)}.juit-tiny-edit .-jte-root .-jte-placeholder{position:absolute;font-style:italic;padding:.25em;pointer-events:none;color:var(--jte-inactive)}.juit-tiny-edit .-jte-root .-jte-editor{width:100%;box-sizing:border-box;padding:.25em;white-space:pre-wrap;border-radius:var(--jte-border-radius);border:1px solid var(--jte-border)}.juit-tiny-edit .-jte-root .-jte-editor:focus{outline:1px solid var(--jte-active);border:1px solid var(--jte-active)}.juit-tiny-edit .-jte-root .-jte-editor a{color:var(--jte-link)}.juit-tiny-edit .-jte-root .-jte-editor link[rel=mention]{display:inline;cursor:default;-webkit-user-select:none;user-select:none;font-weight:400;font-style:normal;text-decoration:none;font-size:.85em;overflow:hidden}.juit-tiny-edit .-jte-root .-jte-editor link[rel=mention]:before{padding:0 .25em;content:"​";background-color:var(--jte-mention);border:1px solid var(--jte-border);border-radius:var(--jte-border-radius);border-right:none;border-top-right-radius:0;border-bottom-right-radius:0}.juit-tiny-edit .-jte-root .-jte-editor link[rel=mention]:after{content:attr(title);padding:0 .5em 0 .25em;background-color:var(--jte-shade);border:1px solid var(--jte-border);border-radius:var(--jte-border-radius);border-left:none;border-top-left-radius:0;border-bottom-left-radius:0}.juit-tiny-edit .-jte-root .-jte-mentions-popup,.juit-tiny-edit .-jte-root .-jte-links-popup{display:none;position:absolute;z-index:1000;font-size:.85em;background-color:var(--jte-popup);border-radius:var(--jte-border-radius);border:1px solid var(--jte-border)}.juit-tiny-edit .-jte-root .-jte-mentions-popup{overflow:hidden;cursor:pointer;min-width:8em}.juit-tiny-edit .-jte-root .-jte-mentions-popup ul{overflow:scroll;max-height:calc(7.5em + 3px);white-space:nowrap;margin:0;padding:0}.juit-tiny-edit .-jte-root .-jte-mentions-popup li{list-style-type:none;border-top:1px solid transparent;border-bottom:1px solid transparent;color:var(--jte-inactive)}.juit-tiny-edit .-jte-root .-jte-mentions-popup li .-jte-mentions-entry{padding-block:.125em;padding-left:.25em;padding-right:1.5em;border-left-width:.5em;border-left-style:solid;border-left-color:transparent}.juit-tiny-edit .-jte-root .-jte-mentions-popup li .-jte-mentions-entry .-jte-mentions-ref{margin-left:.5em;font-size:.85em;font-style:italic}.juit-tiny-edit .-jte-root .-jte-mentions-popup li.-jte-mentions-selected{color:var(--jte-input-text);background-color:var(--jte-shade);border-color:var(--jte-input-border)}.juit-tiny-edit .-jte-root .-jte-mentions-popup li.-jte-mentions-selected .-jte-mentions-entry{border-color:var(--jte-mention)}.juit-tiny-edit .-jte-root .-jte-mentions-popup li:first-child{border-top:none}.juit-tiny-edit .-jte-root .-jte-mentions-popup li:last-child{border-bottom:none}.juit-tiny-edit .-jte-root .-jte-mentions-popup .-jte-mentions-waiting,.juit-tiny-edit .-jte-root .-jte-mentions-popup .-jte-mentions-none{font-size:.85em;color:var(--jte-inactive);border-left-width:.5em;border-left-style:solid;border-color:transparent;padding-block:.125em;padding-inline:.25em}.juit-tiny-edit .-jte-root .-jte-mentions-popup .-jte-mentions-waiting{font-style:italic}.juit-tiny-edit .-jte-root .-jte-mentions-popup .-jte-mentions-none{border-color:var(--jte-shade)}.juit-tiny-edit .-jte-root .-jte-links-popup{-webkit-user-select:none;user-select:none;padding:.125em;width:20em;flex-direction:column}.juit-tiny-edit .-jte-root .-jte-links-popup div{display:flex;flex-direction:row;margin:.25em;position:relative;background-color:var(--jte-input-background);border-radius:var(--jte-border-radius)}.juit-tiny-edit .-jte-root .-jte-links-popup .-jte-icon{display:block;position:absolute;top:1px;left:1px;width:1.25em;height:1.25em;padding:.125em .25em;margin:0;color:var(--jte-input-text);background-color:var(--jte-input-shade);border:none;border-radius:calc(var(--jte-border-radius) - 1px);border-top-right-radius:0;border-bottom-right-radius:0;border-right:none}.juit-tiny-edit .-jte-root .-jte-links-popup input{display:block;flex-grow:1;font-size:1em;height:1.25em;padding:.125em .25em .125em 2.25em;margin:0;color:var(--jte-input-text);border:1px solid var(--jte-input-border);border-radius:var(--jte-border-radius);background-color:transparent}.juit-tiny-edit .-jte-root .-jte-links-popup input:focus{outline:1px solid var(--jte-active);border:1px solid var(--jte-active)}.juit-tiny-edit .-jte-root .-jte-links-popup .-jte-error .-jte-icon,.juit-tiny-edit .-jte-root .-jte-links-popup .-jte-error input{color:var(--jte-error)}.juit-tiny-edit.-jte-editable .-jte-editor{padding-bottom:2.5em;margin-bottom:-2.5em}.juit-tiny-edit.-jte-editable .-jte-toolbox{display:flex}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@juit/vue-tiny-editor",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ },
10
+ "./style.css": "./dist/style.css"
11
+ },
12
+ "scripts": {
13
+ "build": "npm run check && vite build --mode=demo && vite build --mode=production",
14
+ "build-demo": "npm run check && vite build --mode=demo",
15
+ "build-lib": "npm run check && vite build --mode=production",
16
+ "check": "vue-tsc --build --force && vitest run --coverage && eslint",
17
+ "deploy-dev": "wrangler deploy",
18
+ "deploy-prod": "wrangler deploy --env=production",
19
+ "dev": "vite",
20
+ "test": "vitest"
21
+ },
22
+ "peerDependencies": {
23
+ "vue": "^3.5.12"
24
+ },
25
+ "devDependencies": {
26
+ "@plugjs/eslint-plugin": "^0.2.24",
27
+ "@types/node": "<21",
28
+ "@vitejs/plugin-vue": "^5.1.4",
29
+ "@vitest/coverage-v8": "^2.1.4",
30
+ "@vue/test-utils": "^2.4.6",
31
+ "eslint": "^9.14.0",
32
+ "eslint-plugin-vue": "^9.30.0",
33
+ "jsdom": "^25.0.1",
34
+ "typescript": "^5.6.3",
35
+ "vite": "^5.4.10",
36
+ "vite-plugin-dts": "^4.3.0",
37
+ "vite-svg-loader": "^5.1.0",
38
+ "vitest": "^2.1.4",
39
+ "vue": "^3.5.12",
40
+ "vue-tsc": "^2.1.10"
41
+ },
42
+ "author": "Juit Developers <developers@juit.com>",
43
+ "license": "Apache-2.0",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+ssh://git@github.com/juitnow/juit-vue-tiny-editor.git"
47
+ },
48
+ "keywords": [
49
+ "vue",
50
+ "mentions",
51
+ "editor",
52
+ "rich-text"
53
+ ],
54
+ "bugs": {
55
+ "url": "https://github.com/juitnow/juit-vue-tiny-editor/issues"
56
+ },
57
+ "directories": {
58
+ "test": "test"
59
+ },
60
+ "files": [
61
+ "dist"
62
+ ]
63
+ }