@milkdown/plugin-emoji 5.5.0 → 6.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/lib/{src/constant.d.ts → constant.d.ts} +0 -1
- package/lib/constant.d.ts.map +1 -0
- package/lib/{src/filter → filter}/helper.d.ts +0 -0
- package/lib/filter/helper.d.ts.map +1 -0
- package/lib/{src/filter → filter}/index.d.ts +1 -1
- package/lib/filter/index.d.ts.map +1 -0
- package/lib/filter/style.d.ts +3 -0
- package/lib/filter/style.d.ts.map +1 -0
- package/lib/index.d.ts +4 -1
- package/lib/index.d.ts.map +1 -0
- package/lib/index.es.js +75 -158
- package/lib/index.es.js.map +1 -1
- package/lib/node.d.ts +9 -0
- package/lib/node.d.ts.map +1 -0
- package/lib/{src/parse.d.ts → parse.d.ts} +0 -0
- package/lib/parse.d.ts.map +1 -0
- package/lib/{src/remark-twemoji.d.ts → remark-twemoji.d.ts} +0 -0
- package/lib/remark-twemoji.d.ts.map +1 -0
- package/package.json +33 -11
- package/src/constant.ts +0 -1
- package/src/filter/helper.ts +16 -8
- package/src/filter/index.ts +25 -11
- package/src/filter/style.ts +22 -10
- package/src/index.ts +1 -2
- package/src/node.ts +27 -20
- package/src/parse.ts +4 -1
- package/lib/src/constant.d.ts.map +0 -1
- package/lib/src/filter/helper.d.ts.map +0 -1
- package/lib/src/filter/index.d.ts.map +0 -1
- package/lib/src/filter/style.d.ts +0 -3
- package/lib/src/filter/style.d.ts.map +0 -1
- package/lib/src/index.d.ts +0 -4
- package/lib/src/index.d.ts.map +0 -1
- package/lib/src/node.d.ts +0 -6
- package/lib/src/node.d.ts.map +0 -1
- package/lib/src/parse.d.ts.map +0 -1
- package/lib/src/picker.d.ts +0 -5
- package/lib/src/picker.d.ts.map +0 -1
- package/lib/src/remark-twemoji.d.ts.map +0 -1
- package/src/picker.ts +0 -127
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constant.d.ts","sourceRoot":"","sources":["../src/constant.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,QAAqB,CAAC;AACvC,eAAO,MAAM,IAAI,QAAwB,CAAC;AAC1C,eAAO,MAAM,KAAK,QAAmB,CAAC"}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/filter/helper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAKnC,eAAO,MAAM,YAAY,SACf,UAAU,QACV,MAAM,MACR,MAAM,QACJ,MAAM,mBACK,MAAM,MAAM,MAAM,KAAK,IAAI,qBACzB,MAAM,KAAK,IAAI,YAoBrC,CAAC;AAEF,eAAO,MAAM,kBAAkB,SACrB,KAAK,EAAE,YACH,WAAW,WACZ,WAAW,GAAG,IAAI,aAChB,MAAM,IAAI,sBACD,WAAW,GAAG,IAAI,KAAK,IAAI,SAmDlD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Plugin, PluginKey } from '@milkdown/prose';
|
|
2
2
|
import { Utils } from '@milkdown/utils';
|
|
3
3
|
export declare const key: PluginKey<any, any>;
|
|
4
|
-
export declare const filter: (utils: Utils) => Plugin<any, any>;
|
|
4
|
+
export declare const filter: (utils: Utils, maxListSize: number) => Plugin<any, any>;
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filter/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,MAAM,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAMxC,eAAO,MAAM,GAAG,qBAAgD,CAAC;AAEjE,eAAO,MAAM,MAAM,UAAW,KAAK,eAAe,MAAM,qBAuKvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/filter/style.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,OAAO,EAIP,YAAY,EAGf,MAAM,gBAAgB,CAAC;AAExB,eAAO,MAAM,WAAW,iBAAkB,YAAY,eAAe,OAAO,WAyC3E,CAAC"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { AtomList } from '@milkdown/utils';
|
|
2
|
+
export * from './node';
|
|
3
|
+
export declare const emoji: AtomList<import("@milkdown/utils/lib/types").Metadata<import("@milkdown/utils/lib/types").GetPlugin<string, import("./node").EmojiOptions>> & import("@milkdown/core").MilkdownPlugin>;
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,cAAc,QAAQ,CAAC;AAEvB,eAAO,MAAM,KAAK,wLAAiC,CAAC"}
|
package/lib/index.es.js
CHANGED
|
@@ -18,17 +18,18 @@ var __spreadValues = (a, b) => {
|
|
|
18
18
|
};
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
20
|
import { createNode, AtomList } from "@milkdown/utils";
|
|
21
|
-
import { PluginKey, Plugin, calculateNodePosition,
|
|
21
|
+
import { PluginKey, Plugin, calculateNodePosition, InputRule } from "@milkdown/prose";
|
|
22
22
|
import nodeEmoji, { search } from "node-emoji";
|
|
23
23
|
import remarkEmoji from "remark-emoji";
|
|
24
24
|
import twemoji from "twemoji";
|
|
25
|
-
import {
|
|
25
|
+
import { ThemeBorder, ThemeShadow, ThemeSize, ThemeFont, ThemeColor } from "@milkdown/core";
|
|
26
26
|
import emojiRegex from "emoji-regex";
|
|
27
27
|
const part = /:\+1|:-1|:[\w-]+/;
|
|
28
28
|
const full = /:\+1:|:-1:|:[\w-]+:/;
|
|
29
29
|
const input = /(:([^:\s]+):)$/;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
30
|
+
const setAttr = (text) => ({ title: text });
|
|
31
|
+
const parse = (emoji2) => twemoji.parse(emoji2, { attributes: setAttr });
|
|
32
|
+
const checkTrigger = (view, from, to, text, setRange, setSearch) => {
|
|
32
33
|
if (view.composing)
|
|
33
34
|
return false;
|
|
34
35
|
const { state } = view;
|
|
@@ -49,7 +50,9 @@ const checkTrigger$1 = (view, from, to, text, setRange, setSearch) => {
|
|
|
49
50
|
return false;
|
|
50
51
|
};
|
|
51
52
|
const renderDropdownList = (list, dropDown, $active, onConfirm, setActive) => {
|
|
52
|
-
dropDown.
|
|
53
|
+
while (dropDown.firstChild) {
|
|
54
|
+
dropDown.firstChild.remove();
|
|
55
|
+
}
|
|
53
56
|
list.forEach(({ emoji: emoji2, key: key2 }, i) => {
|
|
54
57
|
const container = document.createElement("div");
|
|
55
58
|
container.className = "milkdown-emoji-filter_item";
|
|
@@ -66,7 +69,7 @@ const renderDropdownList = (list, dropDown, $active, onConfirm, setActive) => {
|
|
|
66
69
|
container.classList.add("active");
|
|
67
70
|
setActive(container);
|
|
68
71
|
}
|
|
69
|
-
|
|
72
|
+
const onEnter = (e) => {
|
|
70
73
|
if ($active) {
|
|
71
74
|
$active.classList.remove("active");
|
|
72
75
|
}
|
|
@@ -75,43 +78,48 @@ const renderDropdownList = (list, dropDown, $active, onConfirm, setActive) => {
|
|
|
75
78
|
return;
|
|
76
79
|
target.classList.add("active");
|
|
77
80
|
setActive(target);
|
|
78
|
-
}
|
|
79
|
-
|
|
81
|
+
};
|
|
82
|
+
const onLeave = (e) => {
|
|
80
83
|
const { target } = e;
|
|
81
84
|
if (!(target instanceof HTMLElement))
|
|
82
85
|
return;
|
|
83
86
|
target.classList.remove("active");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
onConfirm();
|
|
87
|
+
};
|
|
88
|
+
const onClick = (e) => {
|
|
87
89
|
e.preventDefault();
|
|
88
|
-
|
|
90
|
+
onConfirm();
|
|
91
|
+
};
|
|
92
|
+
container.addEventListener("mouseenter", onEnter);
|
|
93
|
+
container.addEventListener("mouseleave", onLeave);
|
|
94
|
+
container.addEventListener("mousedown", onClick);
|
|
89
95
|
});
|
|
90
96
|
};
|
|
91
|
-
const injectStyle = (
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
const
|
|
97
|
+
const injectStyle = (themeManager, { css, cx }) => {
|
|
98
|
+
const border = themeManager.get(ThemeBorder, void 0);
|
|
99
|
+
const shadow = themeManager.get(ThemeShadow, void 0);
|
|
100
|
+
const radius = themeManager.get(ThemeSize, "radius");
|
|
101
|
+
const typography = themeManager.get(ThemeFont, "typography");
|
|
102
|
+
const palette = (color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity]);
|
|
95
103
|
const style = css`
|
|
96
104
|
position: absolute;
|
|
97
105
|
&.hide {
|
|
98
106
|
display: none;
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
border-radius: ${
|
|
109
|
+
border-radius: ${radius};
|
|
102
110
|
background: ${palette("surface")};
|
|
103
111
|
|
|
104
112
|
.milkdown-emoji-filter_item {
|
|
105
113
|
display: flex;
|
|
106
|
-
gap: 0.
|
|
107
|
-
height: 2.
|
|
108
|
-
padding: 0
|
|
114
|
+
gap: 0.5em;
|
|
115
|
+
height: 2.57143em;
|
|
116
|
+
padding: 0 1em;
|
|
109
117
|
align-items: center;
|
|
110
118
|
justify-content: flex-start;
|
|
111
119
|
cursor: pointer;
|
|
112
120
|
line-height: 2;
|
|
113
|
-
font-family: ${
|
|
114
|
-
font-size: 0.
|
|
121
|
+
font-family: ${typography};
|
|
122
|
+
font-size: 0.875em;
|
|
115
123
|
&.active {
|
|
116
124
|
background: ${palette("secondary", 0.12)};
|
|
117
125
|
color: ${palette("primary")};
|
|
@@ -127,8 +135,8 @@ const injectStyle = ({ size, mixin, palette, font }, { css, cx }) => {
|
|
|
127
135
|
`;
|
|
128
136
|
return cx(border, shadow, style);
|
|
129
137
|
};
|
|
130
|
-
const key
|
|
131
|
-
const filter = (utils) => {
|
|
138
|
+
const key = new PluginKey("MILKDOWN_PLUGIN_EMOJI_FILTER");
|
|
139
|
+
const filter = (utils, maxListSize) => {
|
|
132
140
|
let trigger = false;
|
|
133
141
|
let _from = 0;
|
|
134
142
|
let _search = "";
|
|
@@ -140,7 +148,7 @@ const filter = (utils) => {
|
|
|
140
148
|
$active = null;
|
|
141
149
|
};
|
|
142
150
|
return new Plugin({
|
|
143
|
-
key
|
|
151
|
+
key,
|
|
144
152
|
props: {
|
|
145
153
|
handleKeyDown(_, event) {
|
|
146
154
|
if (["Delete", "Backspace"].includes(event.key)) {
|
|
@@ -158,7 +166,7 @@ const filter = (utils) => {
|
|
|
158
166
|
return true;
|
|
159
167
|
},
|
|
160
168
|
handleTextInput(view, from, to, text) {
|
|
161
|
-
trigger = checkTrigger
|
|
169
|
+
trigger = checkTrigger(view, from, to, text, (from2) => {
|
|
162
170
|
_from = from2;
|
|
163
171
|
}, (search2) => {
|
|
164
172
|
_search = search2;
|
|
@@ -175,11 +183,15 @@ const filter = (utils) => {
|
|
|
175
183
|
throw new Error();
|
|
176
184
|
}
|
|
177
185
|
const dropDown = document.createElement("div");
|
|
178
|
-
const style = utils.getStyle(injectStyle);
|
|
179
|
-
if (style) {
|
|
180
|
-
style.split(" ").forEach((x) => dropDown.classList.add(x));
|
|
181
|
-
}
|
|
182
186
|
dropDown.classList.add("milkdown-emoji-filter", "hide");
|
|
187
|
+
utils.themeManager.onFlush(() => {
|
|
188
|
+
const className = dropDown.className.split(" ").filter((x) => ["hide", "milkdown-emoji-filter"].includes(x));
|
|
189
|
+
dropDown.className = className.join(" ");
|
|
190
|
+
const style = utils.getStyle((emotion) => injectStyle(utils.themeManager, emotion));
|
|
191
|
+
if (style) {
|
|
192
|
+
style.split(" ").forEach((x) => dropDown.classList.add(x));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
183
195
|
const replace = () => {
|
|
184
196
|
var _a;
|
|
185
197
|
if (!$active)
|
|
@@ -191,7 +203,7 @@ const filter = (utils) => {
|
|
|
191
203
|
dropDown.classList.add("hide");
|
|
192
204
|
};
|
|
193
205
|
parentNode.appendChild(dropDown);
|
|
194
|
-
|
|
206
|
+
const onKeydown = (e) => {
|
|
195
207
|
if (!trigger || !(e instanceof KeyboardEvent))
|
|
196
208
|
return;
|
|
197
209
|
const { key: key2 } = e;
|
|
@@ -210,14 +222,16 @@ const filter = (utils) => {
|
|
|
210
222
|
$active = next;
|
|
211
223
|
return;
|
|
212
224
|
}
|
|
213
|
-
}
|
|
214
|
-
|
|
225
|
+
};
|
|
226
|
+
const onClick = (e) => {
|
|
215
227
|
if (!trigger)
|
|
216
228
|
return;
|
|
217
229
|
e.stopPropagation();
|
|
218
230
|
off();
|
|
219
231
|
dropDown.classList.add("hide");
|
|
220
|
-
}
|
|
232
|
+
};
|
|
233
|
+
parentNode.addEventListener("keydown", onKeydown);
|
|
234
|
+
parentNode.addEventListener("mousedown", onClick);
|
|
221
235
|
return {
|
|
222
236
|
update: (view) => {
|
|
223
237
|
const { selection } = view.state;
|
|
@@ -226,7 +240,7 @@ const filter = (utils) => {
|
|
|
226
240
|
dropDown.classList.add("hide");
|
|
227
241
|
return null;
|
|
228
242
|
}
|
|
229
|
-
const result = search(_search).slice(0,
|
|
243
|
+
const result = search(_search).slice(0, maxListSize);
|
|
230
244
|
const { node } = view.domAtPos(_from);
|
|
231
245
|
if (result.length === 0 || !node) {
|
|
232
246
|
dropDown.classList.add("hide");
|
|
@@ -253,118 +267,15 @@ const filter = (utils) => {
|
|
|
253
267
|
return [top, left];
|
|
254
268
|
});
|
|
255
269
|
return null;
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
};
|
|
261
|
-
const keyword = ":emoji:";
|
|
262
|
-
const key = new PluginKey("MILKDOWN_PLUGIN_EMOJI_PICKER");
|
|
263
|
-
const checkTrigger = (view, from, to, text, setRange) => {
|
|
264
|
-
if (view.composing)
|
|
265
|
-
return false;
|
|
266
|
-
const { state } = view;
|
|
267
|
-
const $from = state.doc.resolve(from);
|
|
268
|
-
if ($from.parent.type.spec.code)
|
|
269
|
-
return false;
|
|
270
|
-
const textBefore = $from.parent.textBetween($from.parentOffset - keyword.length + 1, $from.parentOffset, void 0, "\uFFFC") + text;
|
|
271
|
-
if (textBefore === keyword) {
|
|
272
|
-
setRange(from - keyword.length + 1, to + 1);
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
return false;
|
|
276
|
-
};
|
|
277
|
-
const picker = (utils) => {
|
|
278
|
-
let trigger = false;
|
|
279
|
-
const holder = document.createElement("span");
|
|
280
|
-
let _from = 0;
|
|
281
|
-
let _to = 0;
|
|
282
|
-
const off = () => {
|
|
283
|
-
trigger = false;
|
|
284
|
-
_from = 0;
|
|
285
|
-
_to = 0;
|
|
286
|
-
};
|
|
287
|
-
const plugin = new Plugin({
|
|
288
|
-
key,
|
|
289
|
-
props: {
|
|
290
|
-
handleKeyDown() {
|
|
291
|
-
off();
|
|
292
|
-
return false;
|
|
293
|
-
},
|
|
294
|
-
handleClick() {
|
|
295
|
-
off();
|
|
296
|
-
return false;
|
|
297
|
-
},
|
|
298
|
-
handleTextInput(view, from, to, text) {
|
|
299
|
-
trigger = checkTrigger(view, from, to, text, (from2, to2) => {
|
|
300
|
-
_from = from2;
|
|
301
|
-
_to = to2;
|
|
302
|
-
});
|
|
303
|
-
if (!trigger) {
|
|
304
|
-
off();
|
|
305
|
-
}
|
|
306
|
-
return false;
|
|
307
|
-
},
|
|
308
|
-
decorations(state) {
|
|
309
|
-
if (!trigger)
|
|
310
|
-
return null;
|
|
311
|
-
return DecorationSet.create(state.doc, [Decoration.widget(state.selection.to, holder)]);
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
view: (editorView) => {
|
|
315
|
-
const { parentNode } = editorView.dom;
|
|
316
|
-
if (!parentNode) {
|
|
317
|
-
throw new Error();
|
|
318
|
-
}
|
|
319
|
-
utils.getStyle(({ palette, font }, { injectGlobal }) => {
|
|
320
|
-
const css = injectGlobal;
|
|
321
|
-
css`
|
|
322
|
-
.emoji-picker {
|
|
323
|
-
--dark-search-background-color: ${palette("surface")} !important;
|
|
324
|
-
--dark-text-color: ${palette("neutral", 0.87)} !important;
|
|
325
|
-
--dark-background-color: ${palette("background")} !important;
|
|
326
|
-
--dark-border-color: ${palette("shadow")} !important;
|
|
327
|
-
--dark-hover-color: ${palette("secondary", 0.12)} !important;
|
|
328
|
-
--dark-blue-color: ${palette("primary")} !important;
|
|
329
|
-
--dark-search-icon-color: ${palette("primary")} !important;
|
|
330
|
-
--dark-category-button-color: ${palette("secondary", 0.4)} !important;
|
|
331
|
-
--font: ${font.typography} !important;
|
|
332
|
-
--font-size: 1rem !important;
|
|
333
|
-
}
|
|
334
|
-
`;
|
|
335
|
-
});
|
|
336
|
-
const emojiPicker = new EmojiButton({
|
|
337
|
-
rootElement: parentNode,
|
|
338
|
-
autoFocusSearch: false,
|
|
339
|
-
style: "twemoji",
|
|
340
|
-
theme: "dark",
|
|
341
|
-
zIndex: 99
|
|
342
|
-
});
|
|
343
|
-
emojiPicker.on("emoji", (selection) => {
|
|
344
|
-
const start = _from;
|
|
345
|
-
const end = _to;
|
|
346
|
-
off();
|
|
347
|
-
const html = parse(selection.emoji);
|
|
348
|
-
const node = editorView.state.schema.node("emoji", { html });
|
|
349
|
-
const { tr } = editorView.state;
|
|
350
|
-
editorView.dispatch(tr.replaceRangeWith(start, end, node));
|
|
351
|
-
});
|
|
352
|
-
return {
|
|
353
|
-
update: () => {
|
|
354
|
-
if (!trigger) {
|
|
355
|
-
emojiPicker.hidePicker();
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
emojiPicker.showPicker(holder);
|
|
359
|
-
return null;
|
|
360
270
|
},
|
|
361
271
|
destroy: () => {
|
|
362
|
-
|
|
272
|
+
parentNode.removeEventListener("keydown", onKeydown);
|
|
273
|
+
parentNode.removeEventListener("mousedown", onClick);
|
|
274
|
+
dropDown.remove();
|
|
363
275
|
}
|
|
364
276
|
};
|
|
365
277
|
}
|
|
366
278
|
});
|
|
367
|
-
return plugin;
|
|
368
279
|
};
|
|
369
280
|
const regex = emojiRegex();
|
|
370
281
|
const isParent = (node) => !!node.children;
|
|
@@ -422,19 +333,19 @@ const twemojiPlugin = () => {
|
|
|
422
333
|
}
|
|
423
334
|
return transformer;
|
|
424
335
|
};
|
|
425
|
-
const emojiNode = createNode((utils) => {
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
336
|
+
const emojiNode = createNode((utils, options) => {
|
|
337
|
+
const getStyle = () => utils.getStyle(({ css }) => css`
|
|
338
|
+
display: inline-flex;
|
|
339
|
+
justify-content: center;
|
|
340
|
+
align-items: center;
|
|
430
341
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
342
|
+
.emoji {
|
|
343
|
+
height: 1em;
|
|
344
|
+
width: 1em;
|
|
345
|
+
margin: 0 0.05em 0 0.1em;
|
|
346
|
+
vertical-align: -0.1em;
|
|
347
|
+
}
|
|
348
|
+
`);
|
|
438
349
|
return {
|
|
439
350
|
id: "emoji",
|
|
440
351
|
schema: () => ({
|
|
@@ -459,11 +370,14 @@ const emojiNode = createNode((utils) => {
|
|
|
459
370
|
],
|
|
460
371
|
toDOM: (node) => {
|
|
461
372
|
const span = document.createElement("span");
|
|
462
|
-
span.dataset["type"] = "emoji";
|
|
463
|
-
if (style) {
|
|
464
|
-
span.classList.add(style);
|
|
465
|
-
}
|
|
466
373
|
span.classList.add("emoji-wrapper");
|
|
374
|
+
span.dataset["type"] = "emoji";
|
|
375
|
+
utils.themeManager.onFlush(() => {
|
|
376
|
+
const style = getStyle();
|
|
377
|
+
if (style) {
|
|
378
|
+
span.classList.add(style);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
467
381
|
span.innerHTML = node.attrs["html"];
|
|
468
382
|
return { dom: span };
|
|
469
383
|
},
|
|
@@ -498,7 +412,10 @@ const emojiNode = createNode((utils) => {
|
|
|
498
412
|
})
|
|
499
413
|
],
|
|
500
414
|
remarkPlugins: () => [remarkEmoji, twemojiPlugin],
|
|
501
|
-
prosePlugins: () =>
|
|
415
|
+
prosePlugins: () => {
|
|
416
|
+
var _a;
|
|
417
|
+
return [filter(utils, (_a = options == null ? void 0 : options.maxListSize) != null ? _a : 6)];
|
|
418
|
+
}
|
|
502
419
|
};
|
|
503
420
|
});
|
|
504
421
|
const emoji = AtomList.create([emojiNode()]);
|
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/picker.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]+):)$/;\nexport const keyword = ':emoji:';\n","/* Copyright 2021, Milkdown by Mirone. */\nimport twemoji from 'twemoji';\n\nexport const parse = (emoji: string) => twemoji.parse(emoji, { attributes: (text) => ({ title: text }) });\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport { EditorView } from '@milkdown/prose';\nimport { 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) return false;\n const { state } = view;\n const $from = state.doc.resolve(from);\n if ($from.parent.type.spec.code) 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 $active: HTMLElement | null,\n onConfirm: () => void,\n setActive: (active: HTMLElement | null) => void,\n) => {\n dropDown.innerHTML = '';\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);\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 container.classList.add('active');\n setActive(container);\n }\n\n container.addEventListener('mouseenter', (e) => {\n if ($active) {\n $active.classList.remove('active');\n }\n const { target } = e;\n if (!(target instanceof HTMLElement)) return;\n target.classList.add('active');\n setActive(target);\n });\n container.addEventListener('mouseleave', (e) => {\n const { target } = e;\n if (!(target instanceof HTMLElement)) return;\n target.classList.remove('active');\n });\n container.addEventListener('mousedown', (e) => {\n onConfirm();\n e.preventDefault();\n });\n });\n};\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { Emotion, ThemeTool } from '@milkdown/core';\n\nexport const injectStyle = ({ size, mixin, palette, font }: ThemeTool, { css, cx }: Emotion) => {\n const border = mixin.border?.();\n const shadow = mixin.shadow?.();\n\n const style = css`\n position: absolute;\n &.hide {\n display: none;\n }\n\n border-radius: ${size.radius};\n background: ${palette('surface')};\n\n .milkdown-emoji-filter_item {\n display: flex;\n gap: 0.5rem;\n height: 2.25rem;\n padding: 0 1rem;\n align-items: center;\n justify-content: flex-start;\n cursor: pointer;\n line-height: 2;\n font-family: ${font.typography};\n font-size: 0.875rem;\n &.active {\n background: ${palette('secondary', 0.12)};\n color: ${palette('primary')};\n }\n }\n\n .emoji {\n height: 1em;\n width: 1em;\n margin: 0 0.05em 0 0.1em;\n vertical-align: -0.1em;\n }\n `;\n return cx(border, shadow, style);\n};\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport { calculateNodePosition, Plugin, PluginKey } from '@milkdown/prose';\nimport { Utils } from '@milkdown/utils';\nimport { search } from 'node-emoji';\n\nimport { checkTrigger, renderDropdownList } from './helper';\nimport { injectStyle } from './style';\n\nexport const key = new PluginKey('MILKDOWN_PLUGIN_EMOJI_FILTER');\n\nexport const filter = (utils: Utils) => {\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 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) 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 new Error();\n }\n\n const dropDown = document.createElement('div');\n const style = utils.getStyle(injectStyle);\n\n if (style) {\n style.split(' ').forEach((x) => dropDown.classList.add(x));\n }\n\n dropDown.classList.add('milkdown-emoji-filter', 'hide');\n\n const replace = () => {\n if (!$active) 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 parentNode.addEventListener('keydown', (e) => {\n if (!trigger || !(e instanceof KeyboardEvent)) 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 ($active) {\n $active.classList.remove('active');\n }\n if (!next) return;\n next.classList.add('active');\n $active = next as HTMLElement;\n\n return;\n }\n });\n parentNode.addEventListener('mousedown', (e) => {\n if (!trigger) return;\n\n e.stopPropagation();\n off();\n dropDown.classList.add('hide');\n });\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 = search(_search).slice(0, 5);\n const { node } = view.domAtPos(_from);\n if (result.length === 0 || !node) {\n dropDown.classList.add('hide');\n return null;\n }\n\n dropDown.classList.remove('hide');\n renderDropdownList(result, dropDown, $active, replace, (a) => {\n $active = a;\n });\n calculateNodePosition(view, dropDown, (selected, target, parent) => {\n const $editor = dropDown.parentElement;\n if (!$editor) {\n throw new Error();\n }\n const start = view.coordsAtPos(_from);\n let left = start.left - parent.left;\n let top = selected.bottom - parent.top + 14 + $editor.scrollTop;\n\n if (left < 0) {\n left = 0;\n }\n\n if (window.innerHeight - start.bottom < target.height) {\n top = selected.top - parent.top - target.height - 14 + $editor.scrollTop;\n }\n return [top, left];\n });\n\n return null;\n },\n };\n },\n });\n};\n","/* Copyright 2021, Milkdown by Mirone. */\nimport { EmojiButton } from '@joeattardi/emoji-button';\nimport { Decoration, DecorationSet, EditorView, Plugin, PluginKey } from '@milkdown/prose';\nimport { Utils } from '@milkdown/utils';\n\nimport { parse } from './parse';\n\nconst keyword = ':emoji:';\nexport const key = new PluginKey('MILKDOWN_PLUGIN_EMOJI_PICKER');\n\nconst checkTrigger = (\n view: EditorView,\n from: number,\n to: number,\n text: string,\n setRange: (from: number, to: number) => void,\n) => {\n if (view.composing) return false;\n const { state } = view;\n const $from = state.doc.resolve(from);\n if ($from.parent.type.spec.code) return false;\n const textBefore =\n $from.parent.textBetween($from.parentOffset - keyword.length + 1, $from.parentOffset, undefined, '\\ufffc') +\n text;\n if (textBefore === keyword) {\n setRange(from - keyword.length + 1, to + 1);\n return true;\n }\n return false;\n};\n\nexport const picker = (utils: Utils) => {\n let trigger = false;\n const holder = document.createElement('span');\n let _from = 0;\n let _to = 0;\n const off = () => {\n trigger = false;\n _from = 0;\n _to = 0;\n };\n\n const plugin = new Plugin({\n key,\n props: {\n handleKeyDown() {\n off();\n return false;\n },\n handleClick() {\n off();\n return false;\n },\n handleTextInput(view, from, to, text) {\n trigger = checkTrigger(view, from, to, text, (from, to) => {\n _from = from;\n _to = to;\n });\n\n if (!trigger) {\n off();\n }\n return false;\n },\n decorations(state) {\n if (!trigger) return null;\n\n return DecorationSet.create(state.doc, [Decoration.widget(state.selection.to, holder)]);\n },\n },\n view: (editorView) => {\n const { parentNode } = editorView.dom;\n if (!parentNode) {\n throw new Error();\n }\n utils.getStyle(({ palette, font }, { injectGlobal }) => {\n const css = injectGlobal;\n css`\n .emoji-picker {\n --dark-search-background-color: ${palette('surface')} !important;\n --dark-text-color: ${palette('neutral', 0.87)} !important;\n --dark-background-color: ${palette('background')} !important;\n --dark-border-color: ${palette('shadow')} !important;\n --dark-hover-color: ${palette('secondary', 0.12)} !important;\n --dark-blue-color: ${palette('primary')} !important;\n --dark-search-icon-color: ${palette('primary')} !important;\n --dark-category-button-color: ${palette('secondary', 0.4)} !important;\n --font: ${font.typography} !important;\n --font-size: 1rem !important;\n }\n `;\n });\n const emojiPicker = new EmojiButton({\n rootElement: parentNode as HTMLElement,\n autoFocusSearch: false,\n style: 'twemoji',\n theme: 'dark',\n zIndex: 99,\n });\n emojiPicker.on('emoji', (selection) => {\n const start = _from;\n const end = _to;\n off();\n const html = parse(selection.emoji);\n const node = editorView.state.schema.node('emoji', { html });\n const { tr } = editorView.state;\n\n editorView.dispatch(tr.replaceRangeWith(start, end, node));\n });\n return {\n update: () => {\n if (!trigger) {\n emojiPicker.hidePicker();\n return null;\n }\n emojiPicker.showPicker(holder);\n return null;\n },\n destroy: () => {\n emojiPicker.destroyPicker();\n },\n };\n },\n });\n\n return plugin;\n};\n","/* Copyright 2021, Milkdown by Mirone. */\nimport emojiRegex from 'emoji-regex';\nimport { 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 }\n node.children = out;\n }\n\n return fn(node, index, parent);\n }\n}\n\nexport const twemojiPlugin = () => {\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 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), type: 'emoji' });\n str = str.slice(index + emoji.length);\n }\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 { RemarkPlugin } from '@milkdown/core';\nimport { InputRule } from '@milkdown/prose';\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 { picker } from './picker';\nimport { twemojiPlugin } from './remark-twemoji';\n\nexport const emojiNode = createNode((utils) => {\n const style = utils.getStyle(\n (_, { css }) => css`\n display: inline-flex;\n justify-content: center;\n align-items: center;\n\n .emoji {\n height: 1em;\n width: 1em;\n margin: 0 0.05em 0 0.1em;\n vertical-align: -0.1em;\n }\n `,\n );\n return {\n id: 'emoji',\n schema: () => ({\n group: 'inline',\n inline: true,\n selectable: false,\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 new Error();\n }\n return { html: dom.innerHTML };\n },\n },\n ],\n toDOM: (node) => {\n const span = document.createElement('span');\n span.dataset['type'] = 'emoji';\n if (style) {\n span.classList.add(style);\n }\n span.classList.add('emoji-wrapper');\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) return null;\n const got = nodeEmoji.get(content);\n if (!got || content.includes(got)) return null;\n\n const html = parse(got);\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],\n prosePlugins: () => [picker(utils), filter(utils)],\n };\n});\n","/* Copyright 2021, Milkdown by Mirone. */\nimport { AtomList } from '@milkdown/utils';\n\nimport { emojiNode } from './node';\n\nexport const emoji = AtomList.create([emojiNode()]);\n\nexport { emojiNode } from './node';\n"],"names":["checkTrigger","key"],"mappings":"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"}
|
|
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): string =>\n twemoji.parse(emoji, { attributes: setAttr as unknown as () => void }) as unknown as string;\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport { EditorView } from '@milkdown/prose';\nimport { 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) return false;\n const { state } = view;\n const $from = state.doc.resolve(from);\n if ($from.parent.type.spec.code) 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 $active: HTMLElement | null,\n onConfirm: () => void,\n setActive: (active: HTMLElement | null) => void,\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);\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 container.classList.add('active');\n setActive(container);\n }\n\n const onEnter = (e: MouseEvent) => {\n if ($active) {\n $active.classList.remove('active');\n }\n const { target } = e;\n if (!(target instanceof HTMLElement)) return;\n target.classList.add('active');\n setActive(target);\n };\n\n const onLeave = (e: MouseEvent) => {\n const { target } = e;\n if (!(target instanceof HTMLElement)) return;\n target.classList.remove('active');\n };\n\n const onClick = (e: MouseEvent) => {\n e.preventDefault();\n onConfirm();\n };\n\n container.addEventListener('mouseenter', onEnter);\n container.addEventListener('mouseleave', onLeave);\n container.addEventListener('mousedown', onClick);\n });\n};\n","/* Copyright 2021, Milkdown by Mirone. */\nimport {\n Color,\n Emotion,\n ThemeBorder,\n ThemeColor,\n ThemeFont,\n ThemeManager,\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 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 position: absolute;\n &.hide {\n display: none;\n }\n\n border-radius: ${radius};\n background: ${palette('surface')};\n\n .milkdown-emoji-filter_item {\n display: flex;\n gap: 0.5em;\n height: 2.57143em;\n padding: 0 1em;\n align-items: center;\n justify-content: flex-start;\n cursor: pointer;\n line-height: 2;\n font-family: ${typography};\n font-size: 0.875em;\n &.active {\n background: ${palette('secondary', 0.12)};\n color: ${palette('primary')};\n }\n }\n\n .emoji {\n height: 1em;\n width: 1em;\n margin: 0 0.05em 0 0.1em;\n vertical-align: -0.1em;\n }\n `;\n return cx(border, shadow, style);\n};\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport { calculateNodePosition, Plugin, PluginKey } from '@milkdown/prose';\nimport { Utils } from '@milkdown/utils';\nimport { search } from 'node-emoji';\n\nimport { checkTrigger, renderDropdownList } from './helper';\nimport { injectStyle } from './style';\n\nexport const key = new PluginKey('MILKDOWN_PLUGIN_EMOJI_FILTER');\n\nexport const filter = (utils: Utils, maxListSize: number) => {\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 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) 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 new Error();\n }\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\n const replace = () => {\n if (!$active) 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)) 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 ($active) {\n $active.classList.remove('active');\n }\n if (!next) return;\n next.classList.add('active');\n $active = next as HTMLElement;\n\n return;\n }\n };\n const onClick = (e: Event) => {\n if (!trigger) 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 = 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.classList.remove('hide');\n renderDropdownList(result, dropDown, $active, replace, (a) => {\n $active = a;\n });\n calculateNodePosition(view, dropDown, (selected, target, parent) => {\n const $editor = dropDown.parentElement;\n if (!$editor) {\n throw new Error();\n }\n const start = view.coordsAtPos(_from);\n let left = start.left - parent.left;\n let top = selected.bottom - parent.top + 14 + $editor.scrollTop;\n\n if (left < 0) {\n left = 0;\n }\n\n if (window.innerHeight - start.bottom < target.height) {\n top = selected.top - parent.top - target.height - 14 + $editor.scrollTop;\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 emojiRegex from 'emoji-regex';\nimport { 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 }\n node.children = out;\n }\n\n return fn(node, index, parent);\n }\n}\n\nexport const twemojiPlugin = () => {\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 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), type: 'emoji' });\n str = str.slice(index + emoji.length);\n }\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 { RemarkPlugin } from '@milkdown/core';\nimport { InputRule } from '@milkdown/prose';\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 type EmojiOptions = {\n maxListSize: number;\n};\n\nexport const emojiNode = createNode<string, EmojiOptions>((utils, options) => {\n const getStyle = () =>\n utils.getStyle(\n ({ css }) => css`\n display: inline-flex;\n justify-content: center;\n align-items: center;\n\n .emoji {\n height: 1em;\n width: 1em;\n margin: 0 0.05em 0 0.1em;\n vertical-align: -0.1em;\n }\n `,\n );\n return {\n id: 'emoji',\n schema: () => ({\n group: 'inline',\n inline: true,\n selectable: false,\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 new Error();\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 });\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) return null;\n const got = nodeEmoji.get(content);\n if (!got || content.includes(got)) return null;\n\n const html = parse(got);\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],\n prosePlugins: () => [filter(utils, options?.maxListSize ?? 6)],\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":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;MACa,OAAO;MACP,OAAO;MACP,QAAQ;ACArB,MAAM,UAAU,CAAC,YAAoB,OAAO;MAE/B,QAAQ,CAAC,WAClB,QAAQ,MAAM,QAAO,EAAE,YAAY;MCE1B,eAAe,CACxB,MACA,MACA,IACA,MACA,UACA,cACC;MACG,KAAK;WAAkB;QACrB,EAAE,UAAU;QACZ,QAAQ,MAAM,IAAI,QAAQ;MAC5B,MAAM,OAAO,KAAK,KAAK;WAAa;QAClC,oBACI,OAAO,YAAY,KAAK,IAAI,GAAG,MAAM,eAAe,KAAK,MAAM,cAAc,QAAW,YAAY,MAC5G;MACE,KAAK,KAAK,aAAa;WAChB;AAAA;QAEL,SAAQ,KAAK,KAAK;MACpB,UAAS,OAAM,MAAM,WAAW,SAAS,OAAM,KAAK;UAC9C,QAAQ,OAAM;aACX,cAAc,SAAS,KAAK,SAAS;cACpC;WACH;AAAA;SAEJ;AAAA;MAGE,qBAAqB,CAC9B,MACA,UACA,SACA,WACA,cACC;SACM,SAAS,YAAY;aACf,WAAW;AAAA;OAEnB,QAAQ,CAAC,EAAE,eAAO,aAAO,MAAM;UAC1B,YAAY,SAAS,cAAc;cAC/B,YAAY;UAEhB,YAAY,SAAS,cAAc;cAC/B,YAAY,MAAM;cAElB,YAAY;UAChB,UAAU,SAAS,cAAc;YAC/B,cAAc,MAAM,OAAM;YAC1B,YAAY;cAEV,YAAY;cACZ,YAAY;aACb,YAAY;QAEjB,MAAM,GAAG;gBACC,UAAU,IAAI;gBACd;AAAA;UAGR,UAAU,CAAC,MAAkB;UAC3B,SAAS;gBACD,UAAU,OAAO;AAAA;YAEvB,EAAE,WAAW;UACf,oBAAoB;;aACjB,UAAU,IAAI;gBACX;AAAA;UAGR,UAAU,CAAC,MAAkB;YACzB,EAAE,WAAW;UACf,oBAAoB;;aACjB,UAAU,OAAO;AAAA;UAGtB,UAAU,CAAC,MAAkB;QAC7B;;;cAII,iBAAiB,cAAc;cAC/B,iBAAiB,cAAc;cAC/B,iBAAiB,aAAa;AAAA;AAAA;MC9EnC,cAAc,CAAC,cAA4B,EAAE,KAAK,SAAkB;QACvE,SAAS,aAAa,IAAI,aAAa;QACvC,SAAS,aAAa,IAAI,aAAa;QACvC,SAAS,aAAa,IAAI,WAAW;QACrC,aAAa,aAAa,IAAI,WAAW;QACzC,UAAU,CAAC,OAAc,UAAU,MAAM,aAAa,IAAI,YAAY,CAAC,OAAO;QAE9E,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAMO;AAAA,sBACH,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAWH;AAAA;AAAA;AAAA,8BAGG,QAAQ,aAAa;AAAA,yBAC1B,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;SAWtB,GAAG,QAAQ,QAAQ;AAAA;MC3CjB,MAAM,IAAI,UAAU;MAEpB,SAAS,CAAC,OAAc,gBAAwB;MACrD,UAAU;MACV,QAAQ;MACR,UAAU;MACV,UAA8B;QAE5B,MAAM,MAAM;cACJ;YACF;cACE;cACA;AAAA;SAGP,IAAI,OAAO;AAAA,IACd;AAAA,IACA,OAAO;AAAA,MACH,cAAc,GAAG,OAAO;YAChB,CAAC,UAAU,aAAa,SAAS,MAAM,MAAM;oBACnC,QAAQ,MAAM,GAAG;cACvB,QAAQ,UAAU,GAAG;;;iBAGlB;AAAA;YAEP,CAAC;iBAAgB;YACjB,CAAC,CAAC,WAAW,aAAa,SAAS,SAAS,MAAM,MAAM;iBACjD;AAAA;eAEJ;AAAA;AAAA,MAEX,gBAAgB,MAAM,MAAM,IAAI,MAAM;kBACxB,aACN,MACA,MACA,IACA,MACA,CAAC,UAAS;kBACE;AAAA,WAEZ,CAAC,YAAW;oBACE;AAAA;YAGd,CAAC,SAAS;;;eAGP;AAAA;AAAA;AAAA,IAGf,MAAM,CAAC,eAAe;YACZ,EAAE,eAAe,WAAW;UAC9B,CAAC,YAAY;cACP,IAAI;AAAA;YAGR,WAAW,SAAS,cAAc;eAE/B,UAAU,IAAI,yBAAyB;YAE1C,aAAa,QAAQ,MAAM;cACvB,YAAY,SAAS,UACtB,MAAM,KACN,OAAO,CAAC,MAAM,CAAC,QAAQ,yBAAyB,SAAS;iBACrD,YAAY,UAAU,KAAK;cAC9B,QAAQ,MAAM,SAAS,CAAC,YAAY,YAAY,MAAM,cAAc;YACtE,OAAO;gBACD,MAAM,KAAK,QAAQ,CAAC,MAAM,SAAS,UAAU,IAAI;AAAA;AAAA;YAIzD,UAAU,MAAM;;YACd,CAAC;;cAEC,EAAE,OAAO,WAAW;cACpB,OAAO,WAAW,MAAM,OAAO,KAAK,SAAS,EAAE,MAAM,cAAQ,sBAAR,mBAA2B;mBAE3E,SAAS,GAAG,OAAO,OAAO,QAAQ,QAAQ,QAAQ,OAAO,OAAO;;iBAElE,UAAU,IAAI;AAAA;iBAGhB,YAAY;YACjB,YAAY,CAAC,MAAa;YACxB,CAAC,WAAW,eAAe;;cAEzB,EAAE,cAAQ;YAEZ,SAAQ,SAAS;;;;YAKjB,CAAC,aAAa,WAAW,SAAS,OAAM;gBAClC,OACF,SAAQ,cACF,oCAAS,uBAAsB,SAAS,oBACxC,oCAAS,2BAA0B,SAAS;cAClD,SAAS;oBACD,UAAU,OAAO;AAAA;cAEzB,CAAC;;eACA,UAAU,IAAI;oBACT;;;;YAKZ,UAAU,CAAC,MAAa;YACtB,CAAC;;UAEH;;iBAEO,UAAU,IAAI;AAAA;iBAEhB,iBAAiB,WAAW;iBAC5B,iBAAiB,aAAa;aAElC;AAAA,QACH,QAAQ,CAAC,SAAS;gBACR,EAAE,cAAc,KAAK;cAEvB,UAAU,OAAO,UAAU,OAAO,KAAK,CAAC,SAAS;;qBAExC,UAAU,IAAI;mBAChB;AAAA;gBAEL,SAAS,OAAO,SAAS,MAAM,GAAG;gBAClC,EAAE,SAAS,KAAK,SAAS;cAC3B,OAAO,WAAW,KAAK,CAAC,MAAM;qBACrB,UAAU,IAAI;mBAChB;AAAA;mBAGF,UAAU,OAAO;6BACP,QAAQ,UAAU,SAAS,SAAS,CAAC,MAAM;sBAChD;AAAA;gCAEQ,MAAM,UAAU,CAAC,UAAU,QAAQ,WAAW;kBAC1D,UAAU,SAAS;gBACrB,CAAC,SAAS;oBACJ,IAAI;AAAA;kBAER,QAAQ,KAAK,YAAY;gBAC3B,OAAO,MAAM,OAAO,OAAO;gBAC3B,MAAM,SAAS,SAAS,OAAO,MAAM,KAAK,QAAQ;gBAElD,OAAO,GAAG;qBACH;AAAA;gBAGP,OAAO,cAAc,MAAM,SAAS,OAAO,QAAQ;oBAC7C,SAAS,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK,QAAQ;AAAA;mBAE5D,CAAC,KAAK;AAAA;iBAGV;AAAA;AAAA,QAGX,SAAS,MAAM;qBACA,oBAAoB,WAAW;qBAC/B,oBAAoB,aAAa;mBACnC;AAAA;AAAA;AAAA;AAAA;AAAA;ACvK7B,MAAM,QAAQ;AAEd,MAAM,WAAW,CAAC,SAA+B,CAAC,CAAE,KAAgB;AACpE,MAAM,YAAY,CAAC,SAAgC,CAAC,CAAE,KAAiB;AAEvE,iBAAiB,KAAW,IAAgE;SACjF,UAAU,KAAK,GAAG,MAAM;qBAEZ,MAAY,OAAe,QAAqB;QAC3D,SAAS,OAAO;YACV,MAAM;eACH,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,IAAI,GAAG,KAAK;cAC5C,WAAW,KAAK,SAAS;YAC3B,UAAU;gBACJ,KAAK,UAAU,UAAU,GAAG;cAC9B,IAAI;qBACK,IAAI,GAAG,IAAI,GAAG,QAAQ,IAAI,GAAG,KAAK;oBACjC,OAAO,GAAG;kBACZ,MAAM;oBACF,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;WAMxB,WAAW;AAAA;WAGb,GAAG,MAAM,OAAO;AAAA;AAAA;MAIlB,gBAAgB,MAAM;uBACV,MAAY;YACrB,MAAM,CAAC,SAAS;UAChB,CAAC,UAAU,OAAO;eACX,CAAC;AAAA;YAEN,QAAQ,KAAK;YACb,SAA4B;UAC9B;UACA,MAAM;aACF,QAAQ,MAAM,KAAK,MAAO;cACxB,EAAE,UAAU;cACZ,SAAQ,MAAM;YAChB,QAAO;cACH,QAAQ,GAAG;mBACJ,KAAK,iCAAK,OAAL,EAAW,OAAO,IAAI,MAAM,GAAG;AAAA;iBAExC,KAAK,iCAAK,OAAL,EAAW,OAAO,MAAM,SAAQ,MAAM;gBAC5C,IAAI,MAAM,QAAQ,OAAM;AAAA;AAAA;UAGlC,IAAI,QAAQ;eACL,KAAK,iCAAK,OAAL,EAAW,OAAO;AAAA;aAE3B;AAAA;AAAA;SAGR;AAAA;MCjDE,YAAY,WAAiC,CAAC,OAAO,YAAY;QACpE,WAAW,MACb,MAAM,SACF,CAAC,EAAE,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;SAad;AAAA,IACH,IAAI;AAAA,IACJ,QAAQ;MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,OAAO;AAAA,QACH,MAAM;AAAA,UACF,SAAS;AAAA;AAAA;AAAA,MAGjB,UAAU;AAAA,QACN;AAAA,UACI,KAAK;AAAA,UACL,UAAU,CAAC,QAAQ;gBACX,iBAAiB,cAAc;oBACzB,IAAI;AAAA;mBAEP,EAAE,MAAM,IAAI;AAAA;AAAA;AAAA;AAAA,MAI/B,OAAO,CAAC,SAAS;cACP,OAAO,SAAS,cAAc;aAC/B,UAAU,IAAI;aACd,QAAQ,UAAU;cACjB,aAAa,QAAQ,MAAM;gBACvB,QAAQ;cACV,OAAO;iBACF,UAAU,IAAI;AAAA;AAAA;aAGtB,YAAY,KAAK,MAAM;eACrB,EAAE,KAAK;AAAA;AAAA,MAElB,eAAe;AAAA,QACX,OAAO,CAAC,EAAE,WAAW,SAAS;AAAA,QAC9B,QAAQ,CAAC,OAAO,MAAM,SAAS;gBACrB,QAAQ,MAAM,EAAE,MAAM,KAAK;AAAA;AAAA;AAAA,MAGzC,YAAY;AAAA,QACR,OAAO,CAAC,SAAS,KAAK,KAAK,SAAS;AAAA,QACpC,QAAQ,CAAC,OAAO,SAAS;gBACf,OAAO,SAAS,cAAc;eAC/B,YAAY,KAAK,MAAM;gBACtB,MAAM,KAAK,cAAc;gBACzB,QAAQ,2BAAK;eACd;gBACC,QAAQ,QAAQ,QAAW;AAAA;AAAA;AAAA;AAAA,IAI7C,YAAY,CAAC,aAAa;AAAA,MACtB,IAAI,UAAU,OAAO,CAAC,OAAO,OAAO,OAAO,QAAQ;cACzC,UAAU,MAAM;YAClB,CAAC;iBAAgB;cACf,MAAM,UAAU,IAAI;YACtB,CAAC,OAAO,QAAQ,SAAS;iBAAa;cAEpC,OAAO,MAAM;eAEZ,MAAM,GACR,QAAQ,SAAS,MACjB,iBAAiB,OAAO,KAAK,SAAS,OAAO,EAAE,SAC/C;AAAA;AAAA;AAAA,IAGb,eAAe,MAAM,CAAC,aAA6B;AAAA,IACnD,cAAc;;AAAM,cAAC,OAAO,OAAO,yCAAS,gBAAT,YAAwB;AAAA;AAAA;AAAA;MC/FtD,QAAQ,SAAS,OAAO,CAAC;;"}
|
package/lib/node.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare type EmojiOptions = {
|
|
2
|
+
maxListSize: number;
|
|
3
|
+
};
|
|
4
|
+
export declare const emojiNode: import("@milkdown/utils/lib/types").WithExtend<string, EmojiOptions, import("prosemirror-model").NodeType<any>, {
|
|
5
|
+
id: string;
|
|
6
|
+
schema: (ctx: import("@milkdown/core").Ctx) => import("@milkdown/core").NodeSchema;
|
|
7
|
+
view?: ((ctx: import("@milkdown/core").Ctx) => import("@milkdown/prose").NodeViewFactory) | undefined;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAYA,oBAAY,YAAY,GAAG;IACvB,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,SAAS;;;;EAuFpB,CAAC"}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,UAAW,MAAM,KAAG,MACyD,CAAC"}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,IAAI,EAAU,MAAM,OAAO,CAAC;AAoC9C,eAAO,MAAM,aAAa,eACK,IAAI,SA2BlC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milkdown/plugin-emoji",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/index.es.js",
|
|
6
6
|
"types": "./lib/index.d.ts",
|
|
@@ -15,30 +15,52 @@
|
|
|
15
15
|
"milkdown plugin"
|
|
16
16
|
],
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@milkdown/core": "
|
|
19
|
-
"@milkdown/prose": "
|
|
18
|
+
"@milkdown/core": "6.0.0",
|
|
19
|
+
"@milkdown/prose": "6.0.0"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@milkdown/core": "^
|
|
23
|
-
"@milkdown/prose": "^
|
|
22
|
+
"@milkdown/core": "^6.0.0-next.0",
|
|
23
|
+
"@milkdown/prose": "^6.0.0-next.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@
|
|
27
|
-
"@milkdown/utils": "5.5.0",
|
|
26
|
+
"@milkdown/utils": "6.0.0",
|
|
28
27
|
"@types/node-emoji": "^1.8.1",
|
|
29
|
-
"@types/twemoji": "^12.1.2",
|
|
30
28
|
"emoji-regex": "^10.0.0",
|
|
31
29
|
"node-emoji": "^1.10.0",
|
|
32
30
|
"remark-emoji": "^3.0.1",
|
|
33
31
|
"tslib": "^2.3.1",
|
|
34
|
-
"twemoji": "^
|
|
32
|
+
"twemoji": "^14.0.1",
|
|
35
33
|
"unist-util-visit": "^4.0.0"
|
|
36
34
|
},
|
|
35
|
+
"nx": {
|
|
36
|
+
"targets": {
|
|
37
|
+
"build": {
|
|
38
|
+
"outputs": [
|
|
39
|
+
"packages/plugin-emoji/lib"
|
|
40
|
+
],
|
|
41
|
+
"dependsOn": [
|
|
42
|
+
{
|
|
43
|
+
"target": "build",
|
|
44
|
+
"projects": "dependencies"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"tsc": {
|
|
49
|
+
"outputs": [],
|
|
50
|
+
"dependsOn": [
|
|
51
|
+
{
|
|
52
|
+
"target": "build",
|
|
53
|
+
"projects": "dependencies"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
37
59
|
"scripts": {
|
|
38
|
-
"start": "vite build --watch",
|
|
60
|
+
"start": "concurrently -n es,dts \"vite build --watch\" \"tsc --emitDeclarationOnly --watch\"",
|
|
39
61
|
"test": "vitest",
|
|
40
62
|
"tsc": "tsc --noEmit",
|
|
41
|
-
"build": "vite build"
|
|
63
|
+
"build": "vite build && tsc --emitDeclarationOnly"
|
|
42
64
|
},
|
|
43
65
|
"readme": "# @milkdown/plugin-emoji\n\nEmoji plugin for [milkdown](https://saul-mirone.github.io/milkdown/).\nAdd support for emoji through [shortcuts](https://www.webfx.com/tools/emoji-cheat-sheet/).\nRendered by [twemoji](https://github.com/twitter/twemoji).\n\n# Example Usage\n\n```typescript\nimport { Editor } from '@milkdown/core';\nimport { commonmark } from '@milkdown/preset-commonmark';\nimport { nord } from '@milkdown/theme-nord';\n\nimport { emoji } from '@milkdown/plugin-emoji';\n\nEditor.make().use(nord).use(commonmark).use(emoji).create();\n```\n\n# License\n\nMilkdown is open sourced software licensed under [MIT license](https://github.com/Saul-Mirone/milkdown/blob/main/LICENSE).\n"
|
|
44
66
|
}
|
package/src/constant.ts
CHANGED
package/src/filter/helper.ts
CHANGED
|
@@ -41,7 +41,9 @@ export const renderDropdownList = (
|
|
|
41
41
|
onConfirm: () => void,
|
|
42
42
|
setActive: (active: HTMLElement | null) => void,
|
|
43
43
|
) => {
|
|
44
|
-
dropDown.
|
|
44
|
+
while (dropDown.firstChild) {
|
|
45
|
+
dropDown.firstChild.remove();
|
|
46
|
+
}
|
|
45
47
|
list.forEach(({ emoji, key }, i) => {
|
|
46
48
|
const container = document.createElement('div');
|
|
47
49
|
container.className = 'milkdown-emoji-filter_item';
|
|
@@ -63,7 +65,7 @@ export const renderDropdownList = (
|
|
|
63
65
|
setActive(container);
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
const onEnter = (e: MouseEvent) => {
|
|
67
69
|
if ($active) {
|
|
68
70
|
$active.classList.remove('active');
|
|
69
71
|
}
|
|
@@ -71,15 +73,21 @@ export const renderDropdownList = (
|
|
|
71
73
|
if (!(target instanceof HTMLElement)) return;
|
|
72
74
|
target.classList.add('active');
|
|
73
75
|
setActive(target);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const onLeave = (e: MouseEvent) => {
|
|
76
79
|
const { target } = e;
|
|
77
80
|
if (!(target instanceof HTMLElement)) return;
|
|
78
81
|
target.classList.remove('active');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const onClick = (e: MouseEvent) => {
|
|
82
85
|
e.preventDefault();
|
|
83
|
-
|
|
86
|
+
onConfirm();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
container.addEventListener('mouseenter', onEnter);
|
|
90
|
+
container.addEventListener('mouseleave', onLeave);
|
|
91
|
+
container.addEventListener('mousedown', onClick);
|
|
84
92
|
});
|
|
85
93
|
};
|
package/src/filter/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { injectStyle } from './style';
|
|
|
9
9
|
|
|
10
10
|
export const key = new PluginKey('MILKDOWN_PLUGIN_EMOJI_FILTER');
|
|
11
11
|
|
|
12
|
-
export const filter = (utils: Utils) => {
|
|
12
|
+
export const filter = (utils: Utils, maxListSize: number) => {
|
|
13
13
|
let trigger = false;
|
|
14
14
|
let _from = 0;
|
|
15
15
|
let _search = '';
|
|
@@ -65,14 +65,20 @@ export const filter = (utils: Utils) => {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const dropDown = document.createElement('div');
|
|
68
|
-
const style = utils.getStyle(injectStyle);
|
|
69
|
-
|
|
70
|
-
if (style) {
|
|
71
|
-
style.split(' ').forEach((x) => dropDown.classList.add(x));
|
|
72
|
-
}
|
|
73
68
|
|
|
74
69
|
dropDown.classList.add('milkdown-emoji-filter', 'hide');
|
|
75
70
|
|
|
71
|
+
utils.themeManager.onFlush(() => {
|
|
72
|
+
const className = dropDown.className
|
|
73
|
+
.split(' ')
|
|
74
|
+
.filter((x) => ['hide', 'milkdown-emoji-filter'].includes(x));
|
|
75
|
+
dropDown.className = className.join(' ');
|
|
76
|
+
const style = utils.getStyle((emotion) => injectStyle(utils.themeManager, emotion));
|
|
77
|
+
if (style) {
|
|
78
|
+
style.split(' ').forEach((x) => dropDown.classList.add(x));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
76
82
|
const replace = () => {
|
|
77
83
|
if (!$active) return;
|
|
78
84
|
|
|
@@ -85,7 +91,7 @@ export const filter = (utils: Utils) => {
|
|
|
85
91
|
};
|
|
86
92
|
|
|
87
93
|
parentNode.appendChild(dropDown);
|
|
88
|
-
|
|
94
|
+
const onKeydown = (e: Event) => {
|
|
89
95
|
if (!trigger || !(e instanceof KeyboardEvent)) return;
|
|
90
96
|
|
|
91
97
|
const { key } = e;
|
|
@@ -109,14 +115,16 @@ export const filter = (utils: Utils) => {
|
|
|
109
115
|
|
|
110
116
|
return;
|
|
111
117
|
}
|
|
112
|
-
}
|
|
113
|
-
|
|
118
|
+
};
|
|
119
|
+
const onClick = (e: Event) => {
|
|
114
120
|
if (!trigger) return;
|
|
115
121
|
|
|
116
122
|
e.stopPropagation();
|
|
117
123
|
off();
|
|
118
124
|
dropDown.classList.add('hide');
|
|
119
|
-
}
|
|
125
|
+
};
|
|
126
|
+
parentNode.addEventListener('keydown', onKeydown);
|
|
127
|
+
parentNode.addEventListener('mousedown', onClick);
|
|
120
128
|
|
|
121
129
|
return {
|
|
122
130
|
update: (view) => {
|
|
@@ -127,7 +135,7 @@ export const filter = (utils: Utils) => {
|
|
|
127
135
|
dropDown.classList.add('hide');
|
|
128
136
|
return null;
|
|
129
137
|
}
|
|
130
|
-
const result = search(_search).slice(0,
|
|
138
|
+
const result = search(_search).slice(0, maxListSize);
|
|
131
139
|
const { node } = view.domAtPos(_from);
|
|
132
140
|
if (result.length === 0 || !node) {
|
|
133
141
|
dropDown.classList.add('hide');
|
|
@@ -159,6 +167,12 @@ export const filter = (utils: Utils) => {
|
|
|
159
167
|
|
|
160
168
|
return null;
|
|
161
169
|
},
|
|
170
|
+
|
|
171
|
+
destroy: () => {
|
|
172
|
+
parentNode.removeEventListener('keydown', onKeydown);
|
|
173
|
+
parentNode.removeEventListener('mousedown', onClick);
|
|
174
|
+
dropDown.remove();
|
|
175
|
+
},
|
|
162
176
|
};
|
|
163
177
|
},
|
|
164
178
|
});
|
package/src/filter/style.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
Color,
|
|
4
|
+
Emotion,
|
|
5
|
+
ThemeBorder,
|
|
6
|
+
ThemeColor,
|
|
7
|
+
ThemeFont,
|
|
8
|
+
ThemeManager,
|
|
9
|
+
ThemeShadow,
|
|
10
|
+
ThemeSize,
|
|
11
|
+
} from '@milkdown/core';
|
|
3
12
|
|
|
4
|
-
export const injectStyle = (
|
|
5
|
-
const border =
|
|
6
|
-
const shadow =
|
|
13
|
+
export const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) => {
|
|
14
|
+
const border = themeManager.get(ThemeBorder, undefined);
|
|
15
|
+
const shadow = themeManager.get(ThemeShadow, undefined);
|
|
16
|
+
const radius = themeManager.get(ThemeSize, 'radius');
|
|
17
|
+
const typography = themeManager.get(ThemeFont, 'typography');
|
|
18
|
+
const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity]);
|
|
7
19
|
|
|
8
20
|
const style = css`
|
|
9
21
|
position: absolute;
|
|
@@ -11,20 +23,20 @@ export const injectStyle = ({ size, mixin, palette, font }: ThemeTool, { css, cx
|
|
|
11
23
|
display: none;
|
|
12
24
|
}
|
|
13
25
|
|
|
14
|
-
border-radius: ${
|
|
26
|
+
border-radius: ${radius};
|
|
15
27
|
background: ${palette('surface')};
|
|
16
28
|
|
|
17
29
|
.milkdown-emoji-filter_item {
|
|
18
30
|
display: flex;
|
|
19
|
-
gap: 0.
|
|
20
|
-
height: 2.
|
|
21
|
-
padding: 0
|
|
31
|
+
gap: 0.5em;
|
|
32
|
+
height: 2.57143em;
|
|
33
|
+
padding: 0 1em;
|
|
22
34
|
align-items: center;
|
|
23
35
|
justify-content: flex-start;
|
|
24
36
|
cursor: pointer;
|
|
25
37
|
line-height: 2;
|
|
26
|
-
font-family: ${
|
|
27
|
-
font-size: 0.
|
|
38
|
+
font-family: ${typography};
|
|
39
|
+
font-size: 0.875em;
|
|
28
40
|
&.active {
|
|
29
41
|
background: ${palette('secondary', 0.12)};
|
|
30
42
|
color: ${palette('primary')};
|
package/src/index.ts
CHANGED
package/src/node.ts
CHANGED
|
@@ -8,24 +8,28 @@ import remarkEmoji from 'remark-emoji';
|
|
|
8
8
|
import { input } from './constant';
|
|
9
9
|
import { filter } from './filter';
|
|
10
10
|
import { parse } from './parse';
|
|
11
|
-
import { picker } from './picker';
|
|
12
11
|
import { twemojiPlugin } from './remark-twemoji';
|
|
13
12
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
display: inline-flex;
|
|
18
|
-
justify-content: center;
|
|
19
|
-
align-items: center;
|
|
13
|
+
export type EmojiOptions = {
|
|
14
|
+
maxListSize: number;
|
|
15
|
+
};
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
export const emojiNode = createNode<string, EmojiOptions>((utils, options) => {
|
|
18
|
+
const getStyle = () =>
|
|
19
|
+
utils.getStyle(
|
|
20
|
+
({ css }) => css`
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
align-items: center;
|
|
24
|
+
|
|
25
|
+
.emoji {
|
|
26
|
+
height: 1em;
|
|
27
|
+
width: 1em;
|
|
28
|
+
margin: 0 0.05em 0 0.1em;
|
|
29
|
+
vertical-align: -0.1em;
|
|
30
|
+
}
|
|
31
|
+
`,
|
|
32
|
+
);
|
|
29
33
|
return {
|
|
30
34
|
id: 'emoji',
|
|
31
35
|
schema: () => ({
|
|
@@ -50,11 +54,14 @@ export const emojiNode = createNode((utils) => {
|
|
|
50
54
|
],
|
|
51
55
|
toDOM: (node) => {
|
|
52
56
|
const span = document.createElement('span');
|
|
53
|
-
span.dataset['type'] = 'emoji';
|
|
54
|
-
if (style) {
|
|
55
|
-
span.classList.add(style);
|
|
56
|
-
}
|
|
57
57
|
span.classList.add('emoji-wrapper');
|
|
58
|
+
span.dataset['type'] = 'emoji';
|
|
59
|
+
utils.themeManager.onFlush(() => {
|
|
60
|
+
const style = getStyle();
|
|
61
|
+
if (style) {
|
|
62
|
+
span.classList.add(style);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
58
65
|
span.innerHTML = node.attrs['html'];
|
|
59
66
|
return { dom: span };
|
|
60
67
|
},
|
|
@@ -92,6 +99,6 @@ export const emojiNode = createNode((utils) => {
|
|
|
92
99
|
}),
|
|
93
100
|
],
|
|
94
101
|
remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin],
|
|
95
|
-
prosePlugins: () => [
|
|
102
|
+
prosePlugins: () => [filter(utils, options?.maxListSize ?? 6)],
|
|
96
103
|
};
|
|
97
104
|
});
|
package/src/parse.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
2
|
import twemoji from 'twemoji';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const setAttr = (text: string) => ({ title: text });
|
|
5
|
+
|
|
6
|
+
export const parse = (emoji: string): string =>
|
|
7
|
+
twemoji.parse(emoji, { attributes: setAttr as unknown as () => void }) as unknown as string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constant.d.ts","sourceRoot":"","sources":["constant.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,QAAqB,CAAC;AACvC,eAAO,MAAM,IAAI,QAAwB,CAAC;AAC1C,eAAO,MAAM,KAAK,QAAmB,CAAC;AACtC,eAAO,MAAM,OAAO,YAAY,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["helper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAKnC,eAAO,MAAM,YAAY,SACf,UAAU,QACV,MAAM,MACR,MAAM,QACJ,MAAM,mBACK,MAAM,MAAM,MAAM,KAAK,IAAI,qBACzB,MAAM,KAAK,IAAI,YAoBrC,CAAC;AAEF,eAAO,MAAM,kBAAkB,SACrB,KAAK,EAAE,YACH,WAAW,WACZ,WAAW,GAAG,IAAI,aAChB,MAAM,IAAI,sBACD,WAAW,GAAG,IAAI,KAAK,IAAI,SA2ClD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyB,MAAM,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAMxC,eAAO,MAAM,GAAG,qBAAgD,CAAC;AAEjE,eAAO,MAAM,MAAM,UAAW,KAAK,qBAyJlC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["style.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEzD,eAAO,MAAM,WAAW,mCAAoC,SAAS,eAAe,OAAO,WAsC1F,CAAC"}
|
package/lib/src/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { AtomList } from '@milkdown/utils';
|
|
2
|
-
export declare const emoji: AtomList<import("@milkdown/utils/lib/src/types").Metadata<import("@milkdown/utils/lib/src/types").GetPlugin<string, import("@milkdown/utils").UnknownRecord>> & import("@milkdown/core").MilkdownPlugin>;
|
|
3
|
-
export { emojiNode } from './node';
|
|
4
|
-
//# sourceMappingURL=index.d.ts.map
|
package/lib/src/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAI3C,eAAO,MAAM,KAAK,0MAAiC,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC"}
|
package/lib/src/node.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const emojiNode: import("@milkdown/utils/lib/src/types").WithExtend<string, import("@milkdown/utils").UnknownRecord, import("prosemirror-model").NodeType<any>, {
|
|
2
|
-
id: string;
|
|
3
|
-
schema: (ctx: import("@milkdown/core").Ctx) => import("@milkdown/core").NodeSchema;
|
|
4
|
-
view?: ((ctx: import("@milkdown/core").Ctx) => import("@milkdown/prose").NodeViewFactory) | undefined;
|
|
5
|
-
}>;
|
|
6
|
-
//# sourceMappingURL=node.d.ts.map
|
package/lib/src/node.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["node.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,SAAS;;;;EAmFpB,CAAC"}
|
package/lib/src/parse.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["parse.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,KAAK,UAAW,MAAM,WAAsE,CAAC"}
|
package/lib/src/picker.d.ts
DELETED
package/lib/src/picker.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"picker.d.ts","sourceRoot":"","sources":["picker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAyC,MAAM,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC3F,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAKxC,eAAO,MAAM,GAAG,qBAAgD,CAAC;AAuBjE,eAAO,MAAM,MAAM,UAAW,KAAK,qBA+FlC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["remark-twemoji.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,IAAI,EAAU,MAAM,OAAO,CAAC;AAoC9C,eAAO,MAAM,aAAa,eACK,IAAI,SA2BlC,CAAC"}
|
package/src/picker.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
import { EmojiButton } from '@joeattardi/emoji-button';
|
|
3
|
-
import { Decoration, DecorationSet, EditorView, Plugin, PluginKey } from '@milkdown/prose';
|
|
4
|
-
import { Utils } from '@milkdown/utils';
|
|
5
|
-
|
|
6
|
-
import { parse } from './parse';
|
|
7
|
-
|
|
8
|
-
const keyword = ':emoji:';
|
|
9
|
-
export const key = new PluginKey('MILKDOWN_PLUGIN_EMOJI_PICKER');
|
|
10
|
-
|
|
11
|
-
const checkTrigger = (
|
|
12
|
-
view: EditorView,
|
|
13
|
-
from: number,
|
|
14
|
-
to: number,
|
|
15
|
-
text: string,
|
|
16
|
-
setRange: (from: number, to: number) => void,
|
|
17
|
-
) => {
|
|
18
|
-
if (view.composing) return false;
|
|
19
|
-
const { state } = view;
|
|
20
|
-
const $from = state.doc.resolve(from);
|
|
21
|
-
if ($from.parent.type.spec.code) return false;
|
|
22
|
-
const textBefore =
|
|
23
|
-
$from.parent.textBetween($from.parentOffset - keyword.length + 1, $from.parentOffset, undefined, '\ufffc') +
|
|
24
|
-
text;
|
|
25
|
-
if (textBefore === keyword) {
|
|
26
|
-
setRange(from - keyword.length + 1, to + 1);
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const picker = (utils: Utils) => {
|
|
33
|
-
let trigger = false;
|
|
34
|
-
const holder = document.createElement('span');
|
|
35
|
-
let _from = 0;
|
|
36
|
-
let _to = 0;
|
|
37
|
-
const off = () => {
|
|
38
|
-
trigger = false;
|
|
39
|
-
_from = 0;
|
|
40
|
-
_to = 0;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const plugin = new Plugin({
|
|
44
|
-
key,
|
|
45
|
-
props: {
|
|
46
|
-
handleKeyDown() {
|
|
47
|
-
off();
|
|
48
|
-
return false;
|
|
49
|
-
},
|
|
50
|
-
handleClick() {
|
|
51
|
-
off();
|
|
52
|
-
return false;
|
|
53
|
-
},
|
|
54
|
-
handleTextInput(view, from, to, text) {
|
|
55
|
-
trigger = checkTrigger(view, from, to, text, (from, to) => {
|
|
56
|
-
_from = from;
|
|
57
|
-
_to = to;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
if (!trigger) {
|
|
61
|
-
off();
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
},
|
|
65
|
-
decorations(state) {
|
|
66
|
-
if (!trigger) return null;
|
|
67
|
-
|
|
68
|
-
return DecorationSet.create(state.doc, [Decoration.widget(state.selection.to, holder)]);
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
view: (editorView) => {
|
|
72
|
-
const { parentNode } = editorView.dom;
|
|
73
|
-
if (!parentNode) {
|
|
74
|
-
throw new Error();
|
|
75
|
-
}
|
|
76
|
-
utils.getStyle(({ palette, font }, { injectGlobal }) => {
|
|
77
|
-
const css = injectGlobal;
|
|
78
|
-
css`
|
|
79
|
-
.emoji-picker {
|
|
80
|
-
--dark-search-background-color: ${palette('surface')} !important;
|
|
81
|
-
--dark-text-color: ${palette('neutral', 0.87)} !important;
|
|
82
|
-
--dark-background-color: ${palette('background')} !important;
|
|
83
|
-
--dark-border-color: ${palette('shadow')} !important;
|
|
84
|
-
--dark-hover-color: ${palette('secondary', 0.12)} !important;
|
|
85
|
-
--dark-blue-color: ${palette('primary')} !important;
|
|
86
|
-
--dark-search-icon-color: ${palette('primary')} !important;
|
|
87
|
-
--dark-category-button-color: ${palette('secondary', 0.4)} !important;
|
|
88
|
-
--font: ${font.typography} !important;
|
|
89
|
-
--font-size: 1rem !important;
|
|
90
|
-
}
|
|
91
|
-
`;
|
|
92
|
-
});
|
|
93
|
-
const emojiPicker = new EmojiButton({
|
|
94
|
-
rootElement: parentNode as HTMLElement,
|
|
95
|
-
autoFocusSearch: false,
|
|
96
|
-
style: 'twemoji',
|
|
97
|
-
theme: 'dark',
|
|
98
|
-
zIndex: 99,
|
|
99
|
-
});
|
|
100
|
-
emojiPicker.on('emoji', (selection) => {
|
|
101
|
-
const start = _from;
|
|
102
|
-
const end = _to;
|
|
103
|
-
off();
|
|
104
|
-
const html = parse(selection.emoji);
|
|
105
|
-
const node = editorView.state.schema.node('emoji', { html });
|
|
106
|
-
const { tr } = editorView.state;
|
|
107
|
-
|
|
108
|
-
editorView.dispatch(tr.replaceRangeWith(start, end, node));
|
|
109
|
-
});
|
|
110
|
-
return {
|
|
111
|
-
update: () => {
|
|
112
|
-
if (!trigger) {
|
|
113
|
-
emojiPicker.hidePicker();
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
emojiPicker.showPicker(holder);
|
|
117
|
-
return null;
|
|
118
|
-
},
|
|
119
|
-
destroy: () => {
|
|
120
|
-
emojiPicker.destroyPicker();
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
return plugin;
|
|
127
|
-
};
|