@prosekit/vue 0.6.8 → 0.7.0-beta.1
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosekit/vue",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0-beta.1",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Vue components and utilities for ProseKit",
|
|
7
7
|
"author": {
|
|
@@ -68,11 +68,11 @@
|
|
|
68
68
|
"src"
|
|
69
69
|
],
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@prosemirror-adapter/core": "^0.5.
|
|
72
|
-
"@prosemirror-adapter/vue": "^0.5.
|
|
73
|
-
"@prosekit/
|
|
71
|
+
"@prosemirror-adapter/core": "^0.5.3",
|
|
72
|
+
"@prosemirror-adapter/vue": "^0.5.3",
|
|
73
|
+
"@prosekit/core": "^0.12.0-beta.0",
|
|
74
74
|
"@prosekit/pm": "^0.1.15",
|
|
75
|
-
"@prosekit/
|
|
75
|
+
"@prosekit/web": "^0.8.0-beta.0"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"vue": ">= 3.0.0"
|
|
@@ -88,9 +88,11 @@
|
|
|
88
88
|
"tsdown": "^0.21.4",
|
|
89
89
|
"typescript": "~5.9.3",
|
|
90
90
|
"vitest": "^4.1.1",
|
|
91
|
+
"vitest-browser-vue": "^2.1.0",
|
|
91
92
|
"vue": "^3.5.30",
|
|
92
93
|
"@prosekit/config-ts": "0.0.0",
|
|
93
94
|
"@prosekit/config-vitest": "0.0.0",
|
|
95
|
+
"@prosekit/testing": "0.0.0",
|
|
94
96
|
"@prosekit/config-tsdown": "0.0.0"
|
|
95
97
|
},
|
|
96
98
|
"publishConfig": {
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createEditor, union, type NodeJSON } from '@prosekit/core'
|
|
2
|
+
import { defineTestExtension, type ImageAttrs } from '@prosekit/testing'
|
|
3
|
+
import { beforeEach, describe, expect, it } from 'vitest'
|
|
4
|
+
import { render } from 'vitest-browser-vue'
|
|
5
|
+
import { page } from 'vitest/browser'
|
|
6
|
+
import { computed, defineComponent, h, onMounted, onUnmounted } from 'vue'
|
|
7
|
+
|
|
8
|
+
import { ProseKit } from '../components/prosekit.ts'
|
|
9
|
+
|
|
10
|
+
import { defineVueNodeView, type VueNodeViewComponent, type VueNodeViewProps } from './vue-node-view.ts'
|
|
11
|
+
|
|
12
|
+
describe('VueNodeView', () => {
|
|
13
|
+
const initialState = {
|
|
14
|
+
imageRefresh: {
|
|
15
|
+
mounted: 0,
|
|
16
|
+
unmounted: 0,
|
|
17
|
+
setAttrs: 0,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let state = structuredClone(initialState)
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
state = structuredClone(initialState)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
function defineExtension() {
|
|
28
|
+
return union(
|
|
29
|
+
defineTestExtension(),
|
|
30
|
+
defineVueNodeView({
|
|
31
|
+
name: 'image',
|
|
32
|
+
component: ImageRefreshView as VueNodeViewComponent,
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ImageRefreshView = defineComponent<VueNodeViewProps>(
|
|
38
|
+
{
|
|
39
|
+
name: 'ImageRefreshView',
|
|
40
|
+
props: ['contentRef', 'view', 'getPos', 'setAttrs', 'node', 'selected', 'decorations', 'innerDecorations'],
|
|
41
|
+
setup(props: VueNodeViewProps) {
|
|
42
|
+
const attrs = computed(() => props.node.value.attrs as ImageAttrs)
|
|
43
|
+
const url = computed(() => attrs.value.src)
|
|
44
|
+
onMounted(() => {
|
|
45
|
+
state.imageRefresh.mounted++
|
|
46
|
+
const id = setInterval(() => {
|
|
47
|
+
state.imageRefresh.setAttrs++
|
|
48
|
+
props.setAttrs({ src: String(Math.random()) })
|
|
49
|
+
}, 50)
|
|
50
|
+
onUnmounted(() => {
|
|
51
|
+
state.imageRefresh.unmounted++
|
|
52
|
+
clearInterval(id)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
return () =>
|
|
56
|
+
h('div', {
|
|
57
|
+
'data-testid': 'image-refresh-view',
|
|
58
|
+
'data-url': url.value,
|
|
59
|
+
})
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const TestEditor = defineComponent<{ initialContent?: NodeJSON }>({
|
|
65
|
+
name: 'TestEditor',
|
|
66
|
+
props: ['initialContent'],
|
|
67
|
+
setup(props) {
|
|
68
|
+
const extension = defineExtension()
|
|
69
|
+
const editor = createEditor({
|
|
70
|
+
extension,
|
|
71
|
+
defaultContent: props.initialContent,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return () =>
|
|
75
|
+
h(ProseKit, { editor }, () =>
|
|
76
|
+
h('div', {
|
|
77
|
+
'data-testid': 'editor',
|
|
78
|
+
'ref': (el) => {
|
|
79
|
+
editor.mount(el as HTMLElement | null)
|
|
80
|
+
},
|
|
81
|
+
}))
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const paragraphJSON: NodeJSON = {
|
|
86
|
+
type: 'paragraph',
|
|
87
|
+
content: [{ type: 'text', text: 'Hello' }],
|
|
88
|
+
}
|
|
89
|
+
const imageRefreshJSON: NodeJSON = {
|
|
90
|
+
type: 'image',
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const editor = page.getByTestId('editor')
|
|
94
|
+
const imageRefresh = page.getByTestId('image-refresh-view')
|
|
95
|
+
|
|
96
|
+
it('can render a single self-update image node', async () => {
|
|
97
|
+
const initialContent: NodeJSON = {
|
|
98
|
+
type: 'doc',
|
|
99
|
+
content: [imageRefreshJSON, paragraphJSON],
|
|
100
|
+
}
|
|
101
|
+
const screen = await render(TestEditor, { props: { initialContent } })
|
|
102
|
+
await expect.element(editor).toBeVisible()
|
|
103
|
+
await expect.element(imageRefresh).toBeInTheDocument()
|
|
104
|
+
|
|
105
|
+
const urls = new Set<string>()
|
|
106
|
+
const check = () => {
|
|
107
|
+
imageRefresh.elements().forEach((element) => {
|
|
108
|
+
const url = element.getAttribute('data-url')
|
|
109
|
+
if (url) {
|
|
110
|
+
urls.add(url)
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
return urls.size >= 5
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await expect.poll(check, { interval: 50, timeout: 30_000 }).toBe(true)
|
|
117
|
+
|
|
118
|
+
await screen.unmount()
|
|
119
|
+
|
|
120
|
+
expect(state.imageRefresh.setAttrs).toBeGreaterThanOrEqual(5)
|
|
121
|
+
expect(state.imageRefresh.mounted).toBe(1)
|
|
122
|
+
expect(state.imageRefresh.unmounted).toBe(1)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('can render multiple self-update image nodes', async () => {
|
|
126
|
+
const initialContent: NodeJSON = {
|
|
127
|
+
type: 'doc',
|
|
128
|
+
content: [imageRefreshJSON, paragraphJSON, imageRefreshJSON, imageRefreshJSON],
|
|
129
|
+
}
|
|
130
|
+
const screen = await render(TestEditor, { props: { initialContent } })
|
|
131
|
+
await expect.element(editor).toBeVisible()
|
|
132
|
+
await expect.element(imageRefresh.nth(0)).toBeInTheDocument()
|
|
133
|
+
await expect.element(imageRefresh.nth(1)).toBeInTheDocument()
|
|
134
|
+
await expect.element(imageRefresh.nth(2)).toBeInTheDocument()
|
|
135
|
+
await expect.element(imageRefresh.nth(3)).not.toBeInTheDocument()
|
|
136
|
+
|
|
137
|
+
const urls = new Set<string>()
|
|
138
|
+
const check = () => {
|
|
139
|
+
imageRefresh.elements().forEach((element) => {
|
|
140
|
+
const url = element.getAttribute('data-url')
|
|
141
|
+
if (url) {
|
|
142
|
+
urls.add(url)
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
return urls.size >= 15
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await expect.poll(check, { interval: 50, timeout: 30_000 }).toBe(true)
|
|
149
|
+
|
|
150
|
+
await screen.unmount()
|
|
151
|
+
|
|
152
|
+
expect(state.imageRefresh.setAttrs).toBeGreaterThanOrEqual(15)
|
|
153
|
+
expect(state.imageRefresh.mounted).toBe(3)
|
|
154
|
+
expect(state.imageRefresh.unmounted).toBe(3)
|
|
155
|
+
})
|
|
156
|
+
})
|