import { command , CommandFunction , CreateExtensionPlugin , extension , ExtensionPriority , getTextSelection , Helper , helper , isEmptyObject , OnSetOptionsProps , PlainExtension , PrimitiveSelection , within , } from '@remirror/core' ; import type { EditorState } from '@remirror/pm/state' ; import { ActionType , UpdateAnnotationAction } from './annotation-actions' ; import { AnnotationState } from './annotation-plugin' ; import type { Annotation , AnnotationOptions , GetData , OmitText } from './annotation-types' ; /**
 * Computes a background color based on how many overlapping annotations are in
 * a segment. The more annotations, the darker the background. This gives the
 * illusion that annotations are above each other.
 */ function defaultGetStyle < A extends Annotation > ( annotations ) { const backgroundShade = Math . min ( annotations . length , 5 ) / 5 ; const notBlue = 200 * ( 1 - backgroundShade ) + 55 ; return `background: rgb(${ notBlue }, ${ notBlue }, 255);` ; } /**
 * This extension allows to annotate the content in your editor.
 *
 * Extend the Annotation interface to store application specific information
 * like tags or color.
 */ @ extension < AnnotationOptions > ( { defaultOptions : { getStyle : defaultGetStyle , blockSeparator : undefined , getMap : ( ) => new Map ( ) , transformPosition : ( pos ) => pos , transformPositionBeforeRender : ( pos ) => pos , } , defaultPriority : ExtensionPriority . Low , } ) export class AnnotationExtension < Type extends Annotation = Annotation > extends PlainExtension < AnnotationOptions < Type > > { get name ( ) { return 'annotation' as const ; } protected onSetOptions ( props ) { const { pickChanged } = props ; const changedPluginOptions = pickChanged ( [ 'getMap' , 'transformPosition' , 'transformPositionBeforeRender' , ] ) ; if ( ! isEmptyObject ( changedPluginOptions ) ) { this . store . updateExtensionPlugins ( this ) ; } } /**
     * Create the custom code block plugin which handles the delete key amongst
     * other things.
     */ createPlugin ( ) { const pluginState = new AnnotationState < Type > ( this . options . getStyle , this . options . getMap ( ) , this . options . transformPosition , this . options . transformPositionBeforeRender ) ; return { state : { init ( ) { return pluginState ; } , apply ( tr ) { const action = tr . getMeta ( AnnotationExtension . name ) ; return pluginState . apply ( { tr , action } ) ; } , } , props : { decorations ( state ) { return this . getState ( state ) . decorationSet ; } , } , } ; } param annotationData @ command ( ) addAnnotation ( annotationData ) { return ( { tr , dispatch } ) => { const { empty , from , to } = tr . selection ; if ( empty ) { return false ; } dispatch ?. ( tr . setMeta ( AnnotationExtension . name , { type : ActionType . ADD_ANNOTATION , from , to , annotationData , } ) ) ; return true ; } ; } param id param annotationDataWithoutId @ command ( ) updateAnnotation ( id , annotationDataWithoutId ) { return ( { tr , dispatch } ) => { if ( dispatch ) { const annotationData = { ... annotationDataWithoutId , id , } as GetData < Type > ; const action = { type : ActionType . UPDATE_ANNOTATION , annotationId : id , annotationData , } ; dispatch ( tr . setMeta ( AnnotationExtension . name , action ) ) ; } return true ; } ; } param annotationIds @ command ( ) removeAnnotations ( annotationIds ) { return ( { tr , dispatch } ) => { dispatch ?. ( tr . setMeta ( AnnotationExtension . name , { type : ActionType . REMOVE_ANNOTATIONS , annotationIds , } ) ) ; return true ; } ; } param annotations @ command ( ) setAnnotations ( annotations ) { return ( { tr , dispatch } ) => { dispatch ?. ( tr . setMeta ( AnnotationExtension . name , { type : ActionType . SET_ANNOTATIONS , annotations } ) ) ; return true ; } ; } see https @ command ( ) redrawAnnotations ( ) { return ( { tr , dispatch } ) => { dispatch ?. ( tr . setMeta ( AnnotationExtension . name , { type : ActionType . REDRAW_ANNOTATIONS } ) ) ; return true ; } ; } returns @ helper ( ) getAnnotations ( ) { const state = this . getPluginState ( ) ; return state . annotations . map ( this . enrichText ) ; } param pos param includeEdges returns @ helper ( ) getAnnotationsAt ( pos ? , includeEdges = true ) { const annotations = [ ] ; const { doc , selection } = this . store . getState ( ) ; const state = this . getPluginState ( ) ; const { from , to } = getTextSelection ( pos ?? selection , doc ) ; for ( const annotation of state . annotations ) { if ( within ( from , annotation . from , annotation . to ) || within ( to , annotation . from , annotation . to ) || within ( annotation . from , from , to ) || within ( annotation . to , from , to ) ) { if ( includeEdges ) { annotations . push ( this . enrichText ( annotation ) ) ; } else if ( annotation . from !== from && annotation . to !== to ) { annotations . push ( this . enrichText ( annotation ) ) ; } } } return annotations ; } param pos returns @ helper ( ) selectionHasAnnotation ( pos ? ) { return this . getAnnotationsAt ( pos ) . length > 0 ; } /**
     * Enrich text at annotation
     */ private readonly enrichText = ( annotation ) : Type => { const { doc } = this . store . getState ( ) ; const text = annotation . to <= doc . content . size ? doc . textBetween ( annotation . from , annotation . to , this . options . blockSeparator ) : undefined ; return { ... annotation , text , } as Type ; } ; } declare global { namespace Remirror { interface AllExtensions { annotation : AnnotationExtension ; } } }