From a888150f2e220eff4629551aa03813f92aa0b12f Mon Sep 17 00:00:00 2001
From: bobzel
Date: Mon, 5 Feb 2024 22:56:04 -0500
Subject: changed backgroundColor to set on dataDocs. fixed pivoting on tags.
fixed link description editing popup. fixed showing link editor in property
view - still some weirdness in what is selected. fixed dragging tree view
items to set dragData.treeview and be able to drop at bottom of tree. fixed
addFolder menu option for TreeViews to add locally.. added a function to
collect all docs of a given tag into a collection. fixed setting default
font size to update autolayouts. changed dropping link onto same collection
to not leave pushpin. fixed minimap thumb updating. added fieldvalue
dropdown for dashFieldViews in text.
---
src/client/views/linking/LinkMenuItem.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'src/client/views/linking')
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index ad6deeefb..7427f4310 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -92,8 +92,8 @@ export class LinkMenuItem extends ObservableReactComponent {
DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight);
} else {
SelectionManager.SelectView(this._props.docView, false);
- LinkManager.currentLink = this._props.linkDoc === LinkManager.currentLink ? undefined : this._props.linkDoc;
- LinkManager.currentLinkAnchor = LinkManager.currentLink ? this.sourceAnchor : undefined;
+ LinkManager.Instance.currentLink = this._props.linkDoc === LinkManager.Instance.currentLink ? undefined : this._props.linkDoc;
+ LinkManager.Instance.currentLinkAnchor = LinkManager.Instance.currentLink ? this.sourceAnchor : undefined;
if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
setTimeout(action(() => (SettingsManager.Instance.propertiesWidth = 250)));
@@ -159,7 +159,7 @@ export class LinkMenuItem extends ObservableReactComponent {
style={{
fontSize: this._hover ? 'larger' : undefined,
fontWeight: this._hover ? 'bold' : undefined,
- background: LinkManager.currentLink === this._props.linkDoc ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
+ background: LinkManager.Instance.currentLink === this._props.linkDoc ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor,
}}>
Date: Wed, 7 Feb 2024 16:18:16 -0500
Subject: made various render methods in DocumentView computed getters for
efficiency and to avoid artifacts (LInkanchorBox dragging) when something
else invalidates causing components to regenerate. fixed linklines to
animate when doing a zoom transition and to be able to target texts
hyperlinks. fixed link lines to share properties with ink and updated the
properties panel / menus to allow editing of either. addding toggling link
lines on and off from linkitemmenu
---
src/client/documents/Documents.ts | 2 +-
src/client/views/DocComponent.tsx | 1 +
src/client/views/PropertiesButtons.tsx | 2 +-
src/client/views/PropertiesView.tsx | 104 ++---------
src/client/views/StyleProvider.tsx | 2 +-
.../collectionFreeForm/CollectionFreeFormView.tsx | 1 +
src/client/views/global/globalScripts.ts | 26 +--
src/client/views/linking/LinkMenuItem.tsx | 24 ++-
src/client/views/nodes/DocumentView.tsx | 35 ++--
src/client/views/nodes/LinkBox.tsx | 197 +++++++++++++--------
src/client/views/nodes/formattedText/marks_rts.ts | 5 +-
src/fields/Doc.ts | 3 +-
src/fields/DocSymbols.ts | 1 +
13 files changed, 201 insertions(+), 202 deletions(-)
(limited to 'src/client/views/linking')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 1978c144b..355a4c937 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -599,7 +599,7 @@ export namespace Docs {
_width: 1,
link: '',
link_description: '',
- backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
+ color: 'lightBlue', // lightblue is default color for linking dot and link documents text comment area
_dropPropertiesToRemove: new List(['onClick']),
},
},
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index dacd359c5..99b9c3045 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -35,6 +35,7 @@ export interface ViewBoxInterface {
addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
select?: (ctrlKey: boolean, shiftKey: boolean) => void;
focus?: (textAnchor: Doc, options: FocusViewOptions) => Opt
;
+ viewTransition?: () => Opt; // duration of a view transition animation
isAnyChildContentActive?: () => boolean; // is any child content of the document active
onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected
getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index bba6285c2..cb38ab602 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -411,7 +411,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
docView.noOnClick();
switch (onClick) {
case 'enterPortal':
- docView.makeIntoPortal();
+ DocUtils.makeIntoPortal(docView.Document, docView.layoutDoc, docView.allLinks);
break;
case 'toggleDetail':
docView.setToggleDetail();
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 07f285eaf..3ae2362a1 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -741,7 +741,7 @@ export class PropertiesView extends ObservableReactComponent void) {
@@ -943,19 +949,19 @@ export class PropertiesView extends ObservableReactComponent
-
-
-
Width:
-
{this.stkInput}
-
-
(this.widthStk = e.target.value))}
- onMouseDown={e => {
- this._widthUndo = UndoManager.StartBatch('width undo');
- }}
- onMouseUp={e => {
- this._widthUndo?.end();
- this._widthUndo = undefined;
- }}
- />
-
+ {this.getNumber('Thickness', '', 0, Math.max(50, this.strokeThk), this.strokeThk, (val: number) => !isNaN(val) && (this.strokeThk = val), 50, 1)}
+ {this.getNumber('Arrow Scale', '', 0, Math.max(10, this.markScal), this.markScal, (val: number) => !isNaN(val) && (this.markScal = val), 10, 1)}
-
-
-
Arrow Scale:
- {/*
{this.markScalInput}
*/}
-
-
(this.markScal = +e.target.value))}
- onMouseDown={e => {
- this._widthUndo = UndoManager.StartBatch('scale undo');
- }}
- onMouseUp={e => {
- this._widthUndo?.end();
- this._widthUndo = undefined;
- }}
- />
-
-
-
Show link
-
this.toggleLinkProp(e, 'link_displayLine')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
-
-
-
-
-
Auto-move anchors
-
this.toggleLinkProp(e, 'link_autoMoveAnchors')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
-
-
-
-
-
Display arrow
-
this.toggleLinkProp(e, 'link_displayArrow')}
- onClick={e => e.stopPropagation()}
- className="propertiesButton">
-
-
-
{!hasSelectedAnchor ? null : (
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index b7e64d9a8..0794efe4c 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -134,7 +134,7 @@ export function DefaultStyleProvider(doc: Opt
, props: Opt this._props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
+ viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
fitContentOnce = () => {
const vals = this.fitToContentVals;
this.layoutDoc._freeform_panX = vals.bounds.cx;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 813cb9338..0541a9ca7 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -43,14 +43,14 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b
} else if (selectedViews.length) {
if (checkResult) {
const selView = selectedViews.lastElement();
- const fieldKey = selView.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
+ const fieldKey = selView.Document._layout_isSvg ? 'fillColor' : 'backgroundColor';
const layoutFrameNumber = Cast(selView.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
const contentFrameNumber = Cast(selView.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
return CollectionFreeFormDocumentView.getStringValues(selView?.Document, contentFrameNumber)[fieldKey] ?? 'transparent';
}
selectedViews.some(dv => dv.ComponentView instanceof InkingStroke) && SetActiveFillColor(color ?? 'transparent');
selectedViews.forEach(dv => {
- const fieldKey = dv.Document.type === DocumentType.INK ? 'fillColor' : 'backgroundColor';
+ const fieldKey = dv.Document._layout_isSvg ? 'fillColor' : 'backgroundColor';
const layoutFrameNumber = Cast(dv.containerViewPath?.().lastElement()?.Document?._currentFrame, 'number'); // frame number that container is at which determines layout frame values
const contentFrameNumber = Cast(dv.Document?._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
if (contentFrameNumber !== undefined) {
@@ -344,24 +344,24 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
// prettier-ignore
const map: Map<'inkMask' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([
['inkMask', {
- checkResult: () => ((selected?.type === DocumentType.INK ? BoolCast(selected.stroke_isInkMask) : ActiveIsInkMask())),
- setInk: (doc: Doc) => (doc.stroke_isInkMask = !doc.stroke_isInkMask),
+ checkResult: () => ((selected?._layout_isSvg ? BoolCast(selected[DocData].stroke_isInkMask) : ActiveIsInkMask())),
+ setInk: (doc: Doc) => (doc[DocData].stroke_isInkMask = !doc.stroke_isInkMask),
setMode: () => selected?.type !== DocumentType.INK && SetActiveIsInkMask(!ActiveIsInkMask()),
}],
['fillColor', {
- checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.fillColor) : ActiveFillColor() ?? "transparent"),
- setInk: (doc: Doc) => (doc.fillColor = StrCast(value)),
+ checkResult: () => (selected?._layout_isSvg ? StrCast(selected[DocData].fillColor) : ActiveFillColor() ?? "transparent"),
+ setInk: (doc: Doc) => (doc[DocData].fillColor = StrCast(value)),
setMode: () => SetActiveFillColor(StrCast(value)),
}],
[ 'strokeWidth', {
- checkResult: () => (selected?.type === DocumentType.INK ? NumCast(selected.stroke_width) : ActiveInkWidth()),
- setInk: (doc: Doc) => (doc.stroke_width = NumCast(value)),
- setMode: () => { SetActiveInkWidth(value.toString()); setActiveTool( GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
+ checkResult: () => (selected?._layout_isSvg ? NumCast(selected[DocData].stroke_width) : ActiveInkWidth()),
+ setInk: (doc: Doc) => (doc[DocData].stroke_width = NumCast(value)),
+ setMode: () => { SetActiveInkWidth(value.toString()); selected?.type === DocumentType.INK && setActiveTool( GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
}],
['strokeColor', {
- checkResult: () => (selected?.type === DocumentType.INK ? StrCast(selected.color) : ActiveInkColor()),
- setInk: (doc: Doc) => (doc.color = String(value)),
- setMode: () => { SetActiveInkColor(StrCast(value)); setActiveTool(GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
+ checkResult: () => (selected?._layout_isSvg? StrCast(selected[DocData].color) : ActiveInkColor()),
+ setInk: (doc: Doc) => (doc[DocData].color = String(value)),
+ setMode: () => { SetActiveInkColor(StrCast(value)); selected?.type === DocumentType.INK && setActiveTool(GestureOverlay.Instance.InkShape ?? InkTool.Pen, true, false);},
}],
]);
@@ -369,7 +369,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'fillColor' | '
return map.get(option)?.checkResult();
}
map.get(option)?.setMode();
- SelectionManager.Docs.filter(doc => doc.type === DocumentType.INK).map(doc => map.get(option)?.setInk(doc));
+ SelectionManager.Docs.filter(doc => doc._layout_isSvg).map(doc => map.get(option)?.setInk(doc));
});
/** WEB
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 7427f4310..92c63cd56 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -74,6 +74,14 @@ export class LinkMenuItem extends ObservableReactComponent {
return this._props.sourceDoc;
}
+ onIconDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, returnFalse, returnFalse, () => {
+ if (!this._props.docView._props.removeDocument?.(this._props.linkDoc)) {
+ this._props.docView._props.addDocument?.(this._props.linkDoc);
+ }
+ });
+ };
+
onEdit = (e: React.PointerEvent) => {
setupMoveUpEvents(
this,
@@ -196,12 +204,16 @@ export class LinkMenuItem extends ObservableReactComponent {
) : null}
-
-
-
-
- {this._props.linkDoc.linksToAnnotation && Cast(this._props.destinationDoc.data, WebField)?.url.href === this._props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
-
+
Show/Hide Link }>
+
+
+
+
+ Follow Link }>
+
+ {this._props.linkDoc.linksToAnnotation && Cast(this._props.destinationDoc.data, WebField)?.url.href === this._props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
+
+
{!this._props.linkDoc.link_description ? null :
{StrCast(this._props.linkDoc.link_description).split('\n')[0].substring(0, 50)}
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 5efa028d1..042ae6e55 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -48,6 +48,7 @@ import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
interface Window {
MediaRecorder: MediaRecorder;
}
@@ -726,7 +727,7 @@ export class DocumentViewInternal extends DocComponent () => (link.link_displayLine = false);
- allLinkEndpoints = () => {
+ @computed get allLinkEndpoints() {
// the small blue dots that mark the endpoints of links
if (this._componentView instanceof KeyValueBox || this._props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this._props.dontRegisterView || this.layoutDoc.layout_unrendered) return null;
return this.filteredLinks.map(link => (
@@ -750,9 +751,9 @@ export class DocumentViewInternal extends DocComponent
));
- };
+ }
- viewBoxContents = () => {
+ @computed get viewBoxContents() {
TraceMobx();
const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
const noBackground = this.Document.isGroup && !this._props.LayoutTemplateString?.includes(KeyValueBox.name) && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
@@ -778,10 +779,10 @@ export class DocumentViewInternal extends DocComponent
- {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints()}
+ {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints}
);
- };
+ }
captionStyleProvider = (doc: Opt, props: Opt, property: string) => this._props?.styleProvider?.(doc, props, property + ':caption');
fieldsDropdown = (reqdFields: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => {
@@ -814,7 +815,7 @@ export class DocumentViewInternal extends DocComponent {
+ @computed get titleView() {
const showTitle = this.layout_showTitle?.split(':')[0];
const showTitleHover = this.layout_showTitle?.includes(':hover');
@@ -888,9 +889,9 @@ export class DocumentViewInternal extends DocComponent
);
- };
+ }
- captionView = () => {
+ @computed get captionView() {
return !this.layout_showCaption ? null : (
);
- };
+ }
renderDoc = (style: object) => {
TraceMobx();
@@ -933,15 +934,15 @@ export class DocumentViewInternal extends DocComponent
{this._props.hideTitle || (!showTitle && !this.layout_showCaption) ? (
- this.viewBoxContents()
+ this.viewBoxContents
) : (
- {this.titleView()}
- {this.viewBoxContents()}
- {this.captionView()}
+ {this.titleView}
+ {this.viewBoxContents}
+ {this.captionView}
)}
{this.widgetDecorations ?? null}
@@ -1191,8 +1192,8 @@ export class DocumentView extends DocComponent() {
if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), transition: undefined };
}
// transition is returned so that the bounds will 'update' at the end of an animated transition. This is needed by xAnchor in LinkBox
- const transition = this.docViewPath().find((parent: DocumentView) => parent._props.DataTransition?.() || StrCast(parent.Document.dataTransition));
- return { left, top, right, bottom, transition: transition?._props.DataTransition?.() || StrCast(transition?.Document.dataTransition) };
+ const transition = this.docViewPath().find((parent: DocumentView) => parent.DataTransition?.() || parent.ComponentView?.viewTransition?.());
+ return { left, top, right, bottom, transition: transition?.DataTransition?.() || transition?.ComponentView?.viewTransition?.() };
}
@computed get nativeWidth() {
@@ -1337,6 +1338,7 @@ export class DocumentView extends DocComponent() {
}
}
};
+ DataTransition = () => this._props.DataTransition?.() || StrCast(this.Document.dataTransition);
ShouldNotScale = () => this.shouldNotScale;
NativeWidth = () => this.effectiveNativeWidth;
NativeHeight = () => this.effectiveNativeHeight;
@@ -1402,6 +1404,7 @@ export class DocumentView extends DocComponent() {
() {
@@ -43,19 +46,20 @@ export class LinkBox extends ViewBoxBaseComponent() {
this.disposer = reaction(
() => ({ drag: SnappingManager.IsDragging, a: this.anchor1, b: this.anchor2 }),
({ drag, a, b }) => {
- setTimeout(
- // need to wait for drag manager to set 'hidden' flag on dragged elements
- action(() => {
- let a1 = a && document.getElementById(a.Guid);
- let a2 = b && document.getElementById(b.Guid);
- if (!a1 || !a2 || (a?.ContentDiv as any)?.hidden || (b?.ContentDiv as any)?.hidden) this._hide = true;
- else {
- for (; a1 && !a1.hidden; a1 = a1.parentElement);
- for (; a2 && !a2.hidden; a2 = a2.parentElement);
- this._hide = a1 || a2 ? true : false;
- }
- })
- );
+ !LightboxView.Contains(this.DocumentView?.()) &&
+ setTimeout(
+ // need to wait for drag manager to set 'hidden' flag on dragged elements
+ action(() => {
+ let a1 = a && document.getElementById(a.Guid);
+ let a2 = b && document.getElementById(b.Guid);
+ if (!a1 || !a2 || (a?.ContentDiv as any)?.hidden || (b?.ContentDiv as any)?.hidden) this._hide = true;
+ else {
+ for (; a1 && !a1.hidden; a1 = a1.parentElement);
+ for (; a2 && !a2.hidden; a2 = a2.parentElement);
+ this._hide = a1 || a2 ? true : false;
+ }
+ })
+ );
},
{ fireImmediately: true }
);
@@ -63,89 +67,130 @@ export class LinkBox extends ViewBoxBaseComponent() {
select = (ctrlKey: boolean, shiftKey: boolean) => (LinkManager.Instance.currentLink = this.Document);
- descriptionDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(
- this,
- e,
- returnFalse,
- returnFalse,
- action(() => {
- LinkManager.Instance.currentLink = this.Document;
- LinkDescriptionPopup.Instance.popupX = e.clientX;
- LinkDescriptionPopup.Instance.popupY = e.clientY;
- LinkDescriptionPopup.Instance.display = true;
-
- const rect = document.body.getBoundingClientRect();
- if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) {
- LinkDescriptionPopup.Instance.popupX -= 190;
- }
- if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) {
- LinkDescriptionPopup.Instance.popupY -= 40;
- }
- }),
- false
- );
- };
render() {
- trace();
+ if (this._hide) return null;
const a = this.anchor1;
const b = this.anchor2;
this._forceAnimate;
- if (a && b && !this._hide) {
+ if (a && b && !LightboxView.Contains(this.DocumentView?.())) {
+ // text selection bounds are not directly observable, so we have to
+ // force an update when anything that could affect them changes (text edits causing reflow, scrolling)
+ a.Document[a.LayoutFieldKey];
+ b.Document[b.LayoutFieldKey];
+ a.Document.layout_scrollTop;
+ b.Document.layout_scrollTop;
+
const axf = a.screenToViewTransform(); // these force re-render when a or b moves (so do NOT remove)
const bxf = b.screenToViewTransform();
const scale = a.CollectionFreeFormView === this.DocumentView?.().CollectionFreeFormView ? axf.Scale : bxf.Scale;
const at = a.getBounds?.transition; // these force re-render when a or b change size and at the end of an animated transition
- const bt = b.getBounds?.transition;
+ const bt = b.getBounds?.transition; // inquring getBounds() also causes text anchors to update whether or not they reflow (any size change triggers an invalidation)
+
+ // if there's an element in the DOM with a classname containing a link anchor's id (eg a hypertext ),
+ // 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(DocCast(this.dataDoc.link_anchor_1)[Id])).lastElement();
+ const targetBhyperlink = Array.from(window.document.getElementsByClassName(DocCast(this.dataDoc.link_anchor_2)[Id])).lastElement();
+
+ const aid = targetAhyperlink?.id || a.Document[Id];
+ const bid = targetBhyperlink?.id || b.Document[Id];
+ if (!document.getElementById(aid) || !document.getElementById(bid)) {
+ setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 0.01)));
+ return null;
+ }
+
if (at || bt) setTimeout(action(() => (this._forceAnimate = this._forceAnimate + 0.01))); // this forces an update during a transition animation
const highlight = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Highlighting);
const highlightColor = highlight?.highlightIndex ? highlight?.highlightColor : undefined;
+ const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color);
const fontFamily = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily);
const fontSize = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize);
- const color = (c => (c !== 'transparent' ? c : undefined))(StrCast(this.layoutDoc.link_fontColor));
- const { strokeWidth, stroke_startMarker, stroke_endMarker } = this.Document;
- const dash = StrCast(this.Document.stroke_dash);
- const stroke = highlightColor ?? 'lightblue';
+ const fontColor = (c => (c !== 'transparent' ? c : undefined))(StrCast(this.layoutDoc.link_fontColor));
+ const { stroke_markerScale, stroke_width, stroke_startMarker, stroke_endMarker, stroke_dash } = this.Document;
+ const strokeWidth = NumCast(stroke_width, 4);
const linkDesc = StrCast(this.dataDoc.link_description) || ' ';
const labelText = linkDesc.substring(0, 50) + (linkDesc.length > 50 ? '...' : '');
return (
-
- {labelText}
-
- }
- passProps={{ onPointerDown: this.descriptionDown }}
- />
+ <>
+ {!highlightColor ? null : (
+
+ )}
+
+ linkDesc}
+ SetValue={action(val => {
+ this.Document[DocData].link_description = val;
+ return true;
+ })}
+ />
+
+ {/* (this.Document[DocData].link_description = val))}
+ fillWidth
+ /> */}
+
+ }
+ passProps={{}}
+ />
+ >
);
}
- return null;
return (
(p ? p + ' ' + item.href : item.href), '');
const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string; title: string; anchorId: string }) => (p ? p + ' ' + item.anchorId : item.anchorId), '');
- return ['a', { class: anchorids, 'data-targethrefs': targethrefs, /*'data-noPreview': 'true', */ 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, style: `background: lightBlue` }, 0];
+ return ['a', { id: Utils.GenerateGuid(), class: anchorids, 'data-targethrefs': targethrefs, /*'data-noPreview': 'true', */ 'data-linkdoc': node.attrs.linkDoc, title: node.attrs.title, style: `background: lightBlue` }, 0];
},
},
noAutoLinkAnchor: {
@@ -104,7 +105,7 @@ export const marks: { [index: string]: MarkSpec } = {
node.attrs.title,
],
]
- : ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, style: `text-decoration: underline; cursor: default` }, 0];
+ : ['a', { id: '' + Utils.GenerateGuid(), class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, 'data-noPreview': node.attrs.noPreview, style: `text-decoration: underline; cursor: default` }, 0];
},
},
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 3cd4efcf7..56d50846a 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -16,7 +16,7 @@ import { DateField } from './DateField';
import {
AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, Animation, AudioPlay, Brushed, CachedUpdates, DirectLinks,
DocAcl, DocCss, DocData, DocFields, DocLayout, DocViews, FieldKeys, FieldTuples, ForceServerWrite, Height, Highlight,
- Initializing, Self, SelfProxy, UpdatingFromServer, Width
+ Initializing, Self, SelfProxy, TransitionTimer, UpdatingFromServer, Width
} from './DocSymbols'; // prettier-ignore
import { Copy, FieldChanged, HandleUpdate, Id, Parent, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
import { InkField, InkTool } from './InkField';
@@ -309,6 +309,7 @@ export class Doc extends RefField {
public [DocFields] = () => this[Self][FieldTuples]; // Object.keys(this).reduce((fields, key) => { fields[key] = this[key]; return fields; }, {} as any);
public [Width] = () => NumCast(this[SelfProxy]._width);
public [Height] = () => NumCast(this[SelfProxy]._height);
+ public [TransitionTimer]: any = undefined;
public [ToJavascriptString] = () => `idToDoc("${this[Self][Id]}")`; // what should go here?
public [ToScriptString] = () => `idToDoc("${this[Self][Id]}")`;
public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? '-inaccessible-' : this[SelfProxy].title})`;
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index 9c563abbf..f8a57acd5 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -15,6 +15,7 @@ export const DocLayout = Symbol('DocLayout');
export const DocFields = Symbol('DocFields');
export const DocCss = Symbol('DocCss');
export const DocAcl = Symbol('DocAcl');
+export const TransitionTimer = Symbol('DocTransitionTimer');
export const DirectLinks = Symbol('DocDirectLinks');
export const AclPrivate = Symbol('DocAclOwnerOnly');
export const AclReadonly = Symbol('DocAclReadOnly');
--
cgit v1.2.3-70-g09d2
From 5fa690804ebd0b915488530882564a241315ad09 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 7 Feb 2024 21:15:37 -0500
Subject: changed so link docs are added to common ancestor of anchors so that
they can appear above/below intermediary docs using z order. fixed dragging
to tab bar to start dragging document as a tab.
---
src/client/util/DocumentManager.ts | 14 ++++++++++++++
src/client/util/DragManager.ts | 2 +-
src/client/views/DocComponent.tsx | 1 +
src/client/views/MainView.tsx | 1 -
src/client/views/linking/LinkMenuItem.tsx | 6 ++++--
src/client/views/nodes/DocumentView.tsx | 5 ++---
src/client/views/nodes/LinkBox.tsx | 6 +++---
7 files changed, 25 insertions(+), 10 deletions(-)
(limited to 'src/client/views/linking')
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index eada5af75..7407fa2b3 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -209,6 +209,20 @@ export class DocumentManager {
finished?.();
};
+ public static LinkCommonAncestor(linkDoc: Doc) {
+ const anchor = (which: number) => {
+ const anch = DocCast(linkDoc['link_anchor_' + which]);
+ const anchor = anch?.layout_unrendered ? DocCast(anch.annotationOn) : anch;
+ return DocumentManager.Instance.getDocumentView(anchor);
+ };
+ const anchor1 = anchor(1);
+ const anchor2 = anchor(2);
+ return anchor1
+ ?.docViewPath()
+ .reverse()
+ .find(ancestor => anchor2?.docViewPath().includes(ancestor));
+ }
+
// shows a documentView by:
// traverses down through the viewPath of contexts to the view:
// focusing on each context
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 78356888a..1f093a33c 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -506,7 +506,7 @@ export namespace DragManager {
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey && e.altKey ? 'copy' : e.shiftKey ? 'move' : e.ctrlKey ? 'embed' : dragData.defaultDropAction;
}
- if (((e.target as any)?.className === 'lm_tabs' || (e.target as any)?.className === 'lm_header') && dragData.draggedDocuments.length === 1) {
+ if (['lm_tab', 'lm_title_wrap', 'lm_tabs', 'lm_header'].includes(typeof (e.target as any).className === 'string' ? (e.target as any)?.className : '') && dragData.draggedDocuments.length === 1) {
if (!startWindowDragTimer) {
startWindowDragTimer = setTimeout(async () => {
startWindowDragTimer = undefined;
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 99b9c3045..3d5a5b945 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -33,6 +33,7 @@ export interface ViewBoxInterface {
getView?: (doc: Doc, options: FocusViewOptions) => Promise>; // returns a nested DocumentView for the specified doc or undefined
addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
+ removeDocument?: (doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean) => boolean; // add a document (used only by collections)
select?: (ctrlKey: boolean, shiftKey: boolean) => void;
focus?: (textAnchor: Doc, options: FocusViewOptions) => Opt;
viewTransition?: () => Opt; // duration of a view transition animation
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index efe906981..b6cb845a6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -640,7 +640,6 @@ export class MainView extends ObservableReactComponent<{}> {
}
@computed get mainDocView() {
const headerBar = this._hideUI || !this.headerBarDocHeight?.() ? null : this.headerBarDocView;
- console.log('Header = ' + this._hideUI + ' ' + this.headerBarDocHeight?.() + ' ' + headerBar);
return (
<>
{headerBar}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 92c63cd56..a44ad26cd 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -9,6 +9,7 @@ import { Doc } from '../../../fields/Doc';
import { Cast, DocCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
import { DragManager } from '../../util/DragManager';
import { LinkFollower } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
@@ -76,8 +77,9 @@ export class LinkMenuItem extends ObservableReactComponent {
onIconDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, returnFalse, returnFalse, () => {
- if (!this._props.docView._props.removeDocument?.(this._props.linkDoc)) {
- this._props.docView._props.addDocument?.(this._props.linkDoc);
+ const ancestor = DocumentManager.LinkCommonAncestor(this._props.linkDoc);
+ if (!ancestor?.ComponentView?.removeDocument?.(this._props.linkDoc)) {
+ ancestor?.ComponentView?.addDocument?.(this._props.linkDoc);
}
});
};
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 042ae6e55..73c13b5dd 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { Dropdown, DropdownType, Type } from 'browndash-components';
import { Howl } from 'howler';
-import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal';
@@ -48,7 +48,6 @@ import { KeyValueBox } from './KeyValueBox';
import { LinkAnchorBox } from './LinkAnchorBox';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { PresEffect, PresEffectDirection } from './trails';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
interface Window {
MediaRecorder: MediaRecorder;
}
@@ -490,7 +489,7 @@ export class DocumentViewInternal extends DocComponent() {
const axf = a.screenToViewTransform(); // these force re-render when a or b moves (so do NOT remove)
const bxf = b.screenToViewTransform();
- const scale = !docView ? 1 : a.CollectionFreeFormView === docView.CollectionFreeFormView ? axf.Scale : bxf.Scale;
+ const scale = docView?.screenToViewTransform().Scale ?? 1;
const at = a.getBounds?.transition; // these force re-render when a or b change size and at the end of an animated transition
const bt = b.getBounds?.transition; // inquring getBounds() also causes text anchors to update whether or not they reflow (any size change triggers an invalidation)
// if there's an element in the DOM with a classname containing a link anchor's id (eg a hypertext ),
// 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(DocCast(this.dataDoc.link_anchor_1)[Id])).lastElement();
- const targetBhyperlink = Array.from(window.document.getElementsByClassName(DocCast(this.dataDoc.link_anchor_2)[Id])).lastElement();
+ const targetAhyperlink = Array.from(document.getElementsByClassName(DocCast(this.dataDoc.link_anchor_1)[Id])).lastElement();
+ const targetBhyperlink = Array.from(document.getElementsByClassName(DocCast(this.dataDoc.link_anchor_2)[Id])).lastElement();
const aid = targetAhyperlink?.id || a.Document[Id];
const bid = targetBhyperlink?.id || b.Document[Id];
--
cgit v1.2.3-70-g09d2
From 536cd22988407ab86099595daa4ffe4f054b2914 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 7 Feb 2024 22:43:00 -0500
Subject: fixed icon placement in linkMenuItem.
---
src/client/views/PropertiesView.tsx | 49 ++++++++++++++++--------------
src/client/views/linking/LinkMenuItem.scss | 32 +++++++++----------
src/client/views/linking/LinkMenuItem.tsx | 14 ++++-----
src/client/views/nodes/LinkBox.tsx | 16 +++++-----
4 files changed, 58 insertions(+), 53 deletions(-)
(limited to 'src/client/views/linking')
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 3ae2362a1..e4e7bec32 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -41,6 +41,7 @@ import { DocumentView, OpenWhere } from './nodes/DocumentView';
import { StyleProviderFuncType } from './nodes/FieldView';
import { KeyValueBox } from './nodes/KeyValueBox';
import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails';
+import { LinkBox } from './nodes/LinkBox';
const _global = (window /* browser */ || global) /* node */ as any;
interface PropertiesViewProps {
@@ -69,6 +70,10 @@ export class PropertiesView extends ObservableReactComponent LinkManager.Instance.currentLink,
+ () => this.selectedLink,
link => {
link && this.CloseAll();
link && (this.openLinks = true);
@@ -583,11 +588,11 @@ export class PropertiesView extends ObservableReactComponent
) : null}
- {LinkManager.Instance.currentLink?.title ? (
+ {this.selectedLink?.title ? (
<>
Link:
- {LinkManager.Instance.currentLink.title}
+ {this.selectedLink.title}
>
) : null}
@@ -1190,10 +1195,10 @@ export class PropertiesView extends ObservableReactComponent {
- if (LinkManager.Instance.currentLink && this.selectedDoc) {
+ if (this.selectedLink) {
this.setDescripValue(value);
}
}),
@@ -1212,7 +1217,7 @@ export class PropertiesView extends ObservableReactComponent {
- if (LinkManager.Instance.currentLink && this.selectedDoc) {
+ if (this.selectedLink) {
this.setlinkRelationshipValue(value);
}
}),
@@ -1221,17 +1226,17 @@ export class PropertiesView extends ObservableReactComponent {
- if (LinkManager.Instance.currentLink) {
- Doc.GetProto(LinkManager.Instance.currentLink).link_description = value;
+ if (this.selectedLink) {
+ this.selectedLink[DocData].link_description = value;
}
});
@undoBatch
setlinkRelationshipValue = action((value: string) => {
- if (LinkManager.Instance.currentLink) {
- const prevRelationship = StrCast(LinkManager.Instance.currentLink.link_relationship);
- LinkManager.Instance.currentLink.link_relationship = value;
- Doc.GetProto(LinkManager.Instance.currentLink).link_relationship = value;
+ if (this.selectedLink) {
+ const prevRelationship = StrCast(this.selectedLink.link_relationship);
+ this.selectedLink.link_relationship = value;
+ Doc.GetProto(this.selectedLink).link_relationship = value;
const linkRelationshipList = StrListCast(Doc.UserDoc().link_relationshipList);
const linkRelationshipSizes = NumListCast(Doc.UserDoc().link_relationshipSizes);
const linkColorList = StrListCast(Doc.UserDoc().link_ColorList);
@@ -1315,11 +1320,11 @@ export class PropertiesView extends ObservableReactComponent {
- setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.currentLink && (LinkManager.Instance.currentLink[prop] = !LinkManager.Instance.currentLink[prop]))));
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedLink && (this.selectedLink[prop] = !this.selectedLink[prop]))));
};
@computed get destinationAnchor() {
- const ldoc = LinkManager.Instance.currentLink;
+ const ldoc = this.selectedLink;
const lanch = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.Instance.currentLinkAnchor;
if (ldoc && lanch) return LinkManager.getOppositeAnchor(ldoc, lanch) ?? lanch;
return ldoc ? DocCast(ldoc.link_anchor_2) : ldoc;
@@ -1328,7 +1333,7 @@ export class PropertiesView extends ObservableReactComponent any = val => val) => {
@@ -1354,7 +1359,7 @@ export class PropertiesView extends ObservableReactComponent this.handlelinkRelationshipChange(e.currentTarget.value)}
@@ -1371,7 +1376,7 @@ export class PropertiesView extends ObservableReactComponent this.handleDescriptionChange(e.currentTarget.value)}
@@ -1393,7 +1398,7 @@ export class PropertiesView extends ObservableReactComponent
@@ -1424,7 +1429,7 @@ export class PropertiesView extends ObservableReactComponentOpening in new tab
Replacing current tab
Opening in same collection
- {LinkManager.Instance.currentLink?.linksToAnnotation ? Open in external page : null}
+ {this.selectedLink?.linksToAnnotation ? Open in external page : null}
@@ -1609,7 +1614,7 @@ export class PropertiesView extends ObservableReactComponent
@@ -1643,7 +1648,7 @@ export class PropertiesView extends ObservableReactComponent {
onPointerDown={this.onLinkButtonDown}>
Edit Link
}>
- e.stopPropagation()}>
-
+
e.stopPropagation()}>
+
+
+
+
Show/Hide Link }>
+
+
@@ -206,11 +211,6 @@ export class LinkMenuItem extends ObservableReactComponent {
) : null}
- Show/Hide Link
}>
-
-
-
-
Follow Link}>
{this._props.linkDoc.linksToAnnotation && Cast(this._props.destinationDoc.data, WebField)?.url.href === this._props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 998f4f7aa..decdbb240 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -23,7 +23,7 @@ export class LinkBox extends ViewBoxBaseComponent() {
return FieldView.LayoutString(LinkBox, fieldKey);
}
disposer: IReactionDisposer | undefined;
- @observable _forceAnimate = 0; // forces xArrow to animate when a transition is detected on something that affects an anchor
+ @observable _forceAnimate = 0; // forces xArrow to animate when a transition animation is detected on something that affects an anchor
@observable _hide = false; // don't render if anchor is not visible since that breaks xAnchor
constructor(props: FieldViewProps) {
@@ -38,22 +38,26 @@ export class LinkBox extends ViewBoxBaseComponent() {
const anchor = anch?.layout_unrendered ? DocCast(anch.annotationOn) : anch;
return DocumentManager.Instance.getDocumentView(anchor, this.DocumentView?.().containerViewPath?.().lastElement());
};
- componentWillUnmount(): void {
+ componentWillUnmount() {
this.disposer?.();
}
componentDidMount() {
this._props.setContentViewBox?.(this);
this.disposer = reaction(
- () => ({ drag: SnappingManager.IsDragging, a: this.anchor1, b: this.anchor2 }),
- ({ drag, a, b }) => {
+ () => ({ drag: SnappingManager.IsDragging }),
+ ({ drag }) => {
!LightboxView.Contains(this.DocumentView?.()) &&
setTimeout(
- // need to wait for drag manager to set 'hidden' flag on dragged elements
+ // need to wait for drag manager to set 'hidden' flag on dragged DOM elements
action(() => {
+ const a = this.anchor1,
+ b = this.anchor2;
let a1 = a && document.getElementById(a.Guid);
let a2 = b && document.getElementById(b.Guid);
+ // test whether the anchors themselves are hidden,...
if (!a1 || !a2 || (a?.ContentDiv as any)?.hidden || (b?.ContentDiv as any)?.hidden) this._hide = true;
else {
+ // .. or whether and of their DOM parents are hidden
for (; a1 && !a1.hidden; a1 = a1.parentElement);
for (; a2 && !a2.hidden; a2 = a2.parentElement);
this._hide = a1 || a2 ? true : false;
@@ -65,8 +69,6 @@ export class LinkBox extends ViewBoxBaseComponent() {
);
}
- select = (ctrlKey: boolean, shiftKey: boolean) => (LinkManager.Instance.currentLink = this.Document);
-
render() {
if (this._hide) return null;
const a = this.anchor1;
--
cgit v1.2.3-70-g09d2