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 mountEl = ref<HTMLElement | null>(null)
const isMobileViewport = ref(false) const isMobileViewport = ref(false)
const isEditorReady = 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(() => { const editorContainerStyle = computed(() => {
if (isMobileViewport.value) { if (isMobileViewport.value) {
return { return {
@ -44,6 +81,9 @@ const bridge = createPostBodyMarkdownEditorBridge({
value, value,
isMobile: isMobileViewport.value, isMobile: isMobileViewport.value,
onInput, onInput,
onEditorReady: () => {
void markEditorReadyWhenVisible()
},
onUploadError: () => { onUploadError: () => {
toast.add({ title: '图片上传失败', color: 'warning' }) toast.add({ title: '图片上传失败', color: 'warning' })
}, },
@ -74,6 +114,8 @@ onMounted(async () => {
if (!mountEl.value) { if (!mountEl.value) {
return return
} }
isEditorReady.value = false
startEditorReadyFallbackTimer()
bridge.unmount() bridge.unmount()
bridge.mount(mountEl.value) bridge.mount(mountEl.value)
}, },
@ -89,8 +131,9 @@ onMounted(async () => {
onReady: (ctor) => { onReady: (ctor) => {
vditorCtor.value = ctor vditorCtor.value = ctor
if (mountEl.value) { if (mountEl.value) {
isEditorReady.value = false
startEditorReadyFallbackTimer()
bridge.mount(mountEl.value) bridge.mount(mountEl.value)
isEditorReady.value = true
} }
}, },
onError: (error) => { onError: (error) => {
@ -105,7 +148,9 @@ watch(() => props.modelValue, () => {
onBeforeUnmount(() => { onBeforeUnmount(() => {
unmounted = true unmounted = true
readinessCheckToken += 1
isEditorReady.value = false isEditorReady.value = false
clearEditorReadyFallbackTimer()
if (viewportMql && onViewportChange) { if (viewportMql && onViewportChange) {
viewportMql.removeEventListener('change', onViewportChange) viewportMql.removeEventListener('change', onViewportChange)
} }

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

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

BIN
packages/drizzle-pkg/db.sqlite

Binary file not shown.
Loading…
Cancel
Save