import * as React from 'react'; import Srl from "../data/Srl"; import * as Styles from "./SrlVisualizerStyles"; import SrlLayout from "./SrlLayout/SrlLayout"; import SrlAbsolute from "./SrlAbsolutePosition/SrlAbsolute"; import TokenAbsolute from "./SrlAbsolutePosition/TokenAbsolute"; import LabelAbsolute from "./SrlAbsolutePosition/LabelAbsolute"; import BarAbsolute from "./SrlAbsolutePosition/BarAbsolute"; import Frame from "../data/Frame"; import ArrowAbsolute from "./SrlAbsolutePosition/ArrowAbsolute"; import DefaultInks from "./Inks"; import TokensSelection from "./Selection/TokensSelection"; import RoleSelection from "./Selection/RoleSelection"; import DragingSelection from "./Selection/DragingSelection"; import { LINE_HEIGHT, LABEL_FONT_SIZE, TOKEN_FONT_SIZE, TRIANGLE_SIZE, SELECTION_BG, TOKEN_FONT_FAMILY, LABEL_FONT_FAMILY } from './LayoutConf'; import measureHelper from "./measureHelper"; import {isRoleSelected, isTokenSelected} from "./Selection/isSelected"; import grey from "@material-ui/core/colors/grey"; export type Selection = null|TokensSelection|RoleSelection|DragingSelection; function getTokenAndHostRect(width:number, tokens:string[]) { const host = document.createElement('div'); host.style.width = width+'px'; host.style.fontSize = TOKEN_FONT_SIZE + 'px'; host.style.fontFamily = TOKEN_FONT_FAMILY; const tokenNodes = tokens.map(t=>{ const ret = document.createElement('span'); ret.style.whiteSpace = "nowrap"; ret.textContent = t; return ret; }); for(let i=0; i0) host.appendChild(document.createTextNode(" ")); host.appendChild(tokenNode); } measureHelper.appendChild(host); const ret = { hostRect: host.getBoundingClientRect(), tokenRects: tokenNodes.map(n=>n.getBoundingClientRect()) }; measureHelper.innerHTML = ""; return ret; } function getRoleRect(frames:Frame[]):Map { const names:string[] = []; for(const frame of frames) { for(const role of frame.roles) { names.push(role.name); } } const host = document.createElement('div'); host.style.fontSize = LABEL_FONT_SIZE+'px'; host.style.fontFamily = LABEL_FONT_FAMILY; const nodes = names.map(name=>{ const ret = document.createElement('span'); ret.style.whiteSpace = "nowrap"; ret.textContent = name; return ret; }); for(const node of nodes) { host.appendChild(node); host.appendChild(document.createTextNode(" ")); } measureHelper.appendChild(host); const ret = new Map(); for(let i=0; ivoid, onLayoutChange?: (layout:SrlAbsolute)=>void, width?: number; }; type State = { srlAbsolute: null|SrlAbsolute, width: null|number, hovered: Selection }; class SrlVisualizerNoSelection extends React.PureComponent { selection: Selection = null; constructor(props:Props) { super(props); let state:State = {width:null, srlAbsolute:null, hovered:null}; const width = this.props.width; if(width) { const {tokenRects, hostRect} = getTokenAndHostRect(width, props.srl.tokens); const roleRects = getRoleRect(props.srl.frames); const srlLayout = new SrlLayout(props.srl, tokenRects, roleRects,hostRect); const srlAbsolute = new SrlAbsolute(srlLayout, !props.noArrow, TOKEN_FONT_SIZE*LINE_HEIGHT, LABEL_FONT_SIZE*LINE_HEIGHT, width); state.srlAbsolute = srlAbsolute; this.handleLayoutChange(srlAbsolute); } this.state = state; } getWidth() { return SrlVisualizerNoSelection.getWidth(this.props, this.state); } static getWidth(props:Props, state:State) { if(typeof props.width !== 'undefined') return props.width; return state.width; } select(selection:Selection):void { this.selection = selection; if(this.props.onRequestSelectionChange) this.props.onRequestSelectionChange(selection); } handleLayoutChange(srlAbsolute:SrlAbsolute) { if(this.props.onLayoutChange) this.props.onLayoutChange(srlAbsolute); } componentWillReceiveProps(newProps:Props) { if(newProps.srl === this.props.srl && newProps.noArrow === this.props.noArrow) return; this.setState({width:null, srlAbsolute:null}); } componentWillUpdate(nextProps:Props, nextState:State) { const width = SrlVisualizerNoSelection.getWidth(nextProps, nextState); if(nextState.srlAbsolute == null && width != null) { const {tokenRects, hostRect} = getTokenAndHostRect(width, nextProps.srl.tokens); const roleRects = getRoleRect(nextProps.srl.frames); const srlLayout = new SrlLayout(nextProps.srl, tokenRects, roleRects,hostRect); const srlAbsolute = new SrlAbsolute(srlLayout, !nextProps.noArrow, TOKEN_FONT_SIZE*LINE_HEIGHT, LABEL_FONT_SIZE*LINE_HEIGHT, width); this.setState({srlAbsolute}); this.handleLayoutChange(srlAbsolute); } } handleRefToMeasureWidth = (node:HTMLDivElement) => { if(node == null) return; if(this.state.width != null) return; this.setState({width:node.offsetWidth}); }; render() { const width = this.getWidth(); if(width == null) { return
} if(this.state.srlAbsolute) return this.renderSvg(this.state.srlAbsolute); return null; } renderSvg(srlAbsolute:SrlAbsolute) { return {this.renderSvgTokens(srlAbsolute.tokens)} {this.renderSvgLabels(srlAbsolute.labels)} {this.renderSvgBars(srlAbsolute.bars)} {this.renderSvgArrowHeads(srlAbsolute.labels)} {this.renderSvgArrowTails(srlAbsolute.arrows)} } onWindowMouseUp = ()=>{ if(this.selection instanceof DragingSelection) { this.select(TokensSelection.fromDragingSelection(this.selection)); } }; onRootMouseUp = (e:React.MouseEvent)=>{ if(e.target instanceof Element && e.target.hasAttribute('data-haslistener')) return; this.select(null); }; componentWillMount() {window.addEventListener('mouseup',this.onWindowMouseUp)} // event listeners for drag-selection componentWillUnmount() { window.removeEventListener('mouseup',this.onWindowMouseUp)} renderSvgTokens(tokenAbsolutes:TokenAbsolute[]) { const tokenStrs:string[] = this.props.srl.tokens; return tokenAbsolutes.map(token=>{ const commonTextStyle = { x:token.x, y:token.y+token.h, fontSize:TOKEN_FONT_SIZE, fontFamily: TOKEN_FONT_FAMILY }; const rectFill = isTokenSelected(this.state.hovered, token.index, this.props.srl.frames)?grey[300]:"none"; // event listeners for drag-selection let evtListeners = { onMouseDown:()=>{this.select(new DragingSelection(token.index, token.index))}, onMouseOver:()=>{ this.setState({hovered:new TokensSelection(token.index, token.index+1)}); if(this.selection instanceof DragingSelection) { this.select(new DragingSelection(this.selection.anchor1, token.index)) } }, onMouseOut:()=>{ this.setState({hovered:null}); } }; return {tokenStrs[token.index]} }) } getFrameInk(frameIndex:number) { const inks = this.props.inks || DefaultInks; return inks[frameIndex % inks.length]; } renderSvgLabels(labelAbsolutes:LabelAbsolute[]) { const frames:Frame[] = this.props.srl.frames; return labelAbsolutes.map(label=>{ const key = `${label.frameIndex},${label.roleIndex},${label.labelIndex}`; const commonTextStyle = {x:label.x,y:label.y+label.h,fontSize:LABEL_FONT_SIZE,fontFamily:LABEL_FONT_FAMILY}; const textContent = frames[label.frameIndex].roles[label.roleIndex].name; const rectFill = isRoleSelected(this.state.hovered, label.frameIndex, label.roleIndex)?grey[300]:"none"; const eveListeners = { onMouseOver: () => { this.setState({hovered: new RoleSelection(label.frameIndex, label.roleIndex)}) }, onMouseOut: () => { this.setState({hovered: null}) } }; return this.select(new RoleSelection(label.frameIndex, label.roleIndex))} data-haslistener fill={this.getFrameInk(label.frameIndex)} {...commonTextStyle}>{textContent} }) } renderSvgBars(barAbsolutes:BarAbsolute[]) { return barAbsolutes.map(bar=>) } renderSvgArrowHeads(labelAbsolutes:LabelAbsolute[]) { const frames:Frame[] = this.props.srl.frames; return labelAbsolutes.map(label=>{ if(!frames[label.frameIndex].roles[label.roleIndex].isPredicate) return null; const key = `${label.frameIndex},${label.roleIndex},${label.labelIndex}`; const x = label.x + label.w/2-TRIANGLE_SIZE/2; const y = label.y; return }) } renderSvgArrowTails(arrowAbsolutes:ArrowAbsolute[]) { return arrowAbsolutes.map((arrow,i)=>{ const beginX = arrow.fromX; const y = arrow.bottomY + TRIANGLE_SIZE/2; const endX = arrow.toX; const top = y - arrow.height; return }); } } export default SrlVisualizerNoSelection;