import { BlockDescriptor } from './blocks' ; import { Document , applyTextTransformToSelection , buildEmptyDocument } from './document' ; import { SelectionShape , Selection } from '@delta/Selection' ; import Delta from 'quill-delta' ; import { ImageKind , GenericOp } from '@delta/operations' ; import { DocumentDelta } from '@delta/DocumentDelta' ; import { Attributes } from '@delta/attributes' ; import { Bridge } from '@core/Bridge' ; import { DocumentDeltaAtomicUpdate } from '@delta/DocumentDeltaAtomicUpdate' ; function elementToInsertion ( element , document ) { if ( element . type === 'text' ) { return [ element . content , document . selectedTextAttributes ] ; } const imageOpIns = { kind : 'image' , ... element . description } ; return [ imageOpIns ] ; } function getSelectionAfterTransform ( diff , document ) { const nextPosition = diff . transformPosition ( document . currentSelection . start ) ; return { start : nextPosition , end : nextPosition , } ; } export class Block { public readonly descriptor : BlockDescriptor ; private blocks : Block [ ] ; public get kind ( ) { return this . descriptor . kind ; } public constructor ( descriptor , blocks ) { this . descriptor = descriptor ; this . blocks = blocks ; } public isFirst ( ) { return this . descriptor . blockIndex === 0 ; } public isLast ( ) { return this . descriptor . blockIndex === this . descriptor . maxBlockIndex ; } private applyCursorTranslationToDocument ( position , document ) { const nextSelection = { start : position , end : position , } ; return { ... document , currentSelection : nextSelection , } ; } private applyDiffToDocument ( lastDiff , document ) { const current = new Delta ( document . ops ) ; const nextDelta = current . compose ( lastDiff ) ; const nextOps = nextDelta . ops ; const nextSelection = getSelectionAfterTransform ( lastDiff , document ) ; return { ... document , currentSelection : nextSelection , ops : nextOps , lastDiff : lastDiff . ops , } ; } param blockScopedDiff param document returns private applyBlockScopedDiff ( blockScopedDiff , document ) { return this . applyDiffToDocument ( new Delta ( ) . retain ( this . descriptor . selectableUnitsOffset ) . concat ( blockScopedDiff ) , document ) ; } private getPreviousBlock ( ) { if ( this . isFirst ( ) ) { return null ; } return this . blocks [ this . descriptor . blockIndex - 1 ] ; } private getNextBlock ( ) { if ( this . isLast ( ) ) { return null ; } return this . blocks [ this . descriptor . blockIndex + 1 ] ; } public getDocumentSelection ( blockScopedSelection ) { return { start : blockScopedSelection . start + this . descriptor . selectableUnitsOffset , end : blockScopedSelection . end + this . descriptor . selectableUnitsOffset , } ; } public getBlockScopedSelection ( documentSelection ) { const start = documentSelection . start - this . descriptor . selectableUnitsOffset ; const end = documentSelection . end - this . descriptor . selectableUnitsOffset ; if ( start < 0 || end > this . descriptor . numOfSelectableUnits ) { return null ; } return { start , end , } ; } public getSelectedOps ( document ) { const delta = new DocumentDelta ( document . ops ) ; return delta . getSelected ( Selection . fromShape ( document . currentSelection ) ) . ops ; } private shouldFocusOnLeftEdge ( ) { return this . kind === 'text' || this . descriptor . blockIndex === 0 ; } public isFocused ( { currentSelection } ) { const { selectableUnitsOffset , numOfSelectableUnits } = this . descriptor ; const lowerBoundary = selectableUnitsOffset ; const upperBoundary = selectableUnitsOffset + numOfSelectableUnits ; const nextBlock = this . getNextBlock ( ) ; const isCursor = currentSelection . end - currentSelection . start === 0 ; const isCursorTouchingRightEdge = isCursor && currentSelection . end === upperBoundary ; const isCursorTouchingLeftEdge = isCursor && currentSelection . start === lowerBoundary ; if ( isCursorTouchingRightEdge ) { return nextBlock == null || ! nextBlock . shouldFocusOnLeftEdge ( ) ; } if ( isCursorTouchingLeftEdge ) { return this . shouldFocusOnLeftEdge ( ) ; } return ( currentSelection . start >= lowerBoundary && currentSelection . start <= upperBoundary && currentSelection . end <= upperBoundary ) ; } public isEntirelySelected ( { currentSelection : { start , end } } ) { return ( start === this . descriptor . selectableUnitsOffset && end === this . descriptor . selectableUnitsOffset + this . descriptor . numOfSelectableUnits ) ; } public updateTextAttributesAtSelection ( document ) { const docDelta = new DocumentDelta ( document . ops ) ; const deltaAttributes = docDelta . getSelectedTextAttributes ( Selection . fromShape ( document . currentSelection ) ) ; return { ... document , selectedTextAttributes : deltaAttributes , } ; } public applyAtomicDeltaUpdate ( { diff , selectionAfterChange } , document ) { return { ... this . applyBlockScopedDiff ( diff , document ) , currentSelection : this . getDocumentSelection ( selectionAfterChange ) , } ; } public applyTextTransformToSelection ( attributeName , attributeValue , document ) { if ( this . kind !== 'text' ) { return document ; } return { ... document , ... applyTextTransformToSelection ( attributeName , attributeValue , document ) , } ; } remarks param element param document returns public insertOrReplaceAtSelection ( element , document ) { const deletionLength = document . currentSelection . end - document . currentSelection . start ; const diff = new Delta ( ) . retain ( document . currentSelection . start ) . delete ( deletionLength ) . insert ( ... elementToInsertion ( element , document ) ) ; return this . applyDiffToDocument ( diff , document ) ; } param document public remove ( document ) { if ( this . isFirst ( ) && this . isLast ( ) ) { return buildEmptyDocument ( ) ; } const diff = new Delta ( ) . retain ( this . descriptor . selectableUnitsOffset ) . delete ( this . descriptor . numOfSelectableUnits ) ; return this . applyDiffToDocument ( diff , document ) ; } public updateSelection ( blockScopedSelection , document ) { const nextSelection = { start : this . descriptor . selectableUnitsOffset + blockScopedSelection . start , end : this . descriptor . selectableUnitsOffset + blockScopedSelection . end , } ; return { ... document , currentSelection : nextSelection , } ; } param document returns public select ( document ) { const nextSelection = { start : this . descriptor . selectableUnitsOffset , end : this . descriptor . selectableUnitsOffset + this . descriptor . numOfSelectableUnits , } ; return { ... document , currentSelection : nextSelection , } ; } param document returns public removeOneBefore ( document ) { if ( this . isFirst ( ) ) { return document ; } const diff = new Delta ( ) . retain ( this . descriptor . selectableUnitsOffset - 1 ) . delete ( 1 ) ; const prevBlock = this . getPreviousBlock ( ) as Block ; if ( prevBlock . kind === 'image' ) { return prevBlock . select ( document ) ; } return this . applyDiffToDocument ( diff , document ) ; } public moveBefore ( document ) { if ( this . isFirst ( ) ) { return document ; } const positionBeforeBlock = this . descriptor . selectableUnitsOffset - 1 ; return this . applyCursorTranslationToDocument ( positionBeforeBlock , document ) ; } public moveAfter ( document ) { if ( this . isLast ( ) ) { return document ; } const positionAfterBlock = this . descriptor . selectableUnitsOffset + this . descriptor . numOfSelectableUnits ; return this . applyCursorTranslationToDocument ( positionAfterBlock , document ) ; } }