Browse Source

feat(editor): add editor readiness handling and visibility checks

- Introduced a new optional callback for editor readiness in the markdown editor configuration, allowing for better integration with the component lifecycle.
- Implemented a fallback timer to manage the editor's readiness state, ensuring it reflects visibility accurately.
- Enhanced the component to check for the editor's visibility before marking it as ready, improving user experience during initialization.

These changes enhance the responsiveness and reliability of the markdown editor, providing clearer feedback on its loading state.
main
npmrun 2 weeks ago
parent
commit
7495f3c5c6
  1. 47
      app/components/PostBodyMarkdownEditor.vue
  2. 4
      app/components/post-body-markdown-editor-vditor-config.ts
  3. BIN
      packages/drizzle-pkg/db.sqlite

47
app/components/PostBodyMarkdownEditor.vue

@ -17,6 +17,43 @@ const toast = useToast()
const mountEl = ref<HTMLElement | null>(null)
const isMobileViewport = ref(false)
const isEditorReady = ref(false)
let editorReadyFallbackTimer: ReturnType<typeof setTimeout> | null = null
let readinessCheckToken = 0
function clearEditorReadyFallbackTimer() {
if (editorReadyFallbackTimer) {
clearTimeout(editorReadyFallbackTimer)
editorReadyFallbackTimer = null
}
}
function startEditorReadyFallbackTimer() {
clearEditorReadyFallbackTimer()
editorReadyFallbackTimer = setTimeout(() => {
isEditorReady.value = true
editorReadyFallbackTimer = null
}, 3000)
}
async function markEditorReadyWhenVisible() {
const currentToken = ++readinessCheckToken
await new Promise<void>(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve())))
const deadline = Date.now() + 2000
while (Date.now() < deadline) {
if (currentToken !== readinessCheckToken) {
return
}
const host = mountEl.value
const vditorRoot = host?.querySelector('.vditor') as HTMLElement | null
const hasVisibleEditor = Boolean(vditorRoot && vditorRoot.offsetHeight > 0 && vditorRoot.offsetWidth > 0)
if (hasVisibleEditor) {
isEditorReady.value = true
clearEditorReadyFallbackTimer()
return
}
await new Promise<void>(resolve => setTimeout(resolve, 50))
}
}
const editorContainerStyle = computed(() => {
if (isMobileViewport.value) {
return {
@ -44,6 +81,9 @@ const bridge = createPostBodyMarkdownEditorBridge({
value,
isMobile: isMobileViewport.value,
onInput,
onEditorReady: () => {
void markEditorReadyWhenVisible()
},
onUploadError: () => {
toast.add({ title: '图片上传失败', color: 'warning' })
},
@ -74,6 +114,8 @@ onMounted(async () => {
if (!mountEl.value) {
return
}
isEditorReady.value = false
startEditorReadyFallbackTimer()
bridge.unmount()
bridge.mount(mountEl.value)
},
@ -89,8 +131,9 @@ onMounted(async () => {
onReady: (ctor) => {
vditorCtor.value = ctor
if (mountEl.value) {
isEditorReady.value = false
startEditorReadyFallbackTimer()
bridge.mount(mountEl.value)
isEditorReady.value = true
}
},
onError: (error) => {
@ -105,7 +148,9 @@ watch(() => props.modelValue, () => {
onBeforeUnmount(() => {
unmounted = true
readinessCheckToken += 1
isEditorReady.value = false
clearEditorReadyFallbackTimer()
if (viewportMql && onViewportChange) {
viewportMql.removeEventListener('change', onViewportChange)
}

4
app/components/post-body-markdown-editor-vditor-config.ts

@ -3,6 +3,7 @@ interface BuildVditorOptionsInput {
isMobile: boolean
onInput: (value: string) => void
onUploadError: () => void
onEditorReady?: () => void
}
const DESKTOP_TOOLBAR: ReadonlyArray<string> = [
@ -114,6 +115,9 @@ export function buildPostBodyMarkdownEditorVditorOptions(input: BuildVditorOptio
input(value: string) {
input.onInput(value)
},
after() {
input.onEditorReady?.()
},
}
}

BIN
packages/drizzle-pkg/db.sqlite

Binary file not shown.
Loading…
Cancel
Save