aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-02-07 18:32:22 -0500
committerbobzel <zzzman@gmail.com>2024-02-07 18:32:22 -0500
commit15f23f8b99b2e5ce48e7146c61d4d61b451875ad (patch)
tree1f89419b4166af6260815d20e9f26d2bbb948e48 /src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
parente3fde25014d523c5f43a138093718899fe17d108 (diff)
added back shiftkey to drag and embed. got rid of overlayplane link view. got rid of auto arrange. fixed text edit color for linkBox's
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx318
1 files changed, 0 insertions, 318 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
deleted file mode 100644
index a45a1fb0f..000000000
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ /dev/null
@@ -1,318 +0,0 @@
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, Field } from '../../../../fields/Doc';
-import { Brushed, DocCss } from '../../../../fields/DocSymbols';
-import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { LinkManager } from '../../../util/LinkManager';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { SettingsManager } from '../../../util/SettingsManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Colors } from '../../global/globalEnums';
-import { DocumentView } from '../../nodes/DocumentView';
-import { ObservableReactComponent } from '../../ObservableReactComponent';
-import './CollectionFreeFormLinkView.scss';
-
-export interface CollectionFreeFormLinkViewProps {
- A: DocumentView;
- B: DocumentView;
- LinkDocs: Doc[];
-}
-
-@observer
-export class CollectionFreeFormLinkView extends ObservableReactComponent<CollectionFreeFormLinkViewProps> {
- @observable _opacity: number = 0;
- @observable _start = 0;
- _anchorDisposer: IReactionDisposer | undefined;
- _timeout: NodeJS.Timeout | undefined;
- constructor(props: any) {
- super(props);
- makeObservable(this);
- }
-
- componentWillUnmount() {
- this._anchorDisposer?.();
- }
- @action timeout: any = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
- componentDidMount() {
- this._anchorDisposer = reaction(
- () => [
- this._props.A.screenToViewTransform(),
- Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
- Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
- this._props.B.screenToViewTransform(),
- Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
- Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
- ],
- action(() => {
- this._start = Date.now();
- this._timeout && clearTimeout(this._timeout);
- this._timeout = setTimeout(this.timeout, 25);
- setTimeout(this.placeAnchors, 10); // when docs are dragged, their transforms will update before a render has been performed. placeanchors needs to come after a render to find things in the dom. a 0 timeout will still come before the render
- }),
- { fireImmediately: true }
- );
- }
- placeAnchors = () => {
- const { A, B, LinkDocs } = this._props;
- const linkDoc = LinkDocs[0];
- if (SnappingManager.IsDragging || !A.ContentDiv || !B.ContentDiv) return;
- setTimeout(
- action(() => (this._opacity = 0.75)),
- 0
- ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
- setTimeout(
- action(() => (!LinkDocs.length || !(linkDoc.link_displayLine || Doc.UserDoc().showLinkLines)) && (this._opacity = 0.05)),
- 750
- ); // this will unhighlight the link line.
- const a = A.ContentDiv.getBoundingClientRect();
- const b = B.ContentDiv.getBoundingClientRect();
- const { left: aleft, top: atop, width: awidth, height: aheight } = A.ContentDiv.parentElement!.getBoundingClientRect();
- const { left: bleft, top: btop, width: bwidth, height: bheight } = B.ContentDiv.parentElement!.getBoundingClientRect();
- const apt = Utils.closestPtBetweenRectangles(aleft, atop, awidth, aheight, bleft, btop, bwidth, bheight, a.left + a.width / 2, a.top + a.height / 2);
- const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y);
-
- // really hacky stuff to make the LinkAnchorBox display where we want it to:
- // if there's an element in the DOM with a classname containing a link anchor's id,
- // then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
- // otherwise, we just use the computed nearest point on the document boundary to the target Document
- const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_1 as Doc)[Id])).lastElement();
- const targetBhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.link_anchor_2 as Doc)[Id])).lastElement();
- if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
- if (!targetAhyperlink) {
- if (linkDoc.link_autoMoveAnchors) {
- linkDoc.link_anchor_1_x = ((apt.point.x - aleft) / awidth) * 100;
- linkDoc.link_anchor_1_y = ((apt.point.y - atop) / aheight) * 100;
- }
- } else {
- const m = targetAhyperlink.getBoundingClientRect();
- const mp = A.screenToViewTransform().transformPoint(m.right, m.top + 5);
- const mpx = mp[0] / A._props.PanelWidth();
- const mpy = mp[1] / A._props.PanelHeight();
- if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_1_x = mpx * 100;
- if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_1_y = mpy * 100;
- if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
- else linkDoc.opacity = 1;
- }
- if (!targetBhyperlink) {
- if (linkDoc.link_autoMoveAnchors) {
- linkDoc.link_anchor_2_x = ((bpt.point.x - bleft) / bwidth) * 100;
- linkDoc.link_anchor_2_y = ((bpt.point.y - btop) / bheight) * 100;
- }
- } else {
- const m = targetBhyperlink.getBoundingClientRect();
- const mp = B.screenToViewTransform().transformPoint(m.right, m.top + 5);
- const mpx = mp[0] / B._props.PanelWidth();
- const mpy = mp[1] / B._props.PanelHeight();
- if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_2_x = mpx * 100;
- if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_2_y = mpy * 100;
- if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
- else linkDoc.opacity = 1;
- }
- };
-
- pointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(
- this,
- e,
- (e, down, delta) => {
- this._props.LinkDocs[0].link_relationship_OffsetX = NumCast(this._props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
- this._props.LinkDocs[0].link_relationship_OffsetY = NumCast(this._props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
- return false;
- },
- emptyFunction,
- action(() => {
- SelectionManager.DeselectAll();
- SelectionManager.SelectSchemaViewDoc(this._props.LinkDocs[0], true);
- LinkManager.Instance.currentLink = this._props.LinkDocs[0];
- this.toggleProperties();
- // OverlayView.Instance.addElement(
- // <LinkEditor sourceDoc={this._props.A.Document} linkDoc={this._props.LinkDocs[0]}
- // showLinks={action(() => { })}
- // />, { x: 300, y: 300 });
- })
- );
- };
-
- visibleY = (el: any) => {
- let rect = el.getBoundingClientRect();
- const top = rect.top,
- height = rect.height;
- var el = el.parentNode;
- while (el && el !== document.body) {
- if (el.className === 'tabDocView-content') break;
- rect = el.getBoundingClientRect?.();
- if (rect?.width) {
- if (top <= rect.bottom === false && getComputedStyle(el).overflow === 'hidden') return rect.bottom;
- // Check if the element is out of view due to a container scrolling
- if (top + height <= rect.top && getComputedStyle(el).overflow === 'hidden') return rect.top;
- }
- el = el.parentNode;
- }
- // Check its within the document viewport
- return top; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
- };
- visibleX = (el: any) => {
- let rect = el.getBoundingClientRect();
- const left = rect.left,
- width = rect.width;
- var el = el.parentNode;
- while (el && el !== document.body) {
- rect = el?.getBoundingClientRect();
- if (rect?.width) {
- if (left <= rect.right === false && getComputedStyle(el).overflow === 'hidden') return rect.right;
- // Check if the element is out of view due to a container scrolling
- if (left + width <= rect.left && getComputedStyle(el).overflow === 'hidden') return rect.left;
- }
- el = el.parentNode;
- }
- // Check its within the document viewport
- return left; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
- };
-
- @action
- toggleProperties = () => {
- if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
- SettingsManager.Instance.propertiesWidth = 250;
- }
- };
-
- @action
- onClickLine = () => {
- SelectionManager.DeselectAll();
- SelectionManager.SelectSchemaViewDoc(this._props.LinkDocs[0], true);
- LinkManager.Instance.currentLink = this._props.LinkDocs[0];
- this.toggleProperties();
- };
-
- @computed.struct get renderData() {
- this._start;
- SnappingManager.IsDragging;
- const { A, B, LinkDocs } = this._props;
- if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
- const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
- const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
- const adiv = acont.length ? acont[0] : A.ContentDiv;
- const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
- for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
- for (let bpdiv = bdiv; bpdiv; bpdiv = bpdiv.parentElement as any) if ((bpdiv as any).hidden) return;
- const a = adiv.getBoundingClientRect();
- const b = bdiv.getBoundingClientRect();
- const atop = this.visibleY(adiv);
- const btop = this.visibleY(bdiv);
- if (!a.width || !b.width) return undefined;
- const aDocBounds = (A._props as any).DocumentView?.().getBounds || { left: 0, right: 0, top: 0, bottom: 0 };
- const bDocBounds = (B._props as any).DocumentView?.().getBounds || { left: 0, right: 0, top: 0, bottom: 0 };
- const aleft = this.visibleX(adiv);
- const bleft = this.visibleX(bdiv);
- const aclipped = aleft !== a.left || atop !== a.top;
- const bclipped = bleft !== b.left || btop !== b.top;
- if (aclipped && bclipped) return undefined;
- const clipped = aclipped || bclipped;
- const pt1inside = NumCast(LinkDocs[0].link_anchor_1_x) % 100 !== 0 && NumCast(LinkDocs[0].link_anchor_1_y) % 100 !== 0;
- const pt2inside = NumCast(LinkDocs[0].link_anchor_2_x) % 100 !== 0 && NumCast(LinkDocs[0].link_anchor_2_y) % 100 !== 0;
- const pt1 = [aleft + a.width / 2, atop + a.height / 2];
- const pt2 = [bleft + b.width / 2, btop + b.width / 2];
- const pt2vec = pt2inside ? [-0.7071, 0.7071] : [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]];
- const pt1vec = pt1inside ? [-0.7071, 0.7071] : [(aDocBounds.left + aDocBounds.right) / 2 - pt1[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt1[1]];
- const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
- const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
- const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
- const pt1norm = clipped ? [0, 0] : [-(pt1vec[0] / pt1len) * ptlen, -(pt1vec[1] / pt1len) * ptlen];
- const pt2norm = clipped ? [0, 0] : [-(pt2vec[0] / pt2len) * ptlen, -(pt2vec[1] / pt2len) * ptlen];
- const pt1normlen = Math.sqrt(pt1norm[0] * pt1norm[0] + pt1norm[1] * pt1norm[1]) || 1;
- const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
- const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
- const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen];
- const aActive = A.IsSelected || A.Document[Brushed];
- const bActive = B.IsSelected || B.Document[Brushed];
-
- const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
- const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
- const link = this._props.LinkDocs[0];
- return {
- a,
- b,
- pt1norm,
- pt2norm,
- aActive,
- bActive,
- textX,
- textY,
- // fully connected
- // pt1,
- // pt2,
- // this code adds space between links
- pt1: link.link_displayArrow ? [pt1[0] + pt1normalized[0] * 3 * NumCast(link.link_displayArrow_scale, 4), pt1[1] + pt1normalized[1] * 3 * NumCast(link.link_displayArrow_scale, 3)] : pt1,
- pt2: link.link_displayArrow ? [pt2[0] + pt2normalized[0] * 3 * NumCast(link.link_displayArrow_scale, 4), pt2[1] + pt2normalized[1] * 3 * NumCast(link.link_displayArrow_scale, 3)] : pt2,
- };
- }
-
- render() {
- if (!this.renderData) return null;
-
- const link = this._props.LinkDocs[0];
- const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
- const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship
- const linkRelationshipList = Doc.UserDoc().link_relationshipList as List<string>;
- const linkColorList = Doc.UserDoc().link_ColorList as List<string>;
- const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List<number>;
- const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
- const linkDescription = Field.toString(link.link_description as any as Field).split('\n')[0];
-
- const linkSize = Doc.noviceMode || currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
-
- //access stroke color using index of the relationship in the color list (default black)
- const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? StrCast(link._backgroundColor, 'black') : linkColorList[currRelationshipIndex];
- // const hexStroke = this.rgbToHex(stroke)
-
- //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
- //thickness varies linearly from 3px to 12px for increasing link count
- const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
-
- const arrowScale = NumCast(link.link_displayArrow_scale, 3);
- return link.opacity === 0 || !a.width || !b.width || (!(Doc.UserDoc().showLinkLines || link.link_displayLine) && !aActive && !bActive) ? null : (
- <>
- <defs>
- <marker id={`${link[Id] + 'arrowhead'}`} markerWidth={`${4 * arrowScale}`} markerHeight={`${3 * arrowScale}`} refX="0" refY={`${1.5 * arrowScale}`} orient="auto">
- <polygon points={`0 0, ${3 * arrowScale} ${1.5 * arrowScale}, 0 ${3 * arrowScale}`} fill={stroke} />
- </marker>
- <filter id="outline">
- <feMorphology in="SourceAlpha" result="expanded" operator="dilate" radius="1" />
- <feFlood floodColor={`${Colors.DARK_GRAY}`} />
- <feComposite in2="expanded" operator="in" />
- <feComposite in="SourceGraphic" />
- </filter>
- <filter x="0" y="0" width="1" height="1" id={`${link[Id] + 'background'}`}>
- <feFlood floodColor={`${StrCast(link._backgroundColor, 'white')}`} result="bg" />
- <feMerge>
- <feMergeNode in="bg" />
- <feMergeNode in="SourceGraphic" />
- </feMerge>
- </filter>
- </defs>
- <path
- filter={LinkManager.Instance.currentLink === link ? 'url(#outline)' : ''}
- fill="pink"
- stroke="antiquewhite"
- strokeWidth="4"
- className="collectionfreeformlinkview-linkLine"
- style={{ pointerEvents: 'visibleStroke', opacity: this._opacity, stroke, strokeWidth }}
- onClick={this.onClickLine}
- d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
- markerEnd={link.link_displayArrow ? `url(#${link[Id] + 'arrowhead'})` : ''}
- />
- {textX === undefined || !linkDescription ? null : (
- <text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
- <tspan>&nbsp;</tspan>
- <tspan dy="2">{linkDescription.substring(0, 50) + (linkDescription.length > 50 ? '...' : '')}</tspan>
- <tspan dy="2">&nbsp;</tspan>
- </text>
- )}
- </>
- );
- }
-}