import * as React from 'react' ; import { Component , CSSProperties } from 'react' ; import { Cell , LinkVertex } from './elements' ; import { ElementLayer } from './elementLayer' ; import { Vector } from './geometry' ; import { LinkLayer , LinkMarkers } from './linkLayer' ; import { DiagramModel } from './model' ; import { DiagramView } from './view' ; export interface PaperProps { view : DiagramView ; paperTransform : PaperTransform ; onPointerDown ? : ( e : React . MouseEvent < HTMLElement > , cell : Cell | undefined ) => void ; group ? : string ; linkLayerWidgets ? : React . ReactElement < any > ; elementLayerWidgets ? : React . ReactElement < any > ; } const CLASS_NAME = 'ontodia-paper' ; export class Paper extends Component < PaperProps , { } > { render ( ) { const { view , group , paperTransform , linkLayerWidgets , elementLayerWidgets } = this . props ; const { width , height , originX , originY , scale , paddingX , paddingY } = paperTransform ; const scaledWidth = width * scale ; const scaledHeight = height * scale ; const style = { width : scaledWidth + paddingX , height : scaledHeight + paddingY , marginLeft : paddingX , marginTop : paddingY , paddingRight : paddingX , paddingBottom : paddingY , } ; const htmlTransformStyle = { position : 'absolute' , left : 0 , top : 0 , transform : `scale(${ scale },${ scale })translate(${ originX }px,${ originY }px)` , } ; return ( < div className = { CLASS_NAME } style = { style } onMouseDown = { this . onMouseDown } > < TransformedSvgCanvas className = { `${ CLASS_NAME }__canvas` } style = { { overflow : 'visible' } } paperTransform = { paperTransform } > < LinkMarkers view = { view } /> < LinkLayer view = { view } links = { view . model . links } group = { group } / > < /TransformedSvgCanvas> { linkLayerWidgets } < ElementLayer view = { view } group = { group } style = { htmlTransformStyle } / > { elementLayerWidgets } < /div>); } private onMouseDown = ( e ) => { const { view , onPointerDown } = this . props ; const cell = e . target instanceof Element ? findCell ( e . target , e . currentTarget , view . model ) : undefined ; if ( onPointerDown ) { onPointerDown ( e , cell ) ; } } ; } function findCell ( bottom , top , model ) { let target = bottom ; let vertexIndex ; while ( true ) { if ( target instanceof Element ) { if ( target . hasAttribute ( 'data-element-id' ) ) { return model . getElement ( target . getAttribute ( 'data-element-id' ) ) ; } else if ( target . hasAttribute ( 'data-link-id' ) ) { const link = model . getLinkById ( target . getAttribute ( 'data-link-id' ) ) ; return typeof vertexIndex === 'number' ? new LinkVertex ( link , vertexIndex ) : link ; } else if ( target . hasAttribute ( 'data-vertex' ) ) { vertexIndex = Number ( target . getAttribute ( 'data-vertex' ) ) ; } } if ( ! target || target === top ) { break ; } target = target . parentNode ; } return undefined ; } export interface PaperTransform { width : number ; height : number ; originX : number ; originY : number ; scale : number ; paddingX : number ; paddingY : number ; } export interface TransformedSvgCanvasProps extends React . HTMLProps < SVGSVGElement > { paperTransform : PaperTransform ; } export class TransformedSvgCanvas extends Component < TransformedSvgCanvasProps , { } > { private static readonly SVG_STYLE : CSSProperties = { position : 'absolute' , top : 0 , left : 0 , } ; render ( ) { const { paperTransform , style , children , ... otherProps } = this . props ; const { width , height , originX , originY , scale , paddingX , paddingY } = paperTransform ; const scaledWidth = width * scale ; const scaledHeight = height * scale ; let svgStyle = TransformedSvgCanvas . SVG_STYLE ; if ( style ) { svgStyle = { ... svgStyle , ... style } ; } return ( < svg width = { scaledWidth } height = { scaledHeight } style = { svgStyle } { ... otherProps } > < g transform = { `scale(${ scale },${ scale })translate(${ originX },${ originY })` } > { children } < /g> < /svg>); } } returns export function totalPaneSize ( pt ) { return { x : pt . width * pt . scale + pt . paddingX * 2 , y : pt . height * pt . scale + pt . paddingY * 2 , } ; } returns export function paneTopLeft ( pt ) { return { x : - pt . paddingX , y : - pt . paddingY } ; } export function paneFromPaperCoords ( paper , pt ) { return { x : ( paper . x + pt . originX ) * pt . scale , y : ( paper . y + pt . originY ) * pt . scale , } ; } export function paperFromPaneCoords ( pane , pt ) { return { x : pane . x / pt . scale - pt . originX , y : pane . y / pt . scale - pt . originY , } ; }