@milkdown/plugin-emoji 6.5.4 → 7.0.0-next.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/lib/index.d.ts +14 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.es.js +100 -275
- package/lib/index.es.js.map +1 -1
- package/lib/parse.d.ts.map +1 -1
- package/lib/remark-twemoji.d.ts +1 -1
- package/lib/remark-twemoji.d.ts.map +1 -1
- package/package.json +11 -7
- package/src/index.ts +99 -4
- package/src/parse.ts +1 -1
- package/src/remark-twemoji.ts +1 -1
- package/lib/constant.d.ts +0 -4
- package/lib/constant.d.ts.map +0 -1
- package/lib/filter/helper.d.ts +0 -5
- package/lib/filter/helper.d.ts.map +0 -1
- package/lib/filter/index.d.ts +0 -5
- package/lib/filter/index.d.ts.map +0 -1
- package/lib/filter/style.d.ts +0 -3
- package/lib/filter/style.d.ts.map +0 -1
- package/lib/node.d.ts +0 -6
- package/lib/node.d.ts.map +0 -1
- package/src/constant.ts +0 -4
- package/src/filter/helper.ts +0 -83
- package/src/filter/index.ts +0 -217
- package/src/filter/style.ts +0 -61
- package/src/node.ts +0 -103
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { MilkdownPlugin } from '@milkdown/ctx';
|
|
2
|
+
import type Twemoji from 'twemoji';
|
|
3
|
+
type TwemojiOptions = Exclude<Parameters<typeof Twemoji.parse>[1], Function | undefined>;
|
|
4
|
+
export interface EmojiConfig {
|
|
5
|
+
twemojiOptions?: TwemojiOptions;
|
|
6
|
+
}
|
|
7
|
+
export declare const emojiConfig: import("@milkdown/utils").$Ctx<EmojiConfig, "emojiConfig">;
|
|
8
|
+
export declare const emojiAttr: import("@milkdown/utils").$NodeAttr;
|
|
9
|
+
export declare const emojiSchema: import("@milkdown/utils").$NodeSchema<"emoji">;
|
|
10
|
+
export declare const insertEmojiInputRule: import("@milkdown/utils").$InputRule;
|
|
11
|
+
export declare const remarkEmojiPlugin: import("@milkdown/utils").$Remark;
|
|
12
|
+
export declare const remarkTwemojiPlugin: import("@milkdown/utils").$Remark;
|
|
13
|
+
export declare const emoji: MilkdownPlugin[];
|
|
14
|
+
export {};
|
|
4
15
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAOnD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAKlC,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAA;AAExF,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAED,eAAO,MAAM,WAAW,4DAAsD,CAAA;AAE9E,eAAO,MAAM,SAAS,qCAGnB,CAAA;AAEH,eAAO,MAAM,WAAW,gDA+CrB,CAAA;AAEH,eAAO,MAAM,oBAAoB,sCAc9B,CAAA;AAEH,eAAO,MAAM,iBAAiB,mCAA6C,CAAA;AAC3E,eAAO,MAAM,mBAAmB,mCAAyE,CAAA;AAEzG,eAAO,MAAM,KAAK,EAAE,cAAc,EAO1B,CAAA"}
|
package/lib/index.es.js
CHANGED
|
@@ -1,289 +1,114 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const m = t[0];
|
|
23
|
-
return l(s - (m.length - e.length), d), o(m), !0;
|
|
24
|
-
}
|
|
25
|
-
return !1;
|
|
26
|
-
}, te = (n, s, d, e, l) => {
|
|
27
|
-
for (; s.firstChild; )
|
|
28
|
-
s.firstChild.remove();
|
|
29
|
-
n.forEach(({ emoji: o, key: r }, i) => {
|
|
30
|
-
const a = document.createElement("div");
|
|
31
|
-
a.className = "milkdown-emoji-filter_item";
|
|
32
|
-
const t = document.createElement("span");
|
|
33
|
-
t.innerHTML = C(o, l), t.className = "milkdown-emoji-filter_item-emoji";
|
|
34
|
-
const m = document.createElement("span");
|
|
35
|
-
m.textContent = `:${r}:`, m.className = "milkdown-emoji-filter_item-key", a.appendChild(t), a.appendChild(m), s.appendChild(a), i === 0 && e(a);
|
|
36
|
-
const c = (u) => {
|
|
37
|
-
const { target: j } = u;
|
|
38
|
-
j instanceof HTMLElement && e(j);
|
|
39
|
-
}, w = (u) => {
|
|
40
|
-
u.preventDefault(), d();
|
|
41
|
-
};
|
|
42
|
-
a.addEventListener("mouseenter", c), a.addEventListener("mousedown", w);
|
|
43
|
-
});
|
|
44
|
-
}, ne = (n, { css: s, cx: d }) => {
|
|
45
|
-
const e = n.get(W, void 0), l = n.get(z, void 0), o = n.get(U, void 0), r = n.get(q, "radius"), i = n.get(J, "typography"), a = (m, c = 1) => n.get(G, [m, c]), t = s`
|
|
46
|
-
min-height: 36px;
|
|
47
|
-
max-height: 320px;
|
|
48
|
-
overflow-y: auto;
|
|
49
|
-
border-radius: ${r};
|
|
50
|
-
position: absolute;
|
|
51
|
-
background: ${a("surface")};
|
|
52
|
-
|
|
53
|
-
&.hide {
|
|
54
|
-
display: none;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.milkdown-emoji-filter_item {
|
|
58
|
-
display: flex;
|
|
59
|
-
gap: 8px;
|
|
60
|
-
height: 36px;
|
|
61
|
-
padding: 0 14px;
|
|
62
|
-
align-items: center;
|
|
63
|
-
justify-content: flex-start;
|
|
64
|
-
cursor: pointer;
|
|
65
|
-
line-height: 2;
|
|
66
|
-
font-family: ${i};
|
|
67
|
-
font-size: 14px;
|
|
68
|
-
&.active {
|
|
69
|
-
background: ${a("secondary", 0.12)};
|
|
70
|
-
color: ${a("primary")};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.emoji {
|
|
75
|
-
height: 14px;
|
|
76
|
-
width: 14px;
|
|
77
|
-
margin: 0 1px 0 1.5px;
|
|
78
|
-
vertical-align: -1.5px;
|
|
79
|
-
}
|
|
80
|
-
`;
|
|
81
|
-
return d(e, l, o, t);
|
|
82
|
-
}, oe = new R("MILKDOWN_EMOJI_FILTER"), re = (n, s, d) => {
|
|
83
|
-
let e = !1, l = 0, o = "", r = null;
|
|
84
|
-
const i = () => {
|
|
85
|
-
e = !1, l = 0, o = "", r = null;
|
|
86
|
-
}, a = (t) => {
|
|
87
|
-
r && r.classList.remove("active"), t && t.classList.add("active"), r = t;
|
|
88
|
-
};
|
|
89
|
-
return new B({
|
|
90
|
-
key: oe,
|
|
91
|
-
props: {
|
|
92
|
-
handleKeyDown(t, m) {
|
|
93
|
-
return ["Delete", "Backspace"].includes(m.key) ? (o = o.slice(0, -1), o.length <= 1 && i(), !1) : !(!e || !["ArrowUp", "ArrowDown", "Enter"].includes(m.key));
|
|
94
|
-
},
|
|
95
|
-
handleTextInput(t, m, c, w) {
|
|
96
|
-
return e = ee(
|
|
97
|
-
t,
|
|
98
|
-
m,
|
|
99
|
-
c,
|
|
100
|
-
w,
|
|
101
|
-
(u) => {
|
|
102
|
-
l = u;
|
|
103
|
-
},
|
|
104
|
-
(u) => {
|
|
105
|
-
o = u;
|
|
106
|
-
}
|
|
107
|
-
), e || i(), !1;
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
view: (t) => {
|
|
111
|
-
const { parentNode: m } = t.dom;
|
|
112
|
-
if (!m)
|
|
113
|
-
throw N();
|
|
114
|
-
const c = document.createElement("div");
|
|
115
|
-
c.classList.add("milkdown-emoji-filter", "hide"), n.themeManager.onFlush(() => {
|
|
116
|
-
const h = c.className.split(" ").filter((f) => ["hide", "milkdown-emoji-filter"].includes(f));
|
|
117
|
-
c.className = h.join(" ");
|
|
118
|
-
const p = n.getStyle((f) => ne(n.themeManager, f));
|
|
119
|
-
p && p.split(" ").forEach((f) => c.classList.add(f));
|
|
120
|
-
});
|
|
121
|
-
const w = () => {
|
|
122
|
-
var f;
|
|
123
|
-
if (!r)
|
|
124
|
-
return;
|
|
125
|
-
const { tr: h } = t.state, p = t.state.schema.node("emoji", { html: (f = r.firstElementChild) == null ? void 0 : f.innerHTML });
|
|
126
|
-
t.dispatch(h.delete(l, l + o.length).insert(l, p)), i(), c.classList.add("hide");
|
|
127
|
-
};
|
|
128
|
-
m.appendChild(c);
|
|
129
|
-
const u = (h) => {
|
|
130
|
-
if (!e || !(h instanceof KeyboardEvent))
|
|
131
|
-
return;
|
|
132
|
-
const { key: p } = h;
|
|
133
|
-
if (p === "Enter") {
|
|
134
|
-
w();
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (["ArrowDown", "ArrowUp"].includes(p)) {
|
|
138
|
-
const f = p === "ArrowDown" ? (r == null ? void 0 : r.nextElementSibling) || c.firstElementChild : (r == null ? void 0 : r.previousElementSibling) || c.lastElementChild;
|
|
139
|
-
if (!f)
|
|
140
|
-
return;
|
|
141
|
-
a(f);
|
|
142
|
-
}
|
|
143
|
-
}, j = (h) => {
|
|
144
|
-
!e || (h.stopPropagation(), i(), c.classList.add("hide"));
|
|
145
|
-
};
|
|
146
|
-
return m.addEventListener("keydown", u), m.addEventListener("mousedown", j), {
|
|
147
|
-
update: (h) => {
|
|
148
|
-
const { selection: p } = h.state;
|
|
149
|
-
if (p.from - p.to !== 0 || !e)
|
|
150
|
-
return i(), c.classList.add("hide"), null;
|
|
151
|
-
const f = A.search(o).slice(0, s), { node: D } = h.domAtPos(l);
|
|
152
|
-
return f.length === 0 || !D ? (c.classList.add("hide"), null) : (c.style.maxHeight = "", c.classList.remove("hide"), te(f, c, w, a, d), $(h, c, (me, v, y) => {
|
|
153
|
-
const k = c.parentElement;
|
|
154
|
-
if (!k)
|
|
155
|
-
throw N();
|
|
156
|
-
const x = h.coordsAtPos(l);
|
|
157
|
-
let E = x.left - y.left;
|
|
158
|
-
E < 0 && (E = 0);
|
|
159
|
-
let L, g;
|
|
160
|
-
const T = x.top - y.top, b = y.height + y.top - x.bottom;
|
|
161
|
-
b >= v.height + 28 ? L = "bottom" : T >= v.height + 28 ? L = "top" : b >= T ? (L = "bottom", g = b - 28) : (L = "top", g = T - 28), (T < 0 || b < 0) && (g = y.height - (x.bottom - x.top) - 28, g > v.height && (g = void 0));
|
|
162
|
-
const P = L === "top" ? x.top - y.top - (g != null ? g : v.height) - 14 + k.scrollTop : x.bottom - y.top + 14 + k.scrollTop;
|
|
163
|
-
c.style.maxHeight = g !== void 0 && g > 0 ? `${g}px` : "";
|
|
164
|
-
const M = k.clientWidth - (c.offsetWidth + 4);
|
|
165
|
-
return E > M && (E = M), [P, E];
|
|
166
|
-
}), null);
|
|
167
|
-
},
|
|
168
|
-
destroy: () => {
|
|
169
|
-
m.removeEventListener("keydown", u), m.removeEventListener("mousedown", j), c.remove();
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
}, S = Q(), ie = (n) => !!n.children, se = (n) => !!n.value;
|
|
175
|
-
function le(n, s) {
|
|
176
|
-
return d(n, 0, null)[0];
|
|
177
|
-
function d(e, l, o) {
|
|
178
|
-
if (ie(e)) {
|
|
179
|
-
const r = [];
|
|
180
|
-
for (let i = 0, a = e.children.length; i < a; i++) {
|
|
181
|
-
const t = e.children[i];
|
|
182
|
-
if (t) {
|
|
183
|
-
const m = d(t, i, e);
|
|
184
|
-
if (m)
|
|
185
|
-
for (let c = 0, w = m.length; c < w; c++) {
|
|
186
|
-
const u = m[c];
|
|
187
|
-
u && r.push(u);
|
|
1
|
+
import { expectDomTypeError as E } from "@milkdown/exception";
|
|
2
|
+
import { InputRule as M } from "@milkdown/prose/inputrules";
|
|
3
|
+
import { $ctx as x, $nodeAttr as y, $nodeSchema as k, $inputRule as T, $remark as j } from "@milkdown/utils";
|
|
4
|
+
import L from "node-emoji";
|
|
5
|
+
import $ from "remark-emoji";
|
|
6
|
+
import A from "twemoji";
|
|
7
|
+
import H from "emoji-regex";
|
|
8
|
+
const O = (n) => ({ title: n }), g = (n, e) => A.parse(n, { attributes: O, base: "https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/", ...e }), h = H(), R = (n) => !!n.children, b = (n) => !!n.value;
|
|
9
|
+
function C(n, e) {
|
|
10
|
+
return s(n, 0, null)[0];
|
|
11
|
+
function s(t, r, o) {
|
|
12
|
+
if (R(t)) {
|
|
13
|
+
const m = [];
|
|
14
|
+
for (let i = 0, c = t.children.length; i < c; i++) {
|
|
15
|
+
const l = t.children[i];
|
|
16
|
+
if (l) {
|
|
17
|
+
const a = s(l, i, t);
|
|
18
|
+
if (a)
|
|
19
|
+
for (let u = 0, v = a.length; u < v; u++) {
|
|
20
|
+
const f = a[u];
|
|
21
|
+
f && m.push(f);
|
|
188
22
|
}
|
|
189
23
|
}
|
|
190
24
|
}
|
|
191
|
-
|
|
25
|
+
t.children = m;
|
|
192
26
|
}
|
|
193
|
-
return
|
|
27
|
+
return e(t, r, o);
|
|
194
28
|
}
|
|
195
29
|
}
|
|
196
|
-
const
|
|
197
|
-
function s
|
|
198
|
-
|
|
199
|
-
if (!
|
|
200
|
-
return [
|
|
201
|
-
const
|
|
202
|
-
let
|
|
203
|
-
for (;
|
|
204
|
-
const { index:
|
|
205
|
-
|
|
30
|
+
const I = (n) => () => {
|
|
31
|
+
function e(s) {
|
|
32
|
+
C(s, (t) => {
|
|
33
|
+
if (!b(t))
|
|
34
|
+
return [t];
|
|
35
|
+
const r = t.value, o = [];
|
|
36
|
+
let m, i = r;
|
|
37
|
+
for (; m = h.exec(i); ) {
|
|
38
|
+
const { index: c } = m, l = m[0];
|
|
39
|
+
l && (c > 0 && o.push({ ...t, value: i.slice(0, c) }), o.push({ ...t, value: g(l, n), type: "emoji" }), i = i.slice(c + l.length)), h.lastIndex = 0;
|
|
206
40
|
}
|
|
207
|
-
return i.length && o.push({ ...
|
|
41
|
+
return i.length && o.push({ ...t, value: i }), o;
|
|
208
42
|
});
|
|
209
43
|
}
|
|
210
|
-
return
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
html: {
|
|
230
|
-
default: ""
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
parseDOM: [
|
|
234
|
-
{
|
|
235
|
-
tag: 'span[data-type="emoji"]',
|
|
236
|
-
getAttrs: (e) => {
|
|
237
|
-
if (!(e instanceof HTMLElement))
|
|
238
|
-
throw H(e);
|
|
239
|
-
return { html: e.innerHTML };
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
],
|
|
243
|
-
toDOM: (e) => {
|
|
244
|
-
const l = document.createElement("span");
|
|
245
|
-
return l.classList.add("emoji-wrapper"), l.dataset.type = "emoji", n.themeManager.onFlush(() => {
|
|
246
|
-
const o = d();
|
|
247
|
-
o && l.classList.add(o);
|
|
248
|
-
}), l.innerHTML = e.attrs.html, { dom: l };
|
|
249
|
-
},
|
|
250
|
-
parseMarkdown: {
|
|
251
|
-
match: ({ type: e }) => e === "emoji",
|
|
252
|
-
runner: (e, l, o) => {
|
|
253
|
-
e.addNode(o, { html: l.value });
|
|
254
|
-
}
|
|
255
|
-
},
|
|
256
|
-
toMarkdown: {
|
|
257
|
-
match: (e) => e.type.name === "emoji",
|
|
258
|
-
runner: (e, l) => {
|
|
259
|
-
const o = document.createElement("span");
|
|
260
|
-
o.innerHTML = l.attrs.html;
|
|
261
|
-
const r = o.querySelector("img"), i = r == null ? void 0 : r.title;
|
|
262
|
-
o.remove(), e.addNode("text", void 0, i);
|
|
263
|
-
}
|
|
44
|
+
return e;
|
|
45
|
+
}, p = x({}, "emojiConfig"), d = y("emoji", () => ({
|
|
46
|
+
span: {},
|
|
47
|
+
img: {}
|
|
48
|
+
})), w = k("emoji", (n) => ({
|
|
49
|
+
group: "inline",
|
|
50
|
+
inline: !0,
|
|
51
|
+
attrs: {
|
|
52
|
+
html: {
|
|
53
|
+
default: ""
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
parseDOM: [
|
|
57
|
+
{
|
|
58
|
+
tag: 'span[data-type="emoji"]',
|
|
59
|
+
getAttrs: (e) => {
|
|
60
|
+
if (!(e instanceof HTMLElement))
|
|
61
|
+
throw E(e);
|
|
62
|
+
return { html: e.innerHTML };
|
|
264
63
|
}
|
|
265
|
-
}),
|
|
266
|
-
inputRules: (e) => [
|
|
267
|
-
new F(Z, (l, o, r, i) => {
|
|
268
|
-
const a = o[0];
|
|
269
|
-
if (!a)
|
|
270
|
-
return null;
|
|
271
|
-
const t = A.get(a);
|
|
272
|
-
if (!t || a.includes(t))
|
|
273
|
-
return null;
|
|
274
|
-
const m = C(t, s == null ? void 0 : s.twemojiOptions);
|
|
275
|
-
return l.tr.setMeta("emoji", !0).replaceRangeWith(r, i, e.create({ html: m })).scrollIntoView();
|
|
276
|
-
})
|
|
277
|
-
],
|
|
278
|
-
remarkPlugins: () => [I, ce(s == null ? void 0 : s.twemojiOptions)],
|
|
279
|
-
prosePlugins: () => {
|
|
280
|
-
var e;
|
|
281
|
-
return [re(n, (e = s == null ? void 0 : s.maxListSize) != null ? e : 6, s == null ? void 0 : s.twemojiOptions)];
|
|
282
64
|
}
|
|
283
|
-
|
|
284
|
-
|
|
65
|
+
],
|
|
66
|
+
toDOM: (e) => {
|
|
67
|
+
var o;
|
|
68
|
+
const s = n.get(d.key)(e), t = document.createElement("span");
|
|
69
|
+
t.innerHTML = e.attrs.html;
|
|
70
|
+
const r = (o = t.firstElementChild) == null ? void 0 : o.cloneNode();
|
|
71
|
+
return t.remove(), r && r instanceof HTMLElement && Object.entries(s.img).forEach(([m, i]) => r.setAttribute(m, i)), ["span", { ...s.container, "data-type": "emoji" }, r];
|
|
72
|
+
},
|
|
73
|
+
parseMarkdown: {
|
|
74
|
+
match: ({ type: e }) => e === "emoji",
|
|
75
|
+
runner: (e, s, t) => {
|
|
76
|
+
e.addNode(t, { html: s.value });
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
toMarkdown: {
|
|
80
|
+
match: (e) => e.type.name === "emoji",
|
|
81
|
+
runner: (e, s) => {
|
|
82
|
+
const t = document.createElement("span");
|
|
83
|
+
t.innerHTML = s.attrs.html;
|
|
84
|
+
const r = t.querySelector("img"), o = (r == null ? void 0 : r.title) || (r == null ? void 0 : r.alt);
|
|
85
|
+
t.remove(), e.addNode("text", void 0, o);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
})), P = T((n) => new M(/(:([^:\s]+):)$/, (e, s, t, r) => {
|
|
89
|
+
const o = s[0];
|
|
90
|
+
if (!o)
|
|
91
|
+
return null;
|
|
92
|
+
const m = L.get(o);
|
|
93
|
+
if (!m || o.includes(m))
|
|
94
|
+
return null;
|
|
95
|
+
const i = g(m, n.get(p.key).twemojiOptions);
|
|
96
|
+
return e.tr.setMeta("emoji", !0).replaceRangeWith(t, r, w.type().create({ html: i })).scrollIntoView();
|
|
97
|
+
})), D = j(() => $), N = j((n) => I(n.get(p.key).twemojiOptions)), G = [
|
|
98
|
+
d,
|
|
99
|
+
p,
|
|
100
|
+
D,
|
|
101
|
+
N,
|
|
102
|
+
w,
|
|
103
|
+
P
|
|
104
|
+
].flat();
|
|
285
105
|
export {
|
|
286
|
-
|
|
287
|
-
|
|
106
|
+
G as emoji,
|
|
107
|
+
d as emojiAttr,
|
|
108
|
+
p as emojiConfig,
|
|
109
|
+
w as emojiSchema,
|
|
110
|
+
P as insertEmojiInputRule,
|
|
111
|
+
D as remarkEmojiPlugin,
|
|
112
|
+
N as remarkTwemojiPlugin
|
|
288
113
|
};
|
|
289
114
|
//# sourceMappingURL=index.es.js.map
|
package/lib/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/constant.ts","../src/parse.ts","../src/filter/helper.ts","../src/filter/style.ts","../src/filter/index.ts","../src/remark-twemoji.ts","../src/node.ts","../src/index.ts"],"sourcesContent":["/* Copyright 2021, Milkdown by Mirone. */\nexport const part = /:\\+1|:-1|:[\\w-]+/\nexport const full = /:\\+1:|:-1:|:[\\w-]+:/\nexport const input = /(:([^:\\s]+):)$/\n","/* Copyright 2021, Milkdown by Mirone. */\nimport twemoji from 'twemoji'\n\nconst setAttr = (text: string) => ({ title: text })\n\nexport const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>\n twemoji.parse(emoji, { attributes: setAttr, ...twemojiOptions }) as unknown as string\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport type { EditorView } from '@milkdown/prose/view'\nimport type { Emoji } from 'node-emoji'\n\nimport { full, part } from '../constant'\nimport { parse } from '../parse'\n\nexport const checkTrigger = (\n view: EditorView,\n from: number,\n to: number,\n text: string,\n setRange: (from: number, to: number) => void,\n setSearch: (words: string) => void,\n) => {\n if (view.composing)\n return false\n const { state } = view\n const $from = state.doc.resolve(from)\n if ($from.parent.type.spec.code)\n return false\n const textBefore = (\n $from.parent.textBetween(Math.max(0, $from.parentOffset - 10), $from.parentOffset, undefined, '\\uFFFC') + text\n ).toLowerCase()\n if (full.test(textBefore))\n return false\n\n const regex = part.exec(textBefore)\n if (regex && regex[0] && textBefore.endsWith(regex[0])) {\n const match = regex[0]\n setRange(from - (match.length - text.length), to)\n setSearch(match)\n return true\n }\n return false\n}\n\nexport const renderDropdownList = (\n list: Emoji[],\n dropDown: HTMLElement,\n onConfirm: () => void,\n setActive: (active: HTMLElement | null) => void,\n twemojiOptions?: TwemojiOptions,\n) => {\n while (dropDown.firstChild)\n dropDown.firstChild.remove()\n\n list.forEach(({ emoji, key }, i) => {\n const container = document.createElement('div')\n container.className = 'milkdown-emoji-filter_item'\n\n const emojiSpan = document.createElement('span')\n emojiSpan.innerHTML = parse(emoji, twemojiOptions)\n\n emojiSpan.className = 'milkdown-emoji-filter_item-emoji'\n const keySpan = document.createElement('span')\n keySpan.textContent = `:${key}:`\n keySpan.className = 'milkdown-emoji-filter_item-key'\n\n container.appendChild(emojiSpan)\n container.appendChild(keySpan)\n dropDown.appendChild(container)\n\n if (i === 0)\n setActive(container)\n\n const onEnter = (e: MouseEvent) => {\n const { target } = e\n if (!(target instanceof HTMLElement))\n return\n setActive(target)\n }\n\n const onClick = (e: MouseEvent) => {\n e.preventDefault()\n onConfirm()\n }\n\n container.addEventListener('mouseenter', onEnter)\n container.addEventListener('mousedown', onClick)\n })\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type {\n Color,\n Emotion,\n ThemeManager,\n} from '@milkdown/core'\nimport {\n ThemeBorder,\n ThemeColor,\n ThemeFont,\n ThemeScrollbar,\n ThemeShadow,\n ThemeSize,\n} from '@milkdown/core'\n\nexport const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) => {\n const border = themeManager.get(ThemeBorder, undefined)\n const shadow = themeManager.get(ThemeShadow, undefined)\n const scrollbar = themeManager.get(ThemeScrollbar, undefined)\n const radius = themeManager.get(ThemeSize, 'radius')\n const typography = themeManager.get(ThemeFont, 'typography')\n const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity])\n\n const style = css`\n min-height: 36px;\n max-height: 320px;\n overflow-y: auto;\n border-radius: ${radius};\n position: absolute;\n background: ${palette('surface')};\n\n &.hide {\n display: none;\n }\n\n .milkdown-emoji-filter_item {\n display: flex;\n gap: 8px;\n height: 36px;\n padding: 0 14px;\n align-items: center;\n justify-content: flex-start;\n cursor: pointer;\n line-height: 2;\n font-family: ${typography};\n font-size: 14px;\n &.active {\n background: ${palette('secondary', 0.12)};\n color: ${palette('primary')};\n }\n }\n\n .emoji {\n height: 14px;\n width: 14px;\n margin: 0 1px 0 1.5px;\n vertical-align: -1.5px;\n }\n `\n return cx(border, shadow, scrollbar, style)\n}\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport { missingRootElement } from '@milkdown/exception'\nimport { calculateNodePosition } from '@milkdown/prose'\nimport { Plugin, PluginKey } from '@milkdown/prose/state'\nimport type { ThemeUtils } from '@milkdown/utils'\nimport nodeEmoji from 'node-emoji'\n\nimport { checkTrigger, renderDropdownList } from './helper'\nimport { injectStyle } from './style'\n\nexport const key = new PluginKey('MILKDOWN_EMOJI_FILTER')\n\nexport const filter = (utils: ThemeUtils, maxListSize: number, twemojiOptions?: TwemojiOptions) => {\n let trigger = false\n let _from = 0\n let _search = ''\n let $active: null | HTMLElement = null\n\n const off = () => {\n trigger = false\n _from = 0\n _search = ''\n $active = null\n }\n\n const setActive = (active: HTMLElement | null) => {\n if ($active)\n $active.classList.remove('active')\n\n if (active)\n active.classList.add('active')\n\n $active = active\n }\n\n return new Plugin({\n key,\n props: {\n handleKeyDown(_, event) {\n if (['Delete', 'Backspace'].includes(event.key)) {\n _search = _search.slice(0, -1)\n if (_search.length <= 1)\n off()\n\n return false\n }\n if (!trigger)\n return false\n if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key))\n return false\n\n return true\n },\n handleTextInput(view, from, to, text) {\n trigger = checkTrigger(\n view,\n from,\n to,\n text,\n (from) => {\n _from = from\n },\n (search) => {\n _search = search\n },\n )\n if (!trigger)\n off()\n\n return false\n },\n },\n view: (editorView) => {\n const { parentNode } = editorView.dom\n if (!parentNode)\n throw missingRootElement()\n\n const dropDown = document.createElement('div')\n\n dropDown.classList.add('milkdown-emoji-filter', 'hide')\n\n utils.themeManager.onFlush(() => {\n const className = dropDown.className\n .split(' ')\n .filter(x => ['hide', 'milkdown-emoji-filter'].includes(x))\n dropDown.className = className.join(' ')\n const style = utils.getStyle(emotion => injectStyle(utils.themeManager, emotion))\n if (style)\n style.split(' ').forEach(x => dropDown.classList.add(x))\n })\n\n const replace = () => {\n if (!$active)\n return\n\n const { tr } = editorView.state\n const node = editorView.state.schema.node('emoji', { html: $active.firstElementChild?.innerHTML })\n\n editorView.dispatch(tr.delete(_from, _from + _search.length).insert(_from, node))\n off()\n dropDown.classList.add('hide')\n }\n\n parentNode.appendChild(dropDown)\n const onKeydown = (e: Event) => {\n if (!trigger || !(e instanceof KeyboardEvent))\n return\n\n const { key } = e\n\n if (key === 'Enter') {\n replace()\n return\n }\n\n if (['ArrowDown', 'ArrowUp'].includes(key)) {\n const next\n = key === 'ArrowDown'\n ? $active?.nextElementSibling || dropDown.firstElementChild\n : $active?.previousElementSibling || dropDown.lastElementChild\n if (!next)\n return\n setActive(next as HTMLElement)\n }\n }\n const onClick = (e: Event) => {\n if (!trigger)\n return\n\n e.stopPropagation()\n off()\n dropDown.classList.add('hide')\n }\n parentNode.addEventListener('keydown', onKeydown)\n parentNode.addEventListener('mousedown', onClick)\n\n return {\n update: (view) => {\n const { selection } = view.state\n\n if (selection.from - selection.to !== 0 || !trigger) {\n off()\n dropDown.classList.add('hide')\n return null\n }\n const result = nodeEmoji.search(_search).slice(0, maxListSize)\n const { node } = view.domAtPos(_from)\n if (result.length === 0 || !node) {\n dropDown.classList.add('hide')\n return null\n }\n\n dropDown.style.maxHeight = ''\n dropDown.classList.remove('hide')\n renderDropdownList(result, dropDown, replace, setActive, twemojiOptions)\n calculateNodePosition(view, dropDown, (_selected, target, parent) => {\n const $editor = dropDown.parentElement\n if (!$editor)\n throw missingRootElement()\n\n const start = view.coordsAtPos(_from)\n let left = start.left - parent.left\n\n if (left < 0)\n left = 0\n\n let direction: 'top' | 'bottom'\n let maxHeight: number | undefined\n const startToTop = start.top - parent.top\n const startToBottom = parent.height + parent.top - start.bottom\n if (startToBottom >= target.height + 28) {\n direction = 'bottom'\n }\n else if (startToTop >= target.height + 28) {\n direction = 'top'\n }\n else if (startToBottom >= startToTop) {\n direction = 'bottom'\n maxHeight = startToBottom - 28\n }\n else {\n direction = 'top'\n maxHeight = startToTop - 28\n }\n if (startToTop < 0 || startToBottom < 0) {\n maxHeight = parent.height - (start.bottom - start.top) - 28\n if (maxHeight > target.height)\n maxHeight = undefined\n }\n\n const top\n = direction === 'top'\n ? start.top - parent.top - (maxHeight ?? target.height) - 14 + $editor.scrollTop\n : start.bottom - parent.top + 14 + $editor.scrollTop\n\n dropDown.style.maxHeight = maxHeight !== undefined && maxHeight > 0 ? `${maxHeight}px` : ''\n\n const maxLeft = $editor.clientWidth - (dropDown.offsetWidth + 4)\n if (left > maxLeft)\n left = maxLeft\n\n return [top, left]\n })\n\n return null\n },\n\n destroy: () => {\n parentNode.removeEventListener('keydown', onKeydown)\n parentNode.removeEventListener('mousedown', onClick)\n dropDown.remove()\n },\n }\n },\n })\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { RemarkPlugin } from '@milkdown/core'\nimport emojiRegex from 'emoji-regex'\nimport type { Literal, Node, Parent } from 'unist'\n\nimport { parse } from './parse'\n\nconst regex = emojiRegex()\n\nconst isParent = (node: Node): node is Parent => !!(node as Parent).children\nconst isLiteral = (node: Node): node is Literal => !!(node as Literal).value\n\nfunction flatMap(ast: Node, fn: (node: Node, index: number, parent: Node | null) => Node[]) {\n return transform(ast, 0, null)[0]\n\n function transform(node: Node, index: number, parent: Node | null) {\n if (isParent(node)) {\n const out = []\n for (let i = 0, n = node.children.length; i < n; i++) {\n const nthChild = node.children[i]\n if (nthChild) {\n const xs = transform(nthChild, i, node)\n if (xs) {\n for (let j = 0, m = xs.length; j < m; j++) {\n const item = xs[j]\n if (item)\n out.push(item)\n }\n }\n }\n }\n node.children = out\n }\n\n return fn(node, index, parent)\n }\n}\n\nexport const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin = twemojiOptions => () => {\n function transformer(tree: Node) {\n flatMap(tree, (node) => {\n if (!isLiteral(node))\n return [node]\n\n const value = node.value as string\n const output: Literal<string>[] = []\n let match\n let str = value\n // eslint-disable-next-line no-cond-assign\n while ((match = regex.exec(str))) {\n const { index } = match\n const emoji = match[0]\n if (emoji) {\n if (index > 0)\n output.push({ ...node, value: str.slice(0, index) })\n\n output.push({ ...node, value: parse(emoji, twemojiOptions), type: 'emoji' })\n str = str.slice(index + emoji.length)\n }\n regex.lastIndex = 0\n }\n if (str.length)\n output.push({ ...node, value: str })\n\n return output\n })\n }\n return transformer\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { RemarkPlugin } from '@milkdown/core'\nimport { expectDomTypeError } from '@milkdown/exception'\nimport { InputRule } from '@milkdown/prose/inputrules'\nimport { createNode } from '@milkdown/utils'\nimport nodeEmoji from 'node-emoji'\nimport remarkEmoji from 'remark-emoji'\n\nimport { input } from './constant'\nimport { filter } from './filter'\nimport { parse } from './parse'\nimport { twemojiPlugin } from './remark-twemoji'\n\nexport interface EmojiOptions {\n maxListSize: number\n twemojiOptions: TwemojiOptions\n}\n\nexport const emojiNode = createNode<string, EmojiOptions>((utils, options) => {\n const getStyle = () =>\n utils.getStyle(\n ({ css }) => css`\n .emoji {\n height: 1em;\n width: 1em;\n margin: 0 1px 0 1.5px;\n vertical-align: -1.5px;\n }\n `,\n )\n return {\n id: 'emoji',\n schema: () => ({\n group: 'inline',\n inline: true,\n atom: true,\n attrs: {\n html: {\n default: '',\n },\n },\n parseDOM: [\n {\n tag: 'span[data-type=\"emoji\"]',\n getAttrs: (dom) => {\n if (!(dom instanceof HTMLElement))\n throw expectDomTypeError(dom)\n\n return { html: dom.innerHTML }\n },\n },\n ],\n toDOM: (node) => {\n const span = document.createElement('span')\n span.classList.add('emoji-wrapper')\n span.dataset.type = 'emoji'\n utils.themeManager.onFlush(() => {\n const style = getStyle()\n if (style)\n span.classList.add(style)\n })\n span.innerHTML = node.attrs.html\n return { dom: span }\n },\n parseMarkdown: {\n match: ({ type }) => type === 'emoji',\n runner: (state, node, type) => {\n state.addNode(type, { html: node.value as string })\n },\n },\n toMarkdown: {\n match: node => node.type.name === 'emoji',\n runner: (state, node) => {\n const span = document.createElement('span')\n span.innerHTML = node.attrs.html\n const img = span.querySelector('img')\n const title = img?.title\n span.remove()\n state.addNode('text', undefined, title)\n },\n },\n }),\n inputRules: nodeType => [\n new InputRule(input, (state, match, start, end) => {\n const content = match[0]\n if (!content)\n return null\n const got = nodeEmoji.get(content)\n if (!got || content.includes(got))\n return null\n\n const html = parse(got, options?.twemojiOptions)\n\n return state.tr\n .setMeta('emoji', true)\n .replaceRangeWith(start, end, nodeType.create({ html }))\n .scrollIntoView()\n }),\n ],\n remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin(options?.twemojiOptions)],\n prosePlugins: () => [filter(utils, options?.maxListSize ?? 6, options?.twemojiOptions)],\n }\n})\n","/* Copyright 2021, Milkdown by Mirone. */\nimport { AtomList } from '@milkdown/utils'\n\nimport { emojiNode } from './node'\nexport * from './node'\n\nexport const emoji = AtomList.create([emojiNode()])\n"],"names":["part","full","input","setAttr","text","parse","emoji","twemojiOptions","twemoji","checkTrigger","view","from","to","setRange","setSearch","state","$from","textBefore","regex","match","renderDropdownList","list","dropDown","onConfirm","setActive","key","container","emojiSpan","keySpan","onEnter","e","target","onClick","injectStyle","themeManager","css","cx","border","ThemeBorder","shadow","ThemeShadow","scrollbar","ThemeScrollbar","radius","ThemeSize","typography","ThemeFont","palette","color","opacity","ThemeColor","style","PluginKey","filter","utils","maxListSize","trigger","_from","_search","$active","off","active","Plugin","_","event","search","editorView","parentNode","missingRootElement","className","x","emotion","replace","tr","node","_a","onKeydown","next","selection","result","nodeEmoji","calculateNodePosition","_selected","parent","$editor","start","left","direction","maxHeight","startToTop","startToBottom","top","maxLeft","emojiRegex","isParent","isLiteral","flatMap","ast","fn","transform","index","out","n","nthChild","xs","j","m","item","twemojiPlugin","transformer","tree","value","output","str","emojiNode","createNode","options","getStyle","dom","expectDomTypeError","span","type","img","title","nodeType","InputRule","end","content","got","html","remarkEmoji","AtomList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/parse.ts","../src/remark-twemoji.ts","../src/index.ts"],"sourcesContent":["/* Copyright 2021, Milkdown by Mirone. */\nimport twemoji from 'twemoji'\n\nconst setAttr = (text: string) => ({ title: text })\n\nexport const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>\n twemoji.parse(emoji, { attributes: setAttr, base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/', ...twemojiOptions }) as unknown as string\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { RemarkPlugin } from '@milkdown/transformer'\nimport emojiRegex from 'emoji-regex'\nimport type { Literal, Node, Parent } from 'unist'\n\nimport { parse } from './parse'\n\nconst regex = emojiRegex()\n\nconst isParent = (node: Node): node is Parent => !!(node as Parent).children\nconst isLiteral = (node: Node): node is Literal => !!(node as Literal).value\n\nfunction flatMap(ast: Node, fn: (node: Node, index: number, parent: Node | null) => Node[]) {\n return transform(ast, 0, null)[0]\n\n function transform(node: Node, index: number, parent: Node | null) {\n if (isParent(node)) {\n const out = []\n for (let i = 0, n = node.children.length; i < n; i++) {\n const nthChild = node.children[i]\n if (nthChild) {\n const xs = transform(nthChild, i, node)\n if (xs) {\n for (let j = 0, m = xs.length; j < m; j++) {\n const item = xs[j]\n if (item)\n out.push(item)\n }\n }\n }\n }\n node.children = out\n }\n\n return fn(node, index, parent)\n }\n}\n\nexport const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin = twemojiOptions => () => {\n function transformer(tree: Node) {\n flatMap(tree, (node) => {\n if (!isLiteral(node))\n return [node]\n\n const value = node.value as string\n const output: Literal<string>[] = []\n let match\n let str = value\n // eslint-disable-next-line no-cond-assign\n while ((match = regex.exec(str))) {\n const { index } = match\n const emoji = match[0]\n if (emoji) {\n if (index > 0)\n output.push({ ...node, value: str.slice(0, index) })\n\n output.push({ ...node, value: parse(emoji, twemojiOptions), type: 'emoji' })\n str = str.slice(index + emoji.length)\n }\n regex.lastIndex = 0\n }\n if (str.length)\n output.push({ ...node, value: str })\n\n return output\n })\n }\n return transformer\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { MilkdownPlugin } from '@milkdown/ctx'\nimport type { RemarkPlugin } from '@milkdown/transformer'\nimport { expectDomTypeError } from '@milkdown/exception'\nimport { InputRule } from '@milkdown/prose/inputrules'\nimport { $ctx, $inputRule, $nodeAttr, $nodeSchema, $remark } from '@milkdown/utils'\nimport nodeEmoji from 'node-emoji'\nimport remarkEmoji from 'remark-emoji'\nimport type Twemoji from 'twemoji'\n\nimport { parse } from './parse'\nimport { twemojiPlugin } from './remark-twemoji'\n\ntype TwemojiOptions = Exclude<Parameters<typeof Twemoji.parse>[1], Function | undefined>\n\nexport interface EmojiConfig {\n twemojiOptions?: TwemojiOptions\n}\n\nexport const emojiConfig = $ctx<EmojiConfig, 'emojiConfig'>({}, 'emojiConfig')\n\nexport const emojiAttr = $nodeAttr('emoji', () => ({\n span: {},\n img: {},\n}))\n\nexport const emojiSchema = $nodeSchema('emoji', ctx => ({\n group: 'inline',\n inline: true,\n attrs: {\n html: {\n default: '',\n },\n },\n parseDOM: [\n {\n tag: 'span[data-type=\"emoji\"]',\n getAttrs: (dom) => {\n if (!(dom instanceof HTMLElement))\n throw expectDomTypeError(dom)\n\n return { html: dom.innerHTML }\n },\n },\n ],\n toDOM: (node) => {\n const attrs = ctx.get(emojiAttr.key)(node)\n const tmp = document.createElement('span')\n tmp.innerHTML = node.attrs.html\n const dom = tmp.firstElementChild?.cloneNode()\n tmp.remove()\n if (dom && dom instanceof HTMLElement)\n Object.entries<string>(attrs.img).forEach(([key, value]) => dom.setAttribute(key, value))\n\n return ['span', { ...attrs.container, 'data-type': 'emoji' }, dom]\n },\n parseMarkdown: {\n match: ({ type }) => type === 'emoji',\n runner: (state, node, type) => {\n state.addNode(type, { html: node.value as string })\n },\n },\n toMarkdown: {\n match: node => node.type.name === 'emoji',\n runner: (state, node) => {\n const span = document.createElement('span')\n span.innerHTML = node.attrs.html\n const img = span.querySelector('img')\n const title = img?.title || img?.alt\n span.remove()\n state.addNode('text', undefined, title)\n },\n },\n}))\n\nexport const insertEmojiInputRule = $inputRule(ctx => new InputRule(/(:([^:\\s]+):)$/, (state, match, start, end) => {\n const content = match[0]\n if (!content)\n return null\n const got = nodeEmoji.get(content)\n if (!got || content.includes(got))\n return null\n\n const html = parse(got, ctx.get(emojiConfig.key).twemojiOptions)\n\n return state.tr\n .setMeta('emoji', true)\n .replaceRangeWith(start, end, emojiSchema.type().create({ html }))\n .scrollIntoView()\n}))\n\nexport const remarkEmojiPlugin = $remark(() => remarkEmoji as RemarkPlugin)\nexport const remarkTwemojiPlugin = $remark(ctx => twemojiPlugin(ctx.get(emojiConfig.key).twemojiOptions))\n\nexport const emoji: MilkdownPlugin[] = [\n emojiAttr,\n emojiConfig,\n remarkEmojiPlugin,\n remarkTwemojiPlugin,\n emojiSchema,\n insertEmojiInputRule,\n].flat()\n"],"names":["setAttr","text","parse","emoji","twemojiOptions","twemoji","regex","emojiRegex","isParent","node","isLiteral","flatMap","ast","fn","transform","index","parent","out","n","nthChild","xs","j","m","item","twemojiPlugin","transformer","tree","value","output","match","str","emojiConfig","$ctx","emojiAttr","$nodeAttr","emojiSchema","$nodeSchema","ctx","dom","expectDomTypeError","attrs","tmp","_a","key","type","state","span","img","title","insertEmojiInputRule","$inputRule","InputRule","start","end","content","got","nodeEmoji","html","remarkEmojiPlugin","$remark","remarkEmoji","remarkTwemojiPlugin"],"mappings":"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"}
|
package/lib/parse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,UAAW,MAAM,mBAAmB,cAAc,KAAG,
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,UAAW,MAAM,mBAAmB,cAAc,KAAG,MAC6E,CAAA"}
|
package/lib/remark-twemoji.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAqCzD,eAAO,MAAM,aAAa,EAAE,CAAC,cAAc,CAAC,EAAE,cAAc,KAAK,YA8BhE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milkdown/plugin-emoji",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "7.0.0-next.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
"src"
|
|
21
21
|
],
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@milkdown/core": "^
|
|
24
|
-
"@milkdown/
|
|
23
|
+
"@milkdown/core": "^7.0.0-next.0",
|
|
24
|
+
"@milkdown/ctx": "^7.0.0-next.0",
|
|
25
|
+
"@milkdown/prose": "^7.0.0-next.0",
|
|
26
|
+
"@milkdown/transformer": "^7.0.0-next.0"
|
|
25
27
|
},
|
|
26
28
|
"dependencies": {
|
|
27
29
|
"@types/node-emoji": "^1.8.1",
|
|
@@ -31,13 +33,15 @@
|
|
|
31
33
|
"tslib": "^2.4.0",
|
|
32
34
|
"twemoji": "^14.0.1",
|
|
33
35
|
"unist-util-visit": "^4.0.0",
|
|
34
|
-
"@milkdown/exception": "
|
|
35
|
-
"@milkdown/utils": "
|
|
36
|
+
"@milkdown/exception": "7.0.0-next.0",
|
|
37
|
+
"@milkdown/utils": "7.0.0-next.0"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
38
40
|
"@types/unist": "^2.0.6",
|
|
39
|
-
"@milkdown/core": "
|
|
40
|
-
"@milkdown/
|
|
41
|
+
"@milkdown/core": "7.0.0-next.0",
|
|
42
|
+
"@milkdown/ctx": "7.0.0-next.0",
|
|
43
|
+
"@milkdown/prose": "7.0.0-next.0",
|
|
44
|
+
"@milkdown/transformer": "7.0.0-next.0"
|
|
41
45
|
},
|
|
42
46
|
"nx": {
|
|
43
47
|
"targets": {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,102 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import {
|
|
2
|
+
import type { MilkdownPlugin } from '@milkdown/ctx'
|
|
3
|
+
import type { RemarkPlugin } from '@milkdown/transformer'
|
|
4
|
+
import { expectDomTypeError } from '@milkdown/exception'
|
|
5
|
+
import { InputRule } from '@milkdown/prose/inputrules'
|
|
6
|
+
import { $ctx, $inputRule, $nodeAttr, $nodeSchema, $remark } from '@milkdown/utils'
|
|
7
|
+
import nodeEmoji from 'node-emoji'
|
|
8
|
+
import remarkEmoji from 'remark-emoji'
|
|
9
|
+
import type Twemoji from 'twemoji'
|
|
3
10
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
11
|
+
import { parse } from './parse'
|
|
12
|
+
import { twemojiPlugin } from './remark-twemoji'
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
type TwemojiOptions = Exclude<Parameters<typeof Twemoji.parse>[1], Function | undefined>
|
|
15
|
+
|
|
16
|
+
export interface EmojiConfig {
|
|
17
|
+
twemojiOptions?: TwemojiOptions
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const emojiConfig = $ctx<EmojiConfig, 'emojiConfig'>({}, 'emojiConfig')
|
|
21
|
+
|
|
22
|
+
export const emojiAttr = $nodeAttr('emoji', () => ({
|
|
23
|
+
span: {},
|
|
24
|
+
img: {},
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
export const emojiSchema = $nodeSchema('emoji', ctx => ({
|
|
28
|
+
group: 'inline',
|
|
29
|
+
inline: true,
|
|
30
|
+
attrs: {
|
|
31
|
+
html: {
|
|
32
|
+
default: '',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
parseDOM: [
|
|
36
|
+
{
|
|
37
|
+
tag: 'span[data-type="emoji"]',
|
|
38
|
+
getAttrs: (dom) => {
|
|
39
|
+
if (!(dom instanceof HTMLElement))
|
|
40
|
+
throw expectDomTypeError(dom)
|
|
41
|
+
|
|
42
|
+
return { html: dom.innerHTML }
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
toDOM: (node) => {
|
|
47
|
+
const attrs = ctx.get(emojiAttr.key)(node)
|
|
48
|
+
const tmp = document.createElement('span')
|
|
49
|
+
tmp.innerHTML = node.attrs.html
|
|
50
|
+
const dom = tmp.firstElementChild?.cloneNode()
|
|
51
|
+
tmp.remove()
|
|
52
|
+
if (dom && dom instanceof HTMLElement)
|
|
53
|
+
Object.entries<string>(attrs.img).forEach(([key, value]) => dom.setAttribute(key, value))
|
|
54
|
+
|
|
55
|
+
return ['span', { ...attrs.container, 'data-type': 'emoji' }, dom]
|
|
56
|
+
},
|
|
57
|
+
parseMarkdown: {
|
|
58
|
+
match: ({ type }) => type === 'emoji',
|
|
59
|
+
runner: (state, node, type) => {
|
|
60
|
+
state.addNode(type, { html: node.value as string })
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
toMarkdown: {
|
|
64
|
+
match: node => node.type.name === 'emoji',
|
|
65
|
+
runner: (state, node) => {
|
|
66
|
+
const span = document.createElement('span')
|
|
67
|
+
span.innerHTML = node.attrs.html
|
|
68
|
+
const img = span.querySelector('img')
|
|
69
|
+
const title = img?.title || img?.alt
|
|
70
|
+
span.remove()
|
|
71
|
+
state.addNode('text', undefined, title)
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
}))
|
|
75
|
+
|
|
76
|
+
export const insertEmojiInputRule = $inputRule(ctx => new InputRule(/(:([^:\s]+):)$/, (state, match, start, end) => {
|
|
77
|
+
const content = match[0]
|
|
78
|
+
if (!content)
|
|
79
|
+
return null
|
|
80
|
+
const got = nodeEmoji.get(content)
|
|
81
|
+
if (!got || content.includes(got))
|
|
82
|
+
return null
|
|
83
|
+
|
|
84
|
+
const html = parse(got, ctx.get(emojiConfig.key).twemojiOptions)
|
|
85
|
+
|
|
86
|
+
return state.tr
|
|
87
|
+
.setMeta('emoji', true)
|
|
88
|
+
.replaceRangeWith(start, end, emojiSchema.type().create({ html }))
|
|
89
|
+
.scrollIntoView()
|
|
90
|
+
}))
|
|
91
|
+
|
|
92
|
+
export const remarkEmojiPlugin = $remark(() => remarkEmoji as RemarkPlugin)
|
|
93
|
+
export const remarkTwemojiPlugin = $remark(ctx => twemojiPlugin(ctx.get(emojiConfig.key).twemojiOptions))
|
|
94
|
+
|
|
95
|
+
export const emoji: MilkdownPlugin[] = [
|
|
96
|
+
emojiAttr,
|
|
97
|
+
emojiConfig,
|
|
98
|
+
remarkEmojiPlugin,
|
|
99
|
+
remarkTwemojiPlugin,
|
|
100
|
+
emojiSchema,
|
|
101
|
+
insertEmojiInputRule,
|
|
102
|
+
].flat()
|
package/src/parse.ts
CHANGED
|
@@ -4,4 +4,4 @@ import twemoji from 'twemoji'
|
|
|
4
4
|
const setAttr = (text: string) => ({ title: text })
|
|
5
5
|
|
|
6
6
|
export const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>
|
|
7
|
-
twemoji.parse(emoji, { attributes: setAttr, ...twemojiOptions }) as unknown as string
|
|
7
|
+
twemoji.parse(emoji, { attributes: setAttr, base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/', ...twemojiOptions }) as unknown as string
|
package/src/remark-twemoji.ts
CHANGED
package/lib/constant.d.ts
DELETED
package/lib/constant.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constant.d.ts","sourceRoot":"","sources":["../src/constant.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,QAAqB,CAAA;AACtC,eAAO,MAAM,IAAI,QAAwB,CAAA;AACzC,eAAO,MAAM,KAAK,QAAmB,CAAA"}
|
package/lib/filter/helper.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { EditorView } from '@milkdown/prose/view';
|
|
2
|
-
import type { Emoji } from 'node-emoji';
|
|
3
|
-
export declare const checkTrigger: (view: EditorView, from: number, to: number, text: string, setRange: (from: number, to: number) => void, setSearch: (words: string) => void) => boolean;
|
|
4
|
-
export declare const renderDropdownList: (list: Emoji[], dropDown: HTMLElement, onConfirm: () => void, setActive: (active: HTMLElement | null) => void, twemojiOptions?: TwemojiOptions) => void;
|
|
5
|
-
//# sourceMappingURL=helper.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/filter/helper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAKvC,eAAO,MAAM,YAAY,SACjB,UAAU,QACV,MAAM,MACR,MAAM,QACJ,MAAM,mBACK,MAAM,MAAM,MAAM,KAAK,IAAI,qBACzB,MAAM,KAAK,IAAI,YAsBnC,CAAA;AAED,eAAO,MAAM,kBAAkB,SACvB,KAAK,EAAE,YACH,WAAW,aACV,MAAM,IAAI,sBACD,WAAW,GAAG,IAAI,KAAK,IAAI,mBAC9B,cAAc,SAuChC,CAAA"}
|
package/lib/filter/index.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Plugin, PluginKey } from '@milkdown/prose/state';
|
|
2
|
-
import type { ThemeUtils } from '@milkdown/utils';
|
|
3
|
-
export declare const key: PluginKey<any>;
|
|
4
|
-
export declare const filter: (utils: ThemeUtils, maxListSize: number, twemojiOptions?: TwemojiOptions) => Plugin<any>;
|
|
5
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filter/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAMjD,eAAO,MAAM,GAAG,gBAAyC,CAAA;AAEzD,eAAO,MAAM,MAAM,UAAW,UAAU,eAAe,MAAM,mBAAmB,cAAc,gBA2M7F,CAAA"}
|
package/lib/filter/style.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/filter/style.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,OAAO,EACP,YAAY,EACb,MAAM,gBAAgB,CAAA;AAUvB,eAAO,MAAM,WAAW,iBAAkB,YAAY,eAAe,OAAO,WA6C3E,CAAA"}
|
package/lib/node.d.ts
DELETED
package/lib/node.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,cAAc,CAAA;CAC/B;AAED,eAAO,MAAM,SAAS,6DAoFpB,CAAA"}
|
package/src/constant.ts
DELETED
package/src/filter/helper.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
|
|
3
|
-
import type { EditorView } from '@milkdown/prose/view'
|
|
4
|
-
import type { Emoji } from 'node-emoji'
|
|
5
|
-
|
|
6
|
-
import { full, part } from '../constant'
|
|
7
|
-
import { parse } from '../parse'
|
|
8
|
-
|
|
9
|
-
export const checkTrigger = (
|
|
10
|
-
view: EditorView,
|
|
11
|
-
from: number,
|
|
12
|
-
to: number,
|
|
13
|
-
text: string,
|
|
14
|
-
setRange: (from: number, to: number) => void,
|
|
15
|
-
setSearch: (words: string) => void,
|
|
16
|
-
) => {
|
|
17
|
-
if (view.composing)
|
|
18
|
-
return false
|
|
19
|
-
const { state } = view
|
|
20
|
-
const $from = state.doc.resolve(from)
|
|
21
|
-
if ($from.parent.type.spec.code)
|
|
22
|
-
return false
|
|
23
|
-
const textBefore = (
|
|
24
|
-
$from.parent.textBetween(Math.max(0, $from.parentOffset - 10), $from.parentOffset, undefined, '\uFFFC') + text
|
|
25
|
-
).toLowerCase()
|
|
26
|
-
if (full.test(textBefore))
|
|
27
|
-
return false
|
|
28
|
-
|
|
29
|
-
const regex = part.exec(textBefore)
|
|
30
|
-
if (regex && regex[0] && textBefore.endsWith(regex[0])) {
|
|
31
|
-
const match = regex[0]
|
|
32
|
-
setRange(from - (match.length - text.length), to)
|
|
33
|
-
setSearch(match)
|
|
34
|
-
return true
|
|
35
|
-
}
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const renderDropdownList = (
|
|
40
|
-
list: Emoji[],
|
|
41
|
-
dropDown: HTMLElement,
|
|
42
|
-
onConfirm: () => void,
|
|
43
|
-
setActive: (active: HTMLElement | null) => void,
|
|
44
|
-
twemojiOptions?: TwemojiOptions,
|
|
45
|
-
) => {
|
|
46
|
-
while (dropDown.firstChild)
|
|
47
|
-
dropDown.firstChild.remove()
|
|
48
|
-
|
|
49
|
-
list.forEach(({ emoji, key }, i) => {
|
|
50
|
-
const container = document.createElement('div')
|
|
51
|
-
container.className = 'milkdown-emoji-filter_item'
|
|
52
|
-
|
|
53
|
-
const emojiSpan = document.createElement('span')
|
|
54
|
-
emojiSpan.innerHTML = parse(emoji, twemojiOptions)
|
|
55
|
-
|
|
56
|
-
emojiSpan.className = 'milkdown-emoji-filter_item-emoji'
|
|
57
|
-
const keySpan = document.createElement('span')
|
|
58
|
-
keySpan.textContent = `:${key}:`
|
|
59
|
-
keySpan.className = 'milkdown-emoji-filter_item-key'
|
|
60
|
-
|
|
61
|
-
container.appendChild(emojiSpan)
|
|
62
|
-
container.appendChild(keySpan)
|
|
63
|
-
dropDown.appendChild(container)
|
|
64
|
-
|
|
65
|
-
if (i === 0)
|
|
66
|
-
setActive(container)
|
|
67
|
-
|
|
68
|
-
const onEnter = (e: MouseEvent) => {
|
|
69
|
-
const { target } = e
|
|
70
|
-
if (!(target instanceof HTMLElement))
|
|
71
|
-
return
|
|
72
|
-
setActive(target)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const onClick = (e: MouseEvent) => {
|
|
76
|
-
e.preventDefault()
|
|
77
|
-
onConfirm()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
container.addEventListener('mouseenter', onEnter)
|
|
81
|
-
container.addEventListener('mousedown', onClick)
|
|
82
|
-
})
|
|
83
|
-
}
|
package/src/filter/index.ts
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
|
|
3
|
-
import { missingRootElement } from '@milkdown/exception'
|
|
4
|
-
import { calculateNodePosition } from '@milkdown/prose'
|
|
5
|
-
import { Plugin, PluginKey } from '@milkdown/prose/state'
|
|
6
|
-
import type { ThemeUtils } from '@milkdown/utils'
|
|
7
|
-
import nodeEmoji from 'node-emoji'
|
|
8
|
-
|
|
9
|
-
import { checkTrigger, renderDropdownList } from './helper'
|
|
10
|
-
import { injectStyle } from './style'
|
|
11
|
-
|
|
12
|
-
export const key = new PluginKey('MILKDOWN_EMOJI_FILTER')
|
|
13
|
-
|
|
14
|
-
export const filter = (utils: ThemeUtils, maxListSize: number, twemojiOptions?: TwemojiOptions) => {
|
|
15
|
-
let trigger = false
|
|
16
|
-
let _from = 0
|
|
17
|
-
let _search = ''
|
|
18
|
-
let $active: null | HTMLElement = null
|
|
19
|
-
|
|
20
|
-
const off = () => {
|
|
21
|
-
trigger = false
|
|
22
|
-
_from = 0
|
|
23
|
-
_search = ''
|
|
24
|
-
$active = null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const setActive = (active: HTMLElement | null) => {
|
|
28
|
-
if ($active)
|
|
29
|
-
$active.classList.remove('active')
|
|
30
|
-
|
|
31
|
-
if (active)
|
|
32
|
-
active.classList.add('active')
|
|
33
|
-
|
|
34
|
-
$active = active
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return new Plugin({
|
|
38
|
-
key,
|
|
39
|
-
props: {
|
|
40
|
-
handleKeyDown(_, event) {
|
|
41
|
-
if (['Delete', 'Backspace'].includes(event.key)) {
|
|
42
|
-
_search = _search.slice(0, -1)
|
|
43
|
-
if (_search.length <= 1)
|
|
44
|
-
off()
|
|
45
|
-
|
|
46
|
-
return false
|
|
47
|
-
}
|
|
48
|
-
if (!trigger)
|
|
49
|
-
return false
|
|
50
|
-
if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key))
|
|
51
|
-
return false
|
|
52
|
-
|
|
53
|
-
return true
|
|
54
|
-
},
|
|
55
|
-
handleTextInput(view, from, to, text) {
|
|
56
|
-
trigger = checkTrigger(
|
|
57
|
-
view,
|
|
58
|
-
from,
|
|
59
|
-
to,
|
|
60
|
-
text,
|
|
61
|
-
(from) => {
|
|
62
|
-
_from = from
|
|
63
|
-
},
|
|
64
|
-
(search) => {
|
|
65
|
-
_search = search
|
|
66
|
-
},
|
|
67
|
-
)
|
|
68
|
-
if (!trigger)
|
|
69
|
-
off()
|
|
70
|
-
|
|
71
|
-
return false
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
view: (editorView) => {
|
|
75
|
-
const { parentNode } = editorView.dom
|
|
76
|
-
if (!parentNode)
|
|
77
|
-
throw missingRootElement()
|
|
78
|
-
|
|
79
|
-
const dropDown = document.createElement('div')
|
|
80
|
-
|
|
81
|
-
dropDown.classList.add('milkdown-emoji-filter', 'hide')
|
|
82
|
-
|
|
83
|
-
utils.themeManager.onFlush(() => {
|
|
84
|
-
const className = dropDown.className
|
|
85
|
-
.split(' ')
|
|
86
|
-
.filter(x => ['hide', 'milkdown-emoji-filter'].includes(x))
|
|
87
|
-
dropDown.className = className.join(' ')
|
|
88
|
-
const style = utils.getStyle(emotion => injectStyle(utils.themeManager, emotion))
|
|
89
|
-
if (style)
|
|
90
|
-
style.split(' ').forEach(x => dropDown.classList.add(x))
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
const replace = () => {
|
|
94
|
-
if (!$active)
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
const { tr } = editorView.state
|
|
98
|
-
const node = editorView.state.schema.node('emoji', { html: $active.firstElementChild?.innerHTML })
|
|
99
|
-
|
|
100
|
-
editorView.dispatch(tr.delete(_from, _from + _search.length).insert(_from, node))
|
|
101
|
-
off()
|
|
102
|
-
dropDown.classList.add('hide')
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
parentNode.appendChild(dropDown)
|
|
106
|
-
const onKeydown = (e: Event) => {
|
|
107
|
-
if (!trigger || !(e instanceof KeyboardEvent))
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
const { key } = e
|
|
111
|
-
|
|
112
|
-
if (key === 'Enter') {
|
|
113
|
-
replace()
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (['ArrowDown', 'ArrowUp'].includes(key)) {
|
|
118
|
-
const next
|
|
119
|
-
= key === 'ArrowDown'
|
|
120
|
-
? $active?.nextElementSibling || dropDown.firstElementChild
|
|
121
|
-
: $active?.previousElementSibling || dropDown.lastElementChild
|
|
122
|
-
if (!next)
|
|
123
|
-
return
|
|
124
|
-
setActive(next as HTMLElement)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
const onClick = (e: Event) => {
|
|
128
|
-
if (!trigger)
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
e.stopPropagation()
|
|
132
|
-
off()
|
|
133
|
-
dropDown.classList.add('hide')
|
|
134
|
-
}
|
|
135
|
-
parentNode.addEventListener('keydown', onKeydown)
|
|
136
|
-
parentNode.addEventListener('mousedown', onClick)
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
update: (view) => {
|
|
140
|
-
const { selection } = view.state
|
|
141
|
-
|
|
142
|
-
if (selection.from - selection.to !== 0 || !trigger) {
|
|
143
|
-
off()
|
|
144
|
-
dropDown.classList.add('hide')
|
|
145
|
-
return null
|
|
146
|
-
}
|
|
147
|
-
const result = nodeEmoji.search(_search).slice(0, maxListSize)
|
|
148
|
-
const { node } = view.domAtPos(_from)
|
|
149
|
-
if (result.length === 0 || !node) {
|
|
150
|
-
dropDown.classList.add('hide')
|
|
151
|
-
return null
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
dropDown.style.maxHeight = ''
|
|
155
|
-
dropDown.classList.remove('hide')
|
|
156
|
-
renderDropdownList(result, dropDown, replace, setActive, twemojiOptions)
|
|
157
|
-
calculateNodePosition(view, dropDown, (_selected, target, parent) => {
|
|
158
|
-
const $editor = dropDown.parentElement
|
|
159
|
-
if (!$editor)
|
|
160
|
-
throw missingRootElement()
|
|
161
|
-
|
|
162
|
-
const start = view.coordsAtPos(_from)
|
|
163
|
-
let left = start.left - parent.left
|
|
164
|
-
|
|
165
|
-
if (left < 0)
|
|
166
|
-
left = 0
|
|
167
|
-
|
|
168
|
-
let direction: 'top' | 'bottom'
|
|
169
|
-
let maxHeight: number | undefined
|
|
170
|
-
const startToTop = start.top - parent.top
|
|
171
|
-
const startToBottom = parent.height + parent.top - start.bottom
|
|
172
|
-
if (startToBottom >= target.height + 28) {
|
|
173
|
-
direction = 'bottom'
|
|
174
|
-
}
|
|
175
|
-
else if (startToTop >= target.height + 28) {
|
|
176
|
-
direction = 'top'
|
|
177
|
-
}
|
|
178
|
-
else if (startToBottom >= startToTop) {
|
|
179
|
-
direction = 'bottom'
|
|
180
|
-
maxHeight = startToBottom - 28
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
direction = 'top'
|
|
184
|
-
maxHeight = startToTop - 28
|
|
185
|
-
}
|
|
186
|
-
if (startToTop < 0 || startToBottom < 0) {
|
|
187
|
-
maxHeight = parent.height - (start.bottom - start.top) - 28
|
|
188
|
-
if (maxHeight > target.height)
|
|
189
|
-
maxHeight = undefined
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const top
|
|
193
|
-
= direction === 'top'
|
|
194
|
-
? start.top - parent.top - (maxHeight ?? target.height) - 14 + $editor.scrollTop
|
|
195
|
-
: start.bottom - parent.top + 14 + $editor.scrollTop
|
|
196
|
-
|
|
197
|
-
dropDown.style.maxHeight = maxHeight !== undefined && maxHeight > 0 ? `${maxHeight}px` : ''
|
|
198
|
-
|
|
199
|
-
const maxLeft = $editor.clientWidth - (dropDown.offsetWidth + 4)
|
|
200
|
-
if (left > maxLeft)
|
|
201
|
-
left = maxLeft
|
|
202
|
-
|
|
203
|
-
return [top, left]
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
return null
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
destroy: () => {
|
|
210
|
-
parentNode.removeEventListener('keydown', onKeydown)
|
|
211
|
-
parentNode.removeEventListener('mousedown', onClick)
|
|
212
|
-
dropDown.remove()
|
|
213
|
-
},
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
})
|
|
217
|
-
}
|
package/src/filter/style.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import type {
|
|
3
|
-
Color,
|
|
4
|
-
Emotion,
|
|
5
|
-
ThemeManager,
|
|
6
|
-
} from '@milkdown/core'
|
|
7
|
-
import {
|
|
8
|
-
ThemeBorder,
|
|
9
|
-
ThemeColor,
|
|
10
|
-
ThemeFont,
|
|
11
|
-
ThemeScrollbar,
|
|
12
|
-
ThemeShadow,
|
|
13
|
-
ThemeSize,
|
|
14
|
-
} from '@milkdown/core'
|
|
15
|
-
|
|
16
|
-
export const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) => {
|
|
17
|
-
const border = themeManager.get(ThemeBorder, undefined)
|
|
18
|
-
const shadow = themeManager.get(ThemeShadow, undefined)
|
|
19
|
-
const scrollbar = themeManager.get(ThemeScrollbar, undefined)
|
|
20
|
-
const radius = themeManager.get(ThemeSize, 'radius')
|
|
21
|
-
const typography = themeManager.get(ThemeFont, 'typography')
|
|
22
|
-
const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity])
|
|
23
|
-
|
|
24
|
-
const style = css`
|
|
25
|
-
min-height: 36px;
|
|
26
|
-
max-height: 320px;
|
|
27
|
-
overflow-y: auto;
|
|
28
|
-
border-radius: ${radius};
|
|
29
|
-
position: absolute;
|
|
30
|
-
background: ${palette('surface')};
|
|
31
|
-
|
|
32
|
-
&.hide {
|
|
33
|
-
display: none;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.milkdown-emoji-filter_item {
|
|
37
|
-
display: flex;
|
|
38
|
-
gap: 8px;
|
|
39
|
-
height: 36px;
|
|
40
|
-
padding: 0 14px;
|
|
41
|
-
align-items: center;
|
|
42
|
-
justify-content: flex-start;
|
|
43
|
-
cursor: pointer;
|
|
44
|
-
line-height: 2;
|
|
45
|
-
font-family: ${typography};
|
|
46
|
-
font-size: 14px;
|
|
47
|
-
&.active {
|
|
48
|
-
background: ${palette('secondary', 0.12)};
|
|
49
|
-
color: ${palette('primary')};
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.emoji {
|
|
54
|
-
height: 14px;
|
|
55
|
-
width: 14px;
|
|
56
|
-
margin: 0 1px 0 1.5px;
|
|
57
|
-
vertical-align: -1.5px;
|
|
58
|
-
}
|
|
59
|
-
`
|
|
60
|
-
return cx(border, shadow, scrollbar, style)
|
|
61
|
-
}
|
package/src/node.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import type { RemarkPlugin } from '@milkdown/core'
|
|
3
|
-
import { expectDomTypeError } from '@milkdown/exception'
|
|
4
|
-
import { InputRule } from '@milkdown/prose/inputrules'
|
|
5
|
-
import { createNode } from '@milkdown/utils'
|
|
6
|
-
import nodeEmoji from 'node-emoji'
|
|
7
|
-
import remarkEmoji from 'remark-emoji'
|
|
8
|
-
|
|
9
|
-
import { input } from './constant'
|
|
10
|
-
import { filter } from './filter'
|
|
11
|
-
import { parse } from './parse'
|
|
12
|
-
import { twemojiPlugin } from './remark-twemoji'
|
|
13
|
-
|
|
14
|
-
export interface EmojiOptions {
|
|
15
|
-
maxListSize: number
|
|
16
|
-
twemojiOptions: TwemojiOptions
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const emojiNode = createNode<string, EmojiOptions>((utils, options) => {
|
|
20
|
-
const getStyle = () =>
|
|
21
|
-
utils.getStyle(
|
|
22
|
-
({ css }) => css`
|
|
23
|
-
.emoji {
|
|
24
|
-
height: 1em;
|
|
25
|
-
width: 1em;
|
|
26
|
-
margin: 0 1px 0 1.5px;
|
|
27
|
-
vertical-align: -1.5px;
|
|
28
|
-
}
|
|
29
|
-
`,
|
|
30
|
-
)
|
|
31
|
-
return {
|
|
32
|
-
id: 'emoji',
|
|
33
|
-
schema: () => ({
|
|
34
|
-
group: 'inline',
|
|
35
|
-
inline: true,
|
|
36
|
-
atom: true,
|
|
37
|
-
attrs: {
|
|
38
|
-
html: {
|
|
39
|
-
default: '',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
parseDOM: [
|
|
43
|
-
{
|
|
44
|
-
tag: 'span[data-type="emoji"]',
|
|
45
|
-
getAttrs: (dom) => {
|
|
46
|
-
if (!(dom instanceof HTMLElement))
|
|
47
|
-
throw expectDomTypeError(dom)
|
|
48
|
-
|
|
49
|
-
return { html: dom.innerHTML }
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
],
|
|
53
|
-
toDOM: (node) => {
|
|
54
|
-
const span = document.createElement('span')
|
|
55
|
-
span.classList.add('emoji-wrapper')
|
|
56
|
-
span.dataset.type = 'emoji'
|
|
57
|
-
utils.themeManager.onFlush(() => {
|
|
58
|
-
const style = getStyle()
|
|
59
|
-
if (style)
|
|
60
|
-
span.classList.add(style)
|
|
61
|
-
})
|
|
62
|
-
span.innerHTML = node.attrs.html
|
|
63
|
-
return { dom: span }
|
|
64
|
-
},
|
|
65
|
-
parseMarkdown: {
|
|
66
|
-
match: ({ type }) => type === 'emoji',
|
|
67
|
-
runner: (state, node, type) => {
|
|
68
|
-
state.addNode(type, { html: node.value as string })
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
toMarkdown: {
|
|
72
|
-
match: node => node.type.name === 'emoji',
|
|
73
|
-
runner: (state, node) => {
|
|
74
|
-
const span = document.createElement('span')
|
|
75
|
-
span.innerHTML = node.attrs.html
|
|
76
|
-
const img = span.querySelector('img')
|
|
77
|
-
const title = img?.title
|
|
78
|
-
span.remove()
|
|
79
|
-
state.addNode('text', undefined, title)
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
}),
|
|
83
|
-
inputRules: nodeType => [
|
|
84
|
-
new InputRule(input, (state, match, start, end) => {
|
|
85
|
-
const content = match[0]
|
|
86
|
-
if (!content)
|
|
87
|
-
return null
|
|
88
|
-
const got = nodeEmoji.get(content)
|
|
89
|
-
if (!got || content.includes(got))
|
|
90
|
-
return null
|
|
91
|
-
|
|
92
|
-
const html = parse(got, options?.twemojiOptions)
|
|
93
|
-
|
|
94
|
-
return state.tr
|
|
95
|
-
.setMeta('emoji', true)
|
|
96
|
-
.replaceRangeWith(start, end, nodeType.create({ html }))
|
|
97
|
-
.scrollIntoView()
|
|
98
|
-
}),
|
|
99
|
-
],
|
|
100
|
-
remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin(options?.twemojiOptions)],
|
|
101
|
-
prosePlugins: () => [filter(utils, options?.maxListSize ?? 6, options?.twemojiOptions)],
|
|
102
|
-
}
|
|
103
|
-
})
|