@ -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 )
}
}