@jvs-milkdown/crepe 1.2.16 → 1.2.17
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/lib/cjs/feature/block-edit/index.js +35 -16
- package/lib/cjs/feature/block-edit/index.js.map +1 -1
- package/lib/cjs/feature/toolbar/index.js +47 -16
- package/lib/cjs/feature/toolbar/index.js.map +1 -1
- package/lib/cjs/index.js +210 -39
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/feature/block-edit/index.js +36 -17
- package/lib/esm/feature/block-edit/index.js.map +1 -1
- package/lib/esm/feature/toolbar/index.js +48 -17
- package/lib/esm/feature/toolbar/index.js.map +1 -1
- package/lib/esm/index.js +211 -40
- package/lib/esm/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/feature/block-edit/menu/component.d.ts.map +1 -1
- package/lib/types/feature/fixed-toolbar/outline-panel.d.ts.map +1 -1
- package/lib/types/feature/toolbar/component.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/feature/block-edit/menu/component.tsx +41 -17
- package/src/feature/fixed-toolbar/outline-panel.tsx +219 -32
- package/src/feature/toolbar/component.tsx +19 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../../../src/feature/block-edit/menu/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;AAYhD,OAAO,EAOL,KAAK,GAAG,EAGT,MAAM,KAAK,CAAA;AAEZ,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,IAAI,CAAA;AAwBhD,KAAK,SAAS,GAAG;IACf,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACnB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,MAAM,CAAC,EAAE,sBAAsB,CAAA;IAC/B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../../../src/feature/block-edit/menu/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;AAYhD,OAAO,EAOL,KAAK,GAAG,EAGT,MAAM,KAAK,CAAA;AAEZ,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,IAAI,CAAA;AAwBhD,KAAK,SAAS,GAAG;IACf,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACnB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,MAAM,CAAC,EAAE,sBAAsB,CAAA;IAC/B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACvB,CAAA;AAED,eAAO,MAAM,IAAI,oWAoxCf,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outline-panel.d.ts","sourceRoot":"","sources":["../../../../src/feature/fixed-toolbar/outline-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;AAgBhD,eAAO,MAAM,YAAY;;cAEE,MAAM,GAAG;;;;;cAAT,MAAM,GAAG;;;+
|
|
1
|
+
{"version":3,"file":"outline-panel.d.ts","sourceRoot":"","sources":["../../../../src/feature/fixed-toolbar/outline-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;AAgBhD,eAAO,MAAM,YAAY;;cAEE,MAAM,GAAG;;;;;cAAT,MAAM,GAAG;;;+GA0oBlC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../../src/feature/toolbar/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../../src/feature/toolbar/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;AAgBhD,OAAO,EACL,KAAK,SAAS,EAGf,MAAM,+BAA+B,CAAA;AAUtC,OAAO,EAEL,KAAK,GAAG,EACR,KAAK,UAAU,EAQhB,MAAM,KAAK,CAAA;AAEZ,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,GAAG,CAAA;AAmG7C,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,GAAG,CAAA;IACR,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAClB,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;IAChC,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAeD,eAAO,MAAM,OAAO,0WA2+ElB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jvs-milkdown/crepe",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.17",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"crepe",
|
|
6
6
|
"editor",
|
|
@@ -107,9 +107,9 @@
|
|
|
107
107
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
108
108
|
"@codemirror/view": "^6.26.0",
|
|
109
109
|
"@floating-ui/dom": "^1.7.6",
|
|
110
|
-
"@jvs-milkdown/kit": "^1.2.
|
|
111
|
-
"@jvs-milkdown/prose": "^1.2.
|
|
112
|
-
"@jvs-milkdown/utils": "^1.2.
|
|
110
|
+
"@jvs-milkdown/kit": "^1.2.17",
|
|
111
|
+
"@jvs-milkdown/prose": "^1.2.17",
|
|
112
|
+
"@jvs-milkdown/utils": "^1.2.17",
|
|
113
113
|
"@types/lodash-es": "^4.17.12",
|
|
114
114
|
"clsx": "^2.0.0",
|
|
115
115
|
"codemirror": "^6.0.1",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Ctx } from '@jvs-milkdown/kit/ctx'
|
|
2
2
|
|
|
3
3
|
import { Icon } from '@jvs-milkdown/kit/component'
|
|
4
|
-
import { editorViewCtx, commandsCtx } from '@jvs-milkdown/kit/core'
|
|
4
|
+
import { editorViewCtx, commandsCtx, schemaCtx } from '@jvs-milkdown/kit/core'
|
|
5
5
|
import {
|
|
6
6
|
clearTextInCurrentBlockCommand,
|
|
7
7
|
addBlockTypeCommand,
|
|
@@ -729,21 +729,31 @@ export const Menu = defineComponent<MenuProps>({
|
|
|
729
729
|
let activeBgColor: string | null = null
|
|
730
730
|
|
|
731
731
|
if (node && node.nodeSize > 2) {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
732
|
+
const schema = ctx.get(schemaCtx)
|
|
733
|
+
const tcHasMark = schema.marks[textColorSchema.id]
|
|
734
|
+
const bcHasMark = schema.marks[bgColorSchema.id]
|
|
735
|
+
|
|
736
|
+
if (tcHasMark || bcHasMark) {
|
|
737
|
+
node.descendants((childNode: any) => {
|
|
738
|
+
if (childNode.isText) {
|
|
739
|
+
if (tcHasMark && !activeTextColor) {
|
|
740
|
+
const tcType = textColorSchema.type(ctx)
|
|
741
|
+
const tcMark = childNode.marks.find(
|
|
742
|
+
(m: any) => m.type === tcType
|
|
743
|
+
)
|
|
744
|
+
if (tcMark) activeTextColor = tcMark.attrs.color
|
|
745
|
+
}
|
|
746
|
+
if (bcHasMark && !activeBgColor) {
|
|
747
|
+
const bcType = bgColorSchema.type(ctx)
|
|
748
|
+
const bcMark = childNode.marks.find(
|
|
749
|
+
(m: any) => m.type === bcType
|
|
750
|
+
)
|
|
751
|
+
if (bcMark) activeBgColor = bcMark.attrs.color
|
|
752
|
+
}
|
|
743
753
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
}
|
|
754
|
+
return false
|
|
755
|
+
})
|
|
756
|
+
}
|
|
747
757
|
}
|
|
748
758
|
|
|
749
759
|
const setBlockColor = (colorValue: string | null, isBg: boolean) => {
|
|
@@ -752,6 +762,12 @@ export const Menu = defineComponent<MenuProps>({
|
|
|
752
762
|
const targetNode = tr.doc.nodeAt(pos)
|
|
753
763
|
if (!targetNode) return
|
|
754
764
|
|
|
765
|
+
const schema = ctx.get(schemaCtx)
|
|
766
|
+
const hasMark = isBg
|
|
767
|
+
? schema.marks[bgColorSchema.id]
|
|
768
|
+
: schema.marks[textColorSchema.id]
|
|
769
|
+
if (!hasMark) return
|
|
770
|
+
|
|
755
771
|
const markType = isBg
|
|
756
772
|
? bgColorSchema.type(ctx)
|
|
757
773
|
: textColorSchema.type(ctx)
|
|
@@ -779,11 +795,19 @@ export const Menu = defineComponent<MenuProps>({
|
|
|
779
795
|
const targetNode = tr.doc.nodeAt(pos)
|
|
780
796
|
if (!targetNode) return
|
|
781
797
|
|
|
798
|
+
const schema = ctx.get(schemaCtx)
|
|
799
|
+
const tcHasMark = schema.marks[textColorSchema.id]
|
|
800
|
+
const bcHasMark = schema.marks[bgColorSchema.id]
|
|
801
|
+
|
|
782
802
|
const start = pos + 1
|
|
783
803
|
const end = pos + targetNode.nodeSize - 1
|
|
784
804
|
|
|
785
|
-
|
|
786
|
-
|
|
805
|
+
if (tcHasMark) {
|
|
806
|
+
tr.removeMark(start, end, textColorSchema.type(ctx))
|
|
807
|
+
}
|
|
808
|
+
if (bcHasMark) {
|
|
809
|
+
tr.removeMark(start, end, bgColorSchema.type(ctx))
|
|
810
|
+
}
|
|
787
811
|
|
|
788
812
|
view.dispatch(tr)
|
|
789
813
|
showColorMenu.value = false
|
|
@@ -25,6 +25,12 @@ export const OutlinePanel = defineComponent({
|
|
|
25
25
|
const activeId = ref<string>('')
|
|
26
26
|
const collapsedIds = ref<Set<string>>(new Set())
|
|
27
27
|
let scrollLock = false
|
|
28
|
+
const clickedActiveId = ref<string | null>(null)
|
|
29
|
+
|
|
30
|
+
const clearClickedActive = () => {
|
|
31
|
+
if (scrollLock) return
|
|
32
|
+
clickedActiveId.value = null
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
const hasChildren = (index: number) => {
|
|
30
36
|
const current = items.value[index]
|
|
@@ -97,6 +103,11 @@ export const OutlinePanel = defineComponent({
|
|
|
97
103
|
const view = props.ctx.get(editorViewCtx)
|
|
98
104
|
if (!view || !view.dom) return
|
|
99
105
|
|
|
106
|
+
if (clickedActiveId.value) {
|
|
107
|
+
activeId.value = clickedActiveId.value
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
const headings = Array.from(
|
|
101
112
|
view.dom.querySelectorAll('h1, h2, h3, h4, h5, h6')
|
|
102
113
|
)
|
|
@@ -105,19 +116,61 @@ export const OutlinePanel = defineComponent({
|
|
|
105
116
|
return
|
|
106
117
|
}
|
|
107
118
|
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
// Find the actual scroll container
|
|
120
|
+
let scrollContainer: HTMLElement | null = view.dom.parentElement
|
|
121
|
+
while (scrollContainer && scrollContainer !== document.body) {
|
|
122
|
+
const style = window.getComputedStyle(scrollContainer)
|
|
123
|
+
const overflowY = style.overflowY
|
|
124
|
+
if (
|
|
125
|
+
(overflowY === 'auto' || overflowY === 'scroll') &&
|
|
126
|
+
scrollContainer.scrollHeight > scrollContainer.clientHeight
|
|
127
|
+
) {
|
|
128
|
+
break
|
|
129
|
+
}
|
|
130
|
+
scrollContainer = scrollContainer.parentElement
|
|
131
|
+
}
|
|
112
132
|
|
|
113
|
-
const
|
|
133
|
+
const isRootScroll =
|
|
134
|
+
!scrollContainer ||
|
|
135
|
+
scrollContainer === document.body ||
|
|
136
|
+
scrollContainer === document.documentElement
|
|
137
|
+
const actualScrollContainer = isRootScroll
|
|
138
|
+
? ((document.scrollingElement ||
|
|
139
|
+
document.documentElement ||
|
|
140
|
+
document.body) as HTMLElement)
|
|
141
|
+
: scrollContainer!
|
|
142
|
+
|
|
143
|
+
const rootNode = view.dom.getRootNode() as Document | ShadowRoot
|
|
144
|
+
let toolbar = rootNode.querySelector(
|
|
114
145
|
'.milkdown-fixed-toolbar'
|
|
115
146
|
) as HTMLElement | null
|
|
147
|
+
if (!toolbar) {
|
|
148
|
+
toolbar = document.querySelector(
|
|
149
|
+
'.milkdown-fixed-toolbar'
|
|
150
|
+
) as HTMLElement | null
|
|
151
|
+
}
|
|
116
152
|
const toolbarHeight = toolbar?.offsetHeight || 0
|
|
117
|
-
const containerRect =
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
const
|
|
153
|
+
const containerRect = actualScrollContainer.getBoundingClientRect()
|
|
154
|
+
// Use Math.max(0, containerRect.top) because when container scrolls off-screen,
|
|
155
|
+
// its visual top boundary is the top of the viewport (0)
|
|
156
|
+
const containerTop = isRootScroll ? 0 : Math.max(0, containerRect.top)
|
|
157
|
+
|
|
158
|
+
let scrollTop = actualScrollContainer.scrollTop
|
|
159
|
+
let clientHeight = actualScrollContainer.clientHeight
|
|
160
|
+
let scrollHeight = actualScrollContainer.scrollHeight
|
|
161
|
+
|
|
162
|
+
if (isRootScroll) {
|
|
163
|
+
scrollTop = window.scrollY || document.documentElement.scrollTop
|
|
164
|
+
clientHeight =
|
|
165
|
+
window.innerHeight || document.documentElement.clientHeight
|
|
166
|
+
scrollHeight =
|
|
167
|
+
document.documentElement.scrollHeight || document.body.scrollHeight
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 15
|
|
171
|
+
const threshold = isAtBottom
|
|
172
|
+
? containerTop + clientHeight
|
|
173
|
+
: containerTop + toolbarHeight + 50
|
|
121
174
|
|
|
122
175
|
let active = headings[0]
|
|
123
176
|
for (const h of headings as HTMLElement[]) {
|
|
@@ -128,57 +181,158 @@ export const OutlinePanel = defineComponent({
|
|
|
128
181
|
break
|
|
129
182
|
}
|
|
130
183
|
}
|
|
131
|
-
|
|
184
|
+
|
|
185
|
+
// Compare DOM node references directly to identify active outline item without relying on ID
|
|
186
|
+
const activeItem = items.value.find((item) => {
|
|
187
|
+
try {
|
|
188
|
+
const activePos = view.posAtDOM(active, 0)
|
|
189
|
+
const isPosMatch = item.pos === activePos - 1
|
|
190
|
+
|
|
191
|
+
let isDepthMatch = false
|
|
192
|
+
const $pos = view.state.doc.resolve(activePos)
|
|
193
|
+
for (let d = $pos.depth; d >= 0; d--) {
|
|
194
|
+
if ($pos.node(d).type.name === 'heading') {
|
|
195
|
+
if (item.pos === $pos.before(d)) {
|
|
196
|
+
isDepthMatch = true
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (isPosMatch || isDepthMatch) {
|
|
203
|
+
return true
|
|
204
|
+
}
|
|
205
|
+
} catch (e) {
|
|
206
|
+
// Silent catch
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const dom = view.nodeDOM(item.pos)
|
|
210
|
+
const domMatch =
|
|
211
|
+
dom === active ||
|
|
212
|
+
(dom instanceof HTMLElement && active && dom.contains(active as Node))
|
|
213
|
+
if (domMatch) {
|
|
214
|
+
return true
|
|
215
|
+
}
|
|
216
|
+
return false
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const newActiveId = activeItem ? activeItem.id : (active as any)?.id || ''
|
|
220
|
+
activeId.value = newActiveId
|
|
132
221
|
}
|
|
133
222
|
|
|
134
|
-
const scrollToHeading = (
|
|
223
|
+
const scrollToHeading = (item: OutlineItem) => {
|
|
135
224
|
const view = props.ctx.get(editorViewCtx)
|
|
136
225
|
if (!view) return
|
|
137
226
|
|
|
138
227
|
try {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
228
|
+
let headingEl = view.nodeDOM(item.pos) as HTMLElement | null
|
|
229
|
+
if (!headingEl) {
|
|
230
|
+
try {
|
|
231
|
+
const domAt = view.domAtPos(item.pos)
|
|
232
|
+
let node = domAt.node
|
|
233
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
234
|
+
node = node.parentElement!
|
|
235
|
+
}
|
|
236
|
+
if (node instanceof HTMLElement) {
|
|
237
|
+
headingEl = node.closest(
|
|
238
|
+
'h1, h2, h3, h4, h5, h6'
|
|
239
|
+
) as HTMLElement | null
|
|
240
|
+
if (!headingEl && domAt.offset < node.childNodes.length) {
|
|
241
|
+
const child = node.childNodes[domAt.offset]
|
|
242
|
+
if (child instanceof HTMLElement) {
|
|
243
|
+
headingEl = (child.closest('h1, h2, h3, h4, h5, h6') ||
|
|
244
|
+
child.querySelector(
|
|
245
|
+
'h1, h2, h3, h4, h5, h6'
|
|
246
|
+
)) as HTMLElement | null
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch {}
|
|
251
|
+
}
|
|
252
|
+
if (!headingEl) {
|
|
253
|
+
headingEl = view.dom.querySelector(
|
|
254
|
+
`[id="${item.id}"]`
|
|
255
|
+
) as HTMLElement | null
|
|
256
|
+
}
|
|
142
257
|
if (!headingEl) return
|
|
143
258
|
|
|
144
|
-
|
|
145
|
-
|
|
259
|
+
// Find the actual scroll container
|
|
260
|
+
let scrollContainer: HTMLElement | null = view.dom.parentElement
|
|
261
|
+
while (scrollContainer && scrollContainer !== document.body) {
|
|
262
|
+
const style = window.getComputedStyle(scrollContainer)
|
|
263
|
+
const overflowY = style.overflowY
|
|
264
|
+
if (
|
|
265
|
+
(overflowY === 'auto' || overflowY === 'scroll') &&
|
|
266
|
+
scrollContainer.scrollHeight > scrollContainer.clientHeight
|
|
267
|
+
) {
|
|
268
|
+
break
|
|
269
|
+
}
|
|
270
|
+
scrollContainer = scrollContainer.parentElement
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const isRootScroll =
|
|
274
|
+
!scrollContainer ||
|
|
275
|
+
scrollContainer === document.body ||
|
|
276
|
+
scrollContainer === document.documentElement
|
|
277
|
+
const actualScrollContainer = isRootScroll
|
|
278
|
+
? ((document.scrollingElement ||
|
|
279
|
+
document.documentElement ||
|
|
280
|
+
document.body) as HTMLElement)
|
|
281
|
+
: scrollContainer!
|
|
146
282
|
|
|
147
283
|
scrollLock = true
|
|
148
284
|
|
|
149
|
-
const
|
|
285
|
+
const rootNode = view.dom.getRootNode() as Document | ShadowRoot
|
|
286
|
+
let toolbar = rootNode.querySelector(
|
|
150
287
|
'.milkdown-fixed-toolbar'
|
|
151
288
|
) as HTMLElement | null
|
|
289
|
+
if (!toolbar) {
|
|
290
|
+
toolbar = document.querySelector(
|
|
291
|
+
'.milkdown-fixed-toolbar'
|
|
292
|
+
) as HTMLElement | null
|
|
293
|
+
}
|
|
152
294
|
const toolbarHeight = toolbar?.offsetHeight || 0
|
|
153
|
-
const
|
|
295
|
+
const containerRectTop = isRootScroll
|
|
296
|
+
? 0
|
|
297
|
+
: actualScrollContainer.getBoundingClientRect().top
|
|
154
298
|
const headingRect = headingEl.getBoundingClientRect()
|
|
155
299
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
20,
|
|
163
|
-
behavior: 'smooth',
|
|
164
|
-
})
|
|
300
|
+
const targetTop =
|
|
301
|
+
actualScrollContainer.scrollTop +
|
|
302
|
+
headingRect.top -
|
|
303
|
+
containerRectTop -
|
|
304
|
+
toolbarHeight -
|
|
305
|
+
20
|
|
165
306
|
|
|
307
|
+
const scrollTarget = isRootScroll ? window : actualScrollContainer
|
|
166
308
|
let timer: ReturnType<typeof setTimeout>
|
|
167
309
|
const onScrollEnd = () => {
|
|
168
310
|
clearTimeout(timer)
|
|
169
311
|
timer = setTimeout(() => {
|
|
170
312
|
scrollLock = false
|
|
171
|
-
|
|
313
|
+
scrollTarget.removeEventListener('scroll', onScrollEnd)
|
|
172
314
|
checkActive()
|
|
173
315
|
}, 150)
|
|
174
316
|
}
|
|
175
|
-
|
|
317
|
+
scrollTarget.addEventListener('scroll', onScrollEnd)
|
|
176
318
|
|
|
177
319
|
// Safety timeout in case scroll events are not fired
|
|
178
320
|
setTimeout(() => {
|
|
179
321
|
scrollLock = false
|
|
180
|
-
|
|
322
|
+
scrollTarget.removeEventListener('scroll', onScrollEnd)
|
|
181
323
|
}, 2000)
|
|
324
|
+
|
|
325
|
+
if (isRootScroll) {
|
|
326
|
+
window.scrollTo({
|
|
327
|
+
top: targetTop,
|
|
328
|
+
behavior: 'smooth',
|
|
329
|
+
})
|
|
330
|
+
} else {
|
|
331
|
+
actualScrollContainer.scrollTo({
|
|
332
|
+
top: targetTop,
|
|
333
|
+
behavior: 'smooth',
|
|
334
|
+
})
|
|
335
|
+
}
|
|
182
336
|
} catch {
|
|
183
337
|
scrollLock = false
|
|
184
338
|
updateOutline()
|
|
@@ -192,6 +346,31 @@ export const OutlinePanel = defineComponent({
|
|
|
192
346
|
onMounted(() => {
|
|
193
347
|
updateOutline()
|
|
194
348
|
checkActive()
|
|
349
|
+
|
|
350
|
+
window.addEventListener('wheel', clearClickedActive, { passive: true })
|
|
351
|
+
window.addEventListener('touchmove', clearClickedActive, {
|
|
352
|
+
passive: true,
|
|
353
|
+
})
|
|
354
|
+
window.addEventListener('pointerdown', clearClickedActive, {
|
|
355
|
+
passive: true,
|
|
356
|
+
})
|
|
357
|
+
const keyHandler = (e: KeyboardEvent) => {
|
|
358
|
+
if (
|
|
359
|
+
[
|
|
360
|
+
'ArrowUp',
|
|
361
|
+
'ArrowDown',
|
|
362
|
+
'PageUp',
|
|
363
|
+
'PageDown',
|
|
364
|
+
'Home',
|
|
365
|
+
'End',
|
|
366
|
+
].includes(e.key)
|
|
367
|
+
) {
|
|
368
|
+
clearClickedActive()
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
window.addEventListener('keydown', keyHandler, { passive: true })
|
|
372
|
+
;(onMounted as any)._keyHandler = keyHandler
|
|
373
|
+
|
|
195
374
|
interval = setInterval(() => {
|
|
196
375
|
if (viewState.value.outlineVisible) {
|
|
197
376
|
updateOutline()
|
|
@@ -212,6 +391,13 @@ export const OutlinePanel = defineComponent({
|
|
|
212
391
|
if (interval) clearInterval(interval)
|
|
213
392
|
if (pollInterval) clearInterval(pollInterval)
|
|
214
393
|
window.removeEventListener('scroll', checkActive, true)
|
|
394
|
+
window.removeEventListener('wheel', clearClickedActive)
|
|
395
|
+
window.removeEventListener('touchmove', clearClickedActive)
|
|
396
|
+
window.removeEventListener('pointerdown', clearClickedActive)
|
|
397
|
+
const keyHandler = (onMounted as any)._keyHandler
|
|
398
|
+
if (keyHandler) {
|
|
399
|
+
window.removeEventListener('keydown', keyHandler)
|
|
400
|
+
}
|
|
215
401
|
})
|
|
216
402
|
|
|
217
403
|
// Drag to resize logic
|
|
@@ -359,7 +545,8 @@ export const OutlinePanel = defineComponent({
|
|
|
359
545
|
key={item.id}
|
|
360
546
|
onClick={() => {
|
|
361
547
|
activeId.value = item.id
|
|
362
|
-
|
|
548
|
+
clickedActiveId.value = item.id
|
|
549
|
+
scrollToHeading(item)
|
|
363
550
|
}}
|
|
364
551
|
style={{
|
|
365
552
|
display: 'flex',
|
|
@@ -429,7 +616,7 @@ export const OutlinePanel = defineComponent({
|
|
|
429
616
|
'transparent'
|
|
430
617
|
}}
|
|
431
618
|
>
|
|
432
|
-
|
|
619
|
+
{'\u25BC'}{' '}
|
|
433
620
|
</span>
|
|
434
621
|
{/* Text - click to scroll */}
|
|
435
622
|
<span
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
EditorStatus,
|
|
8
8
|
editorViewCtx,
|
|
9
9
|
commandsCtx,
|
|
10
|
+
schemaCtx,
|
|
10
11
|
} from '@jvs-milkdown/kit/core'
|
|
11
12
|
import {
|
|
12
13
|
addBlockTypeCommand,
|
|
@@ -1094,6 +1095,12 @@ export const Toolbar = defineComponent<ToolbarProps>({
|
|
|
1094
1095
|
|
|
1095
1096
|
const view = ctx.get(editorViewCtx)
|
|
1096
1097
|
const { state } = view
|
|
1098
|
+
const schema = ctx.get(schemaCtx)
|
|
1099
|
+
|
|
1100
|
+
const tcHasMark = schema.marks[textColorSchema.id]
|
|
1101
|
+
const bcHasMark = schema.marks[bgColorSchema.id]
|
|
1102
|
+
|
|
1103
|
+
if (!tcHasMark || !bcHasMark) return { textColor: null, bgColor: null }
|
|
1097
1104
|
|
|
1098
1105
|
const tcType = textColorSchema.type(ctx)
|
|
1099
1106
|
const bcType = bgColorSchema.type(ctx)
|
|
@@ -1147,6 +1154,12 @@ export const Toolbar = defineComponent<ToolbarProps>({
|
|
|
1147
1154
|
const { state, dispatch } = view
|
|
1148
1155
|
const { tr } = state
|
|
1149
1156
|
const { from, to, empty } = state.selection
|
|
1157
|
+
const schema = ctx.get(schemaCtx)
|
|
1158
|
+
|
|
1159
|
+
const tcHasMark = schema.marks[textColorSchema.id]
|
|
1160
|
+
const bcHasMark = schema.marks[bgColorSchema.id]
|
|
1161
|
+
|
|
1162
|
+
if (!tcHasMark || !bcHasMark) return
|
|
1150
1163
|
|
|
1151
1164
|
const textColorType = textColorSchema.type(ctx)
|
|
1152
1165
|
const bgColorType = bgColorSchema.type(ctx)
|
|
@@ -1173,6 +1186,12 @@ export const Toolbar = defineComponent<ToolbarProps>({
|
|
|
1173
1186
|
|
|
1174
1187
|
const view = ctx.get(editorViewCtx)
|
|
1175
1188
|
const { state } = view
|
|
1189
|
+
const schema = ctx.get(schemaCtx)
|
|
1190
|
+
|
|
1191
|
+
const ffHasMark = schema.marks[fontFamilySchema.id]
|
|
1192
|
+
const fsHasMark = schema.marks[fontSizeSchema.id]
|
|
1193
|
+
|
|
1194
|
+
if (!ffHasMark || !fsHasMark) return { fontFamily: null, fontSize: null }
|
|
1176
1195
|
|
|
1177
1196
|
const ffType = fontFamilySchema.type(ctx)
|
|
1178
1197
|
const fsType = fontSizeSchema.type(ctx)
|