import { Numbers } from "polar-shared/src/util/Numbers" ; import { arrayStream } from "polar-shared/src/util/ArrayStreams" ; export namespace NodeTextBoundingClientRects { interface INodeTextBoundingClientRect { readonly text : Text ; readonly start : number ; readonly end : number ; readonly left : number ; readonly top : number ; readonly width : number ; readonly height : number ; } export function compute ( element ) { const childNodes = Array . from ( element . childNodes ) ; const result = [ ] ; for ( const childNode of childNodes ) { switch ( childNode . nodeType ) { case Node . ELEMENT_NODE : result . push ( ... compute ( childNode as HTMLElement ) ) ; break ; case Node . TEXT_NODE : result . push ( ... computeForTextNode ( childNode as Text ) ) ; break ; } } return result ; } function computeNodeTextBoundingClientRect ( text , start , end ) { const range = document . createRange ( ) ; range . setStart ( text , start ) ; range . setEnd ( text , end ) ; const bcr = range . getBoundingClientRect ( ) ; return { text , start , end , left : bcr . left , top : bcr . top , width : bcr . width , height : bcr . height } ; } function computeForTextNode ( text ) { return Numbers . range ( 0 , text . length - 1 ) . map ( start => computeNodeTextBoundingClientRect ( text , start , start + 1 ) ) ; } export function computeNearest ( nodes , left , top ) { interface IDistance { readonly node : INodeTextBoundingClientRect ; readonly distance : number ; } function computeDistance ( node ) { const distance = Math . abs ( left - node . left ) + Math . abs ( top - node . top ) ; return { distance , node } ; } return arrayStream ( nodes ) . map ( computeDistance ) . sort ( ( a , b ) => a . distance - b . distance ) . first ( ) ?. node ; } }