Skip to content

Commit f0efe86

Browse files
authored
feat: 🎸 migrate link tooltip to vue (#1807)
1 parent 9f6552c commit f0efe86

File tree

19 files changed

+268
-223
lines changed

19 files changed

+268
-223
lines changed

Diff for: packages/components/src/__internal__/components/icon.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ h
66
type IconProps = {
77
icon?: string | null
88
class?: string
9+
onClick?: (event: PointerEvent) => void
910
}
1011

11-
export function Icon({ icon, class: className }: IconProps) {
12+
export function Icon({ icon, class: className, onClick }: IconProps) {
1213
return (
1314
<span
1415
class={clsx('milkdown-icon', className)}
16+
onPointerdown={onClick}
1517
ref={(el) => {
1618
if (el && icon) {
1719
;(el as HTMLElement).innerHTML = icon.trim()
@@ -30,4 +32,8 @@ Icon.props = {
3032
type: String,
3133
required: false,
3234
},
35+
onClick: {
36+
type: Function,
37+
required: false,
38+
},
3339
}

Diff for: packages/components/src/code-block/view/node-view.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Compartment, EditorState } from '@codemirror/state'
1111
import type { Line, SelectionRange } from '@codemirror/state'
1212
import { exitCode } from '@milkdown/prose/commands'
1313
import { TextSelection } from '@milkdown/prose/state'
14-
import { createApp, ref, watchEffect, type WatchHandle } from 'vue'
14+
import { createApp, ref, watchEffect, type App, type WatchHandle } from 'vue'
1515

1616
import type { CodeBlockConfig } from '../config'
1717
import type { LanguageLoader } from './loader'
@@ -20,6 +20,7 @@ import { CodeBlock } from './components/code-block'
2020
export class CodeMirrorBlock implements NodeView {
2121
dom: HTMLElement
2222
cm: CodeMirror
23+
app: App
2324

2425
readonly = ref(false)
2526
selected = ref(false)
@@ -55,7 +56,9 @@ export class CodeMirrorBlock implements NodeView {
5556
],
5657
})
5758

58-
this.dom = this.createDom()
59+
this.app = this.createApp()
60+
61+
this.dom = this.createDom(this.app)
5962

6063
this.disposeSelectedWatcher = watchEffect(() => {
6164
const isSelected = this.selected.value
@@ -93,11 +96,8 @@ export class CodeMirrorBlock implements NodeView {
9396
}
9497
}
9598

96-
private createDom() {
97-
const dom = document.createElement('div')
98-
dom.className = 'milkdown-code-block'
99-
this.text.value = this.node.textContent
100-
const app = createApp(CodeBlock, {
99+
private createApp = () => {
100+
return createApp(CodeBlock, {
101101
text: this.text,
102102
selected: this.selected,
103103
readonly: this.readonly,
@@ -107,6 +107,12 @@ export class CodeMirrorBlock implements NodeView {
107107
setLanguage: this.setLanguage,
108108
config: this.config,
109109
})
110+
}
111+
112+
private createDom(app: App) {
113+
const dom = document.createElement('div')
114+
dom.className = 'milkdown-code-block'
115+
this.text.value = this.node.textContent
110116
app.mount(dom)
111117
return dom
112118
}
@@ -248,6 +254,7 @@ export class CodeMirrorBlock implements NodeView {
248254
}
249255

250256
destroy() {
257+
this.app.unmount()
251258
this.cm.destroy()
252259
this.disposeSelectedWatcher()
253260
}
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { defineComponent, ref, watch, type Ref, h } from 'vue'
2+
import type { LinkTooltipConfig } from '../slices'
3+
import { Icon } from '../../__internal__/components/icon'
4+
5+
h
6+
7+
type EditLinkProps = {
8+
config: Ref<LinkTooltipConfig>
9+
src: Ref<string>
10+
onConfirm: (href: string) => void
11+
onCancel: () => void
12+
}
13+
14+
export const EditLink = defineComponent<EditLinkProps>({
15+
props: {
16+
config: {
17+
type: Object,
18+
required: true,
19+
},
20+
src: {
21+
type: Object,
22+
required: true,
23+
},
24+
onConfirm: {
25+
type: Function,
26+
required: true,
27+
},
28+
onCancel: {
29+
type: Function,
30+
required: true,
31+
},
32+
},
33+
setup({ config, src, onConfirm, onCancel }) {
34+
const link = ref(src)
35+
36+
watch(src, (value) => {
37+
link.value = value
38+
})
39+
40+
const onConfirmEdit = () => {
41+
onConfirm(link.value)
42+
}
43+
44+
const onKeydown = (e: KeyboardEvent) => {
45+
e.stopPropagation()
46+
if (e.key === 'Enter') {
47+
e.preventDefault()
48+
onConfirmEdit()
49+
}
50+
if (e.key === 'Escape') {
51+
e.preventDefault()
52+
onCancel()
53+
}
54+
}
55+
56+
return () => {
57+
return (
58+
<div class="link-edit">
59+
<input
60+
class="input-area"
61+
placeholder={config.value.inputPlaceholder}
62+
onKeydown={onKeydown}
63+
onInput={(e: Event) => {
64+
link.value = (e.target as HTMLInputElement).value
65+
}}
66+
value={link.value}
67+
/>
68+
{link.value ? (
69+
<Icon
70+
class="button confirm"
71+
icon={config.value.confirmButton()}
72+
onClick={onConfirmEdit}
73+
/>
74+
) : null}
75+
</div>
76+
)
77+
}
78+
},
79+
})

Diff for: packages/components/src/link-tooltip/edit/edit-component.ts

-72
This file was deleted.

Diff for: packages/components/src/link-tooltip/edit/edit-configure.ts

-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import type { Ctx } from '@milkdown/ctx'
22
import { linkTooltipAPI } from '../slices'
33
import { linkEditTooltip } from '../tooltips'
4-
import { defIfNotExists } from '../../__internal__/helper'
5-
import { LinkEditElement } from './edit-component'
64
import { LinkEditTooltip } from './edit-view'
75

8-
defIfNotExists('milkdown-link-edit', LinkEditElement)
96
export function configureLinkEditTooltip(ctx: Ctx) {
107
let linkEditTooltipView: LinkEditTooltip | null
118

Diff for: packages/components/src/link-tooltip/edit/edit-view.ts

+32-12
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ import { TooltipProvider } from '@milkdown/plugin-tooltip'
77
import { editorViewCtx } from '@milkdown/core'
88
import { linkSchema } from '@milkdown/preset-commonmark'
99
import { posToDOMRect } from '@milkdown/prose'
10-
import { linkTooltipConfig, linkTooltipState } from '../slices'
11-
import { LinkEditElement } from './edit-component'
10+
import {
11+
linkTooltipConfig,
12+
linkTooltipState,
13+
type LinkTooltipConfig,
14+
} from '../slices'
15+
import { createApp, ref, type App, type Ref } from 'vue'
16+
import { EditLink } from './component'
1217

1318
interface Data {
1419
from: number
@@ -23,28 +28,43 @@ const defaultData: Data = {
2328
}
2429

2530
export class LinkEditTooltip implements PluginView {
26-
#content = new LinkEditElement()
31+
#content: HTMLElement
2732
#provider: TooltipProvider
2833
#data: Data = { ...defaultData }
34+
#app: App
35+
#config: Ref<LinkTooltipConfig>
36+
#src = ref('')
2937

3038
constructor(
3139
readonly ctx: Ctx,
3240
view: EditorView
3341
) {
42+
this.#config = ref(this.ctx.get(linkTooltipConfig.key))
43+
44+
const content = document.createElement('div')
45+
content.className = 'milkdown-link-edit'
46+
47+
const app = createApp(EditLink, {
48+
config: this.#config,
49+
src: this.#src,
50+
onConfirm: this.#confirmEdit,
51+
onCancel: this.#reset,
52+
})
53+
app.mount(content)
54+
this.#app = app
55+
56+
this.#content = content
3457
this.#provider = new TooltipProvider({
35-
content: this.#content,
58+
content,
3659
debounce: 0,
3760
shouldShow: () => false,
3861
})
3962
this.#provider.onHide = () => {
40-
this.#content.update().catch((e) => {
41-
throw e
63+
requestAnimationFrame(() => {
64+
view.dom.focus({ preventScroll: true })
4265
})
43-
view.dom.focus({ preventScroll: true })
4466
}
4567
this.#provider.update(view)
46-
this.#content.onConfirm = this.#confirmEdit
47-
this.#content.onCancel = this.#reset
4868
}
4969

5070
#reset = () => {
@@ -76,14 +96,13 @@ export class LinkEditTooltip implements PluginView {
7696

7797
#enterEditMode = (value: string, from: number, to: number) => {
7898
const config = this.ctx.get(linkTooltipConfig.key)
79-
this.#content.config = config
80-
this.#content.src = value
99+
this.#config.value = config
100+
this.#src.value = value
81101
this.ctx.update(linkTooltipState.key, (state) => ({
82102
...state,
83103
mode: 'edit' as const,
84104
}))
85105
const view = this.ctx.get(editorViewCtx)
86-
// this.#setRect(posToDOMRect(view, from, to))
87106
view.dispatch(
88107
view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to))
89108
)
@@ -106,6 +125,7 @@ export class LinkEditTooltip implements PluginView {
106125
}
107126

108127
destroy = () => {
128+
this.#app.unmount()
109129
this.#provider.destroy()
110130
this.#content.remove()
111131
}

0 commit comments

Comments
 (0)