@standardnotes/markdown-visual 1.0.7 → 1.3.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/CHANGELOG.md +108 -0
- package/config-overrides.js +2 -0
- package/package.json +43 -49
- package/{build → public}/favicon.ico +0 -0
- package/public/index.html +31 -0
- package/{build → public}/logo192.png +0 -0
- package/{build → public}/logo512.png +0 -0
- package/{build → public}/manifest.json +0 -0
- package/{build → public}/robots.txt +0 -0
- package/public/sample.ext.json +15 -0
- package/src/components/CodeMirror/index.tsx +94 -0
- package/src/components/CodeMirror/styles.scss +55 -0
- package/src/components/Milkdown/editor.ts +62 -0
- package/src/components/Milkdown/index.tsx +77 -0
- package/src/components/Milkdown/plugins/advanced-menu/README.md +3 -0
- package/src/components/Milkdown/plugins/advanced-menu/button.ts +102 -0
- package/src/components/Milkdown/plugins/advanced-menu/config.ts +94 -0
- package/src/components/Milkdown/plugins/advanced-menu/divider.ts +37 -0
- package/src/components/Milkdown/plugins/advanced-menu/index.ts +64 -0
- package/src/components/Milkdown/plugins/advanced-menu/manager.ts +122 -0
- package/src/components/Milkdown/plugins/advanced-menu/menuBar.ts +108 -0
- package/src/components/Milkdown/plugins/advanced-menu/select.ts +163 -0
- package/src/components/Milkdown/styles.scss +209 -0
- package/src/components/SplitView/index.tsx +42 -0
- package/src/components/SplitView/styles.scss +51 -0
- package/src/index.tsx +215 -0
- package/src/react-app-env.d.ts +1 -0
- package/src/setupTests.ts +5 -0
- package/src/stylesheets/main.scss +45 -0
- package/src/stylesheets/prism/material-light.css +207 -0
- package/tsconfig.json +21 -0
- package/.env +0 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- package/.github/ISSUE_TEMPLATE/config.yml +0 -4
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -22
- package/.github/ISSUE_TEMPLATE/help-request.md +0 -38
- package/.husky/pre-commit +0 -4
- package/build/asset-manifest.json +0 -83
- package/build/index.html +0 -1
- package/build/package.json +0 -22
- package/build/static/css/main.904cd38b.css +0 -2
- package/build/static/css/main.904cd38b.css.map +0 -1
- package/build/static/js/main.ad13c3af.js +0 -3
- package/build/static/js/main.ad13c3af.js.LICENSE.txt +0 -65
- package/build/static/js/main.ad13c3af.js.map +0 -1
- package/build/static/media/KaTeX_AMS-Regular.73ea273a72f4aca30ca5.woff2 +0 -0
- package/build/static/media/KaTeX_AMS-Regular.853be92419a6c3766b9a.ttf +0 -0
- package/build/static/media/KaTeX_AMS-Regular.d562e886c52f12660a41.woff +0 -0
- package/build/static/media/KaTeX_Caligraphic-Bold.7489a2fbfb9bfe704420.ttf +0 -0
- package/build/static/media/KaTeX_Caligraphic-Bold.a1abf90dfd72792a577a.woff2 +0 -0
- package/build/static/media/KaTeX_Caligraphic-Bold.d757c535a2e5902f1325.woff +0 -0
- package/build/static/media/KaTeX_Caligraphic-Regular.7e873d3833eb108a0758.ttf +0 -0
- package/build/static/media/KaTeX_Caligraphic-Regular.d6484fce1ef428d5bd94.woff2 +0 -0
- package/build/static/media/KaTeX_Caligraphic-Regular.db074fa22cf224af93d7.woff +0 -0
- package/build/static/media/KaTeX_Fraktur-Bold.354501bac435c3264834.woff +0 -0
- package/build/static/media/KaTeX_Fraktur-Bold.4c761b3711973ab04edf.ttf +0 -0
- package/build/static/media/KaTeX_Fraktur-Bold.931d67ea207ab37ee693.woff2 +0 -0
- package/build/static/media/KaTeX_Fraktur-Regular.172d3529b26f8cedef6b.woff2 +0 -0
- package/build/static/media/KaTeX_Fraktur-Regular.6fdf0ac577be0ba82a4c.woff +0 -0
- package/build/static/media/KaTeX_Fraktur-Regular.ed305b5434865e06ffde.ttf +0 -0
- package/build/static/media/KaTeX_Main-Bold.0c3b8929d377c0e9b2f3.woff +0 -0
- package/build/static/media/KaTeX_Main-Bold.39890742bc957b368704.woff2 +0 -0
- package/build/static/media/KaTeX_Main-Bold.8169508bf58f8bd92ad8.ttf +0 -0
- package/build/static/media/KaTeX_Main-BoldItalic.20f389c4120be058d80a.woff2 +0 -0
- package/build/static/media/KaTeX_Main-BoldItalic.428978dc7837d46de091.woff +0 -0
- package/build/static/media/KaTeX_Main-BoldItalic.828abcb200061cffbaae.ttf +0 -0
- package/build/static/media/KaTeX_Main-Italic.fa675e5e4bec9eb250b6.ttf +0 -0
- package/build/static/media/KaTeX_Main-Italic.fd947498bc16392e76c2.woff +0 -0
- package/build/static/media/KaTeX_Main-Italic.fe2176f79edaa716e621.woff2 +0 -0
- package/build/static/media/KaTeX_Main-Regular.4f35fbcc9ee8614c2bcc.woff +0 -0
- package/build/static/media/KaTeX_Main-Regular.9eba1d77abcf2aa6e94e.ttf +0 -0
- package/build/static/media/KaTeX_Main-Regular.f650f111a3b890d116f1.woff2 +0 -0
- package/build/static/media/KaTeX_Math-BoldItalic.3f07ed67f06c720120ce.woff +0 -0
- package/build/static/media/KaTeX_Math-BoldItalic.bf2d440b3a42ea78a998.ttf +0 -0
- package/build/static/media/KaTeX_Math-BoldItalic.dcbcbd93bac0470b462d.woff2 +0 -0
- package/build/static/media/KaTeX_Math-Italic.6d3d25f4820d0da8f01f.woff2 +0 -0
- package/build/static/media/KaTeX_Math-Italic.8a5f936332e8028c7278.ttf +0 -0
- package/build/static/media/KaTeX_Math-Italic.96759856b4e70f3a8338.woff +0 -0
- package/build/static/media/KaTeX_SansSerif-Bold.5b49f4993ae22d7975b4.ttf +0 -0
- package/build/static/media/KaTeX_SansSerif-Bold.95591a929f0d32aa282a.woff2 +0 -0
- package/build/static/media/KaTeX_SansSerif-Bold.b9cd458ac6d5889ff9c3.woff +0 -0
- package/build/static/media/KaTeX_SansSerif-Italic.7d393d382f3e7fb1c637.woff2 +0 -0
- package/build/static/media/KaTeX_SansSerif-Italic.8d593cfaa96238d5e2f8.woff +0 -0
- package/build/static/media/KaTeX_SansSerif-Italic.b257a18c016f37ee4543.ttf +0 -0
- package/build/static/media/KaTeX_SansSerif-Regular.02271ec5cb9f5b4588ac.woff +0 -0
- package/build/static/media/KaTeX_SansSerif-Regular.2f7bc363fc5424ebda59.ttf +0 -0
- package/build/static/media/KaTeX_SansSerif-Regular.cd5e231e0cc53b2cb2c0.woff2 +0 -0
- package/build/static/media/KaTeX_Script-Regular.073b3402d036714b4370.woff +0 -0
- package/build/static/media/KaTeX_Script-Regular.c81d1b2a4b75d3eded60.woff2 +0 -0
- package/build/static/media/KaTeX_Script-Regular.fc9ba5249878cd8f8d88.ttf +0 -0
- package/build/static/media/KaTeX_Size1-Regular.0108e89c9003e8c14ea3.woff +0 -0
- package/build/static/media/KaTeX_Size1-Regular.6de7d4b539221a49e9e2.ttf +0 -0
- package/build/static/media/KaTeX_Size1-Regular.6eec866c69313624be60.woff2 +0 -0
- package/build/static/media/KaTeX_Size2-Regular.2960900c4f271311eb36.woff2 +0 -0
- package/build/static/media/KaTeX_Size2-Regular.3a99e70aee4076660d38.woff +0 -0
- package/build/static/media/KaTeX_Size2-Regular.57f5c1837853986ea1db.ttf +0 -0
- package/build/static/media/KaTeX_Size3-Regular.7947224e8a9914fa332b.woff +0 -0
- package/build/static/media/KaTeX_Size3-Regular.8d6b6822586eea3d3b20.ttf +0 -0
- package/build/static/media/KaTeX_Size3-Regular.e1951519f6f0596f7356.woff2 +0 -0
- package/build/static/media/KaTeX_Size4-Regular.4ad7c7e8bb8d10a34bb7.ttf +0 -0
- package/build/static/media/KaTeX_Size4-Regular.aeffd8025cba3647f1a6.woff +0 -0
- package/build/static/media/KaTeX_Size4-Regular.e418bf257af1052628d8.woff2 +0 -0
- package/build/static/media/KaTeX_Typewriter-Regular.4c6b94fd1d07f8beff7c.woff +0 -0
- package/build/static/media/KaTeX_Typewriter-Regular.c295e7f71970f03c0549.woff2 +0 -0
- package/build/static/media/KaTeX_Typewriter-Regular.c5c02d763c89380dcb4e.ttf +0 -0
- package/build/static/media/material-icons-outlined.123a7ad6784163c39aaa.woff +0 -0
- package/build/static/media/material-icons-outlined.5d7deb03b9cecba7d247.woff2 +0 -0
- package/build/static/media/material-icons-round.159dc8004e17a33f287f.woff +0 -0
- package/build/static/media/material-icons-round.7c985a8aea387341edf9.woff2 +0 -0
- package/build/static/media/material-icons-sharp.25f4d306806e85bc60f6.woff2 +0 -0
- package/build/static/media/material-icons-sharp.77bc1b022b575be35fa7.woff +0 -0
- package/build/static/media/material-icons-two-tone.7e94e72135150c6b387c.woff2 +0 -0
- package/build/static/media/material-icons-two-tone.ee71463e28071436d096.woff +0 -0
- package/build/static/media/material-icons.824b570fb059ee0a44a9.woff +0 -0
- package/build/static/media/material-icons.aba5a2a316a1d312db26.woff2 +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
+
|
|
3
|
+
import { css } from '@emotion/css'
|
|
4
|
+
import { CmdKey, commandsCtx, Ctx } from '@milkdown/core'
|
|
5
|
+
import type { Icon } from '@milkdown/design-system'
|
|
6
|
+
import type { EditorView } from '@milkdown/prose'
|
|
7
|
+
import type { Utils } from '@milkdown/utils'
|
|
8
|
+
|
|
9
|
+
import type { MenuCommonConfig } from './config'
|
|
10
|
+
|
|
11
|
+
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> &
|
|
12
|
+
{
|
|
13
|
+
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
|
|
14
|
+
}[Keys]
|
|
15
|
+
|
|
16
|
+
type Config<T = any> = {
|
|
17
|
+
type: 'button'
|
|
18
|
+
icon: Icon
|
|
19
|
+
key?: CmdKey<T>
|
|
20
|
+
callback?: () => void
|
|
21
|
+
options?: T
|
|
22
|
+
active?: (view?: EditorView) => boolean
|
|
23
|
+
alwaysVisible: boolean
|
|
24
|
+
} & MenuCommonConfig
|
|
25
|
+
|
|
26
|
+
export type ButtonConfig = RequireAtLeastOne<Config, 'key' | 'callback'>
|
|
27
|
+
|
|
28
|
+
export const button = (utils: Utils, config: Config, ctx: Ctx, view: EditorView) => {
|
|
29
|
+
const buttonStyle = utils.getStyle((themeTool) => {
|
|
30
|
+
return css`
|
|
31
|
+
border: 0;
|
|
32
|
+
box-sizing: unset;
|
|
33
|
+
width: 1.5rem;
|
|
34
|
+
height: 1.5rem;
|
|
35
|
+
padding: 0.25rem;
|
|
36
|
+
margin: 0.5rem;
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
display: flex;
|
|
39
|
+
justify-content: center;
|
|
40
|
+
align-items: center;
|
|
41
|
+
background-color: ${themeTool.palette('surface')};
|
|
42
|
+
color: ${themeTool.palette('solid')};
|
|
43
|
+
transition: all 0.4s ease-in-out;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
&.active,
|
|
46
|
+
&:hover {
|
|
47
|
+
background-color: ${themeTool.palette('secondary', 0.12)};
|
|
48
|
+
color: ${themeTool.palette('primary')};
|
|
49
|
+
}
|
|
50
|
+
&:disabled {
|
|
51
|
+
display: none;
|
|
52
|
+
}
|
|
53
|
+
`
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const $button = document.createElement('button')
|
|
57
|
+
$button.setAttribute('type', 'button')
|
|
58
|
+
$button.classList.add('button')
|
|
59
|
+
|
|
60
|
+
if (buttonStyle) {
|
|
61
|
+
$button.classList.add(buttonStyle)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const $label = utils.themeTool.slots.label(config.icon)
|
|
65
|
+
if ($label) {
|
|
66
|
+
$button.setAttribute('aria-label', $label)
|
|
67
|
+
$button.setAttribute('title', $label)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const $icon = utils.themeTool.slots.icon(config.icon)
|
|
71
|
+
$button.appendChild($icon)
|
|
72
|
+
$button.addEventListener('click', (e) => {
|
|
73
|
+
e.preventDefault()
|
|
74
|
+
e.stopPropagation()
|
|
75
|
+
|
|
76
|
+
config.callback && config.callback()
|
|
77
|
+
config.key && ctx.get(commandsCtx).call(config.key, config.options)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (config.active) {
|
|
81
|
+
const active = config.active()
|
|
82
|
+
if (active) {
|
|
83
|
+
$button.classList.add('active')
|
|
84
|
+
} else {
|
|
85
|
+
$button.classList.remove('active')
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (config.alwaysVisible) {
|
|
90
|
+
$button.removeAttribute('disabled')
|
|
91
|
+
return $button
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const disabled = !view.editable || (config.disabled && config.disabled(view))
|
|
95
|
+
if (disabled) {
|
|
96
|
+
$button.setAttribute('disabled', 'true')
|
|
97
|
+
} else {
|
|
98
|
+
$button.removeAttribute('disabled')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return $button
|
|
102
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InsertHr,
|
|
3
|
+
InsertImage,
|
|
4
|
+
LiftListItem,
|
|
5
|
+
SinkListItem,
|
|
6
|
+
WrapInBlockquote,
|
|
7
|
+
WrapInBulletList,
|
|
8
|
+
WrapInOrderedList,
|
|
9
|
+
} from '@milkdown/preset-commonmark'
|
|
10
|
+
import { InsertTable, TurnIntoTaskList } from '@milkdown/preset-gfm'
|
|
11
|
+
import { EditorView, liftListItem, sinkListItem, wrapIn } from '@milkdown/prose'
|
|
12
|
+
import { ButtonConfig } from './button'
|
|
13
|
+
import { SelectConfig } from './select'
|
|
14
|
+
|
|
15
|
+
export type MenuCommonConfig = {
|
|
16
|
+
disabled?: (view: EditorView) => boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type MenuConfigItem = SelectConfig | ButtonConfig
|
|
20
|
+
export type MenuConfig = Array<Array<MenuConfigItem>>
|
|
21
|
+
|
|
22
|
+
export const menuConfig: any = [
|
|
23
|
+
[
|
|
24
|
+
{
|
|
25
|
+
type: 'button',
|
|
26
|
+
icon: 'bulletList',
|
|
27
|
+
key: WrapInBulletList,
|
|
28
|
+
disabled: (view: EditorView) => {
|
|
29
|
+
const { state } = view
|
|
30
|
+
return !wrapIn(state.schema.nodes.bullet_list)(state)
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: 'button',
|
|
35
|
+
icon: 'orderedList',
|
|
36
|
+
key: WrapInOrderedList,
|
|
37
|
+
disabled: (view: EditorView) => {
|
|
38
|
+
const { state } = view
|
|
39
|
+
return !wrapIn(state.schema.nodes.ordered_list)(state)
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'button',
|
|
44
|
+
icon: 'taskList',
|
|
45
|
+
key: TurnIntoTaskList,
|
|
46
|
+
disabled: (view: EditorView) => {
|
|
47
|
+
const { state } = view
|
|
48
|
+
return !wrapIn(state.schema.nodes.task_list_item)(state)
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'button',
|
|
53
|
+
icon: 'liftList',
|
|
54
|
+
key: LiftListItem,
|
|
55
|
+
disabled: (view: EditorView) => {
|
|
56
|
+
const { state } = view
|
|
57
|
+
return !liftListItem(state.schema.nodes.list_item)(state)
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'button',
|
|
62
|
+
icon: 'sinkList',
|
|
63
|
+
key: SinkListItem,
|
|
64
|
+
disabled: (view: EditorView) => {
|
|
65
|
+
const { state } = view
|
|
66
|
+
return !sinkListItem(state.schema.nodes.list_item)(state)
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
{
|
|
72
|
+
type: 'button',
|
|
73
|
+
icon: 'image',
|
|
74
|
+
key: InsertImage,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: 'button',
|
|
78
|
+
icon: 'table',
|
|
79
|
+
key: InsertTable,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
{
|
|
84
|
+
type: 'button',
|
|
85
|
+
icon: 'quote',
|
|
86
|
+
key: WrapInBlockquote,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: 'button',
|
|
90
|
+
icon: 'divider',
|
|
91
|
+
key: InsertHr,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
+
|
|
3
|
+
import { css } from '@emotion/css'
|
|
4
|
+
import { Utils } from '@milkdown/utils'
|
|
5
|
+
|
|
6
|
+
export type DividerConfig = {
|
|
7
|
+
type: 'divider'
|
|
8
|
+
group: HTMLElement[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const divider = (utils: Utils, config: DividerConfig) => {
|
|
12
|
+
const dividerStyle = utils.getStyle((themeTool) => {
|
|
13
|
+
return css`
|
|
14
|
+
flex-shrink: 0;
|
|
15
|
+
width: ${themeTool.size.lineWidth};
|
|
16
|
+
background-color: ${themeTool.palette('line')};
|
|
17
|
+
margin: 0.75rem 1rem;
|
|
18
|
+
`
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const $divider = document.createElement('div')
|
|
22
|
+
$divider.classList.add('divider')
|
|
23
|
+
|
|
24
|
+
if (dividerStyle) {
|
|
25
|
+
$divider.classList.add(dividerStyle)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const disabled = config.group.every((x) => x.getAttribute('disabled') || x.classList.contains('disabled'))
|
|
29
|
+
|
|
30
|
+
if (disabled) {
|
|
31
|
+
$divider.classList.add('disabled')
|
|
32
|
+
} else {
|
|
33
|
+
$divider.classList.remove('disabled')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return $divider
|
|
37
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
+
|
|
3
|
+
import { createCmd, createCmdKey, Ctx } from '@milkdown/core'
|
|
4
|
+
import { EditorView, Plugin, PluginKey, selectParentNode } from '@milkdown/prose'
|
|
5
|
+
import { createPlugin } from '@milkdown/utils'
|
|
6
|
+
|
|
7
|
+
import { MenuConfig, menuConfig } from './config'
|
|
8
|
+
import { Manager } from './manager'
|
|
9
|
+
import { HandleDOM, MenuBar } from './menuBar'
|
|
10
|
+
|
|
11
|
+
export type Options = {
|
|
12
|
+
config: MenuConfig
|
|
13
|
+
domHandler: HandleDOM
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { menuConfig } from './config'
|
|
17
|
+
|
|
18
|
+
export const menu = createPlugin<string, Options>((utils, options) => {
|
|
19
|
+
const config = options?.config ?? menuConfig
|
|
20
|
+
const domHandler = options?.domHandler
|
|
21
|
+
|
|
22
|
+
let restoreDOM: (() => void) | null = null
|
|
23
|
+
let menu: HTMLDivElement | null = null
|
|
24
|
+
let manager: Manager | null = null
|
|
25
|
+
|
|
26
|
+
const SelectParent = createCmdKey()
|
|
27
|
+
|
|
28
|
+
const initIfNecessary = (ctx: Ctx, editorView: EditorView) => {
|
|
29
|
+
if (!menu) {
|
|
30
|
+
const [_menu, _restoreDOM] = MenuBar(utils, editorView, ctx, domHandler)
|
|
31
|
+
menu = _menu
|
|
32
|
+
restoreDOM = () => {
|
|
33
|
+
_restoreDOM()
|
|
34
|
+
menu = null
|
|
35
|
+
manager = null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!manager) {
|
|
40
|
+
manager = new Manager(config, utils, ctx, menu, editorView)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
commands: () => [createCmd(SelectParent, () => selectParentNode)],
|
|
46
|
+
prosePlugins: (_, ctx) => {
|
|
47
|
+
const plugin = new Plugin({
|
|
48
|
+
key: new PluginKey('milkdown-advanced-menu'),
|
|
49
|
+
view: (editorView) => {
|
|
50
|
+
initIfNecessary(ctx, editorView)
|
|
51
|
+
if (editorView.editable) {
|
|
52
|
+
manager?.update(editorView)
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
update: (view) => manager?.update(view),
|
|
56
|
+
destroy: () => restoreDOM?.(),
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return [plugin]
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
})
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
+
|
|
3
|
+
import { Ctx } from '@milkdown/core'
|
|
4
|
+
import { EditorView } from '@milkdown/prose'
|
|
5
|
+
import { Utils } from '@milkdown/utils'
|
|
6
|
+
|
|
7
|
+
import { button, ButtonConfig } from './button'
|
|
8
|
+
import { MenuConfig, MenuConfigItem } from './config'
|
|
9
|
+
import { divider, DividerConfig } from './divider'
|
|
10
|
+
import { select, SelectConfig } from './select'
|
|
11
|
+
|
|
12
|
+
type InnerConfig = (MenuConfigItem | DividerConfig) & { $: HTMLElement }
|
|
13
|
+
|
|
14
|
+
export class Manager {
|
|
15
|
+
private config: InnerConfig[]
|
|
16
|
+
|
|
17
|
+
constructor(originalConfig: MenuConfig, private utils: Utils, private ctx: Ctx, menu: HTMLElement, view: EditorView) {
|
|
18
|
+
this.config = originalConfig
|
|
19
|
+
.map((xs) =>
|
|
20
|
+
xs.map((x) => ({
|
|
21
|
+
...x,
|
|
22
|
+
$: this.$create(x, view),
|
|
23
|
+
})),
|
|
24
|
+
)
|
|
25
|
+
.map((xs, i): Array<InnerConfig> => {
|
|
26
|
+
if (i === originalConfig.length - 1) {
|
|
27
|
+
return xs
|
|
28
|
+
}
|
|
29
|
+
const dividerConfig: DividerConfig = {
|
|
30
|
+
type: 'divider',
|
|
31
|
+
group: xs.map((x) => x.$),
|
|
32
|
+
}
|
|
33
|
+
return [
|
|
34
|
+
...xs,
|
|
35
|
+
{
|
|
36
|
+
...dividerConfig,
|
|
37
|
+
$: this.$create(dividerConfig, view),
|
|
38
|
+
},
|
|
39
|
+
]
|
|
40
|
+
})
|
|
41
|
+
.flat()
|
|
42
|
+
|
|
43
|
+
this.config.forEach((x) => menu.appendChild(x.$))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public update(view: EditorView) {
|
|
47
|
+
const enabled = view.editable
|
|
48
|
+
|
|
49
|
+
this.config.forEach((config) => {
|
|
50
|
+
switch (config.type) {
|
|
51
|
+
case 'button': {
|
|
52
|
+
if (config.active) {
|
|
53
|
+
const active = config.active(view)
|
|
54
|
+
if (active) {
|
|
55
|
+
config.$.classList.add('active')
|
|
56
|
+
} else {
|
|
57
|
+
config.$.classList.remove('active')
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (config.alwaysVisible) {
|
|
62
|
+
config.$.removeAttribute('disabled')
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const disabled = !enabled || (config.disabled && config.disabled(view))
|
|
67
|
+
if (disabled) {
|
|
68
|
+
config.$.setAttribute('disabled', 'true')
|
|
69
|
+
} else {
|
|
70
|
+
config.$.removeAttribute('disabled')
|
|
71
|
+
}
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case 'select': {
|
|
76
|
+
if (config.alwaysVisible) {
|
|
77
|
+
config.$.removeAttribute('disabled')
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const disabled = !enabled || (config.disabled && config.disabled(view))
|
|
82
|
+
if (disabled) {
|
|
83
|
+
config.$.classList.add('disabled')
|
|
84
|
+
config.$.children[0].setAttribute('disabled', 'true')
|
|
85
|
+
} else {
|
|
86
|
+
config.$.classList.remove('disabled')
|
|
87
|
+
config.$.children[0].removeAttribute('disabled')
|
|
88
|
+
}
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'divider': {
|
|
93
|
+
const disabled = config.group.every((x) => x.getAttribute('disabled') || x.classList.contains('disabled'))
|
|
94
|
+
if (disabled) {
|
|
95
|
+
config.$.classList.add('disabled')
|
|
96
|
+
} else {
|
|
97
|
+
config.$.classList.remove('disabled')
|
|
98
|
+
}
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private $create(item: ButtonConfig | DividerConfig | SelectConfig, view: EditorView): HTMLElement {
|
|
106
|
+
const { utils, ctx } = this
|
|
107
|
+
|
|
108
|
+
switch (item.type) {
|
|
109
|
+
case 'button': {
|
|
110
|
+
return button(utils, item, ctx, view)
|
|
111
|
+
}
|
|
112
|
+
case 'select': {
|
|
113
|
+
return select(utils, item, ctx, view)
|
|
114
|
+
}
|
|
115
|
+
case 'divider': {
|
|
116
|
+
return divider(utils, item)
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
throw new Error()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
+
|
|
3
|
+
import { css } from '@emotion/css'
|
|
4
|
+
import { Ctx, rootCtx } from '@milkdown/core'
|
|
5
|
+
import { EditorView } from '@milkdown/prose'
|
|
6
|
+
import { Utils } from '@milkdown/utils'
|
|
7
|
+
|
|
8
|
+
export const MenuBar = (utils: Utils, view: EditorView, ctx: Ctx, domHandler: HandleDOM = defaultDOMHandler) => {
|
|
9
|
+
const menuWrapper = document.createElement('div')
|
|
10
|
+
menuWrapper.classList.add('milkdown-menu-wrapper')
|
|
11
|
+
const menu = document.createElement('div')
|
|
12
|
+
menu.classList.add('milkdown-menu')
|
|
13
|
+
|
|
14
|
+
const editorDOM = view.dom as HTMLDivElement
|
|
15
|
+
|
|
16
|
+
const editorWrapperStyle = utils.getStyle((themeTool) => {
|
|
17
|
+
return themeTool.mixin.scrollbar('y')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (editorWrapperStyle) {
|
|
21
|
+
editorDOM.classList.add(editorWrapperStyle)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const menuStyle = utils.getStyle((themeTool) => {
|
|
25
|
+
const border = themeTool.mixin.border()
|
|
26
|
+
const scrollbar = themeTool.mixin.scrollbar('x')
|
|
27
|
+
const style = css`
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
width: 100%;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-wrap: nowrap;
|
|
32
|
+
overflow-x: auto;
|
|
33
|
+
${border};
|
|
34
|
+
${scrollbar};
|
|
35
|
+
background: ${themeTool.palette('surface')};
|
|
36
|
+
-webkit-overflow-scrolling: auto;
|
|
37
|
+
.disabled {
|
|
38
|
+
display: none;
|
|
39
|
+
}
|
|
40
|
+
`
|
|
41
|
+
return style
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (menuStyle) {
|
|
45
|
+
menuStyle.split(' ').forEach((x) => menu.classList.add(x))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const root = ctx.get(rootCtx)
|
|
49
|
+
|
|
50
|
+
const editorRoot = getRoot(root) as HTMLElement
|
|
51
|
+
const milkdownDOM = editorDOM.parentElement
|
|
52
|
+
|
|
53
|
+
if (!milkdownDOM) {
|
|
54
|
+
throw new Error('No parent node found')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
domHandler({
|
|
58
|
+
menu,
|
|
59
|
+
menuWrapper,
|
|
60
|
+
editorDOM,
|
|
61
|
+
editorRoot,
|
|
62
|
+
milkdownDOM,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const restoreDOM = () => {
|
|
66
|
+
restore({
|
|
67
|
+
menu,
|
|
68
|
+
menuWrapper,
|
|
69
|
+
editorDOM,
|
|
70
|
+
editorRoot,
|
|
71
|
+
milkdownDOM,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [menu, restoreDOM] as const
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type HandleDOMParams = {
|
|
79
|
+
menu: HTMLDivElement
|
|
80
|
+
menuWrapper: HTMLDivElement
|
|
81
|
+
editorRoot: HTMLElement
|
|
82
|
+
milkdownDOM: HTMLElement
|
|
83
|
+
editorDOM: HTMLDivElement
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type HandleDOM = (params: HandleDOMParams) => void
|
|
87
|
+
|
|
88
|
+
const restore: HandleDOM = ({ milkdownDOM, editorRoot, menu, menuWrapper }) => {
|
|
89
|
+
editorRoot.appendChild(milkdownDOM)
|
|
90
|
+
menuWrapper.remove()
|
|
91
|
+
menu.remove()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const defaultDOMHandler: HandleDOM = ({ menu, menuWrapper, editorRoot, milkdownDOM }) => {
|
|
95
|
+
menuWrapper.appendChild(menu)
|
|
96
|
+
editorRoot.replaceChild(menuWrapper, milkdownDOM)
|
|
97
|
+
menuWrapper.appendChild(milkdownDOM)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const getRoot = (root: string | Node | null | undefined) => {
|
|
101
|
+
if (!root) return document.body
|
|
102
|
+
if (typeof root === 'string') {
|
|
103
|
+
const el = document.querySelector(root)
|
|
104
|
+
if (el) return el
|
|
105
|
+
return document.body
|
|
106
|
+
}
|
|
107
|
+
return root
|
|
108
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
+
|
|
3
|
+
import { css } from '@emotion/css'
|
|
4
|
+
import { CmdKey, commandsCtx, Ctx } from '@milkdown/core'
|
|
5
|
+
import { EditorView } from '@milkdown/prose'
|
|
6
|
+
import { Utils } from '@milkdown/utils'
|
|
7
|
+
|
|
8
|
+
import type { MenuCommonConfig } from './config'
|
|
9
|
+
|
|
10
|
+
type SelectOptions = {
|
|
11
|
+
id: string
|
|
12
|
+
text: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type SelectConfig<T = any> = {
|
|
16
|
+
type: 'select'
|
|
17
|
+
text: string
|
|
18
|
+
options: SelectOptions[]
|
|
19
|
+
active?: (view: EditorView) => string
|
|
20
|
+
onSelect: (id: string, view: EditorView) => [key: CmdKey<T>, info?: T]
|
|
21
|
+
alwaysVisible: boolean
|
|
22
|
+
} & MenuCommonConfig
|
|
23
|
+
|
|
24
|
+
export const select = (utils: Utils, config: SelectConfig, ctx: Ctx, view: EditorView) => {
|
|
25
|
+
const selectStyle = utils.getStyle((themeTool) => {
|
|
26
|
+
return css`
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
font-weight: 500;
|
|
29
|
+
font-size: 0.875rem;
|
|
30
|
+
${themeTool.mixin.border('right')};
|
|
31
|
+
${themeTool.mixin.border('left')};
|
|
32
|
+
.menu-selector {
|
|
33
|
+
border: 0;
|
|
34
|
+
box-sizing: unset;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
font: inherit;
|
|
37
|
+
text-align: left;
|
|
38
|
+
justify-content: space-between;
|
|
39
|
+
align-items: center;
|
|
40
|
+
color: ${themeTool.palette('neutral', 0.87)};
|
|
41
|
+
display: flex;
|
|
42
|
+
padding: 0.25rem 0.5rem;
|
|
43
|
+
margin: 0.5rem;
|
|
44
|
+
background: ${themeTool.palette('secondary', 0.12)};
|
|
45
|
+
width: 10.375rem;
|
|
46
|
+
&:disabled {
|
|
47
|
+
display: none;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
.menu-selector-value {
|
|
51
|
+
flex: 1;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
text-overflow: ellipsis;
|
|
54
|
+
}
|
|
55
|
+
.menu-selector-list {
|
|
56
|
+
width: calc(12.375rem);
|
|
57
|
+
position: absolute;
|
|
58
|
+
top: 3rem;
|
|
59
|
+
background: ${themeTool.palette('surface')};
|
|
60
|
+
${themeTool.mixin.border()};
|
|
61
|
+
${themeTool.mixin.shadow()};
|
|
62
|
+
border-bottom-left-radius: ${themeTool.size.radius};
|
|
63
|
+
border-bottom-right-radius: ${themeTool.size.radius};
|
|
64
|
+
z-index: 3;
|
|
65
|
+
}
|
|
66
|
+
.menu-selector-list-item {
|
|
67
|
+
background-color: transparent;
|
|
68
|
+
border: 0;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
display: block;
|
|
71
|
+
font: inherit;
|
|
72
|
+
text-align: left;
|
|
73
|
+
padding: 0.75rem 1rem;
|
|
74
|
+
line-height: 1.5rem;
|
|
75
|
+
width: 100%;
|
|
76
|
+
color: ${themeTool.palette('neutral', 0.87)};
|
|
77
|
+
&:hover {
|
|
78
|
+
background: ${themeTool.palette('secondary', 0.12)};
|
|
79
|
+
color: ${themeTool.palette('primary')};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
&.fold {
|
|
83
|
+
border-color: transparent;
|
|
84
|
+
.menu-selector {
|
|
85
|
+
background: unset;
|
|
86
|
+
}
|
|
87
|
+
.menu-selector-list {
|
|
88
|
+
display: none;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
`
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const selectorWrapper = document.createElement('div')
|
|
95
|
+
selectorWrapper.classList.add('menu-selector-wrapper', 'fold')
|
|
96
|
+
|
|
97
|
+
const selector = document.createElement('button')
|
|
98
|
+
selector.setAttribute('type', 'button')
|
|
99
|
+
selector.classList.add('menu-selector', 'fold')
|
|
100
|
+
selector.addEventListener('mousedown', (e) => {
|
|
101
|
+
e.preventDefault()
|
|
102
|
+
e.stopPropagation()
|
|
103
|
+
selectorWrapper.classList.toggle('fold')
|
|
104
|
+
selectorList.style.left = `${
|
|
105
|
+
selectorWrapper.getBoundingClientRect().left - view.dom.getBoundingClientRect().left
|
|
106
|
+
}px`
|
|
107
|
+
})
|
|
108
|
+
view.dom.addEventListener('click', () => {
|
|
109
|
+
selectorWrapper.classList.add('fold')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const selectorValue = document.createElement('span')
|
|
113
|
+
selectorValue.classList.add('menu-selector-value')
|
|
114
|
+
selectorValue.textContent = config.text
|
|
115
|
+
|
|
116
|
+
const selectorButton = utils.themeTool.slots.icon('downArrow')
|
|
117
|
+
selectorButton.setAttribute('aria-hidden', 'true')
|
|
118
|
+
|
|
119
|
+
selectorWrapper.appendChild(selector)
|
|
120
|
+
selector.appendChild(selectorValue)
|
|
121
|
+
selector.appendChild(selectorButton)
|
|
122
|
+
|
|
123
|
+
const selectorList = document.createElement('div')
|
|
124
|
+
selectorList.classList.add('menu-selector-list')
|
|
125
|
+
config.options.forEach((option) => {
|
|
126
|
+
const selectorListItem = document.createElement('button')
|
|
127
|
+
selectorListItem.setAttribute('type', 'button')
|
|
128
|
+
selectorListItem.dataset.id = option.id
|
|
129
|
+
selectorListItem.textContent = option.text
|
|
130
|
+
selectorListItem.classList.add('menu-selector-list-item')
|
|
131
|
+
selectorList.appendChild(selectorListItem)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
selectorList.addEventListener('mousedown', (e) => {
|
|
135
|
+
const { target } = e
|
|
136
|
+
if (target instanceof HTMLButtonElement && target.dataset.id) {
|
|
137
|
+
ctx.get(commandsCtx).call(...config.onSelect(target.dataset.id, view))
|
|
138
|
+
selectorWrapper.classList.add('fold')
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
selectorWrapper.appendChild(selectorList)
|
|
143
|
+
|
|
144
|
+
if (selectStyle) {
|
|
145
|
+
selectorWrapper.classList.add(selectStyle)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (config.alwaysVisible) {
|
|
149
|
+
selector.removeAttribute('disabled')
|
|
150
|
+
return selectorWrapper
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const disabled = !view.editable || (config.disabled && config.disabled(view))
|
|
154
|
+
if (disabled) {
|
|
155
|
+
selector.classList.add('disabled')
|
|
156
|
+
selector.children[0].setAttribute('disabled', 'true')
|
|
157
|
+
} else {
|
|
158
|
+
selector.classList.remove('disabled')
|
|
159
|
+
selector.children[0].removeAttribute('disabled')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return selectorWrapper
|
|
163
|
+
}
|