@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.
@@ -1,31 +1,38 @@
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
- ThemeShadow,
10
- ThemeSize,
11
- } from '@milkdown/core';
8
+ ThemeBorder,
9
+ ThemeColor,
10
+ ThemeFont,
11
+ ThemeScrollbar,
12
+ ThemeShadow,
13
+ ThemeSize,
14
+ } from '@milkdown/core'
12
15
 
13
16
  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]);
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])
19
23
 
20
- const style = css`
24
+ const style = css`
25
+ min-height: 36px;
26
+ max-height: 320px;
27
+ overflow-y: auto;
28
+ border-radius: ${radius};
21
29
  position: absolute;
30
+ background: ${palette('surface')};
31
+
22
32
  &.hide {
23
33
  display: none;
24
34
  }
25
35
 
26
- border-radius: ${radius};
27
- background: ${palette('surface')};
28
-
29
36
  .milkdown-emoji-filter_item {
30
37
  display: flex;
31
38
  gap: 8px;
@@ -49,6 +56,6 @@ export const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) =>
49
56
  margin: 0 1px 0 1.5px;
50
57
  vertical-align: -1.5px;
51
58
  }
52
- `;
53
- return cx(border, shadow, style);
54
- };
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()])
package/src/node.ts CHANGED
@@ -1,25 +1,25 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { RemarkPlugin } from '@milkdown/core';
3
- import { expectDomTypeError } from '@milkdown/exception';
4
- import { InputRule } from '@milkdown/prose/inputrules';
5
- import { createNode } from '@milkdown/utils';
6
- import nodeEmoji from 'node-emoji';
7
- import remarkEmoji from 'remark-emoji';
2
+ import type { RemarkPlugin } from '@milkdown/core'
3
+ import { expectDomTypeError } from '@milkdown/exception'
4
+ import { InputRule } from '@milkdown/prose/inputrules'
5
+ import { createNode } from '@milkdown/utils'
6
+ import nodeEmoji from 'node-emoji'
7
+ import remarkEmoji from 'remark-emoji'
8
8
 
9
- import { input } from './constant';
10
- import { filter } from './filter';
11
- import { parse } from './parse';
12
- import { twemojiPlugin } from './remark-twemoji';
9
+ import { input } from './constant'
10
+ import { filter } from './filter'
11
+ import { parse } from './parse'
12
+ import { twemojiPlugin } from './remark-twemoji'
13
13
 
14
- export type EmojiOptions = {
15
- maxListSize: number;
16
- twemojiOptions: TwemojiOptions;
17
- };
14
+ export interface EmojiOptions {
15
+ maxListSize: number
16
+ twemojiOptions: TwemojiOptions
17
+ }
18
18
 
19
19
  export const emojiNode = createNode<string, EmojiOptions>((utils, options) => {
20
- const getStyle = () =>
21
- utils.getStyle(
22
- ({ css }) => css`
20
+ const getStyle = () =>
21
+ utils.getStyle(
22
+ ({ css }) => css`
23
23
  .emoji {
24
24
  height: 1em;
25
25
  width: 1em;
@@ -27,76 +27,77 @@ export const emojiNode = createNode<string, EmojiOptions>((utils, options) => {
27
27
  vertical-align: -1.5px;
28
28
  }
29
29
  `,
30
- );
31
- return {
32
- id: 'emoji',
33
- schema: () => ({
34
- group: 'inline',
35
- inline: true,
36
- atom: true,
37
- attrs: {
38
- html: {
39
- default: '',
40
- },
41
- },
42
- parseDOM: [
43
- {
44
- tag: 'span[data-type="emoji"]',
45
- getAttrs: (dom) => {
46
- if (!(dom instanceof HTMLElement)) {
47
- throw expectDomTypeError(dom);
48
- }
49
- return { html: dom.innerHTML };
50
- },
51
- },
52
- ],
53
- toDOM: (node) => {
54
- const span = document.createElement('span');
55
- span.classList.add('emoji-wrapper');
56
- span.dataset['type'] = 'emoji';
57
- utils.themeManager.onFlush(() => {
58
- const style = getStyle();
59
- if (style) {
60
- span.classList.add(style);
61
- }
62
- });
63
- span.innerHTML = node.attrs['html'];
64
- return { dom: span };
65
- },
66
- parseMarkdown: {
67
- match: ({ type }) => type === 'emoji',
68
- runner: (state, node, type) => {
69
- state.addNode(type, { html: node['value'] as string });
70
- },
71
- },
72
- toMarkdown: {
73
- match: (node) => node.type.name === 'emoji',
74
- runner: (state, node) => {
75
- const span = document.createElement('span');
76
- span.innerHTML = node.attrs['html'];
77
- const img = span.querySelector('img');
78
- const title = img?.title;
79
- span.remove();
80
- state.addNode('text', undefined, title);
81
- },
82
- },
83
- }),
84
- inputRules: (nodeType) => [
85
- new InputRule(input, (state, match, start, end) => {
86
- const content = match[0];
87
- if (!content) return null;
88
- const got = nodeEmoji.get(content);
89
- if (!got || content.includes(got)) return null;
30
+ )
31
+ return {
32
+ id: 'emoji',
33
+ schema: () => ({
34
+ group: 'inline',
35
+ inline: true,
36
+ atom: true,
37
+ attrs: {
38
+ html: {
39
+ default: '',
40
+ },
41
+ },
42
+ parseDOM: [
43
+ {
44
+ tag: 'span[data-type="emoji"]',
45
+ getAttrs: (dom) => {
46
+ if (!(dom instanceof HTMLElement))
47
+ throw expectDomTypeError(dom)
90
48
 
91
- const html = parse(got, options?.twemojiOptions);
49
+ return { html: dom.innerHTML }
50
+ },
51
+ },
52
+ ],
53
+ toDOM: (node) => {
54
+ const span = document.createElement('span')
55
+ span.classList.add('emoji-wrapper')
56
+ span.dataset.type = 'emoji'
57
+ utils.themeManager.onFlush(() => {
58
+ const style = getStyle()
59
+ if (style)
60
+ span.classList.add(style)
61
+ })
62
+ span.innerHTML = node.attrs.html
63
+ return { dom: span }
64
+ },
65
+ parseMarkdown: {
66
+ match: ({ type }) => type === 'emoji',
67
+ runner: (state, node, type) => {
68
+ state.addNode(type, { html: node.value as string })
69
+ },
70
+ },
71
+ toMarkdown: {
72
+ match: node => node.type.name === 'emoji',
73
+ runner: (state, node) => {
74
+ const span = document.createElement('span')
75
+ span.innerHTML = node.attrs.html
76
+ const img = span.querySelector('img')
77
+ const title = img?.title
78
+ span.remove()
79
+ state.addNode('text', undefined, title)
80
+ },
81
+ },
82
+ }),
83
+ inputRules: nodeType => [
84
+ new InputRule(input, (state, match, start, end) => {
85
+ const content = match[0]
86
+ if (!content)
87
+ return null
88
+ const got = nodeEmoji.get(content)
89
+ if (!got || content.includes(got))
90
+ return null
92
91
 
93
- return state.tr
94
- .setMeta('emoji', true)
95
- .replaceRangeWith(start, end, nodeType.create({ html }))
96
- .scrollIntoView();
97
- }),
98
- ],
99
- remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin(options?.twemojiOptions)],
100
- prosePlugins: () => [filter(utils, options?.maxListSize ?? 6, options?.twemojiOptions)],
101
- };
102
- });
92
+ const html = parse(got, options?.twemojiOptions)
93
+
94
+ return state.tr
95
+ .setMeta('emoji', true)
96
+ .replaceRangeWith(start, end, nodeType.create({ html }))
97
+ .scrollIntoView()
98
+ }),
99
+ ],
100
+ remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin(options?.twemojiOptions)],
101
+ prosePlugins: () => [filter(utils, options?.maxListSize ?? 6, options?.twemojiOptions)],
102
+ }
103
+ })
package/src/parse.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import twemoji from 'twemoji';
2
+ import twemoji from 'twemoji'
3
3
 
4
- const setAttr = (text: string) => ({ title: text });
4
+ const setAttr = (text: string) => ({ title: text })
5
5
 
6
6
  export const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>
7
- twemoji.parse(emoji, { attributes: setAttr, ...twemojiOptions }) as unknown as string;
7
+ twemoji.parse(emoji, { attributes: setAttr, ...twemojiOptions }) as unknown as string
@@ -1,69 +1,69 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { RemarkPlugin } from '@milkdown/core';
3
- import emojiRegex from 'emoji-regex';
4
- import { Literal, Node, Parent } from 'unist';
2
+ import type { RemarkPlugin } from '@milkdown/core'
3
+ import emojiRegex from 'emoji-regex'
4
+ import type { Literal, Node, Parent } from 'unist'
5
5
 
6
- import { parse } from './parse';
6
+ import { parse } from './parse'
7
7
 
8
- const regex = emojiRegex();
8
+ const regex = emojiRegex()
9
9
 
10
- const isParent = (node: Node): node is Parent => !!(node as Parent).children;
11
- const isLiteral = (node: Node): node is Literal => !!(node as Literal).value;
10
+ const isParent = (node: Node): node is Parent => !!(node as Parent).children
11
+ const isLiteral = (node: Node): node is Literal => !!(node as Literal).value
12
12
 
13
13
  function flatMap(ast: Node, fn: (node: Node, index: number, parent: Node | null) => Node[]) {
14
- return transform(ast, 0, null)[0];
14
+ return transform(ast, 0, null)[0]
15
15
 
16
- function transform(node: Node, index: number, parent: Node | null) {
17
- if (isParent(node)) {
18
- const out = [];
19
- for (let i = 0, n = node.children.length; i < n; i++) {
20
- const nthChild = node.children[i];
21
- if (nthChild) {
22
- const xs = transform(nthChild, i, node);
23
- if (xs) {
24
- for (let j = 0, m = xs.length; j < m; j++) {
25
- const item = xs[j];
26
- if (item) {
27
- out.push(item);
28
- }
29
- }
30
- }
31
- }
16
+ function transform(node: Node, index: number, parent: Node | null) {
17
+ if (isParent(node)) {
18
+ const out = []
19
+ for (let i = 0, n = node.children.length; i < n; i++) {
20
+ const nthChild = node.children[i]
21
+ if (nthChild) {
22
+ const xs = transform(nthChild, i, node)
23
+ if (xs) {
24
+ for (let j = 0, m = xs.length; j < m; j++) {
25
+ const item = xs[j]
26
+ if (item)
27
+ out.push(item)
32
28
  }
33
- node.children = out;
29
+ }
34
30
  }
35
-
36
- return fn(node, index, parent);
31
+ }
32
+ node.children = out
37
33
  }
34
+
35
+ return fn(node, index, parent)
36
+ }
38
37
  }
39
38
 
40
- export const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin = (twemojiOptions) => () => {
41
- function transformer(tree: Node) {
42
- flatMap(tree, (node) => {
43
- if (!isLiteral(node)) {
44
- return [node];
45
- }
46
- const value = node.value as string;
47
- const output: Literal<string>[] = [];
48
- let match;
49
- let str = value;
50
- while ((match = regex.exec(str))) {
51
- const { index } = match;
52
- const emoji = match[0];
53
- if (emoji) {
54
- if (index > 0) {
55
- output.push({ ...node, value: str.slice(0, index) });
56
- }
57
- output.push({ ...node, value: parse(emoji, twemojiOptions), type: 'emoji' });
58
- str = str.slice(index + emoji.length);
59
- }
60
- regex.lastIndex = 0;
61
- }
62
- if (str.length) {
63
- output.push({ ...node, value: str });
64
- }
65
- return output;
66
- });
67
- }
68
- return transformer;
69
- };
39
+ export const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin = twemojiOptions => () => {
40
+ function transformer(tree: Node) {
41
+ flatMap(tree, (node) => {
42
+ if (!isLiteral(node))
43
+ return [node]
44
+
45
+ const value = node.value as string
46
+ const output: Literal<string>[] = []
47
+ let match
48
+ let str = value
49
+ // eslint-disable-next-line no-cond-assign
50
+ while ((match = regex.exec(str))) {
51
+ const { index } = match
52
+ const emoji = match[0]
53
+ if (emoji) {
54
+ if (index > 0)
55
+ output.push({ ...node, value: str.slice(0, index) })
56
+
57
+ output.push({ ...node, value: parse(emoji, twemojiOptions), type: 'emoji' })
58
+ str = str.slice(index + emoji.length)
59
+ }
60
+ regex.lastIndex = 0
61
+ }
62
+ if (str.length)
63
+ output.push({ ...node, value: str })
64
+
65
+ return output
66
+ })
67
+ }
68
+ return transformer
69
+ }