@milkdown/plugin-emoji 6.5.1 → 6.5.3

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/node.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export declare type EmojiOptions = {
1
+ export interface EmojiOptions {
2
2
  maxListSize: number;
3
3
  twemojiOptions: TwemojiOptions;
4
- };
4
+ }
5
5
  export declare const emojiNode: import("@milkdown/utils").NodeCreator<string, EmojiOptions>;
6
6
  //# sourceMappingURL=node.d.ts.map
package/lib/node.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAaA,oBAAY,YAAY,GAAG;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;CAClC,CAAC;AAEF,eAAO,MAAM,SAAS,6DAmFpB,CAAC"}
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"}
@@ -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,MACkB,CAAC"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,UAAW,MAAM,mBAAmB,cAAc,KAAG,MACgB,CAAA"}
@@ -1,3 +1,3 @@
1
- import { RemarkPlugin } from '@milkdown/core';
1
+ import type { RemarkPlugin } from '@milkdown/core';
2
2
  export declare const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin;
3
3
  //# sourceMappingURL=remark-twemoji.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAsC9C,eAAO,MAAM,aAAa,EAAE,CAAC,cAAc,CAAC,EAAE,cAAc,KAAK,YA6BhE,CAAC"}
1
+ {"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAqClD,eAAO,MAAM,aAAa,EAAE,CAAC,cAAc,CAAC,EAAE,cAAc,KAAK,YA8BhE,CAAA"}
package/package.json CHANGED
@@ -1,38 +1,38 @@
1
1
  {
2
2
  "name": "@milkdown/plugin-emoji",
3
- "version": "6.5.1",
4
3
  "type": "module",
4
+ "version": "6.5.3",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "milkdown",
8
+ "milkdown plugin"
9
+ ],
10
+ "sideEffects": false,
5
11
  "main": "./lib/index.es.js",
6
12
  "types": "./lib/index.d.ts",
7
- "sideEffects": false,
8
- "license": "MIT",
9
13
  "files": [
10
14
  "lib",
11
15
  "src"
12
16
  ],
13
- "keywords": [
14
- "milkdown",
15
- "milkdown plugin"
16
- ],
17
- "devDependencies": {
18
- "@milkdown/core": "6.5.0",
19
- "@milkdown/prose": "6.5.0",
20
- "@types/unist": "^2.0.6"
21
- },
22
17
  "peerDependencies": {
23
18
  "@milkdown/core": "^6.0.1",
24
19
  "@milkdown/prose": "^6.0.1"
25
20
  },
26
21
  "dependencies": {
27
- "@milkdown/exception": "6.5.0",
28
- "@milkdown/utils": "6.5.0",
29
22
  "@types/node-emoji": "^1.8.1",
30
23
  "emoji-regex": "^10.0.0",
31
24
  "node-emoji": "^1.10.0",
32
25
  "remark-emoji": "^3.0.1",
33
26
  "tslib": "^2.4.0",
34
27
  "twemoji": "^14.0.1",
35
- "unist-util-visit": "^4.0.0"
28
+ "unist-util-visit": "^4.0.0",
29
+ "@milkdown/exception": "6.5.3",
30
+ "@milkdown/utils": "6.5.3"
31
+ },
32
+ "devDependencies": {
33
+ "@types/unist": "^2.0.6",
34
+ "@milkdown/core": "6.5.3",
35
+ "@milkdown/prose": "6.5.3"
36
36
  },
37
37
  "nx": {
38
38
  "targets": {
package/src/constant.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- export const part = /:\+1|:-1|:[\w-]+/;
3
- export const full = /:\+1:|:-1:|:[\w-]+:/;
4
- export const input = /(:([^:\s]+):)$/;
2
+ export const part = /:\+1|:-1|:[\w-]+/
3
+ export const full = /:\+1:|:-1:|:[\w-]+:/
4
+ export const input = /(:([^:\s]+):)$/
@@ -1,94 +1,83 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
2
 
3
- import { EditorView } from '@milkdown/prose/view';
4
- import type { Emoji } from 'node-emoji';
3
+ import type { EditorView } from '@milkdown/prose/view'
4
+ import type { Emoji } from 'node-emoji'
5
5
 
6
- import { full, part } from '../constant';
7
- import { parse } from '../parse';
6
+ import { full, part } from '../constant'
7
+ import { parse } from '../parse'
8
8
 
9
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,
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
16
  ) => {
17
- if (view.composing) return false;
18
- const { state } = view;
19
- const $from = state.doc.resolve(from);
20
- if ($from.parent.type.spec.code) return false;
21
- const textBefore = (
22
- $from.parent.textBetween(Math.max(0, $from.parentOffset - 10), $from.parentOffset, undefined, '\ufffc') + text
23
- ).toLowerCase();
24
- if (full.test(textBefore)) {
25
- return false;
26
- }
27
- const regex = part.exec(textBefore);
28
- if (regex && regex[0] && textBefore.endsWith(regex[0])) {
29
- const match = regex[0];
30
- setRange(from - (match.length - text.length), to);
31
- setSearch(match);
32
- return true;
33
- }
34
- return false;
35
- };
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
+ }
36
38
 
37
39
  export const renderDropdownList = (
38
- list: Emoji[],
39
- dropDown: HTMLElement,
40
- $active: HTMLElement | null,
41
- onConfirm: () => void,
42
- setActive: (active: HTMLElement | null) => void,
43
- twemojiOptions?: TwemojiOptions,
40
+ list: Emoji[],
41
+ dropDown: HTMLElement,
42
+ onConfirm: () => void,
43
+ setActive: (active: HTMLElement | null) => void,
44
+ twemojiOptions?: TwemojiOptions,
44
45
  ) => {
45
- while (dropDown.firstChild) {
46
- dropDown.firstChild.remove();
47
- }
48
- list.forEach(({ emoji, key }, i) => {
49
- const container = document.createElement('div');
50
- container.className = 'milkdown-emoji-filter_item';
46
+ while (dropDown.firstChild)
47
+ dropDown.firstChild.remove()
51
48
 
52
- const emojiSpan = document.createElement('span');
53
- emojiSpan.innerHTML = parse(emoji, twemojiOptions);
49
+ list.forEach(({ emoji, key }, i) => {
50
+ const container = document.createElement('div')
51
+ container.className = 'milkdown-emoji-filter_item'
54
52
 
55
- emojiSpan.className = 'milkdown-emoji-filter_item-emoji';
56
- const keySpan = document.createElement('span');
57
- keySpan.textContent = ':' + key + ':';
58
- keySpan.className = 'milkdown-emoji-filter_item-key';
53
+ const emojiSpan = document.createElement('span')
54
+ emojiSpan.innerHTML = parse(emoji, twemojiOptions)
59
55
 
60
- container.appendChild(emojiSpan);
61
- container.appendChild(keySpan);
62
- dropDown.appendChild(container);
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'
63
60
 
64
- if (i === 0) {
65
- container.classList.add('active');
66
- setActive(container);
67
- }
61
+ container.appendChild(emojiSpan)
62
+ container.appendChild(keySpan)
63
+ dropDown.appendChild(container)
68
64
 
69
- const onEnter = (e: MouseEvent) => {
70
- if ($active) {
71
- $active.classList.remove('active');
72
- }
73
- const { target } = e;
74
- if (!(target instanceof HTMLElement)) return;
75
- target.classList.add('active');
76
- setActive(target);
77
- };
65
+ if (i === 0)
66
+ setActive(container)
78
67
 
79
- const onLeave = (e: MouseEvent) => {
80
- const { target } = e;
81
- if (!(target instanceof HTMLElement)) return;
82
- target.classList.remove('active');
83
- };
68
+ const onEnter = (e: MouseEvent) => {
69
+ const { target } = e
70
+ if (!(target instanceof HTMLElement))
71
+ return
72
+ setActive(target)
73
+ }
84
74
 
85
- const onClick = (e: MouseEvent) => {
86
- e.preventDefault();
87
- onConfirm();
88
- };
75
+ const onClick = (e: MouseEvent) => {
76
+ e.preventDefault()
77
+ onConfirm()
78
+ }
89
79
 
90
- container.addEventListener('mouseenter', onEnter);
91
- container.addEventListener('mouseleave', onLeave);
92
- container.addEventListener('mousedown', onClick);
93
- });
94
- };
80
+ container.addEventListener('mouseenter', onEnter)
81
+ container.addEventListener('mousedown', onClick)
82
+ })
83
+ }
@@ -1,188 +1,217 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
2
 
3
- import { missingRootElement } from '@milkdown/exception';
4
- import { calculateNodePosition } from '@milkdown/prose';
5
- import { Plugin, PluginKey } from '@milkdown/prose/state';
6
- import { ThemeUtils } from '@milkdown/utils';
7
- import nodeEmoji from 'node-emoji';
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
8
 
9
- import { checkTrigger, renderDropdownList } from './helper';
10
- import { injectStyle } from './style';
9
+ import { checkTrigger, renderDropdownList } from './helper'
10
+ import { injectStyle } from './style'
11
11
 
12
- export const key = new PluginKey('MILKDOWN_EMOJI_FILTER');
12
+ export const key = new PluginKey('MILKDOWN_EMOJI_FILTER')
13
13
 
14
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
- return new Plugin({
28
- key,
29
- props: {
30
- handleKeyDown(_, event) {
31
- if (['Delete', 'Backspace'].includes(event.key)) {
32
- _search = _search.slice(0, -1);
33
- if (_search.length <= 1) {
34
- off();
35
- }
36
- return false;
37
- }
38
- if (!trigger) return false;
39
- if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key)) {
40
- return false;
41
- }
42
- return true;
43
- },
44
- handleTextInput(view, from, to, text) {
45
- trigger = checkTrigger(
46
- view,
47
- from,
48
- to,
49
- text,
50
- (from) => {
51
- _from = from;
52
- },
53
- (search) => {
54
- _search = search;
55
- },
56
- );
57
- if (!trigger) {
58
- off();
59
- }
60
- return false;
61
- },
62
- },
63
- view: (editorView) => {
64
- const { parentNode } = editorView.dom;
65
- if (!parentNode) {
66
- throw missingRootElement();
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
67
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
+ },
68
208
 
69
- const dropDown = document.createElement('div');
70
-
71
- dropDown.classList.add('milkdown-emoji-filter', 'hide');
72
-
73
- utils.themeManager.onFlush(() => {
74
- const className = dropDown.className
75
- .split(' ')
76
- .filter((x) => ['hide', 'milkdown-emoji-filter'].includes(x));
77
- dropDown.className = className.join(' ');
78
- const style = utils.getStyle((emotion) => injectStyle(utils.themeManager, emotion));
79
- if (style) {
80
- style.split(' ').forEach((x) => dropDown.classList.add(x));
81
- }
82
- });
83
-
84
- const replace = () => {
85
- if (!$active) return;
86
-
87
- const { tr } = editorView.state;
88
- const node = editorView.state.schema.node('emoji', { html: $active.firstElementChild?.innerHTML });
89
-
90
- editorView.dispatch(tr.delete(_from, _from + _search.length).insert(_from, node));
91
- off();
92
- dropDown.classList.add('hide');
93
- };
94
-
95
- parentNode.appendChild(dropDown);
96
- const onKeydown = (e: Event) => {
97
- if (!trigger || !(e instanceof KeyboardEvent)) return;
98
-
99
- const { key } = e;
100
-
101
- if (key === 'Enter') {
102
- replace();
103
- return;
104
- }
105
-
106
- if (['ArrowDown', 'ArrowUp'].includes(key)) {
107
- const next =
108
- key === 'ArrowDown'
109
- ? $active?.nextElementSibling || dropDown.firstElementChild
110
- : $active?.previousElementSibling || dropDown.lastElementChild;
111
- if ($active) {
112
- $active.classList.remove('active');
113
- }
114
- if (!next) return;
115
- next.classList.add('active');
116
- $active = next as HTMLElement;
117
-
118
- return;
119
- }
120
- };
121
- const onClick = (e: Event) => {
122
- if (!trigger) return;
123
-
124
- e.stopPropagation();
125
- off();
126
- dropDown.classList.add('hide');
127
- };
128
- parentNode.addEventListener('keydown', onKeydown);
129
- parentNode.addEventListener('mousedown', onClick);
130
-
131
- return {
132
- update: (view) => {
133
- const { selection } = view.state;
134
-
135
- if (selection.from - selection.to !== 0 || !trigger) {
136
- off();
137
- dropDown.classList.add('hide');
138
- return null;
139
- }
140
- const result = nodeEmoji.search(_search).slice(0, maxListSize);
141
- const { node } = view.domAtPos(_from);
142
- if (result.length === 0 || !node) {
143
- dropDown.classList.add('hide');
144
- return null;
145
- }
146
-
147
- dropDown.classList.remove('hide');
148
- renderDropdownList(
149
- result,
150
- dropDown,
151
- $active,
152
- replace,
153
- (a) => {
154
- $active = a;
155
- },
156
- twemojiOptions,
157
- );
158
- calculateNodePosition(view, dropDown, (selected, target, parent) => {
159
- const $editor = dropDown.parentElement;
160
- if (!$editor) {
161
- throw missingRootElement();
162
- }
163
- const start = view.coordsAtPos(_from);
164
- let left = start.left - parent.left;
165
- let top = selected.bottom - parent.top + 14 + $editor.scrollTop;
166
-
167
- if (left < 0) {
168
- left = 0;
169
- }
170
-
171
- if (window.innerHeight - start.bottom < target.height) {
172
- top = selected.top - parent.top - target.height - 14 + $editor.scrollTop;
173
- }
174
- return [top, left];
175
- });
176
-
177
- return null;
178
- },
179
-
180
- destroy: () => {
181
- parentNode.removeEventListener('keydown', onKeydown);
182
- parentNode.removeEventListener('mousedown', onClick);
183
- dropDown.remove();
184
- },
185
- };
209
+ destroy: () => {
210
+ parentNode.removeEventListener('keydown', onKeydown)
211
+ parentNode.removeEventListener('mousedown', onClick)
212
+ dropDown.remove()
186
213
  },
187
- });
188
- };
214
+ }
215
+ },
216
+ })
217
+ }