@milkdown/plugin-emoji 6.5.2 → 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.
@@ -1,219 +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.style.maxHeight = '';
148
- dropDown.classList.remove('hide');
149
- renderDropdownList(
150
- result,
151
- dropDown,
152
- $active,
153
- replace,
154
- (a) => {
155
- $active = a;
156
- },
157
- twemojiOptions,
158
- );
159
- calculateNodePosition(view, dropDown, (_selected, target, parent) => {
160
- const $editor = dropDown.parentElement;
161
- if (!$editor) {
162
- throw missingRootElement();
163
- }
164
- const start = view.coordsAtPos(_from);
165
- let left = start.left - parent.left;
166
-
167
- if (left < 0) {
168
- left = 0;
169
- }
170
-
171
- let direction: 'top' | 'bottom';
172
- let maxHeight: number | undefined;
173
- const startToTop = start.top - parent.top;
174
- const startToBottom = parent.height + parent.top - start.bottom;
175
- if (startToBottom >= target.height + 28) {
176
- direction = 'bottom';
177
- } else if (startToTop >= target.height + 28) {
178
- direction = 'top';
179
- } else if (startToBottom >= startToTop) {
180
- direction = 'bottom';
181
- maxHeight = startToBottom - 28;
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
-
193
- const top =
194
- direction === 'top'
195
- ? start.top - parent.top - (maxHeight ?? target.height) - 14 + $editor.scrollTop
196
- : start.bottom - parent.top + 14 + $editor.scrollTop;
197
-
198
- dropDown.style.maxHeight = maxHeight !== undefined && maxHeight > 0 ? `${maxHeight}px` : '';
199
-
200
- const maxLeft = $editor.clientWidth - (dropDown.offsetWidth + 4);
201
- if (left > maxLeft) {
202
- left = maxLeft;
203
- }
204
-
205
- return [top, left];
206
- });
207
-
208
- return null;
209
- },
210
-
211
- destroy: () => {
212
- parentNode.removeEventListener('keydown', onKeydown);
213
- parentNode.removeEventListener('mousedown', onClick);
214
- dropDown.remove();
215
- },
216
- };
209
+ destroy: () => {
210
+ parentNode.removeEventListener('keydown', onKeydown)
211
+ parentNode.removeEventListener('mousedown', onClick)
212
+ dropDown.remove()
217
213
  },
218
- });
219
- };
214
+ }
215
+ },
216
+ })
217
+ }
@@ -1,25 +1,27 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
+ import type {
3
+ Color,
4
+ Emotion,
5
+ ThemeManager,
6
+ } from '@milkdown/core'
2
7
  import {
3
- Color,
4
- Emotion,
5
- ThemeBorder,
6
- ThemeColor,
7
- ThemeFont,
8
- ThemeManager,
9
- ThemeScrollbar,
10
- ThemeShadow,
11
- ThemeSize,
12
- } from '@milkdown/core';
8
+ ThemeBorder,
9
+ ThemeColor,
10
+ ThemeFont,
11
+ ThemeScrollbar,
12
+ ThemeShadow,
13
+ ThemeSize,
14
+ } from '@milkdown/core'
13
15
 
14
16
  export const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) => {
15
- const border = themeManager.get(ThemeBorder, undefined);
16
- const shadow = themeManager.get(ThemeShadow, undefined);
17
- const scrollbar = themeManager.get(ThemeScrollbar, undefined);
18
- const radius = themeManager.get(ThemeSize, 'radius');
19
- const typography = themeManager.get(ThemeFont, 'typography');
20
- const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity]);
17
+ const border = themeManager.get(ThemeBorder, undefined)
18
+ const shadow = themeManager.get(ThemeShadow, undefined)
19
+ const scrollbar = themeManager.get(ThemeScrollbar, undefined)
20
+ const radius = themeManager.get(ThemeSize, 'radius')
21
+ const typography = themeManager.get(ThemeFont, 'typography')
22
+ const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity])
21
23
 
22
- const style = css`
24
+ const style = css`
23
25
  min-height: 36px;
24
26
  max-height: 320px;
25
27
  overflow-y: auto;
@@ -54,6 +56,6 @@ export const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) =>
54
56
  margin: 0 1px 0 1.5px;
55
57
  vertical-align: -1.5px;
56
58
  }
57
- `;
58
- return cx(border, shadow, scrollbar, style);
59
- };
59
+ `
60
+ return cx(border, shadow, scrollbar, style)
61
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { AtomList } from '@milkdown/utils';
2
+ import { AtomList } from '@milkdown/utils'
3
3
 
4
- import { emojiNode } from './node';
5
- export * from './node';
4
+ import { emojiNode } from './node'
5
+ export * from './node'
6
6
 
7
- export const emoji = AtomList.create([emojiNode()]);
7
+ export const emoji = AtomList.create([emojiNode()])