diff options
Diffstat (limited to 'src/client/views/nodes/LinkBox.tsx')
-rw-r--r-- | src/client/views/nodes/LinkBox.tsx | 146 |
1 files changed, 87 insertions, 59 deletions
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 38ff21209..8b6293806 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,19 +1,18 @@ -import React = require('react'); import { Bezier } from 'bezier-js'; -import { computed, action } from 'mobx'; +import { computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { Height, Width } from '../../../fields/DocSymbols'; +import * as React from 'react'; import { Id } from '../../../fields/FieldSymbols'; import { DocCast, NumCast, StrCast } from '../../../fields/Types'; import { aggregateBounds, emptyFunction, returnAlways, returnFalse, Utils } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; +import { Transform } from '../../util/Transform'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { ViewBoxBaseComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { ComparisonBox } from './ComparisonBox'; import { FieldView, FieldViewProps } from './FieldView'; import './LinkBox.scss'; -import { CollectionFreeFormView } from '../collections/collectionFreeForm'; -import { Transform } from '../../util/Transform'; @observer export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { @@ -21,26 +20,28 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { return FieldView.LayoutString(LinkBox, fieldKey); } - onClickScriptDisable = returnAlways; - componentDidMount() { - this.props.setContentView?.(this); + constructor(props: FieldViewProps) { + super(props); + makeObservable(this); } + + onClickScriptDisable = returnAlways; @computed get anchor1() { - const anchor1 = DocCast(this.rootDoc.link_anchor_1); + const anchor1 = DocCast(this.dataDoc.link_anchor_1); const anchor_1 = anchor1?.layout_unrendered ? DocCast(anchor1.annotationOn) : anchor1; - return DocumentManager.Instance.getDocumentView(anchor_1, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement()); + return DocumentManager.Instance.getDocumentView(anchor_1, this.DocumentView?.().containerViewPath?.().lastElement()); } @computed get anchor2() { - const anchor2 = DocCast(this.rootDoc.link_anchor_2); + const anchor2 = DocCast(this.dataDoc.link_anchor_2); const anchor_2 = anchor2?.layout_unrendered ? DocCast(anchor2.annotationOn) : anchor2; - return DocumentManager.Instance.getDocumentView(anchor_2, this.props.docViewPath()[this.props.docViewPath().length - 2]); // this.props.docViewPath().lastElement()); + return DocumentManager.Instance.getDocumentView(anchor_2, this.DocumentView?.().containerViewPath?.().lastElement()); } screenBounds = () => { - if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { - const a_invXf = this.anchor1.props.ScreenToLocalTransform().inverse(); - const b_invXf = this.anchor2.props.ScreenToLocalTransform().inverse(); - const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(this.anchor1.rootDoc[Width](), this.anchor1.rootDoc[Height]()) }; - const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(this.anchor2.rootDoc[Width](), this.anchor2.rootDoc[Height]()) }; + if (this.layoutDoc._layout_isSvg && this.anchor1 && this.anchor2 && this.anchor1.CollectionFreeFormView) { + const a_invXf = this.anchor1.screenToViewTransform().inverse(); + const b_invXf = this.anchor2.screenToViewTransform().inverse(); + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(this.anchor1.Document._width), NumCast(this.anchor1.Document._height)) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(this.anchor2.Document._width), NumCast(this.anchor2.Document._height)) }; const pts = [] as number[][]; pts.push([(a_scrBds.tl[0] + a_scrBds.br[0]) / 2, (a_scrBds.tl[1] + a_scrBds.br[1]) / 2]); @@ -54,53 +55,81 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { ); return { left: agg.x, top: agg.y, right: agg.r, bottom: agg.b, center: undefined }; } - return { left: 0, top: 0, right: 0, bottom: 0, center: undefined }; + return undefined; }; - render() { - if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) { - const a = (this.anchor1 ?? this.anchor2)!; - const b = (this.anchor2 ?? this.anchor1)!; - - const parxf = this.props.docViewPath()[this.props.docViewPath().length - 2].ComponentView as CollectionFreeFormView; - const this_xf = parxf?.getTransform() ?? Transform.Identity; //this.props.ScreenToLocalTransform(); - const a_invXf = a.props.ScreenToLocalTransform().inverse(); - const b_invXf = b.props.ScreenToLocalTransform().inverse(); - const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(a.rootDoc[Width](), a.rootDoc[Height]()) }; - const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(b.rootDoc[Width](), b.rootDoc[Height]()) }; - const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) }; - const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) }; + disposer: IReactionDisposer | undefined; + componentDidMount() { + this._props.setContentViewBox?.(this); + this.disposer = reaction( + () => { + if (this.layoutDoc._layout_isSvg && (this.anchor1 || this.anchor2)?.CollectionFreeFormView) { + const a = (this.anchor1 ?? this.anchor2)!; + const b = (this.anchor2 ?? this.anchor1)!; - const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2]; - const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2); - const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2); - const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2]; + const parxf = this.DocumentView?.().containerViewPath?.().lastElement().ComponentView as CollectionFreeFormView; + const this_xf = parxf?.screenToFreeformContentsXf ?? Transform.Identity; //this.ScreenToLocalTransform(); + const a_invXf = a.screenToViewTransform().inverse(); + const b_invXf = b.screenToViewTransform().inverse(); + const a_scrBds = { tl: a_invXf.transformPoint(0, 0), br: a_invXf.transformPoint(NumCast(a.Document._width), NumCast(a.Document._height)) }; + const b_scrBds = { tl: b_invXf.transformPoint(0, 0), br: b_invXf.transformPoint(NumCast(b.Document._width), NumCast(b.Document._height)) }; + const a_bds = { tl: this_xf.transformPoint(a_scrBds.tl[0], a_scrBds.tl[1]), br: this_xf.transformPoint(a_scrBds.br[0], a_scrBds.br[1]) }; + const b_bds = { tl: this_xf.transformPoint(b_scrBds.tl[0], b_scrBds.tl[1]), br: this_xf.transformPoint(b_scrBds.br[0], b_scrBds.br[1]) }; - const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]); - const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])]; - setTimeout( - action(() => { - this.layoutDoc.x = lx; - this.layoutDoc.y = ty; - this.layoutDoc._width = rx - lx; - this.layoutDoc._height = by - ty; - }) - ); + const ppt1 = [(a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2]; + const pt1 = Utils.getNearestPointInPerimeter(a_bds.tl[0], a_bds.tl[1], a_bds.br[0] - a_bds.tl[0], a_bds.br[1] - a_bds.tl[1], (b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2); + const pt2 = Utils.getNearestPointInPerimeter(b_bds.tl[0], b_bds.tl[1], b_bds.br[0] - b_bds.tl[0], b_bds.br[1] - b_bds.tl[1], (a_bds.tl[0] + a_bds.br[0]) / 2, (a_bds.tl[1] + a_bds.br[1]) / 2); + const ppt2 = [(b_bds.tl[0] + b_bds.br[0]) / 2, (b_bds.tl[1] + b_bds.br[1]) / 2]; - const highlight = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Highlighting); + const pts = [ppt1, pt1, pt2, ppt2].map(pt => [pt[0], pt[1]]); + const [lx, rx, ty, by] = [Math.min(pt1[0], pt2[0]), Math.max(pt1[0], pt2[0]), Math.min(pt1[1], pt2[1]), Math.max(pt1[1], pt2[1])]; + return { pts, lx, rx, ty, by }; + } + return undefined; + }, + params => { + this.renderProps = params; + if (params) { + if ( + Math.abs(params.lx - NumCast(this.layoutDoc.x)) > 1e-5 || + Math.abs(params.ty - NumCast(this.layoutDoc.y)) > 1e-5 || + Math.abs(params.rx - params.lx - NumCast(this.layoutDoc._width)) > 1e-5 || + Math.abs(params.by - params.ty - NumCast(this.layoutDoc._height)) > 1e-5 + ) { + this.layoutDoc.x = params?.lx; + this.layoutDoc.y = params?.ty; + this.layoutDoc._width = params.rx - params?.lx; + this.layoutDoc._height = params?.by - params?.ty; + } + } else { + this.layoutDoc._width = Math.max(50, NumCast(this.layoutDoc._width)); + this.layoutDoc._height = Math.max(50, NumCast(this.layoutDoc._height)); + } + }, + { fireImmediately: true } + ); + } + componentWillUnmount(): void { + this.disposer?.(); + } + @observable renderProps: { lx: number; rx: number; ty: number; by: number; pts: number[][] } | undefined = undefined; + render() { + if (this.renderProps) { + const highlight = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting); const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined; - const bez = new Bezier(pts.map(p => ({ x: p[0], y: p[1] }))); + const bez = new Bezier(this.renderProps.pts.map(p => ({ x: p[0], y: p[1] }))); const text = bez.get(0.5); - const linkDesc = StrCast(this.rootDoc.link_description) || 'description'; - const strokeWidth = NumCast(this.rootDoc.stroke_width, 4); - const dash = StrCast(this.rootDoc.stroke_dash); + const linkDesc = StrCast(this.dataDoc.link_description) || 'description'; + const strokeWidth = NumCast(this.dataDoc.stroke_width, 4); + const dash = StrCast(this.Document.stroke_dash); const strokeDasharray = dash && Number(dash) ? String(strokeWidth * Number(dash)) : undefined; + const { pts, lx, ty, rx, by } = this.renderProps; return ( <div style={{ transition: 'inherit', pointerEvents: 'none', position: 'absolute', width: '100%', height: '100%' }}> <svg width={Math.max(100, rx - lx)} height={Math.max(100, by - ty)} style={{ transition: 'inherit', overflow: 'visible' }}> <defs> - <filter x="0" y="0" width="1" height="1" id={`${this.rootDoc[Id] + 'background'}`}> - <feFlood floodColor={`${StrCast(this.rootDoc._backgroundColor, 'lightblue')}`} result="bg" /> + <filter x="0" y="0" width="1" height="1" id={`${this.Document[Id] + 'background'}`}> + <feFlood floodColor={`${StrCast(this.layoutDoc._backgroundColor, 'lightblue')}`} result="bg" /> <feMerge> <feMergeNode in="bg" /> <feMergeNode in="SourceGraphic" /> @@ -110,7 +139,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { <path className="collectionfreeformlinkview-linkLine" style={{ - pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'visibleStroke', // + pointerEvents: this._props.pointerEvents?.() === 'none' ? 'none' : 'visibleStroke', // stroke: highlightColor ?? 'lightblue', strokeDasharray, strokeWidth, @@ -120,8 +149,8 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { ${pts[2][0] + pts[2][0] - pts[3][0] - lx} ${pts[2][1] + pts[2][1] - pts[3][1] - ty}, ${pts[2][0] - lx} ${pts[2][1] - ty}`} /> <text - filter={`url(#${this.rootDoc[Id] + 'background'})`} - style={{ pointerEvents: this.props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }} + filter={`url(#${this.Document[Id] + 'background'})`} + style={{ pointerEvents: this._props.pointerEvents?.() === 'none' ? 'none' : 'all', textAnchor: 'middle', fontSize: '12', stroke: 'black' }} x={text.x - lx} y={text.y - ty}> <tspan> </tspan> @@ -133,14 +162,13 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { ); } return ( - <div className={`linkBox-container${this.props.isContentActive() ? '-interactive' : ''}`} style={{ background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor) }}> + <div className={`linkBox-container${this._props.isContentActive() ? '-interactive' : ''}`} style={{ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) }}> <ComparisonBox - {...this.props} + {...this._props} // fieldKey="link_anchor" setHeight={emptyFunction} dontRegisterView={true} - renderDepth={this.props.renderDepth + 1} - isContentActive={this.props.isContentActive} + renderDepth={this._props.renderDepth + 1} addDocument={returnFalse} removeDocument={returnFalse} moveDocument={returnFalse} |