aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/LinkAnchorBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/LinkAnchorBox.tsx')
-rw-r--r--src/client/views/nodes/LinkAnchorBox.tsx115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
new file mode 100644
index 000000000..0a4325d8c
--- /dev/null
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -0,0 +1,115 @@
+import { action, computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Utils, emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { Doc } from '../../../fields/Doc';
+import { NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { SelectionManager } from '../../util/SelectionManager';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { StyleProp } from '../StyleProvider';
+import { FieldView, FieldViewProps } from './FieldView';
+import './LinkAnchorBox.scss';
+import { LinkInfo } from './LinkDocPreview';
+const { default: { MEDIUM_GRAY }, } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
+@observer
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(LinkAnchorBox, fieldKey);
+ }
+ _doubleTap = false;
+ _lastTap: number = 0;
+ _ref = React.createRef<HTMLDivElement>();
+ _isOpen = false;
+ _timeout: NodeJS.Timeout | undefined;
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ componentDidMount() {
+ this._props.setContentViewBox?.(this);
+ }
+
+ @computed get linkSource() {
+ return this.DocumentView?.().containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource);
+ }
+
+ onPointerDown = (e: React.PointerEvent) => {
+ const linkSource = this.linkSource;
+ linkSource &&
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (e, doubleTap) => {
+ if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false);
+ else this._props.select(false);
+ });
+ };
+ onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
+ const cdiv = this._ref?.current?.parentElement;
+ if (!this._isOpen && cdiv) {
+ const bounds = cdiv.getBoundingClientRect();
+ const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY);
+ const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
+ if (separation > 100) {
+ const dragData = new DragManager.DocumentDragData([this.Document]);
+ dragData.dropAction = dropActionType.embed;
+ dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick'];
+ DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
+ return true;
+ } else {
+ this.layoutDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
+ this.layoutDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
+ this.layoutDoc.link_autoMoveAnchors = false;
+ }
+ }
+ return false;
+ });
+
+ specificContextMenu = (e: React.MouseEvent): void => {};
+
+ render() {
+ TraceMobx();
+ const small = this._props.PanelWidth() <= 1; // this happens when rendered in a treeView
+ const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100);
+ const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100);
+ const background = this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.BackgroundColor + ':anchor');
+ const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1';
+ const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
+ const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
+ const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1')
+ ? 'link_anchor_1'
+ : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2')
+ ? 'link_anchor_2'
+ : '';
+ return (
+ <div
+ ref={this._ref}
+ title={targetTitle}
+ className={`linkAnchorBox-cont${small ? '-small' : ''}`}
+ onPointerEnter={e =>
+ LinkInfo.SetLinkInfo({
+ DocumentView: this.DocumentView,
+ styleProvider: this._props.styleProvider,
+ linkSrc: this.linkSource,
+ linkDoc: this.Document,
+ showHeader: true,
+ location: [e.clientX, e.clientY + 20],
+ noPreview: false,
+ })
+ }
+ onPointerDown={this.onPointerDown}
+ onContextMenu={this.specificContextMenu}
+ style={{
+ border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined,
+ background,
+ left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
+ top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
+ transform: `scale(${anchorScale})`,
+ cursor: 'grab',
+ }}
+ />
+ );
+ }
+}