import { ContentBlock , DraftEditorCommand , DraftHandleValue , DraftInlineStyle , EditorProps , } from 'draft-js' ; import { CSSProperties , KeyboardEvent } from 'react' ; import { EditorCommand , EditorPlugin , PluginFunctions } from '..' ; type EditorKeys = keyof EditorPlugin ; type EditorHandleKeys = keyof Pick < EditorPlugin , 'handlePastedText' | 'handleBeforeInput' | 'handleDrop' | 'handleDroppedFiles' | 'handleKeyCommand' | 'handlePastedFiles' | 'handleReturn' > ; type EditorEventKeys = keyof Pick < EditorPlugin , 'onBlur' | 'onDownArrow' | 'onEscape' | 'onFocus' | 'onLeftArrow' | 'onRightArrow' | 'onTab' | 'onUpArrow' > ; type EditorFnKeys = keyof Pick < EditorPlugin , 'blockRendererFn' | 'blockStyleFn' | 'customStyleFn' | 'keyBindingFn' > ; function isEditorHandleKey ( key ) { return key . startsWith ( 'handle' ) ; } function isEditorEventKey ( key ) { return key . startsWith ( 'on' ) ; } function isEditorFnKey ( key ) { return key . endsWith ( 'Fn' ) ; } interface ResultBlock { props : Record < string , unknown > ; component ? : unknown ; } function blockRendererFnHook ( plugins , pluginMethods ) { return ( block ) : unknown => { let resultBlock = { props : { } } ; plugins . forEach ( ( plugin ) => { if ( typeof plugin . blockRendererFn !== 'function' ) { return ; } const result = plugin . blockRendererFn ( block , pluginMethods ) ; if ( result !== undefined && result !== null ) { const { props : pluginProps , ... pluginRest } = result ; const { props , ... rest } = resultBlock ; resultBlock = { ... rest , ... pluginRest , props : { ... props , ... pluginProps } , } ; } } ) ; return resultBlock . component ? resultBlock : false ; } ; } function blockStyleFnHook ( plugins , pluginMethods ) { return ( block ) : string => { const styles = [ ] ; plugins . forEach ( ( plugin ) => { if ( typeof plugin . blockStyleFn !== 'function' ) { return ; } const result = plugin . blockStyleFn ( block , pluginMethods ) ; if ( result !== undefined && result !== null ) { styles . push ( result ) ; } } ) ; return styles . join ( ' ' ) ; } ; } function customStyleFnHook ( plugins , pluginMethods ) { return ( style , block ) : CSSProperties => { let result ; const wasHandled = plugins . some ( ( plugin ) => { if ( typeof plugin . customStyleFn !== 'function' ) { return false ; } result = plugin . customStyleFn ( style , block , pluginMethods ) ; return result !== undefined ; } ) ; return wasHandled && result ? result : { } ; } ; } function keyBindingFnHook ( plugins , pluginMethods ) { return ( event ) : DraftEditorCommand | null => { let result = null ; const wasHandled = plugins . some ( ( plugin ) => { if ( typeof plugin . keyBindingFn !== 'function' ) { return false ; } result = plugin . keyBindingFn ( event , pluginMethods ) ; return result !== undefined ; } ) ; return wasHandled ? result : null ; } ; } function createHandleHooks < T extends EditorHandleKeys > ( methodName , plugins , pluginMethods ) { return ( ... args ) : DraftHandleValue => plugins . some ( ( plugin ) => { const fn = plugin [ methodName ] ; return ( typeof fn === 'function' && fn ( ... args , pluginMethods ) === 'handled' ) ; } ) ? 'handled' : 'not-handled' ; } function createEventHooks < T extends EditorEventKeys > ( methodName , plugins , pluginMethods ) { return ( ... args ) : boolean => plugins . some ( ( plugin ) => { const fn = plugin [ methodName ] ; return typeof fn === 'function' && fn ( ... args , pluginMethods ) === true ; } ) ; } export function createPluginHooks ( plugins , pluginFunction ) { const pluginHooks = { } ; const handledAttributes = new Set < EditorKeys > ( [ 'onChange' ] ) ; plugins . forEach ( ( plugin ) => { ( Object . keys ( plugin ) as EditorKeys [ ] ) . forEach ( ( attrName ) => { if ( handledAttributes . has ( attrName ) ) { return ; } handledAttributes . add ( attrName ) ; if ( isEditorEventKey ( attrName ) ) { pluginHooks [ attrName ] = createEventHooks ( attrName , plugins , pluginFunction ) ; } else if ( isEditorHandleKey ( attrName ) ) { pluginHooks [ attrName ] = createHandleHooks ( attrName , plugins , pluginFunction ) ; } else if ( isEditorFnKey ( attrName ) ) { if ( attrName === 'blockRendererFn' ) { pluginHooks . blockRendererFn = blockRendererFnHook ( plugins , pluginFunction ) ; } else if ( attrName === 'blockStyleFn' ) { pluginHooks . blockStyleFn = blockStyleFnHook ( plugins , pluginFunction ) ; } else if ( attrName === 'customStyleFn' ) { pluginHooks . customStyleFn = customStyleFnHook ( plugins , pluginFunction ) ; } else if ( attrName === 'keyBindingFn' ) { pluginHooks . keyBindingFn = keyBindingFnHook ( plugins , pluginFunction ) ; } } } ) ; } ) ; return pluginHooks ; }