From 99ffc8ec1508f05dfb126318b1215a3ee597738f Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Fri, 7 Jun 2019 18:30:48 -0400 Subject: Fixed some schema stuff? Tests aren't running --- src/new_fields/Schema.ts | 8 +++++--- src/new_fields/util.ts | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src/new_fields') diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts index 40ffaecd5..2355304d5 100644 --- a/src/new_fields/Schema.ts +++ b/src/new_fields/Schema.ts @@ -2,6 +2,7 @@ import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListS import { Doc, Field } from "./Doc"; import { ObjectField } from "./ObjectField"; import { RefField } from "./RefField"; +import { SelfProxy } from "./FieldSymbols"; type AllToInterface = { 1: ToInterface> & AllToInterface>, @@ -56,9 +57,10 @@ export function makeInterface(...schemas: T): InterfaceFu } }); const fn = (doc: Doc) => { - if (!(doc instanceof Doc)) { - throw new Error("Currently wrapping a schema in another schema isn't supported"); - } + doc = doc[SelfProxy]; + // if (!(doc instanceof Doc)) { + // throw new Error("Currently wrapping a schema in another schema isn't supported"); + // } const obj = Object.create(proto, { doc: { value: doc, writable: false } }); return obj; }; diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index ad07d05a3..d9c2a9866 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -6,7 +6,7 @@ import { FieldValue } from "./Types"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action } from "mobx"; -import { Parent, OnUpdate, Update, Id } from "./FieldSymbols"; +import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols"; import { ComputedField } from "../fields/ScriptField"; export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { @@ -18,6 +18,7 @@ export const setter = action(function (target: any, prop: string | symbol | numb target[prop] = value; return true; } + value = value[SelfProxy] || value; const curValue = target.__fields[prop]; if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically @@ -28,11 +29,10 @@ export const setter = action(function (target: any, prop: string | symbol | numb value = new ProxyField(value); } if (value instanceof ObjectField) { - //TODO Instead of target, maybe use target[Self] - if (value[Parent] && value[Parent] !== target) { + if (value[Parent] && value[Parent] !== receiver) { throw new Error("Can't put the same object in multiple documents at the same time"); } - value[Parent] = target; + value[Parent] = receiver; value[OnUpdate] = updateFunction(target, prop, value, receiver); } if (curValue instanceof ObjectField) { -- cgit v1.2.3-70-g09d2 From 6abf829099e4f61f2f557078f645fb9f2aa2414c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 8 Jun 2019 22:58:23 -0400 Subject: Moved ScriptField to new_fields --- src/client/util/Scripting.ts | 2 +- src/fields/ScriptField.ts | 93 ------------------------------------------- src/new_fields/ScriptField.ts | 93 +++++++++++++++++++++++++++++++++++++++++++ src/new_fields/util.ts | 2 +- 4 files changed, 95 insertions(+), 95 deletions(-) delete mode 100644 src/fields/ScriptField.ts create mode 100644 src/new_fields/ScriptField.ts (limited to 'src/new_fields') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40e2ad6bb..688716d5f 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -12,7 +12,7 @@ import { Doc, Field } from '../../new_fields/Doc'; import { ImageField, PdfField, VideoField, AudioField } from '../../new_fields/URLField'; import { List } from '../../new_fields/List'; import { RichTextField } from '../../new_fields/RichTextField'; -import { ScriptField, ComputedField } from '../../fields/ScriptField'; +import { ScriptField, ComputedField } from '../../new_fields/ScriptField'; export interface ScriptSucccess { success: true; diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts deleted file mode 100644 index d4fed1a1d..000000000 --- a/src/fields/ScriptField.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { ObjectField } from "../new_fields/ObjectField"; -import { CompiledScript, CompileScript } from "../client/util/Scripting"; -import { Copy, ToScriptString, Parent, SelfProxy } from "../new_fields/FieldSymbols"; -import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; -import { Deserializable } from "../client/util/SerializationHelper"; -import { computed } from "mobx"; - -function optional(propSchema: PropSchema) { - return custom(value => { - if (value !== undefined) { - return propSchema.serializer(value); - } - return SKIP; - }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => { - if (jsonValue !== undefined) { - return propSchema.deserializer(jsonValue, callback, context, oldValue); - } - return SKIP; - }); -} - -const optionsSchema = createSimpleSchema({ - requiredType: true, - addReturn: true, - typecheck: true, - params: optional(map(primitive())) -}); - -const scriptSchema = createSimpleSchema({ - options: object(optionsSchema), - originalScript: true -}); - -function deserializeScript(script: ScriptField) { - const comp = CompileScript(script.script.originalScript, script.script.options); - if (!comp.compiled) { - throw new Error("Couldn't compile loaded script"); - } - (script as any).script = comp; -} - -@Deserializable("script", deserializeScript) -export class ScriptField extends ObjectField { - @serializable(object(scriptSchema)) - readonly script: CompiledScript; - - constructor(script: CompiledScript) { - super(); - - this.script = script; - } - - // init(callback: (res: Field) => any) { - // const options = this.options!; - // const keys = Object.keys(options.options.capturedIds); - // Server.GetFields(keys).then(fields => { - // let captured: { [name: string]: Field } = {}; - // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); - // const opts: ScriptOptions = { - // addReturn: options.options.addReturn, - // params: options.options.params, - // requiredType: options.options.requiredType, - // capturedVariables: captured - // }; - // const script = CompileScript(options.script, opts); - // if (!script.compiled) { - // throw new Error("Can't compile script"); - // } - // this._script = script; - // callback(this); - // }); - // } - - [Copy](): ObjectField { - return new ScriptField(this.script); - } - - [ToScriptString]() { - return "script field"; - } -} - -@Deserializable("computed", deserializeScript) -export class ComputedField extends ScriptField { - @computed - get value() { - const val = this.script.run({ this: (this[Parent] as any)[SelfProxy] }); - if (val.success) { - return val.result; - } - return undefined; - } -} \ No newline at end of file diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts new file mode 100644 index 000000000..0839a823d --- /dev/null +++ b/src/new_fields/ScriptField.ts @@ -0,0 +1,93 @@ +import { ObjectField } from "./ObjectField"; +import { CompiledScript, CompileScript } from "../client/util/Scripting"; +import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols"; +import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; +import { Deserializable } from "../client/util/SerializationHelper"; +import { computed } from "mobx"; + +function optional(propSchema: PropSchema) { + return custom(value => { + if (value !== undefined) { + return propSchema.serializer(value); + } + return SKIP; + }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => { + if (jsonValue !== undefined) { + return propSchema.deserializer(jsonValue, callback, context, oldValue); + } + return SKIP; + }); +} + +const optionsSchema = createSimpleSchema({ + requiredType: true, + addReturn: true, + typecheck: true, + params: optional(map(primitive())) +}); + +const scriptSchema = createSimpleSchema({ + options: object(optionsSchema), + originalScript: true +}); + +function deserializeScript(script: ScriptField) { + const comp = CompileScript(script.script.originalScript, script.script.options); + if (!comp.compiled) { + throw new Error("Couldn't compile loaded script"); + } + (script as any).script = comp; +} + +@Deserializable("script", deserializeScript) +export class ScriptField extends ObjectField { + @serializable(object(scriptSchema)) + readonly script: CompiledScript; + + constructor(script: CompiledScript) { + super(); + + this.script = script; + } + + // init(callback: (res: Field) => any) { + // const options = this.options!; + // const keys = Object.keys(options.options.capturedIds); + // Server.GetFields(keys).then(fields => { + // let captured: { [name: string]: Field } = {}; + // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); + // const opts: ScriptOptions = { + // addReturn: options.options.addReturn, + // params: options.options.params, + // requiredType: options.options.requiredType, + // capturedVariables: captured + // }; + // const script = CompileScript(options.script, opts); + // if (!script.compiled) { + // throw new Error("Can't compile script"); + // } + // this._script = script; + // callback(this); + // }); + // } + + [Copy](): ObjectField { + return new ScriptField(this.script); + } + + [ToScriptString]() { + return "script field"; + } +} + +@Deserializable("computed", deserializeScript) +export class ComputedField extends ScriptField { + @computed + get value() { + const val = this.script.run({ this: this[Parent] }); + if (val.success) { + return val.result; + } + return undefined; + } +} \ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index d9c2a9866..a37abb732 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -7,7 +7,7 @@ import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action } from "mobx"; import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols"; -import { ComputedField } from "../fields/ScriptField"; +import { ComputedField } from "./ScriptField"; export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { -- cgit v1.2.3-70-g09d2 From c5e401cb0a7fec2279ceecbc8d1429dcdd2f04b9 Mon Sep 17 00:00:00 2001 From: Fawn Date: Wed, 19 Jun 2019 22:27:21 -0400 Subject: buttons on cut links functional except for when dragged from link menu --- src/client/documents/Documents.ts | 47 +++++++-- src/client/util/DragManager.ts | 7 ++ src/client/util/LinkManager.ts | 15 ++- .../CollectionFreeFormLinkView.scss | 59 ++++++++--- .../CollectionFreeFormLinkView.tsx | 2 +- .../CollectionFreeFormLinkWithProxyView.tsx | 109 +++++++++++++++++---- .../CollectionFreeFormLinksView.tsx | 42 +++++--- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 14 ++- src/client/views/nodes/FieldView.tsx | 6 ++ src/client/views/nodes/LinkBox.scss | 76 -------------- src/client/views/nodes/LinkBox.tsx | 107 -------------------- src/client/views/nodes/LinkButtonBox.scss | 18 ++++ src/client/views/nodes/LinkButtonBox.tsx | 63 ++++++++++++ src/client/views/nodes/LinkMenu.tsx | 4 +- src/client/views/nodes/LinkMenuItem.scss | 76 ++++++++++++++ src/client/views/nodes/LinkMenuItem.tsx | 107 ++++++++++++++++++++ src/new_fields/LinkButtonField.ts | 35 +++++++ 18 files changed, 548 insertions(+), 242 deletions(-) delete mode 100644 src/client/views/nodes/LinkBox.scss delete mode 100644 src/client/views/nodes/LinkBox.tsx create mode 100644 src/client/views/nodes/LinkButtonBox.scss create mode 100644 src/client/views/nodes/LinkButtonBox.tsx create mode 100644 src/client/views/nodes/LinkMenuItem.scss create mode 100644 src/client/views/nodes/LinkMenuItem.tsx create mode 100644 src/new_fields/LinkButtonField.ts (limited to 'src/new_fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 79ba433c8..4d4fa2645 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -26,7 +26,7 @@ import { OmitKeys } from "../../Utils"; import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; -import { Cast, NumCast } from "../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../new_fields/Types"; import { IconField } from "../../new_fields/IconField"; import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; @@ -36,6 +36,10 @@ import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; +import { LinkButtonBox } from "../views/nodes/LinkButtonBox"; +import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField"; +import { DocumentManager } from "../util/DocumentManager"; +import { Id } from "../../new_fields/FieldSymbols"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -102,14 +106,32 @@ export namespace DocUtils { linkDocProto.context = targetContext; - let proxy1 = Docs.TextDocument({ width: 300, height: 100, borderRounding: 0 }); - let proxy1Proto = Doc.GetProto(proxy1); - let proxy2 = Docs.TextDocument({ width: 300, height: 100, borderRounding: 0 }); - let proxy2Proto = Doc.GetProto(proxy2); + let sourceViews = DocumentManager.Instance.getDocumentViews(source); + let targetViews = DocumentManager.Instance.getDocumentViews(target); + sourceViews.forEach(sv => { + targetViews.forEach(tv => { - linkDocProto.proxy1 = proxy1; // src: 1 targ: 2 - linkDocProto.proxy2 = proxy2; // src: 2 targ: 1 + // TODO: do only for when diff contexts + let proxy1 = Docs.LinkButtonDocument( + { sourceViewId: StrCast(sv.props.Document[Id]), targetViewId: StrCast(tv.props.Document[Id]) }, + { width: 200, height: 100, borderRounding: 0 }); + let proxy1Proto = Doc.GetProto(proxy1); + proxy1Proto.sourceViewId = StrCast(sv.props.Document[Id]); + proxy1Proto.targetViewId = StrCast(tv.props.Document[Id]); + proxy1Proto.isLinkButton = true; + let proxy2 = Docs.LinkButtonDocument( + { sourceViewId: StrCast(tv.props.Document[Id]), targetViewId: StrCast(sv.props.Document[Id]) }, + { width: 200, height: 100, borderRounding: 0 }); + let proxy2Proto = Doc.GetProto(proxy2); + proxy2Proto.sourceViewId = StrCast(tv.props.Document[Id]); + proxy2Proto.targetViewId = StrCast(sv.props.Document[Id]); + proxy2Proto.isLinkButton = true; + + LinkManager.Instance.linkProxies.push(proxy1); + LinkManager.Instance.linkProxies.push(proxy2); + }); + }); LinkManager.Instance.allLinks.push(linkDoc); @@ -131,6 +153,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; + let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; const pdfProtoId = "pdfProto"; @@ -141,6 +164,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; + const linkProtoId = "linkProto"; export function initProtos(): Promise { return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { @@ -154,6 +178,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); + linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); }); } @@ -186,6 +211,11 @@ export namespace Docs { { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); return iconProto; } + function CreateLinkPrototype(): Doc { + let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), + { x: 0, y: 0, width: 300 }); + return linkProto; + } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" }); @@ -272,6 +302,9 @@ export namespace Docs { export function IconDocument(icon: string, options: DocumentOptions = {}) { return CreateInstance(iconProto, new IconField(icon), options); } + export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { + return CreateInstance(linkProto, new LinkButtonField(data), options); + } export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4787ac40f..be00778e7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -7,6 +7,8 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { LinkManager } from "./LinkManager"; import { URLField } from "../../new_fields/URLField"; import { SelectionManager } from "./SelectionManager"; +import { Docs } from "../documents/Documents"; +import { DocumentManager } from "./DocumentManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, options?: any, dontHideOnDrop?: boolean) { @@ -223,6 +225,11 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } + export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + runInAction(() => StartDragFunctions.map(func => func())); + StartDrag([ele], dragData, downX, downY, options); + } + export let AbortDrag: () => void = emptyFunction; function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 23ba9d2e4..544f2edda 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,4 +1,4 @@ -import { observable } from "mobx"; +import { observable, action } from "mobx"; import { StrCast, Cast } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; @@ -32,6 +32,7 @@ export class LinkManager { @observable public allLinks: Array = []; // list of link docs @observable public groupMetadataKeys: Map> = new Map(); // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls + @observable public linkProxies: Array = []; // list of linkbutton docs - used to visualize link when an anchors are not in the same context // finds all links that contain the given anchor public findAllRelatedLinks(anchor: Doc): Array { @@ -134,4 +135,16 @@ export class LinkManager { } } + @action + public addLinkProxy(proxy: Doc) { + LinkManager.Instance.linkProxies.push(proxy); + } + + public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { + let index = LinkManager.Instance.linkProxies.findIndex(p => { + return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; + }); + return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; + } + } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index fc5212edd..d8d518147 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -1,19 +1,50 @@ -.collectionfreeformlinkview-linkLine { - stroke: black; +// .collectionfreeformlinkview-linkLine { +// stroke: black; +// transform: translate(10000px,10000px); +// opacity: 0.5; +// pointer-events: all; +// } +// .collectionfreeformlinkview-linkCircle { +// stroke: rgb(0,0,0); +// opacity: 0.5; +// transform: translate(10000px,10000px); +// pointer-events: all; +// cursor: pointer; +// } +// .collectionfreeformlinkview-linkText { +// stroke: rgb(0,0,0); +// opacity: 0.5; +// transform: translate(10000px,10000px); +// pointer-events: all; +// } + +.linkview-ele { transform: translate(10000px,10000px); - opacity: 0.5; pointer-events: all; + + &.linkview-line { + stroke: black; + stroke-width: 2px; + opacity: 0.5; + } } -.collectionfreeformlinkview-linkCircle { - stroke: rgb(0,0,0); - opacity: 0.5; - transform: translate(10000px,10000px); - pointer-events: all; + +.linkview-button { + width: 200px; + height: 100px; + border-radius: 5px; + padding: 10px; + position: relative; + background-color: black; cursor: pointer; -} -.collectionfreeformlinkview-linkText { - stroke: rgb(0,0,0); - opacity: 0.5; - transform: translate(10000px,10000px); - pointer-events: all; + + p { + width: calc(100% - 20px); + color: white; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f995a35e3..13b5dc575 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -57,7 +57,7 @@ export class CollectionFreeFormLinkView extends React.Component - diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx index 81a00ba95..a4d122af2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx @@ -7,13 +7,17 @@ import React = require("react"); import v5 = require("uuid/v5"); import { DocumentView } from "../../nodes/DocumentView"; import { Docs } from "../../../documents/Documents"; -import { observable } from "mobx"; +import { observable, action } from "mobx"; +import { CollectionDockingView } from "../CollectionDockingView"; +import { dropActionType, DragManager } from "../../../util/DragManager"; +import { emptyFunction } from "../../../../Utils"; +import { DocumentManager } from "../../../util/DocumentManager"; export interface CollectionFreeFormLinkViewProps { sourceView: DocumentView; targetView: DocumentView; proxyDoc: Doc; - addDocTab: (document: Doc, where: string) => void; + // addDocTab: (document: Doc, where: string) => void; } @observer @@ -21,37 +25,104 @@ export class CollectionFreeFormLinkWithProxyView extends React.Component(); + private _downX: number = 0; + private _downY: number = 0; + @observable _x: number = 0; + @observable _y: number = 0; + // @observable private _proxyDoc: Doc = Docs.TextDocument(); // used for positioning + + @action + componentDidMount() { + let a2 = this.props.proxyDoc; + this._x = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); + this._y = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); + } + followButton = (e: React.PointerEvent): void => { - // TODO: would be nicer to open docview in context e.stopPropagation(); - console.log("follow"); - this.props.addDocTab(this.props.targetView.props.Document, "onRight"); + let open = this.props.targetView.props.ContainingCollectionView ? this.props.targetView.props.ContainingCollectionView.props.Document : this.props.targetView.props.Document; + CollectionDockingView.Instance.AddRightSplit(open); + DocumentManager.Instance.jumpToDocument(this.props.targetView.props.Document, e.altKey); + } + + @action + setPosition(x: number, y: number) { + this._x = x; + this._y = y; + } + + startDragging(x: number, y: number) { + if (this._ref.current) { + let dragData = new DragManager.DocumentDragData([this.props.proxyDoc]); + + DragManager.StartLinkProxyDrag(this._ref.current, dragData, x, y, { + handlers: { + dragComplete: action(() => { + let a2 = this.props.proxyDoc; + let offset = NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; + let x = NumCast(a2.x);// + NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; + let y = NumCast(a2.y);// + NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2; + this.setPosition(x, y); + + // this is a hack :'( theres prob a better way to make the input doc not render + let views = DocumentManager.Instance.getDocumentViews(this.props.proxyDoc); + views.forEach(dv => { + dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); + }); + }), + }, + hideSource: true //? + }); + } + } + + onPointerDown = (e: React.PointerEvent): void => { + this._downX = e.clientX; + this._downY = e.clientY; + + e.stopPropagation(); + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + onPointerMove = (e: PointerEvent): void => { + if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + this.startDragging(this._downX, this._downY); + } + e.stopPropagation(); + e.preventDefault(); + } + onPointerUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); } render() { let a1 = this.props.sourceView; - let a2 = this.props.proxyDoc; let x1 = NumCast(a1.Document.x) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.width) / NumCast(a1.Document.zoomBasis, 1) / 2); let y1 = NumCast(a1.Document.y) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.height) / NumCast(a1.Document.zoomBasis, 1) / 2); - let x2 = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); - let y2 = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); - - // let containing = ""; - // if (this.props.targetView.props.ContainingCollectionView) { - // containing = StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title); - // } + let context = this.props.targetView.props.ContainingCollectionView ? + (" in the context of " + StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title)) : ""; + let text = "link to " + StrCast(this.props.targetView.props.Document.title) + context; - // let text = "link to " + StrCast(this.props.targetView.props.Document.title) + (containing === "" ? "" : (" in the context of " + containing)); return ( <> - - {/* - {text} */} + x2={`${this._x}`} y2={`${this._y}`} /> + +
+

{text}

+
+
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index eaef1f32a..bde68001b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,4 +1,4 @@ -import { computed, IReactionDisposer, reaction } from "mobx"; +import { computed, IReactionDisposer, reaction, action } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -13,6 +13,8 @@ import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); import { CollectionFreeFormLinkWithProxyView } from "./CollectionFreeFormLinkWithProxyView"; import { Docs } from "../../../documents/Documents"; +import { LinkButtonField } from "../../../../new_fields/LinkButtonField"; +import { LinkManager } from "../../../util/LinkManager"; @observer export class CollectionFreeFormLinksView extends React.Component { @@ -195,18 +197,32 @@ export class CollectionFreeFormLinksView extends React.Component); } else { - let proxyKey = Doc.AreProtosEqual(u.sourceView.Document, Cast(u.linkDoc.anchor1, Doc, new Doc)) ? "proxy1" : "proxy2"; - let proxy = Cast(u.linkDoc[proxyKey], Doc, new Doc); - - let context = u.targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(u.targetView.props.ContainingCollectionView.props.Document.title)) : ""; - let text = proxyKey + " link to " + StrCast(u.targetView.props.Document.title) + context; - - let proxyProto = Doc.GetProto(proxy); - proxyProto.data = text; - - this.props.addDocument(proxy, false); - uniqueList.push(); + let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); + if (!proxy) { + proxy = Docs.LinkButtonDocument( + { sourceViewId: StrCast(u.sourceView.props.Document[Id]), targetViewId: StrCast(u.targetView.props.Document[Id]) }, + { width: 200, height: 100, borderRounding: 0 }); + let proxy1Proto = Doc.GetProto(proxy); + proxy1Proto.sourceViewId = StrCast(u.sourceView.props.Document[Id]); + proxy1Proto.targetViewId = StrCast(u.targetView.props.Document[Id]); + proxy1Proto.isLinkButton = true; + + // LinkManager.Instance.linkProxies.push(proxy); + LinkManager.Instance.addLinkProxy(proxy); + } + uniqueList.push(); + + // let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); + // if (proxy) { + // this.props.addDocument(proxy, false); + // uniqueList.push(); + // } + // let proxyKey = Doc.AreProtosEqual(u.sourceView.Document, Cast(u.linkDoc.anchor1, Doc, new Doc)) ? "proxy1" : "proxy2"; + // let proxy = Cast(u.linkDoc[proxyKey], Doc, new Doc); + // this.props.addDocument(proxy, false); + + // uniqueList.push(); } } }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 02396c3af..940b00a90 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -12,6 +12,7 @@ import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; import { IconBox } from "./IconBox"; +import { LinkButtonBox } from "./LinkButtonBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; @@ -103,7 +104,7 @@ export class DocumentContentsView extends React.Component(Docu var scaling = this.props.ContentScaling(); var nativeHeight = this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + + // // for linkbutton docs + // let isLinkButton = BoolCast(this.props.Document.isLinkButton); + // let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); + // let display = isLinkButton ? activeDvs.reduce((found, dv) => { + // let matchSv = this.props.Document.sourceViewId === StrCast(dv.props.Document[Id]); + // let matchTv = this.props.Document.targetViewId === StrCast(dv.props.Document[Id]); + // let match = matchSv || matchTv; + // return match || found; + // }, false) : true; + return (
(Docu background: this.Document.backgroundColor || "", width: nativeWidth, height: nativeHeight, - transform: `scale(${scaling}, ${scaling})` + transform: `scale(${scaling}, ${scaling})`, + // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 5a83de8e3..6fecb34d7 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -18,6 +18,8 @@ import { FormattedTextBox } from "./FormattedTextBox"; import { IconBox } from "./IconBox"; import { ImageBox } from "./ImageBox"; import { VideoBox } from "./VideoBox"; +import { LinkButtonBox } from "./LinkButtonBox"; +import { LinkButtonField } from "../../../new_fields/LinkButtonField"; // @@ -49,6 +51,7 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") { + console.log("LAYOUT STRING", fieldType.name, fieldStr); return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"} />`; } @@ -74,6 +77,9 @@ export class FieldView extends React.Component { else if (field instanceof IconField) { return ; } + else if (field instanceof LinkButtonField) { + return ; + } else if (field instanceof VideoField) { return ; } diff --git a/src/client/views/nodes/LinkBox.scss b/src/client/views/nodes/LinkBox.scss deleted file mode 100644 index 77462f611..000000000 --- a/src/client/views/nodes/LinkBox.scss +++ /dev/null @@ -1,76 +0,0 @@ -@import "../globalCssVariables"; -.linkMenu-item { - border-top: 0.5px solid $main-accent; - padding: 6px; - position: relative; - display: flex; - font-size: 12px; - - .linkMenu-item-content { - width: 100%; - } - - .link-metadata { - margin-top: 6px; - padding: 3px; - border-top: 0.5px solid $light-color-secondary; - .link-metadata-row { - margin-left: 6px; - } - } - - &:last-child { - border-bottom: 0.5px solid $main-accent; - } - &:hover { - .linkMenu-item-buttons { - display: flex; - } - .linkMenu-item-content { - &.expand-two { - width: calc(100% - 46px); - } - &.expand-three { - width: calc(100% - 78px); - } - } - } -} - -.linkMenu-item-buttons { - display: none; - position: absolute; - top: 50%; - right: 0; - transform: translateY(-50%); - - .button { - width: 20px; - height: 20px; - margin: 0; - margin-right: 6px; - border-radius: 50%; - cursor: pointer; - pointer-events: auto; - background-color: $dark-color; - color: $light-color; - font-size: 65%; - transition: transform 0.2s; - text-align: center; - position: relative; - - .fa-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - - &:last-child { - margin-right: 0; - } - &:hover { - background: $main-accent; - } - } -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx deleted file mode 100644 index 8d07547ed..000000000 --- a/src/client/views/nodes/LinkBox.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEdit, faEye, faTimes, faArrowRight, faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { observer } from "mobx-react"; -import { DocumentManager } from "../../util/DocumentManager"; -import { undoBatch } from "../../util/UndoManager"; -import './LinkBox.scss'; -import React = require("react"); -import { Doc } from '../../../new_fields/Doc'; -import { StrCast, Cast } from '../../../new_fields/Types'; -import { observable, action } from 'mobx'; -import { LinkManager } from '../../util/LinkManager'; -import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; -import { SelectionManager } from '../../util/SelectionManager'; -library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); - - -interface Props { - groupType: string; - linkDoc: Doc; - sourceDoc: Doc; - destinationDoc: Doc; - showEditor: () => void; -} - -@observer -export class LinkBox extends React.Component { - private _drag = React.createRef(); - @observable private _showMore: boolean = false; - @action toggleShowMore() { this._showMore = !this._showMore; } - - @undoBatch - onFollowLink = async (e: React.PointerEvent): Promise => { - e.stopPropagation(); - DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); - } - - onEdit = (e: React.PointerEvent): void => { - e.stopPropagation(); - this.props.showEditor(); - } - - renderMetadata = (): JSX.Element => { - let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); - let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase()); - let groupDoc = index > -1 ? groups[index] : undefined; - - let mdRows: Array = []; - if (groupDoc) { - let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); - mdRows = keys!.map(key => { - return (
{key}: {StrCast(mdDoc[key])}
); - }); - } - - return (
{mdRows}
); - } - - onLinkButtonDown = (e: React.PointerEvent): void => { - e.stopPropagation(); - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.addEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - document.addEventListener("pointerup", this.onLinkButtonUp); - } - - onLinkButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - e.stopPropagation(); - } - - onLinkButtonMoved = async (e: PointerEvent) => { - if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) { - document.removeEventListener("pointermove", this.onLinkButtonMoved); - document.removeEventListener("pointerup", this.onLinkButtonUp); - - DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc); - } - e.stopPropagation(); - } - - render() { - - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); - let canExpand = keys ? keys.length > 0 : false; - - return ( -
-
-
-

{StrCast(this.props.destinationDoc.title)}

-
- {canExpand ?
this.toggleShowMore()}> -
: <>} -
-
-
-
- {this._showMore ? this.renderMetadata() : <>} -
- -
- ); - } -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss new file mode 100644 index 000000000..24bfd2c9f --- /dev/null +++ b/src/client/views/nodes/LinkButtonBox.scss @@ -0,0 +1,18 @@ +.linkBox-cont { + width: 200px; + height: 100px; + background-color: black; + text-align: center; + color: white; + padding: 10px; + border-radius: 5px; + position: relative; + + .linkBox-cont-wrapper { + width: calc(100% - 20px); + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx new file mode 100644 index 000000000..8a7c1ed8b --- /dev/null +++ b/src/client/views/nodes/LinkButtonBox.tsx @@ -0,0 +1,63 @@ +import React = require("react"); +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { computed, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { FieldView, FieldViewProps } from './FieldView'; +import "./LinkButtonBox.scss"; +import { DocumentView } from "./DocumentView"; +import { Doc } from "../../../new_fields/Doc"; +import { LinkButtonField } from "../../../new_fields/LinkButtonField"; +import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { DocumentManager } from "../../util/DocumentManager"; +import { Id } from "../../../new_fields/FieldSymbols"; + +library.add(faCaretUp); +library.add(faObjectGroup); +library.add(faStickyNote); +library.add(faFilePdf); +library.add(faFilm); + +@observer +export class LinkButtonBox extends React.Component { + public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } + + followLink = (): void => { + console.log("follow link???"); + let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); + let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); + if (targetView && targetView.props.ContainingCollectionView) { + CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); + } + } + + render() { + + let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); + let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); + + let text = "Could not find link"; + if (targetView) { + let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; + text = "Link to " + StrCast(targetView.props.Document.title) + context; + } + + let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); + let display = activeDvs.reduce((found, dv) => { + let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); + let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); + let match = matchSv || matchTv; + return match || found; + }, false); + + return ( +
+
+

{text}

+
+
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index aef56df67..7e4c15312 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -1,7 +1,7 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { DocumentView } from "./DocumentView"; -import { LinkBox } from "./LinkBox"; +import { LinkMenuItem } from "./LinkMenuItem"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); @@ -23,7 +23,7 @@ export class LinkMenu extends React.Component { let source = this.props.docView.Document; return group.map(linkDoc => { let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); - return this._editingLink = linkDoc)} />; + return this._editingLink = linkDoc)} />; }); } diff --git a/src/client/views/nodes/LinkMenuItem.scss b/src/client/views/nodes/LinkMenuItem.scss new file mode 100644 index 000000000..77462f611 --- /dev/null +++ b/src/client/views/nodes/LinkMenuItem.scss @@ -0,0 +1,76 @@ +@import "../globalCssVariables"; +.linkMenu-item { + border-top: 0.5px solid $main-accent; + padding: 6px; + position: relative; + display: flex; + font-size: 12px; + + .linkMenu-item-content { + width: 100%; + } + + .link-metadata { + margin-top: 6px; + padding: 3px; + border-top: 0.5px solid $light-color-secondary; + .link-metadata-row { + margin-left: 6px; + } + } + + &:last-child { + border-bottom: 0.5px solid $main-accent; + } + &:hover { + .linkMenu-item-buttons { + display: flex; + } + .linkMenu-item-content { + &.expand-two { + width: calc(100% - 46px); + } + &.expand-three { + width: calc(100% - 78px); + } + } + } +} + +.linkMenu-item-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + cursor: pointer; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + &:hover { + background: $main-accent; + } + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx new file mode 100644 index 000000000..c68365584 --- /dev/null +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -0,0 +1,107 @@ +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faEdit, faEye, faTimes, faArrowRight, faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { observer } from "mobx-react"; +import { DocumentManager } from "../../util/DocumentManager"; +import { undoBatch } from "../../util/UndoManager"; +import './LinkMenuItem.scss'; +import React = require("react"); +import { Doc } from '../../../new_fields/Doc'; +import { StrCast, Cast } from '../../../new_fields/Types'; +import { observable, action } from 'mobx'; +import { LinkManager } from '../../util/LinkManager'; +import { DragLinksAsDocuments, DragLinkAsDocument } from '../../util/DragManager'; +import { SelectionManager } from '../../util/SelectionManager'; +library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); + + +interface LinkMenuItemProps { + groupType: string; + linkDoc: Doc; + sourceDoc: Doc; + destinationDoc: Doc; + showEditor: () => void; +} + +@observer +export class LinkMenuItem extends React.Component { + private _drag = React.createRef(); + @observable private _showMore: boolean = false; + @action toggleShowMore() { this._showMore = !this._showMore; } + + @undoBatch + onFollowLink = async (e: React.PointerEvent): Promise => { + e.stopPropagation(); + DocumentManager.Instance.jumpToDocument(this.props.destinationDoc, e.altKey); + } + + onEdit = (e: React.PointerEvent): void => { + e.stopPropagation(); + this.props.showEditor(); + } + + renderMetadata = (): JSX.Element => { + let groups = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); + let index = groups.findIndex(groupDoc => StrCast(groupDoc.type).toUpperCase() === this.props.groupType.toUpperCase()); + let groupDoc = index > -1 ? groups[index] : undefined; + + let mdRows: Array = []; + if (groupDoc) { + let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); + let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + mdRows = keys!.map(key => { + return (
{key}: {StrCast(mdDoc[key])}
); + }); + } + + return (
{mdRows}
); + } + + onLinkButtonDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.addEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + document.addEventListener("pointerup", this.onLinkButtonUp); + } + + onLinkButtonUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + e.stopPropagation(); + } + + onLinkButtonMoved = async (e: PointerEvent) => { + if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) { + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + + DragLinkAsDocument(this._drag.current, e.x, e.y, this.props.linkDoc, this.props.sourceDoc); + } + e.stopPropagation(); + } + + render() { + + let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + let canExpand = keys ? keys.length > 0 : false; + + return ( +
+
+
+

{StrCast(this.props.destinationDoc.title)}

+
+ {canExpand ?
this.toggleShowMore()}> +
: <>} +
+
+
+
+ {this._showMore ? this.renderMetadata() : <>} +
+ +
+ ); + } +} \ No newline at end of file diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts new file mode 100644 index 000000000..92e1ed922 --- /dev/null +++ b/src/new_fields/LinkButtonField.ts @@ -0,0 +1,35 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive, createSimpleSchema, object } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString } from "./FieldSymbols"; +import { Doc } from "./Doc"; +import { DocumentView } from "../client/views/nodes/DocumentView"; + +export type LinkButtonData = { + sourceViewId: string, + targetViewId: string +}; + +const LinkButtonSchema = createSimpleSchema({ + sourceViewId: true, + targetViewId: true +}); + +@Deserializable("linkButton") +export class LinkButtonField extends ObjectField { + @serializable(object(LinkButtonSchema)) + readonly data: LinkButtonData; + + constructor(data: LinkButtonData) { + super(); + this.data = data; + } + + [Copy]() { + return new LinkButtonField(this.data); + } + + [ToScriptString]() { + return "invalid"; + } +} -- cgit v1.2.3-70-g09d2 From 542501fc3247dafa5879482f62a28147ec4b3793 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 21 Jun 2019 13:13:18 -0400 Subject: lots more template stuff. --- src/client/util/DocumentManager.ts | 6 +- src/client/util/DragManager.ts | 18 ++-- src/client/views/DocumentDecorations.tsx | 35 ++++---- src/client/views/MainOverlayTextBox.tsx | 8 +- .../views/collections/CollectionDockingView.tsx | 100 +++++++++++---------- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 33 +++++-- src/client/views/collections/CollectionView.tsx | 4 +- .../views/collections/ParentDocumentSelector.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 27 +++--- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 43 +++++---- src/client/views/pdf/PDFViewer.tsx | 1 - src/new_fields/Doc.ts | 8 ++ 15 files changed, 174 insertions(+), 119 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 862395d74..8421c902e 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -138,7 +138,7 @@ export class DocumentManager { docContext.panTransformType = "Ease"; targetContextView.props.focus(docDelegate); } else { - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext); + (dockFunc || CollectionDockingView.Instance.AddRightSplit)(docContext, docContext); setTimeout(() => { this.jumpToDocument(docDelegate, forceDockFunc, dockFunc, linkPage); }, 10); @@ -147,7 +147,7 @@ export class DocumentManager { const actualDoc = Doc.MakeAlias(docDelegate); actualDoc.libraryBrush = true; if (linkPage !== undefined) actualDoc.curPage = linkPage; - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc); + (dockFunc || CollectionDockingView.Instance.AddRightSplit)(actualDoc, actualDoc); } } else { let contextView: DocumentView | null; @@ -156,7 +156,7 @@ export class DocumentManager { contextDoc.panTransformType = "Ease"; contextView.props.focus(docDelegate); } else { - (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc); + (dockFunc || CollectionDockingView.Instance.AddRightSplit)(contextDoc, contextDoc); setTimeout(() => { this.jumpToDocument(docDelegate, forceDockFunc, dockFunc, linkPage); }, 10); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c3c92daa5..cc83b7346 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -15,7 +15,8 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); - var dragData = new DragManager.DocumentDragData([await docFunc()]); + let doc = await docFunc(); + var dragData = new DragManager.DocumentDragData([doc], [doc]); dragData.dropAction = dropAction; dragData.moveDocument = moveFunc; dragData.options = options; @@ -31,7 +32,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () if (e.button === 0) { e.stopPropagation(); if (e.shiftKey && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag([await docFunc()], e); + CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]); } else { document.addEventListener("pointermove", onRowMove); document.addEventListener("pointerup", onRowUp); @@ -59,7 +60,8 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let doc = await Cast(draggedDoc.annotationOn, Doc); if (doc) moddrag.push(doc); } - let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); + let linkDocs = moddrag.length ? moddrag : draggedDocs; + let dragData = new DragManager.DocumentDragData(linkDocs, linkDocs); // bcz: dataDocs? DragManager.StartDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), @@ -141,13 +143,15 @@ export namespace DragManager { export type MoveFunction = (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; export class DocumentDragData { - constructor(dragDoc: Doc[]) { + constructor(dragDoc: Doc[], dragDataDocs: Doc[]) { this.draggedDocuments = dragDoc; + this.draggedDataDocs = dragDataDocs; this.droppedDocuments = dragDoc; this.xOffset = 0; this.yOffset = 0; } draggedDocuments: Doc[]; + draggedDataDocs: Doc[]; droppedDocuments: Doc[]; xOffset: number; yOffset: number; @@ -238,6 +242,8 @@ export namespace DragManager { const docs: Doc[] = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : []; + const datadocs: Doc[] = + dragData instanceof DocumentDragData ? dragData.draggedDataDocs : dragData instanceof AnnotationDragData ? [dragData.dragDocument] : []; let dragElements = eles.map(ele => { const w = ele.offsetWidth, h = ele.offsetHeight; @@ -314,12 +320,12 @@ export namespace DragManager { } if (e.shiftKey && CollectionDockingView.Instance) { AbortDrag(); - CollectionDockingView.Instance.StartOtherDrag(docs, { + CollectionDockingView.Instance.StartOtherDrag({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 - }); + }, docs, datadocs); } //TODO: Why can't we use e.movementX and e.movementY? let moveX = e.pageX - lastX; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3a366b4d2..8f02f04d7 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -166,7 +166,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let dragDocView = SelectionManager.SelectedDocuments()[0]; const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0); const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top); - let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); + let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc)); dragData.xOffset = xoff; dragData.yOffset = yoff; dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument; @@ -485,9 +485,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; - let zoomBasis = NumCast(doc.zoomBasis, 1); - let width = (doc.width || 0) / zoomBasis; - let height = (doc.height || (nheight / nwidth * width)) / zoomBasis; + let width = (doc.width || 0); + let height = (doc.height || (nheight / nwidth * width)); let scale = element.props.ScreenToLocalTransform().Scale; let actualdW = Math.max(width + (dW * scale), 20); let actualdH = Math.max(height + (dH * scale), 20); @@ -502,25 +501,27 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } if (nwidth > 0 && nheight > 0) { if (Math.abs(dW) > Math.abs(dH)) { - if (!fixedAspect) proto.nativeWidth = zoomBasis * actualdW / (doc.width || 1) * NumCast(proto.nativeWidth); - doc.width = zoomBasis * actualdW; - // doc.zoomBasis = zoomBasis * width / actualdW; + if (!fixedAspect) { + Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true); + } + doc.width = actualdW; if (fixedAspect) doc.height = nheight / nwidth * doc.width; - else doc.height = zoomBasis * actualdH; - proto.nativeHeight = (doc.height || 0) / doc.width * NumCast(proto.nativeWidth); + else doc.height = actualdH; + Doc.SetInPlace(element.props.Document, "nativeHeight", (doc.height || 0) / doc.width * (doc.nativeWidth || 0), true); } else { - if (!fixedAspect) proto.nativeHeight = zoomBasis * actualdH / (doc.height || 1) * NumCast(proto.nativeHeight); - doc.height = zoomBasis * actualdH; - //doc.zoomBasis = zoomBasis * height / actualdH; + if (!fixedAspect) { + Doc.SetInPlace(element.props.Document, "nativeHeight", actualdH / (doc.height || 1) * (doc.nativeHeight || 0), true); + } + doc.height = actualdH; if (fixedAspect) doc.width = nwidth / nheight * doc.height; - else doc.width = zoomBasis * actualdW; - proto.nativeWidth = (doc.width || 0) / doc.height * NumCast(proto.nativeHeight); + else doc.width = actualdW; + Doc.SetInPlace(element.props.Document, "nativeWidth", (doc.width || 0) / doc.height * (doc.nativeHeight || 0), true); } } else { - dW && (doc.width = zoomBasis * actualdW); - dH && (doc.height = zoomBasis * actualdH); - proto.autoHeight = undefined; + dW && (doc.width = actualdW); + dH && (doc.height = actualdH); + Doc.SetInPlace(element.props.Document, "autoHeight", undefined, true); } } }); diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 64271650b..73d59ce31 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -82,10 +82,10 @@ export class MainOverlayTextBox extends React.Component } @action textBoxMove = (e: PointerEvent) => { - if (e.movementX > 1 || e.movementY > 1) { + if ((e.movementX > 1 || e.movementY > 1) && FormattedTextBox.InputBoxOverlay) { document.removeEventListener("pointermove", this.textBoxMove); document.removeEventListener('pointerup', this.textBoxUp); - let dragData = new DragManager.DocumentDragData(FormattedTextBox.InputBoxOverlay ? [FormattedTextBox.InputBoxOverlay.props.Document] : []); + let dragData = new DragManager.DocumentDragData([FormattedTextBox.InputBoxOverlay.props.Document], [FormattedTextBox.InputBoxOverlay.props.DataDoc]); const [left, top] = this._textXf().inverse().transformPoint(0, 0); dragData.xOffset = e.clientX - left; dragData.yOffset = e.clientY - top; @@ -102,9 +102,9 @@ export class MainOverlayTextBox extends React.Component document.removeEventListener('pointerup', this.textBoxUp); } - addDocTab = (doc: Doc, location: string) => { + addDocTab = (doc: Doc, dataDoc: Doc, location: string) => { if (true) { // location === "onRight") { need to figure out stack to add "inTab" - CollectionDockingView.Instance.AddRightSplit(doc); + CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } } render() { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 79a0d18df..89fadab57 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -28,7 +28,7 @@ import { MainView } from '../MainView'; @observer export class CollectionDockingView extends React.Component { public static Instance: CollectionDockingView; - public static makeDocumentConfig(document: Doc, width?: number) { + public static makeDocumentConfig(document: Doc, dataDoc: Doc, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', @@ -36,6 +36,7 @@ export class CollectionDockingView extends React.Component - this.AddRightSplit(dragDoc, true).contentItems[0].tab._dragListener. + dragDocs.map((dragDoc, i) => + this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : dragDoc, true).contentItems[0].tab._dragListener. onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); } @action - public OpenFullScreen(document: Doc) { + public OpenFullScreen(document: Doc, dataDoc: Doc) { let newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document)] + content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)] }; var docconfig = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); this._goldenLayout.root.contentItems[0].addChild(docconfig); @@ -123,14 +124,14 @@ export class CollectionDockingView extends React.Component { + public AddRightSplit = (document: Doc, dataDoc: Doc, minimize: boolean = false) => { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); } let newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document)] + content: [CollectionDockingView.makeDocumentConfig(document, dataDoc)] }; var newContentItem = this._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, this._goldenLayout); @@ -160,12 +161,12 @@ export class CollectionDockingView extends React.Component { + public AddTab = (stack: any, document: Doc, dataDocument: Doc) => { let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); } - let docContentConfig = CollectionDockingView.makeDocumentConfig(document); + let docContentConfig = CollectionDockingView.makeDocumentConfig(document, dataDocument); var newContentItem = stack.layoutManager.createContentItem(docContentConfig, this._goldenLayout); stack.addChild(newContentItem.contentItems[0], undefined); this.layoutChanged(); @@ -281,14 +282,16 @@ export class CollectionDockingView extends React.Component) => { + DocServer.GetRefField(docid).then(action(async (f: Opt) => { if (f instanceof Doc) { - DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f]), x, y, + let dataDoc = (datadocid !== docid) ? await DocServer.GetRefField(datadocid) : f; + DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f], [dataDoc instanceof Doc ? dataDoc : f]), x, y, { handlers: { dragComplete: emptyFunction, @@ -338,33 +341,34 @@ export class CollectionDockingView extends React.Component { - if (doc instanceof Doc) { - let counter: any = this.htmlToElement(`0
`); - tab.element.append(counter); - let upDiv = document.createElement("span"); - const stack = tab.contentItem.parent; - // shifts the focus to this tab when another tab is dragged over it - tab.element[0].onmouseenter = (e: any) => { - if (!this._isPointerDown) return; - var activeContentItem = tab.header.parent.getActiveContentItem(); - if (tab.contentItem !== activeContentItem) { - tab.header.parent.setActiveContentItem(tab.contentItem); - } - tab.setActive(true); - }; - ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc)} />, upDiv); - tab.reactComponents = [upDiv]; - tab.element.append(upDiv); - counter.DashDocId = tab.contentItem.config.props.documentId; - tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], - () => { - counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; - tab.titleElement[0].textContent = doc.title; - }, { fireImmediately: true }); - tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; - } - }); + let doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId) as Doc; + let dataDoc = await DocServer.GetRefField(tab.contentItem.config.props.dataDocumentId) as Doc; + if (doc instanceof Doc && dataDoc instanceof Doc) { + let counter: any = this.htmlToElement(`0`); + tab.element.append(counter); + let upDiv = document.createElement("span"); + const stack = tab.contentItem.parent; + // shifts the focus to this tab when another tab is dragged over it + tab.element[0].onmouseenter = (e: any) => { + if (!this._isPointerDown) return; + var activeContentItem = tab.header.parent.getActiveContentItem(); + if (tab.contentItem !== activeContentItem) { + tab.header.parent.setActiveContentItem(tab.contentItem); + } + tab.setActive(true); + }; + ReactDOM.render( CollectionDockingView.Instance.AddTab(stack, doc, dataDoc)} />, upDiv); + tab.reactComponents = [upDiv]; + tab.element.append(upDiv); + counter.DashDocId = tab.contentItem.config.props.documentId; + counter.DashDataDocId = tab.contentItem.config.props.dataDocumentId; + tab.reactionDisposer = reaction(() => [doc.linkedFromDocs, doc.LinkedToDocs, doc.title], + () => { + counter.innerHTML = DocListCast(doc.linkedFromDocs).length + DocListCast(doc.linkedToDocs).length; + tab.titleElement[0].textContent = doc.title; + }, { fireImmediately: true }); + tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; + } } tab.titleElement[0].Tab = tab; tab.closeElement.off('click') //unbind the current click handler @@ -427,6 +431,7 @@ export class CollectionDockingView extends React.Component { @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; + @observable private _dataDoc: Opt; get _stack(): any { let parent = (this.props as any).glContainer.parent.parent; if (this._document && this._document.excludeFromLibrary && parent.parent && parent.parent.contentItems.length > 1) { @@ -444,7 +450,11 @@ export class DockedFrameRenderer extends React.Component { } constructor(props: any) { super(props); - DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => this._document = f as Doc)); + DocServer.GetRefField(this.props.documentId).then(action((f: Opt) => { + this._document = f as Doc; + if (!this.props.dataDocumentId || this.props.documentId === this.props.dataDocumentId) this._dataDoc = this._document; + })); + if (this.props.dataDocumentId && this.props.documentId !== this.props.dataDocumentId) DocServer.GetRefField(this.props.dataDocumentId).then(action((f: Opt) => this._dataDoc = f as Doc)); } nativeWidth = () => NumCast(this._document!.nativeWidth, this._panelWidth); @@ -481,17 +491,17 @@ export class DockedFrameRenderer extends React.Component { } get previewPanelCenteringOffset() { return (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2; } - addDocTab = (doc: Doc, location: string) => { + addDocTab = (doc: Doc, dataDoc: Doc, location: string) => { if (doc.dockingConfig) { MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { - CollectionDockingView.Instance.AddRightSplit(doc); + CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } else { - CollectionDockingView.Instance.AddTab(this._stack, doc); + CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc); } } get content() { - if (!this._document) { + if (!this._document || !this._dataDoc) { return (null); } return ( @@ -499,7 +509,7 @@ export class DockedFrameRenderer extends React.Component { style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px) scale(${this.scaleToFitMultiplier}, ${this.scaleToFitMultiplier})` }}> boolean; active: () => boolean; whenActiveChanged: (isActive: boolean) => void; - addDocTab: (document: Doc, where: string) => void; + addDocTab: (document: Doc, dataDoc: Doc, where: string) => void; setPreviewScript: (script: string) => void; previewScript?: string; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 18d6751c6..83a7c9e3a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -26,6 +26,8 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { FormattedTextBox } from '../nodes/FormattedTextBox'; +import { ImageField } from '../../../new_fields/URLField'; +import { ImageBox } from '../nodes/ImageBox'; export interface TreeViewProps { @@ -35,7 +37,7 @@ export interface TreeViewProps { deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; - addDocTab: (doc: Doc, where: string) => void; + addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void; panelWidth: () => number; panelHeight: () => number; addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; @@ -73,7 +75,7 @@ class TreeView extends React.Component { } @undoBatch delete = () => this.props.deleteDoc(this.props.document); - @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "onRight"); + @undoBatch openRight = async () => this.props.addDocTab(this.props.document, this.props.dataDoc, "onRight"); onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { @@ -148,6 +150,25 @@ class TreeView extends React.Component { SetValue={(value: string) => { let res = (Doc.GetProto(this.props.document)[key] = value) ? true : true; + if (value.startsWith(">>")) { + let metaKey = value.slice(2, value.length); + let collection = this.props.containingCollection; + Doc.GetProto(collection)[metaKey] = new ImageField("http://www.cs.brown.edu/~bcz/face.gif"); + let template = Doc.MakeAlias(collection); + template.title = metaKey; + template.embed = true; + template.layout = ImageBox.LayoutString(metaKey); + template.x = 0; + template.y = 0; + template.nativeWidth = 300; + template.nativeHeight = 300; + template.width = 300; + template.height = 300; + template.isTemplate = true; + template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); + Doc.AddDocToList(collection, "data", template); + this.delete(); + } if (value.startsWith(">")) { let metaKey = value.slice(1, value.length); let collection = this.props.containingCollection; @@ -236,10 +257,10 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.props.document)) }); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.KVPDocument(this.props.document, { width: 300, height: 300 }), "onRight"), icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { - ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, "inTab"), icon: "folder" }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, "onRight"), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.props.dataDoc, "inTab"), icon: "folder" }); + ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.props.dataDoc, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.props.document).length) { ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.props.document).map(view => view.props.focus(this.props.document)) }); } @@ -339,7 +360,7 @@ class TreeView extends React.Component { remove: ((doc: Doc) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, - addDocTab: (doc: Doc, where: string) => void, + addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void, screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: () => boolean, diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 89e5ad74c..10e6fb885 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -18,6 +18,7 @@ import { Doc } from '../../../new_fields/Doc'; import { FormattedTextBox } from '../nodes/FormattedTextBox'; import { Docs } from '../../documents/Documents'; import { List } from '../../../new_fields/List'; +import { ImageField } from '../../../new_fields/URLField'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh); @@ -63,8 +64,9 @@ export class CollectionView extends React.Component { let otherdoc = Docs.TextDocument({ width: 100, height: 50, title: "applied template" }); Doc.GetProto(otherdoc).description = "THIS DESCRIPTION IS REALLY IMPORTANT!"; Doc.GetProto(otherdoc).summary = "THIS SUMMARY IS MEANINGFUL!"; + Doc.GetProto(otherdoc).photo = new ImageField("http://www.cs.brown.edu/~bcz/snowbeast.JPG"); Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document); - this.props.addDocTab && this.props.addDocTab(otherdoc, "onRight"); + this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight"); }), icon: "project-diagram" }); } diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index f11af04a3..fa7058d5a 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -9,7 +9,7 @@ import { CollectionDockingView } from "./CollectionDockingView"; import { NumCast } from "../../../new_fields/Types"; import { CollectionViewType } from "./CollectionBaseView"; -type SelectorProps = { Document: Doc, addDocTab(doc: Doc, location: string): void }; +type SelectorProps = { Document: Doc, addDocTab(doc: Doc, dataDoc: Doc, location: string): void }; @observer export class SelectorContextMenu extends React.Component { @observable private _docs: { col: Doc, target: Doc }[] = []; @@ -43,7 +43,7 @@ export class SelectorContextMenu extends React.Component { col.panX = newPanX; col.panY = newPanY; } - this.props.addDocTab(col, "inTab"); + this.props.addDocTab(col, col, "inTab"); // bcz: dataDoc? }; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index da50d0b53..08f58dcd6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -81,7 +81,7 @@ export interface DocumentViewProps { parentActive: () => boolean; whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc) => void; - addDocTab: (doc: Doc, where: string) => void; + addDocTab: (doc: Doc, dataDoc: Doc, where: string) => void; animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; } @@ -212,8 +212,9 @@ export class DocumentView extends DocComponent(Docu startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { if (this._mainCont.current) { let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; + let alldataConnected = [this.props.DataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); - let dragData = new DragManager.DocumentDragData(allConnected); + let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; dragData.xOffset = xoff; @@ -268,7 +269,7 @@ export class DocumentView extends DocComponent(Docu let altKey = e.altKey; let ctrlKey = e.ctrlKey; if (this._doubleTap && !this.props.isTopMost) { - this.props.addDocTab(this.props.Document, "inTab"); + this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; } @@ -311,7 +312,7 @@ export class DocumentView extends DocComponent(Docu if (dataDocs) { expandedDocs.forEach(maxDoc => (!CollectionDockingView.Instance.CloseRightSplit(Doc.GetProto(maxDoc)) && - this.props.addDocTab(getDispDoc(maxDoc), maxLocation))); + this.props.addDocTab(getDispDoc(maxDoc), getDispDoc(maxDoc), maxLocation))); } } else { let scrpt = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(NumCast(this.Document.width) / 2, NumCast(this.Document.height) / 2); @@ -334,7 +335,7 @@ export class DocumentView extends DocComponent(Docu if (!linkedFwdDocs.some(l => l instanceof Promise)) { let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab"); let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext); + DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, document => this.props.addDocTab(document, document, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext); } } } @@ -345,7 +346,7 @@ export class DocumentView extends DocComponent(Docu this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag([Doc.MakeAlias(this.props.Document)], e); + CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.props.DataDoc]); e.stopPropagation(); } else { if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. @@ -376,7 +377,7 @@ export class DocumentView extends DocComponent(Docu } deleteClicked = (): void => { this.props.removeDocument && this.props.removeDocument(this.props.Document); }; - fieldsClicked = (): void => { this.props.addDocTab(Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }), "onRight"); }; + fieldsClicked = (): void => { let kvp = Docs.KVPDocument(this.props.Document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }; makeBtnClicked = (): void => { let doc = Doc.GetProto(this.props.Document); doc.isButton = !BoolCast(doc.isButton, false); @@ -390,7 +391,7 @@ export class DocumentView extends DocComponent(Docu } } fullScreenClicked = (): void => { - CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeCopy(this.props.Document, false)); + CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.props.DataDoc); SelectionManager.DeselectAll(); } @@ -473,10 +474,10 @@ export class DocumentView extends DocComponent(Docu const cm = ContextMenu.Instance; let subitems: ContextMenuProps[] = []; subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" }); - subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "inTab"), icon: "folder" }); - subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "inTab"), icon: "folder" }); - subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" }); - subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); @@ -486,7 +487,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Find aliases", event: async () => { const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), "onRight"); + this.props.addDocTab && this.props.addDocTab(Docs.SchemaDocument(["title"], aliases, {}), Docs.SchemaDocument(["title"], aliases, {}), "onRight"); // bcz: dataDoc? }, icon: "search" }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document), icon: "crosshairs" }); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 35608af86..8879da55f 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -36,7 +36,7 @@ export interface FieldViewProps { isTopMost: boolean; selectOnLoad: boolean; addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean; - addDocTab: (document: Doc, where: string) => void; + addDocTab: (document: Doc, dataDoc: Doc, where: string) => void; removeDocument?: (document: Doc) => boolean; moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f59af226d..39444fd6a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -270,7 +270,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; if (this._linkClicked) { DocServer.GetRefField(this._linkClicked).then(f => { - (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, "inTab")); + (f instanceof Doc) && DocumentManager.Instance.jumpToDocument(f, ctrlKey, document => this.props.addDocTab(document, document, "inTab")); }); e.stopPropagation(); e.preventDefault(); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index f56a2d926..241a593b0 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,13 +1,13 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faImage } from '@fortawesome/free-solid-svg-icons'; -import { action, observable } from 'mobx'; +import { action, observable, computed } from 'mobx'; import { observer } from "mobx-react"; import Lightbox from 'react-image-lightbox'; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; +import { Cast, FieldValue, NumCast, StrCast, BoolCast } from '../../../new_fields/Types'; import { ImageField } from '../../../new_fields/URLField'; import { Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; @@ -36,7 +36,7 @@ const ImageDocument = makeInterface(pageSchema, positionSchema); @observer export class ImageBox extends DocComponent(ImageDocument) { - public static LayoutString() { return FieldView.LayoutString(ImageBox); } + public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject = React.createRef(); private _downX: number = 0; private _downY: number = 0; @@ -46,6 +46,8 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; + @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } + protected createDropTarget = (ele: HTMLDivElement) => { if (this.dropDisposer) { @@ -66,19 +68,24 @@ export class ImageBox extends DocComponent(ImageD drop = (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.DocumentDragData) { de.data.droppedDocuments.forEach(action((drop: Doc) => { - let layout = StrCast(drop.backgroundLayout); - if (layout.indexOf(ImageBox.name) !== -1) { - let imgData = this.props.Document[this.props.fieldKey]; - if (imgData instanceof ImageField) { - Doc.SetOnPrototype(this.props.Document, "data", new List([imgData])); - } - let imgList = Cast(this.props.Document[this.props.fieldKey], listSpec(ImageField), [] as any[]); - if (imgList) { - let field = drop.data; - if (field instanceof ImageField) imgList.push(field); - else if (field instanceof List) imgList.concat(field); - } + if (this.dataDoc !== this.props.Document && drop.data instanceof ImageField) { + this.dataDoc[this.props.fieldKey] = new ImageField(drop.data.url); e.stopPropagation(); + } else { + let layout = StrCast(drop.backgroundLayout); + if (layout.indexOf(ImageBox.name) !== -1) { + let imgData = this.dataDoc[this.props.fieldKey]; + if (imgData instanceof ImageField) { + Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([imgData])); + } + let imgList = Cast(this.dataDoc[this.props.fieldKey], listSpec(ImageField), [] as any[]); + if (imgList) { + let field = drop.data; + if (field instanceof ImageField) imgList.push(field); + else if (field instanceof List) imgList.concat(field); + } + e.stopPropagation(); + } } })); // de.data.removeDocument() bcz: need to implement @@ -206,7 +213,7 @@ export class ImageBox extends DocComponent(ImageD let paths: string[] = ["http://www.cs.brown.edu/~bcz/noImage.png"]; // this._curSuffix = ""; // if (w > 20) { - let field = this.Document[this.props.fieldKey]; + let field = this.dataDoc[this.props.fieldKey]; // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; @@ -214,8 +221,8 @@ export class ImageBox extends DocComponent(ImageD else if (field instanceof List) paths = field.filter(val => val instanceof ImageField).map(p => this.choosePath((p as ImageField).url)); // } let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; - let rotation = NumCast(this.props.Document.rotation, 0); - let aspect = (rotation % 180) ? this.props.Document[HeightSym]() / this.props.Document[WidthSym]() : 1; + let rotation = NumCast(this.dataDoc.rotation, 0); + let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1; let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; return (
{ this._isPage[page] = "page"; this._visibleElements[page] = ( Date: Fri, 21 Jun 2019 16:27:03 -0400 Subject: can drag just a group of links on a doc --- src/client/documents/Documents.ts | 4 +- src/client/util/DocumentManager.ts | 46 ++----- src/client/util/DragManager.ts | 47 ++++++- .../CollectionFreeFormLinkView.scss | 38 +++--- .../CollectionFreeFormLinkView.tsx | 88 ++++++------ .../CollectionFreeFormLinksView.tsx | 148 +++------------------ src/client/views/nodes/DocumentView.tsx | 2 + src/client/views/nodes/LinkMenu.scss | 15 ++- src/client/views/nodes/LinkMenu.tsx | 18 +-- src/client/views/nodes/LinkMenuGroup.tsx | 74 +++++++++++ src/client/views/nodes/LinkMenuItem.tsx | 8 +- src/new_fields/Doc.ts | 16 ++- 12 files changed, 243 insertions(+), 261 deletions(-) create mode 100644 src/client/views/nodes/LinkMenuGroup.tsx (limited to 'src/new_fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index df5c39562..7cef48b98 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -91,7 +91,9 @@ export namespace DocUtils { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; export function MakeLink(source: Doc, target: Doc, targetContext?: Doc) { - if (LinkManager.Instance.doesLinkExist(source, target)) return; + if (LinkManager.Instance.doesLinkExist(source, target)) { + console.log("LINK EXISTS"); return; + } UndoManager.RunInBatch(() => { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 325f4894d..c4cb6721a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -85,51 +85,25 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - // console.log("linked"); - // let docs = DocListCast(CurrentUserUtils.UserDocument.data); - // docs.forEach(d => { - // console.log("d", StrCast(d.title)); - - // }); - - // let d = Cast(CurrentUserUtils.UserDocument.activeWorkspace, Doc, new Doc); - // console.log("DOC", StrCast(d.title)); - - - - let linked = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - // console.log("FINDING LINKED DVs FOR", StrCast(dv.props.Document.title)); + return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); + // let linksList = DocListCast(dv.props.Document.linkedToDocs); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { - // if (link) { - let destination = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); - // console.log("FINDING FOR", StrCast(dv.Document.title), StrCast(destination.title)); - - if (destination) { - let dvs = DocumentManager.Instance.getDocumentViews(destination); - if (dvs.length > 0) { - dvs.map(docView1 => { - // console.log("PUSHING LINK BETWEEN", StrCast(dv.props.Document.title), StrCast(docView1.props.Document.title)); - // TODO: if any docviews are not in the same context, draw a proxy - // let sameContent = dv.props.ContainingCollectionView === docView1.props.ContainingCollectionView; - pairs.push({ anchor1View: dv, anchor2View: docView1, linkDoc: link }); - // console.log("PUSHED", StrCast(dv.props.Document.title), StrCast(docView1.Document.title)); - }); - } else { - let dv = DocumentManager.Instance.getDocumentView(destination); - dv ? console.log(StrCast(dv.props.Document.title)) : console.log("cant find"); - } + if (link) { + let linkToDoc = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + // console.log("FOUND ", DocumentManager.Instance.getDocumentViews(linkToDoc).length, "DOCVIEWS FOR", StrCast(linkToDoc.title), "WITH SOURCE", StrCast(dv.props.Document.title)); + DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => + pairs.push({ a: dv, b: docView1, l: link })); } - // } return pairs; - }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[])); + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } return pairs; - }, [] as { anchor1View: DocumentView, anchor2View: DocumentView, linkDoc: Doc }[]); - return linked; + }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); } + @undoBatch public jumpToDocument = async (docDelegate: Doc, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number, docContext?: Doc): Promise => { let doc = Doc.GetProto(docDelegate); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 11530ef09..2abcff4f7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -48,12 +48,10 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) { let draggeddoc = LinkManager.Instance.findOppositeAnchor(linkDoc, sourceDoc); - // TODO: if not in same context then don't drag - let moddrag = await Cast(draggeddoc.annotationOn, Doc); let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]); dragData.dropAction = "alias" as dropActionType; - DragManager.StartDocumentDrag([dragEle], dragData, x, y, { + DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -83,11 +81,21 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n if (doc) moddrag.push(doc); } let dragData = new DragManager.DocumentDragData(moddrag.length ? moddrag : draggedDocs); - dragData.dropAction = "alias" as dropActionType; // dragData.moveDocument = (document, targetCollection, addDocument) => { // return false; // }; - DragManager.StartDocumentDrag([dragEle], dragData, x, y, { + + // runInAction(() => StartDragFunctions.map(func => func())); + // (eles, dragData, downX, downY, options, + // (dropData: { [id: string]: any }) => { + // (dropData.droppedDocuments = dragData.userDropAction === "alias" || (!dragData.userDropAction && dragData.dropAction === "alias") ? + // dragData.draggedDocuments.map(d => Doc.MakeAlias(d)) : + // dragData.userDropAction === "copy" || (!dragData.userDropAction && dragData.dropAction === "copy") ? + // dragData.draggedDocuments.map(d => Doc.MakeCopy(d, true)) : + // dragData.draggedDocuments + // ); + // }); + DragManager.StartLinkedDocumentDrag([dragEle], dragData, x, y, { handlers: { dragComplete: action(emptyFunction), }, @@ -215,6 +223,35 @@ export namespace DragManager { }); } + export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + + runInAction(() => StartDragFunctions.map(func => func())); + StartDrag(eles, dragData, downX, downY, options, + (dropData: { [id: string]: any }) => { + dropData.droppedDocuments = dragData.draggedDocuments.map(d => { + let dv = DocumentManager.Instance.getDocumentView(d); + // console.log("DRAG", StrCast(d.title)); + + if (dv) { + console.log("DRAG", StrCast(d.title), "has view"); + if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { + console.log("DRAG", StrCast(d.title), "same"); + return d; + } else { + console.log("DRAG", StrCast(d.title), "diff"); + return Doc.MakeAlias(d); + } + } else { + console.log("DRAG", StrCast(d.title), "has no view"); + return Doc.MakeAlias(d); + } + // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ? + // Doc.MakeAlias(d) : d; + }); + + }); + } + export function StartAnnotationDrag(eles: HTMLElement[], dragData: AnnotationDragData, downX: number, downY: number, options?: DragOptions) { StartDrag(eles, dragData, downX, downY, options); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index d8d518147..239c2ce56 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -1,22 +1,22 @@ -// .collectionfreeformlinkview-linkLine { -// stroke: black; -// transform: translate(10000px,10000px); -// opacity: 0.5; -// pointer-events: all; -// } -// .collectionfreeformlinkview-linkCircle { -// stroke: rgb(0,0,0); -// opacity: 0.5; -// transform: translate(10000px,10000px); -// pointer-events: all; -// cursor: pointer; -// } -// .collectionfreeformlinkview-linkText { -// stroke: rgb(0,0,0); -// opacity: 0.5; -// transform: translate(10000px,10000px); -// pointer-events: all; -// } +.collectionfreeformlinkview-linkLine { + stroke: black; + transform: translate(10000px,10000px); + opacity: 0.5; + pointer-events: all; +} +.collectionfreeformlinkview-linkCircle { + stroke: rgb(0,0,0); + opacity: 0.5; + transform: translate(10000px,10000px); + pointer-events: all; + cursor: pointer; +} +.collectionfreeformlinkview-linkText { + stroke: rgb(0,0,0); + opacity: 0.5; + transform: translate(10000px,10000px); + pointer-events: all; +} .linkview-ele { transform: translate(10000px,10000px); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 65d5ac474..5c7f080e0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -5,70 +5,60 @@ import { InkingControl } from "../../InkingControl"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); import v5 = require("uuid/v5"); -import { DocumentView } from "../../nodes/DocumentView"; -import { Docs } from "../../../documents/Documents"; export interface CollectionFreeFormLinkViewProps { - // anchor1: Doc; - // anchor2: Doc; - // LinkDocs: Doc[]; - // addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; - // removeDocument: (document: Doc) => boolean; - // sameContext: boolean; - - // sourceView: DocumentView; - // targetView: DocumentView; - sourceView: Doc; - targetView: Doc; + A: Doc; + B: Doc; + LinkDocs: Doc[]; + addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Doc) => boolean; } @observer export class CollectionFreeFormLinkView extends React.Component { - // onPointerDown = (e: React.PointerEvent) => { - // if (e.button === 0 && !InkingControl.Instance.selectedTool) { - // let a = this.props.A; - // let b = this.props.B; - // let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : a[WidthSym]() / 2); - // let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : a[HeightSym]() / 2); - // let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : b[WidthSym]() / 2); - // let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : b[HeightSym]() / 2); - // this.props.LinkDocs.map(l => { - // let width = l[WidthSym](); - // l.x = (x1 + x2) / 2 - width / 2; - // l.y = (y1 + y2) / 2 + 10; - // if (!this.props.removeDocument(l)) this.props.addDocument(l, false); - // }); - // e.stopPropagation(); - // e.preventDefault(); - // } - // } - - + onPointerDown = (e: React.PointerEvent) => { + if (e.button === 0 && !InkingControl.Instance.selectedTool) { + let a = this.props.A; + let b = this.props.B; + let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : a[WidthSym]() / 2); + let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : a[HeightSym]() / 2); + let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : b[WidthSym]() / 2); + let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : b[HeightSym]() / 2); + this.props.LinkDocs.map(l => { + let width = l[WidthSym](); + l.x = (x1 + x2) / 2 - width / 2; + l.y = (y1 + y2) / 2 + 10; + if (!this.props.removeDocument(l)) this.props.addDocument(l, false); + }); + e.stopPropagation(); + e.preventDefault(); + } + } render() { - // let l = this.props.LinkDocs; - // let a = this.props.A; - // let b = this.props.B; - let a1 = this.props.sourceView; - let a2 = this.props.targetView; - let x1 = NumCast(a1.x) + (BoolCast(a1.isMinimized, false) ? 5 : NumCast(a1.width) / NumCast(a1.zoomBasis, 1) / 2); - let y1 = NumCast(a1.y) + (BoolCast(a1.isMinimized, false) ? 5 : NumCast(a1.height) / NumCast(a1.zoomBasis, 1) / 2); - - let x2 = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); - let y2 = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); - + let l = this.props.LinkDocs; + let a = this.props.A; + let b = this.props.B; + let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.width) / NumCast(a.zoomBasis, 1) / 2); + let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : NumCast(a.height) / NumCast(a.zoomBasis, 1) / 2); + let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / NumCast(b.zoomBasis, 1) / 2); + let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / NumCast(b.zoomBasis, 1) / 2); + let text = ""; + let first = this.props.LinkDocs[0]; + if (this.props.LinkDocs.length === 1) text += first.title + (first.linkDescription ? "(" + StrCast(first.linkDescription) + ")" : ""); + else text = "-multiple-"; + text = ""; return ( <> - - {/* */} - {/* + {text} - */} + ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 9d2f8946b..854122592 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,4 +1,4 @@ -import { computed, IReactionDisposer, reaction, action } from "mobx"; +import { computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -11,10 +11,6 @@ import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import { CollectionFreeFormLinkWithProxyView } from "./CollectionFreeFormLinkWithProxyView"; -import { Docs } from "../../../documents/Documents"; -import { LinkButtonField } from "../../../../new_fields/LinkButtonField"; -import { LinkManager } from "../../../util/LinkManager"; @observer export class CollectionFreeFormLinksView extends React.Component { @@ -98,150 +94,42 @@ export class CollectionFreeFormLinksView extends React.Component { - // console.log("CONNECTION BETWEEN", StrCast(connection.anchor1View.props.Document.title), StrCast(connection.anchor2View.props.Document.title)); - let srcViews = this.documentAnchors(connection.anchor1View); - // srcViews.forEach(sv => { - // console.log("DOCANCHORS SRC", StrCast(connection.anchor1View.Document.title), StrCast(sv.Document.title)); - // }); - - let targetViews = this.documentAnchors(connection.anchor2View); - // targetViews.forEach(sv => { - // console.log("DOCANCHORS TARG", StrCast(connection.anchor2View.Document.title), StrCast(sv.Document.title)); - // }); - - // console.log("lengths", srcViews.length, targetViews.length); + // DocumentManager.Instance.LinkedDocumentViews.forEach(d => { + // console.log("CONNECTION", StrCast(d.a.props.Document.title), StrCast(d.b.props.Document.title)); + // }); - // srcViews.forEach(v => { - // console.log("SOURCE VIEW", StrCast(v.props.Document.title)); - // }); - // targetViews.forEach(v => { - // console.log("TARGET VIEW", StrCast(v.Document.title)); - // }); - - let possiblePairs: { anchor1: Doc, anchor2: Doc }[] = []; - // srcViews.map(sv => { - // console.log("SOURCE VIEW", StrCast(sv.props.Document.title)); - // targetViews.map(tv => { - // console.log("TARGET VIEW", StrCast(tv.props.Document.title)); - // // console.log("PUSHING PAIR", StrCast(sv.props.Document.title), StrCast(tv.props.Document.title)); - // possiblePairs.push({ anchor1: sv.props.Document, anchor2: tv.props.Document }); - // }); - // console.log("END\n"); - // }); - srcViews.forEach(sv => { - // console.log("SOURCE VIEW", StrCast(sv.props.Document.title)); - targetViews.forEach(tv => { - // console.log("TARGET VIEW", StrCast(tv.props.Document.title)); - // console.log("PUSHING PAIR", StrCast(sv.props.Document.title), StrCast(tv.props.Document.title)); - possiblePairs.push({ anchor1: sv.props.Document, anchor2: tv.props.Document }); - }); - // console.log("END\n"); - }); - // console.log("POSSIBLE PAIRS LENGTH", possiblePairs.length); + let connections = DocumentManager.Instance.LinkedDocumentViews.reduce((drawnPairs, connection) => { + let srcViews = this.documentAnchors(connection.a); + let targetViews = this.documentAnchors(connection.b); + let possiblePairs: { a: Doc, b: Doc, }[] = []; + srcViews.map(sv => targetViews.map(tv => possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }))); possiblePairs.map(possiblePair => { - // console.log("POSSIBLEPAIR", StrCast(possiblePair.anchor1.title), StrCast(possiblePair.anchor2.title)); if (!drawnPairs.reduce((found, drawnPair) => { - let match1 = (Doc.AreProtosEqual(possiblePair.anchor1, drawnPair.anchor1) && Doc.AreProtosEqual(possiblePair.anchor2, drawnPair.anchor2)); - let match2 = (Doc.AreProtosEqual(possiblePair.anchor1, drawnPair.anchor2) && Doc.AreProtosEqual(possiblePair.anchor2, drawnPair.anchor1)); + let match1 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.a) && Doc.AreProtosEqual(possiblePair.b, drawnPair.b)); + let match2 = (Doc.AreProtosEqual(possiblePair.a, drawnPair.b) && Doc.AreProtosEqual(possiblePair.b, drawnPair.a)); let match = match1 || match2; - if (match && !drawnPair.linkDocs.reduce((found, link) => found || link[Id] === connection.linkDoc[Id], false)) { - drawnPair.linkDocs.push(connection.linkDoc); + if (match && !drawnPair.l.reduce((found, link) => found || link[Id] === connection.l[Id], false)) { + drawnPair.l.push(connection.l); } return match || found; }, false)) { - drawnPairs.push({ anchor1: possiblePair.anchor1, anchor2: possiblePair.anchor2, linkDocs: [connection.linkDoc] }); + drawnPairs.push({ a: possiblePair.a, b: possiblePair.b, l: [connection.l] }); } }); return drawnPairs; - }, [] as { anchor1: Doc, anchor2: Doc, linkDocs: Doc[] }[]); + }, [] as { a: Doc, b: Doc, l: Doc[] }[]); return connections.map(c => { - let x = c.linkDocs.reduce((p, l) => p + l[Id], ""); - return ; + let x = c.l.reduce((p, l) => p + l[Id], ""); + return ; }); } - // findUniquePairs = (): JSX.Element[] => { - // let connections = DocumentManager.Instance.LinkedDocumentViews; - - // // console.log("CONNECTIONS"); - // // connections.forEach(c => console.log(StrCast(c.anchor1View.Document.title), StrCast(c.anchor2View.Document.title))); - - // let unique: Set<{ sourceView: DocumentView, targetView: DocumentView, linkDoc: Doc }> = new Set(); - // connections.forEach(c => { - - // // let match1Index = unique.findIndex(u => (c.anchor1View === u.sourceView) && (c.anchor2View === u.targetView)); - // // let match2Index = unique.findIndex(u => (c.anchor1View === u.targetView) && (c.anchor2View === u.sourceView)); - // let match1 = unique.has({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); - // let match2 = unique.has({ sourceView: c.anchor2View, targetView: c.anchor1View, linkDoc: c.linkDoc }); - // let sameContext = c.anchor1View.props.ContainingCollectionView === c.anchor2View.props.ContainingCollectionView; - - // // console.log("CONNECTION", StrCast(c.anchor1View.props.Document.title), StrCast(c.anchor2View.props.Document.title), match1, match2); - - - // // if in same context, push if docview pair does not already exist - // // else push both directions of pair - // if (sameContext) { - // if (!(match1 || match2)) unique.add({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); - // } else { - // unique.add({ sourceView: c.anchor1View, targetView: c.anchor2View, linkDoc: c.linkDoc }); - // unique.add({ sourceView: c.anchor2View, targetView: c.anchor1View, linkDoc: c.linkDoc }); - // } - // }); - - // let uniqueList: JSX.Element[] = []; - // unique.forEach(u => { - // // TODO: make better key - // let key = StrCast(u.sourceView.Document[Id]) + "-link-" + StrCast(u.targetView.Document[Id]) + "-" + Date.now() + Math.random(); - // let sourceIn = u.sourceView.props.ContainingCollectionView ? u.sourceView.props.ContainingCollectionView.props.Document === this.props.Document : false; - // let targetIn = u.targetView.props.ContainingCollectionView ? u.targetView.props.ContainingCollectionView.props.Document === this.props.Document : false; - // let sameContext = u.sourceView.props.ContainingCollectionView === u.targetView.props.ContainingCollectionView; - // let inContainer = sameContext ? sourceIn || targetIn : sourceIn; - - // if (inContainer) { - // // let alias = Doc.MakeAlias(proxy); - // if (sameContext) { - // uniqueList.push(); - // } else { - // let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); - // if (!proxy) { - // proxy = Docs.LinkButtonDocument( - // { sourceViewId: StrCast(u.sourceView.props.Document[Id]), targetViewId: StrCast(u.targetView.props.Document[Id]) }, - // { width: 200, height: 100, borderRounding: 0 }); - // let proxy1Proto = Doc.GetProto(proxy); - // proxy1Proto.sourceViewId = StrCast(u.sourceView.props.Document[Id]); - // proxy1Proto.targetViewId = StrCast(u.targetView.props.Document[Id]); - // proxy1Proto.isLinkButton = true; - - // // LinkManager.Instance.linkProxies.push(proxy); - // LinkManager.Instance.addLinkProxy(proxy); - // } - // uniqueList.push(); - - // // let proxy = LinkManager.Instance.findLinkProxy(StrCast(u.sourceView.props.Document[Id]), StrCast(u.targetView.props.Document[Id])); - // // if (proxy) { - // // this.props.addDocument(proxy, false); - // // uniqueList.push(); - // // } - // // let proxyKey = Doc.AreProtosEqual(u.sourceView.Document, Cast(u.linkDoc.anchor1, Doc, new Doc)) ? "proxy1" : "proxy2"; - // // let proxy = Cast(u.linkDoc[proxyKey], Doc, new Doc); - // // this.props.addDocument(proxy, false); - - // // uniqueList.push(); - // } - // } - // }); - // return uniqueList; - // } - render() { return (
{this.uniqueConnections} - {/* {this.findUniquePairs()} */} {this.props.children}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index dbbae2d59..1fc2cf770 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -161,7 +161,9 @@ export class DocumentView extends DocComponent(Docu this._animateToIconDisposer = reaction(() => this.props.Document.isIconAnimating, (values) => (values instanceof List) && this.animateBetweenIcon(values, values[2], values[3] ? true : false) , { fireImmediately: true }); + // console.log("CREATED NEW DOC VIEW", StrCast(this.props.Document.title), DocumentManager.Instance.DocumentViews.length); DocumentManager.Instance.DocumentViews.push(this); + // console.log("ADDED TO DOC MAN", StrCast(this.props.Document.title), DocumentManager.Instance.DocumentViews.length); } animateBetweenIcon = (iconPos: number[], startTime: number, maximizing: boolean) => { diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index d2e411c3d..c01ed23c4 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -18,8 +18,19 @@ } .linkMenu-group-name { - border-bottom: 0.5px solid lightgray; - margin-bottom: 4px; + padding: 4px 6px; + line-height: 12px; + border-radius: 5px; + margin-bottom: 2px; + + &:hover { + background-color: lightgray; + } + } + + .linkMenu-group-wrapper { + border-top: 0.5px solid lightgray; + padding-top: 4px; } } diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 8aca130a5..f96c7d2e4 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -8,6 +8,9 @@ import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { LinkManager } from "../../util/LinkManager"; +import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; +import { emptyFunction } from "../../../Utils"; +import { LinkMenuGroup } from "./LinkMenuGroup"; interface Props { docView: DocumentView; @@ -19,24 +22,11 @@ export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; - renderGroup = (group: Doc[], groupType: string): Array => { - let source = this.props.docView.Document; - return group.map(linkDoc => { - let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, source); - return this._editingLink = linkDoc)} />; - }); - } - renderAllGroups = (groups: Map>): Array => { let linkItems: Array = []; groups.forEach((group, groupType) => { linkItems.push( -
-

{groupType}:

-
- {this.renderGroup(group, groupType)} -
-
+ this._editingLink = linkDoc)} /> ); }); diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx new file mode 100644 index 000000000..229143d99 --- /dev/null +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -0,0 +1,74 @@ +import { action, observable } from "mobx"; +import { observer } from "mobx-react"; +import { DocumentView } from "./DocumentView"; +import { LinkMenuItem } from "./LinkMenuItem"; +import { LinkEditor } from "./LinkEditor"; +import './LinkMenu.scss'; +import React = require("react"); +import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { LinkManager } from "../../util/LinkManager"; +import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; +import { emptyFunction } from "../../../Utils"; + +interface LinkMenuGroupProps { + sourceDoc: Doc; + group: Doc[]; + groupType: string; + showEditor: (linkDoc: Doc) => void; +} + +@observer +export class LinkMenuGroup extends React.Component { + + private _drag = React.createRef(); + + onLinkButtonDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.addEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + document.addEventListener("pointerup", this.onLinkButtonUp); + } + + onLinkButtonUp = (e: PointerEvent): void => { + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + e.stopPropagation(); + } + + onLinkButtonMoved = async (e: PointerEvent) => { + if (this._drag.current !== null && (e.movementX > 1 || e.movementY > 1)) { + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.removeEventListener("pointerup", this.onLinkButtonUp); + + let draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc)); + let dragData = new DragManager.DocumentDragData(draggedDocs); + + DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, { + handlers: { + dragComplete: action(emptyFunction), + }, + hideSource: false + }); + } + e.stopPropagation(); + } + + render() { + let groupItems = this.props.group.map(linkDoc => { + let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc); + return ; + }); + + return ( +
+

{this.props.groupType}:

+
+ {groupItems} +
+
+ ) + } +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 1ef4690b0..42ef353b7 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -21,7 +21,7 @@ interface LinkMenuItemProps { linkDoc: Doc; sourceDoc: Doc; destinationDoc: Doc; - showEditor: () => void; + showEditor: (linkDoc: Doc) => void; } @observer @@ -42,7 +42,7 @@ export class LinkMenuItem extends React.Component { onEdit = (e: React.PointerEvent): void => { e.stopPropagation(); - this.props.showEditor(); + this.props.showEditor(this.props.linkDoc); } renderMetadata = (): JSX.Element => { @@ -95,12 +95,12 @@ export class LinkMenuItem extends React.Component {
-

{StrCast(this.props.destinationDoc.title)}

+

{StrCast(this.props.destinationDoc.title)}

{canExpand ?
this.toggleShowMore()}>
: <>}
-
+
{this._showMore ? this.renderMetadata() : <>} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1b0ff812f..5ce47fc2f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -3,11 +3,13 @@ import { serializable, primitive, map, alias, list } from "serializr"; import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty } from "./util"; -import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types"; +import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, StrCast } from "./Types"; import { listSpec } from "./Schema"; import { ObjectField } from "./ObjectField"; import { RefField, FieldId } from "./RefField"; import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols"; +import { LinkManager } from "../client/util/LinkManager"; +import { DocUtils } from "../client/documents/Documents"; export namespace Field { export function toScriptString(field: Field): string { @@ -247,6 +249,18 @@ export namespace Doc { } } }); + console.log("COPY", StrCast(doc.title)); + let links = LinkManager.Instance.findAllRelatedLinks(doc); + links.forEach(linkDoc => { + let opp = LinkManager.Instance.findOppositeAnchor(linkDoc, doc); + console.log("OPP", StrCast(opp.title)); + DocUtils.MakeLink(opp, copy); + }); + + LinkManager.Instance.allLinks.forEach(l => { + console.log("LINK", StrCast(Cast(l.anchor1, Doc, new Doc).title), StrCast(Cast(l.anchor2, Doc, new Doc).title)); + }); + return copy; } -- cgit v1.2.3-70-g09d2 From 32ef8d83d5829e2faadbebaf6f9b694df5d7ea02 Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 21 Jun 2019 17:41:20 -0400 Subject: link menu styling --- src/client/documents/Documents.ts | 9 +++--- src/client/util/LinkManager.ts | 10 +++++-- src/client/views/nodes/LinkEditor.scss | 50 ++++++++++++++++++---------------- src/client/views/nodes/LinkEditor.tsx | 32 ++++++++++++++-------- src/client/views/nodes/LinkMenu.scss | 5 ---- src/new_fields/Doc.ts | 17 ++++-------- 6 files changed, 66 insertions(+), 57 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7cef48b98..64032e096 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -90,7 +90,7 @@ export namespace DocUtils { // export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; - export function MakeLink(source: Doc, target: Doc, targetContext?: Doc) { + export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { if (LinkManager.Instance.doesLinkExist(source, target)) { console.log("LINK EXISTS"); return; } @@ -99,9 +99,10 @@ export namespace DocUtils { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); let linkDocProto = Doc.GetProto(linkDoc); - // linkDocProto.title = title === "" ? source.title + " to " + target.title : title; - // linkDocProto.linkDescription = description; - // linkDocProto.linkTags = tags; + + linkDocProto.title = title; //=== "" ? source.title + " to " + target.title : title; + linkDocProto.linkDescription = description; + linkDocProto.linkTags = tags; linkDocProto.anchor1 = source; linkDocProto.anchor1Page = source.curPage; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 544f2edda..fef996bb9 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -3,6 +3,7 @@ import { StrCast, Cast } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; +import { Id } from "../../new_fields/FieldSymbols"; /* @@ -36,8 +37,13 @@ export class LinkManager { // finds all links that contain the given anchor public findAllRelatedLinks(anchor: Doc): Array { - return LinkManager.Instance.allLinks.filter( - link => Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)) || Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc))); + return LinkManager.Instance.allLinks.filter(link => { + let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)); + let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc)); + let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]); + let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]); + return protomatch1 || protomatch2 || idmatch1 || idmatch2; + }); } // returns map of group type to anchor's links in that group type diff --git a/src/client/views/nodes/LinkEditor.scss b/src/client/views/nodes/LinkEditor.scss index 154b4abe3..760887fa2 100644 --- a/src/client/views/nodes/LinkEditor.scss +++ b/src/client/views/nodes/LinkEditor.scss @@ -25,17 +25,22 @@ } } +.linkEditor-button { + width: 20px; + height: 20px; + margin-left: 6px; + padding: 0; + font-size: 12px; + border-radius: 10px; + + &:disabled { + background-color: gray; + } +} + .linkEditor-groupsLabel { display: flex; justify-content: space-between; - - button { - width: 20px; - height: 20px; - margin-left: 6px; - padding: 0; - font-size: 14px; - } } .linkEditor-group { @@ -109,23 +114,20 @@ .linkEditor-group-buttons { height: 20px; display: flex; - justify-content: space-between; - - .linkEditor-groupOpts { - width: calc(20% - 3px); - height: 20px; - padding: 0; - font-size: 10px; + justify-content: flex-end; - &:disabled { - background-color: gray; - } + .linkEditor-button { + margin-left: 6px; } - .linkEditor-groupOpts button { - width: 100%; - height: 20px; - font-size: 10px; - padding: 0; - } + // .linkEditor-groupOpts { + // width: calc(20% - 3px); + // height: 20px; + // padding: 0; + // font-size: 10px; + + // &:disabled { + // background-color: gray; + // } + // } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 29ead7388..0d13c6cc8 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -7,12 +7,13 @@ import { Doc } from "../../../new_fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { Docs } from "../../documents/Documents"; import { Utils } from "../../../Utils"; -import { faArrowLeft, faEllipsisV, faTable, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; +import { anchorPoints, Flyout } from "../DocumentDecorations"; -library.add(faArrowLeft, faEllipsisV, faTable, faTrash); +library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog); interface GroupTypesDropdownProps { @@ -256,9 +257,9 @@ export class LinkEditor extends React.Component { let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); let ref = React.createRef(); - return
; + return
; } else { - return ; + return ; } } @@ -273,12 +274,20 @@ export class LinkEditor extends React.Component {
{this.renderMetadata(groupId)}
- {groupDoc.type === "New Group" ? : - } - - - + {groupDoc.type === "New Group" ? : + } {this.viewGroupAsTable(groupId, type)} + + + + + + }> + +
); @@ -290,6 +299,7 @@ export class LinkEditor extends React.Component { @action addMetadata = (groupType: string): void => { + console.log("ADD MD"); let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); if (mdKeys) { // only add "new key" if there is no other key with value "new key"; prevents spamming @@ -331,11 +341,11 @@ export class LinkEditor extends React.Component {

editing link to: {destination.proto!.title}

- +
Relationships: - +
{groups.length > 0 ? groups :
There are currently no relationships associated with this link.
}
diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index 1ca669d00..ae3446e25 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -11,7 +11,6 @@ } .linkMenu-group { - // margin-bottom: 10px; border-bottom: 0.5px solid lightgray; padding: 5px 0; @@ -30,10 +29,6 @@ background-color: lightgray; } } - - // .linkMenu-group-wrapper { - // padding-top: 4px; - // } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5ce47fc2f..fda788f2d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -234,32 +234,27 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { + console.log(key); const field = doc[key]; if (key === "proto" && copyProto) { + console.log(1); if (field instanceof Doc) { + console.log(2); copy[key] = Doc.MakeCopy(field); } } else { if (field instanceof RefField) { + console.log(3); copy[key] = field; } else if (field instanceof ObjectField) { + console.log(4); copy[key] = ObjectField.MakeCopy(field); } else { + console.log(5); copy[key] = field; } } }); - console.log("COPY", StrCast(doc.title)); - let links = LinkManager.Instance.findAllRelatedLinks(doc); - links.forEach(linkDoc => { - let opp = LinkManager.Instance.findOppositeAnchor(linkDoc, doc); - console.log("OPP", StrCast(opp.title)); - DocUtils.MakeLink(opp, copy); - }); - - LinkManager.Instance.allLinks.forEach(l => { - console.log("LINK", StrCast(Cast(l.anchor1, Doc, new Doc).title), StrCast(Cast(l.anchor2, Doc, new Doc).title)); - }); return copy; } -- cgit v1.2.3-70-g09d2 From 6df09d7d646c16e6469b198e7d270b6a1e45b0c7 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 24 Jun 2019 13:23:28 -0400 Subject: fixes for templates with annotations and more. --- src/client/util/TooltipTextMenu.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 55 +++++------ src/client/views/InkingCanvas.tsx | 7 +- src/client/views/InkingControl.tsx | 3 +- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionBaseView.tsx | 1 - .../views/collections/CollectionTreeView.tsx | 106 +++++++++------------ src/client/views/collections/CollectionView.tsx | 17 +--- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- src/client/views/pdf/PDFViewer.tsx | 1 + src/new_fields/Doc.ts | 2 +- 11 files changed, 82 insertions(+), 121 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c9216199b..048fb7133 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -222,7 +222,7 @@ export class TooltipTextMenu { if (DocumentManager.Instance.getDocumentView(f)) { DocumentManager.Instance.getDocumentView(f)!.props.focus(f); } - else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f); + else if (CollectionDockingView.Instance) CollectionDockingView.Instance.AddRightSplit(f, f); } })); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0d5cca9f1..9be5c9cd6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -24,9 +24,10 @@ import { LinkMenu } from "./nodes/LinkMenu"; import { TemplateMenu } from "./TemplateMenu"; import { Template, Templates } from "./Templates"; import React = require("react"); -import { URLField } from '../../new_fields/URLField'; +import { URLField, ImageField } from '../../new_fields/URLField'; import { templateLiteral } from 'babel-types'; import { CollectionViewType } from './collections/CollectionBaseView'; +import { ImageBox } from './nodes/ImageBox'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -75,41 +76,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (text[0] === '#') { this._fieldKey = text.slice(1, text.length); this._title = this.selectionTitle; - } else if (text.startsWith(">>>")) { - let metaKey = text.slice(3, text.length); - let collection = SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView!.props.Document; - Doc.GetProto(collection)[metaKey] = new List([ - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - ]); - let template = Doc.MakeAlias(collection); + } else if (text.startsWith(">")) { + let metaKey = text.slice(text.startsWith(">>>") ? 3 : text.startsWith(">>") ? 2 : 1, text.length); + let field = SelectionManager.SelectedDocuments()[0]; + let collectionKey = field.props.ContainingCollectionView!.props.fieldKey; + let collection = field.props.ContainingCollectionView!.props.Document; + let collectionKeyProp = `fieldKey={"${collectionKey}"}`; + let collectionAnnotationsKeyProp = `fieldKey={"annotations"}`; + let metaKeyProp = `fieldKey={"${metaKey}"}`; + let metaAnnotationsKeyProp = `fieldKey={"${metaKey}_annotations"}`; + let template = Doc.MakeAlias(field.props.Document); + template.proto = collection; template.title = metaKey; + template.nativeWidth = Cast(field.nativeWidth, "number"); + template.nativeHeight = Cast(field.nativeHeight, "number"); template.embed = true; - template.layout = CollectionView.LayoutString(metaKey); - template.viewType = CollectionViewType.Freeform; - template.x = 0; - template.y = 0; - template.width = 300; - template.height = 300; template.isTemplate = true; - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document)); - } else if (text[0] === ">") { - let metaKey = text.slice(1, text.length); - let first = SelectionManager.SelectedDocuments()[0].props.Document!; - let collection = SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView!.props.Document; - Doc.GetProto(collection)[metaKey] = "-empty field-"; - let template = Doc.MakeAlias(collection); - template.title = metaKey; - template.layout = FormattedTextBox.LayoutString(metaKey); - template.isTemplate = true; - template.x = NumCast(first.x); - template.y = NumCast(first.y); - template.width = first[WidthSym](); - template.height = first[HeightSym](); - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); + template.templates = new List([Templates.TitleBar(metaKey)]); + template.layout = StrCast(field.props.Document.layout).replace(collectionKeyProp, metaKeyProp); + if (field.props.Document.backgroundLayout) { + template.layout = StrCast(field.props.Document.layout).replace(collectionAnnotationsKeyProp, metaAnnotationsKeyProp); + template.backgroundLayout = StrCast(field.props.Document.backgroundLayout).replace(collectionKeyProp, metaKeyProp); + } + Doc.AddDocToList(collection, collectionKey, template); SelectionManager.SelectedDocuments().map(dv => dv.props.removeDocument && dv.props.removeDocument(dv.props.Document)); } else { diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 5d4ea76cd..fd7e5b07d 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -14,6 +14,7 @@ import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; interface InkCanvasProps { getScreenTransform: () => Transform; Document: Doc; + inkFieldKey: string; children: () => JSX.Element[]; } @@ -40,7 +41,7 @@ export class InkingCanvas extends React.Component { } componentDidMount() { - PromiseValue(Cast(this.props.Document.ink, InkField)).then(ink => runInAction(() => { + PromiseValue(Cast(this.props.Document[this.props.inkFieldKey], InkField)).then(ink => runInAction(() => { if (ink) { let bounds = Array.from(ink.inkData).reduce(([mix, max, miy, may], [id, strokeData]) => strokeData.pathData.reduce(([mix, max, miy, may], p) => @@ -55,12 +56,12 @@ export class InkingCanvas extends React.Component { @computed get inkData(): Map { - let map = Cast(this.props.Document.ink, InkField); + let map = Cast(this.props.Document[this.props.inkFieldKey], InkField); return !map ? new Map : new Map(map.inkData); } set inkData(value: Map) { - Doc.GetProto(this.props.Document).ink = new InkField(value); + Doc.GetProto(this.props.Document)[this.props.inkFieldKey] = new InkField(value); } @action diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 0837e07a9..6cde73933 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -8,6 +8,7 @@ import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-s import { SelectionManager } from "../util/SelectionManager"; import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; +import { InkingCanvas } from "./InkingCanvas"; library.add(faPen, faHighlighter, faEraser, faBan); @@ -39,7 +40,7 @@ export class InkingControl extends React.Component { @action switchColor = (color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); - SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor); + if (InkingControl.Instance.selectedTool === InkTool.None) SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor); } @action diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8198b88d2..a72f25b99 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -144,7 +144,7 @@ export class MainView extends React.Component { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, 600)] }] }; + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] }; let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id); list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) @@ -177,7 +177,7 @@ export class MainView extends React.Component { openNotifsCol = () => { if (this._notifsCol && CollectionDockingView.Instance) { - CollectionDockingView.Instance.AddRightSplit(this._notifsCol); + CollectionDockingView.Instance.AddRightSplit(this._notifsCol, this._notifsCol); } } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 75bdf755c..79a9f3be0 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -100,7 +100,6 @@ export class CollectionBaseView extends React.Component { } return false; } - @computed get isAnnotationOverlay() { return this.props.fieldKey === "annotations"; } @action.bound addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b3f1b1c88..f5f323269 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -28,7 +28,6 @@ import React = require("react"); import { FormattedTextBox } from '../nodes/FormattedTextBox'; import { ImageField } from '../../../new_fields/URLField'; import { ImageBox } from '../nodes/ImageBox'; -import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionView } from './CollectionView'; @@ -71,11 +70,23 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { + let keys = Array.from(Object.keys(this.dataDoc)); + if (this.dataDoc.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.dataDoc.proto))); + while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); + } + let keyList: string[] = []; + keys.map(key => { + let docList = Cast(this.dataDoc[key], listSpec(Doc)); + if (docList && docList.length > 0) { + keyList.push(key); + } + }); let layout = StrCast(this.props.document.layout); if (layout.indexOf("fieldKey={\"") !== -1) { return layout.split("fieldKey={\"")[1].split("\"")[0]; } - return "data"; + return keyList.length ? keyList[0] : "data"; } @computed get dataDoc() { return (BoolCast(this.props.document.isTemplate) ? this.props.dataDoc : this.props.document); } @@ -163,63 +174,39 @@ class TreeView extends React.Component { SetValue={(value: string) => { let res = (Doc.GetProto(this.dataDoc)[key] = value) ? true : true; - if (value.startsWith(">>>")) { - let metaKey = value.slice(3, value.length); + if (value.startsWith(">")) { + let metaKey = value.slice(value.startsWith(">>>") ? 3 : value.startsWith(">>") ? 2 : 1, value.length); let collection = this.props.containingCollection; - Doc.GetProto(collection)[metaKey] = new List([ - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - ]); let template = Doc.MakeAlias(collection); template.title = metaKey; template.embed = true; - template.layout = CollectionView.LayoutString(metaKey); - template.viewType = CollectionViewType.Freeform; template.x = 0; template.y = 0; template.width = 300; template.height = 300; template.isTemplate = true; template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - this.delete(); - } else - if (value.startsWith(">>")) { - let metaKey = value.slice(2, value.length); - let collection = this.props.containingCollection; + if (value.startsWith(">>>")) { // Collection + Doc.GetProto(collection)[metaKey] = new List([ + Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), + Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), + ]); + template.layout = CollectionView.LayoutString(metaKey); + template.viewType = CollectionViewType.Freeform; + } else if (value.startsWith(">>")) { // Image Doc.GetProto(collection)[metaKey] = new ImageField("http://www.cs.brown.edu/~bcz/face.gif"); - let template = Doc.MakeAlias(collection); - template.title = metaKey; - template.embed = true; template.layout = ImageBox.LayoutString(metaKey); - template.x = 0; - template.y = 0; template.nativeWidth = 300; template.nativeHeight = 300; - template.width = 300; - template.height = 300; - template.isTemplate = true; - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - this.delete(); - } else - if (value.startsWith(">")) { - let metaKey = value.slice(1, value.length); - let collection = this.props.containingCollection; - Doc.GetProto(collection)[metaKey] = "-empty field-"; - let template = Doc.MakeAlias(collection); - template.title = metaKey; - template.embed = true; - template.layout = FormattedTextBox.LayoutString(metaKey); - template.x = 0; - template.y = 0; - template.width = 100; - template.height = 50; - template.isTemplate = true; - template.templates = new List([Templates.TitleBar(metaKey)]);//`{props.DataDoc.${metaKey}_text}`)]); - Doc.AddDocToList(collection, "data", template); - this.delete(); - } + } else if (value.startsWith(">")) { // Text + Doc.GetProto(collection)[metaKey] = "-empty field-"; + template.layout = FormattedTextBox.LayoutString(metaKey); + template.width = 100; + template.height = 50; + } + Doc.AddDocToList(collection, "data", template); + this.delete(); + } return res; }} @@ -238,14 +225,8 @@ class TreeView extends React.Component { keys.push(...Array.from(Object.keys(this.dataDoc.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } - let keyList: string[] = []; - keys.map(key => { - let docList = Cast(this.dataDoc[key], listSpec(Doc)); - let doc = Cast(this.dataDoc[key], Doc); - if (doc instanceof Doc || docList) { - keyList.push(key); - } - }); + let keyList: string[] = keys.reduce((l, key) => Cast(this.dataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]); + keys.map(key => Cast(this.dataDoc[key], Doc) instanceof Doc && keyList.push(key)); if (keyList.indexOf(this.fieldKey) !== -1) { keyList.splice(keyList.indexOf(this.fieldKey), 1); } @@ -291,16 +272,16 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)) }); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.dataDoc, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { - ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.props.document, "inTab"), icon: "folder" }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.props.document, "onRight"), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "inTab"), icon: "folder" }); + ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) { ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document)) }); } - ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.dataDoc)) }); + ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } else { - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.dataDoc)) }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); @@ -349,14 +330,14 @@ class TreeView extends React.Component { if (!this._collapsed) { if (!this.props.document.embed) { contentElement =
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.props.dataDoc, this._chosenKey, addDoc, remDoc, this.move, + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.dataDoc, this._chosenKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)}
; } else { contentElement =
{ active: () => boolean, panelWidth: () => number, ) { - let docList = docs.filter(child => !child.excludeFromLibrary && (key !== this.fieldKey || !child.isMinimized)); + let docList = docs.filter(child => !child.excludeFromLibrary); let rowWidth = () => panelWidth() - 20; return docList.map((child, i) => { let indent = i === 0 ? undefined : () => { @@ -475,6 +456,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + @computed get dataDoc() { return (BoolCast(this.props.DataDoc.isTemplate) ? this.props.DataDoc : this.props.Document); } render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; @@ -502,7 +484,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { }} />
    { - TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove, + TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.dataDoc, this.props.fieldKey, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth) }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 872cb3f1c..cc097f371 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -2,8 +2,10 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; import { observer } from "mobx-react"; import * as React from 'react'; +import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; +import { Docs } from '../../documents/Documents'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; @@ -14,11 +16,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV import { CollectionSchemaView } from "./CollectionSchemaView"; import { CollectionStackingView } from './CollectionStackingView'; import { CollectionTreeView } from "./CollectionTreeView"; -import { Doc } from '../../../new_fields/Doc'; -import { FormattedTextBox } from '../nodes/FormattedTextBox'; -import { Docs } from '../../documents/Documents'; -import { List } from '../../../new_fields/List'; -import { ImageField } from '../../../new_fields/URLField'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh); @@ -62,17 +59,7 @@ export class CollectionView extends React.Component { ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => { let otherdoc = Docs.TextDocument({ width: 100, height: 50, title: "applied template" }); - Doc.GetProto(otherdoc).description = "THIS DESCRIPTION IS REALLY IMPORTANT!"; - Doc.GetProto(otherdoc).summary = "THIS SUMMARY IS MEANINGFUL!"; - Doc.GetProto(otherdoc).photo = new ImageField("http://www.cs.brown.edu/~bcz/snowbeast.JPG"); Doc.GetProto(otherdoc).layout = Doc.MakeDelegate(this.props.Document); - Doc.GetProto(otherdoc).publication = new List([ - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { width: 300, height: 300 }), - Docs.TextDocument({ documentText: "hello world!", width: 300, height: 300 }), - ]); this.props.addDocTab && this.props.addDocTab(otherdoc, otherdoc, "onRight"); }), icon: "project-diagram" }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 71964ef82..c6f003a81 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -370,7 +370,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } private childViews = () => [ - , + , ...this.views ] render() { @@ -387,7 +387,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { easing={easing} zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> - + {this.childViews} @@ -414,6 +414,7 @@ class CollectionFreeFormOverlayView extends React.Component boolean }> { @computed get backgroundView() { + let props = this.props; return (); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 7000352e7..6adead626 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -203,6 +203,7 @@ class Viewer extends React.Component { this._isPage[page] = "page"; this._visibleElements[page] = ( Date: Mon, 24 Jun 2019 13:41:39 -0400 Subject: Added ReadOnly mode for docs and changed computed values a bit --- src/client/util/Scripting.ts | 12 +++++++++++- src/client/views/nodes/KeyValueBox.tsx | 6 +++--- src/fields/ScriptField.ts | 9 +++++---- src/new_fields/Doc.ts | 11 ++++++++++- src/new_fields/Proxy.ts | 4 +--- src/new_fields/util.ts | 36 +++++++++++++++++++++++++++------- 6 files changed, 59 insertions(+), 19 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40e2ad6bb..a59d9f201 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -39,7 +39,6 @@ export interface CompileError { } export type CompileResult = CompiledScript | CompileError; - function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); if ((options.typecheck !== false && errors) || !script) { @@ -64,10 +63,20 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an } } let thisParam = args.this || capturedVariables.this; + let batch: { end(): void } | undefined = undefined; try { + if (!options.editable) { + batch = Doc.MakeReadOnly(); + } const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray); + if (batch) { + batch.end(); + } return { success: true, result }; } catch (error) { + if (batch) { + batch.end(); + } return { success: false, error }; } }; @@ -133,6 +142,7 @@ export interface ScriptOptions { params?: { [name: string]: string }; capturedVariables?: { [name: string]: Field }; typecheck?: boolean; + editable?: boolean; } export function CompileScript(script: string, options: ScriptOptions = {}): CompileResult { diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 3d626eef0..a4c14ae38 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -38,10 +38,11 @@ export class KeyValueBox extends React.Component { } public static SetField(doc: Doc, key: string, value: string) { let eq = value.startsWith("="); + let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; let dubEq = value.startsWith(":="); value = dubEq ? value.substr(2) : value; - let options: ScriptOptions = { addReturn: true }; + let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } }; if (dubEq) options.typecheck = false; let script = CompileScript(value, options); if (!script.compiled) { @@ -49,12 +50,11 @@ export class KeyValueBox extends React.Component { } let field = new ComputedField(script); if (!dubEq) { - let res = script.run(); + let res = script.run({ this: target }); if (!res.success) return false; field = res.result; } if (Field.IsField(field, true)) { - let target = eq ? doc : Doc.GetProto(doc); target[key] = field; return true; } diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index ac46ccf90..dbca74720 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -3,7 +3,7 @@ import { CompiledScript, CompileScript } from "../client/util/Scripting"; import { Copy, ToScriptString, Parent, SelfProxy } from "../new_fields/FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; -import { computed } from "mobx"; +import { Doc } from "../new_fields/Doc"; function optional(propSchema: PropSchema) { return custom(value => { @@ -23,6 +23,7 @@ const optionsSchema = createSimpleSchema({ requiredType: true, addReturn: true, typecheck: true, + readonly: true, params: optional(map(primitive())) }); @@ -86,9 +87,9 @@ export class ScriptField extends ObjectField { @Deserializable("computed", deserializeScript) export class ComputedField extends ScriptField { - @computed - get value() { - const val = this._script.run({ this: (this[Parent] as any)[SelfProxy] }); + //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc + value(doc: Doc) { + const val = this._script.run({ this: doc }); if (val.success) { return val.result; } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 9bacf49ba..cce4fff5d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -2,7 +2,7 @@ import { observable, action } from "mobx"; import { serializable, primitive, map, alias, list } from "serializr"; import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; import { DocServer } from "../client/DocServer"; -import { setter, getter, getField, updateFunction, deleteProperty } from "./util"; +import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util"; import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast } from "./Types"; import { listSpec } from "./Schema"; import { ObjectField } from "./ObjectField"; @@ -156,6 +156,15 @@ export namespace Doc { // return Cast(field, ctor); // }); // } + export function MakeReadOnly(): { end(): void } { + makeReadOnly(); + return { + end() { + makeEditable(); + } + }; + } + export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult { const self = doc[Self]; return getField(self, key, ignoreProto); diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index 130ec066e..38d874a68 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -48,9 +48,8 @@ export class ProxyField extends ObjectField { private failed = false; private promise?: Promise; - value(callback?: ((field: T | undefined) => void)): T | undefined | FieldWaiting { + value(): T | undefined | FieldWaiting { if (this.cache) { - callback && callback(this.cache); return this.cache; } if (this.failed) { @@ -64,7 +63,6 @@ export class ProxyField extends ObjectField { return field; })); } - callback && this.promise.then(callback); return this.promise; } } diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 8cb1db953..cc937c567 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -6,10 +6,13 @@ import { FieldValue } from "./Types"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action } from "mobx"; -import { Parent, OnUpdate, Update, Id, SelfProxy } from "./FieldSymbols"; +import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; import { ComputedField } from "../fields/ScriptField"; -export const setter = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { +function _readOnlySetter(): never { + throw new Error("Documents can't be modified in read-only mode"); +} +const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { if (SerializationHelper.IsSerializing()) { target[prop] = value; return true; @@ -53,6 +56,20 @@ export const setter = action(function (target: any, prop: string | symbol | numb return true; }); +let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl; + +export function makeReadOnly() { + _setter = _readOnlySetter; +} + +export function makeEditable() { + _setter = _setterImpl; +} + +export function setter(target: any, prop: string | symbol | number, value: any, receiver: any): boolean { + return _setter(target, prop, value, receiver); +} + export function getter(target: any, prop: string | symbol | number, receiver: any): any { if (typeof prop === "symbol") { return target.__fields[prop] || target[prop]; @@ -60,25 +77,30 @@ export function getter(target: any, prop: string | symbol | number, receiver: an if (SerializationHelper.IsSerializing()) { return target[prop]; } - return getField(target, prop); + return getFieldImpl(target, prop, receiver); } -export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { +function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any { + receiver = receiver || target[SelfProxy]; const field = target.__fields[prop]; if (field instanceof ProxyField) { return field.value(); } if (field instanceof ComputedField) { - return field.value; + return field.value(receiver); } if (field === undefined && !ignoreProto && prop !== "proto") { - const proto = getField(target, "proto", true); + const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters if (proto instanceof Doc) { - return proto[prop]; + return getFieldImpl(proto[Self], prop, receiver, ignoreProto); } return undefined; } return field; + +} +export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { + return getFieldImpl(target, prop, undefined, ignoreProto); } export function deleteProperty(target: any, prop: string | number | symbol) { -- cgit v1.2.3-70-g09d2 From d475b19e9ba7bc8870ec7bc1e10b5cc88decea0b Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 24 Jun 2019 15:56:42 -0400 Subject: fixed crash --- src/new_fields/util.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/new_fields') diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index cadd8f8d0..abb777adf 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -21,7 +21,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number target[prop] = value; return true; } - value = value[SelfProxy] || value; + if (value !== undefined) { + value = value[SelfProxy] || value; + } const curValue = target.__fields[prop]; if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically -- cgit v1.2.3-70-g09d2 From 41cf1e8536964764f18ab752140e484e36cbe464 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 17:09:36 -0400 Subject: links can save --- src/client/documents/Documents.ts | 58 +--- src/client/util/DocumentManager.ts | 14 +- src/client/util/DragManager.ts | 24 +- src/client/util/LinkManager.ts | 239 +++++++++++---- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.tsx | 7 + .../views/collections/CollectionDockingView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 18 +- .../CollectionFreeFormLinksView.tsx | 7 +- src/client/views/nodes/DocumentView.tsx | 8 +- src/client/views/nodes/LinkButtonBox.scss | 34 +-- src/client/views/nodes/LinkButtonBox.tsx | 126 ++++---- src/client/views/nodes/LinkEditor.tsx | 323 +++++++++------------ src/client/views/nodes/LinkMenu.tsx | 2 +- src/client/views/nodes/LinkMenuGroup.tsx | 8 +- src/client/views/nodes/LinkMenuItem.tsx | 6 +- src/new_fields/Doc.ts | 9 +- src/new_fields/LinkButtonField.ts | 58 ++-- 18 files changed, 503 insertions(+), 444 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 64032e096..fbd96fb66 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,8 +35,8 @@ import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; -import { LinkButtonBox } from "../views/nodes/LinkButtonBox"; -import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField"; +// import { LinkButtonBox } from "../views/nodes/LinkButtonBox"; +// import { LinkButtonField, LinkButtonData } from "../../new_fields/LinkButtonField"; import { DocumentManager } from "../util/DocumentManager"; import { Id } from "../../new_fields/FieldSymbols"; var requestImageSize = require('request-image-size'); @@ -100,6 +100,7 @@ export namespace DocUtils { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); let linkDocProto = Doc.GetProto(linkDoc); + linkDocProto.context = targetContext; linkDocProto.title = title; //=== "" ? source.title + " to " + target.title : title; linkDocProto.linkDescription = description; linkDocProto.linkTags = tags; @@ -111,36 +112,7 @@ export namespace DocUtils { linkDocProto.anchor2Page = target.curPage; linkDocProto.anchor2Groups = new List([]); - linkDocProto.context = targetContext; - - let sourceViews = DocumentManager.Instance.getDocumentViews(source); - let targetViews = DocumentManager.Instance.getDocumentViews(target); - sourceViews.forEach(sv => { - targetViews.forEach(tv => { - - // TODO: do only for when diff contexts - let proxy1 = Docs.LinkButtonDocument( - { sourceViewId: StrCast(sv.props.Document[Id]), targetViewId: StrCast(tv.props.Document[Id]) }, - { width: 200, height: 100, borderRounding: 0 }); - let proxy1Proto = Doc.GetProto(proxy1); - proxy1Proto.sourceViewId = StrCast(sv.props.Document[Id]); - proxy1Proto.targetViewId = StrCast(tv.props.Document[Id]); - proxy1Proto.isLinkButton = true; - - let proxy2 = Docs.LinkButtonDocument( - { sourceViewId: StrCast(tv.props.Document[Id]), targetViewId: StrCast(sv.props.Document[Id]) }, - { width: 200, height: 100, borderRounding: 0 }); - let proxy2Proto = Doc.GetProto(proxy2); - proxy2Proto.sourceViewId = StrCast(tv.props.Document[Id]); - proxy2Proto.targetViewId = StrCast(sv.props.Document[Id]); - proxy2Proto.isLinkButton = true; - - LinkManager.Instance.linkProxies.push(proxy1); - LinkManager.Instance.linkProxies.push(proxy2); - }); - }); - - LinkManager.Instance.allLinks.push(linkDoc); + LinkManager.Instance.addLink(linkDoc); return linkDoc; }, "make link"); @@ -160,7 +132,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; - let linkProto: Doc; + // let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; const pdfProtoId = "pdfProto"; @@ -171,7 +143,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; - const linkProtoId = "linkProto"; + // const linkProtoId = "linkProto"; export function initProtos(): Promise { return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { @@ -185,7 +157,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); - linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); + // linkProto = fields[linkProtoId] as Doc || CreateLinkPrototype(); }); } @@ -218,11 +190,11 @@ export namespace Docs { { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); return iconProto; } - function CreateLinkPrototype(): Doc { - let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), - { x: 0, y: 0, width: 300 }); - return linkProto; - } + // function CreateLinkPrototype(): Doc { + // let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), + // { x: 0, y: 0, width: 300 }); + // return linkProto; + // } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" }); @@ -309,9 +281,9 @@ export namespace Docs { export function IconDocument(icon: string, options: DocumentOptions = {}) { return CreateInstance(iconProto, new IconField(icon), options); } - export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { - return CreateInstance(linkProto, new LinkButtonField(data), options); - } + // export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { + // return CreateInstance(linkProto, new LinkButtonField(data), options); + // } export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 89e6183d6..767abe63f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -85,12 +85,11 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - console.log("link"); - return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); + let pairs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { + let linksList = LinkManager.Instance.getAllRelatedLinks(dv.props.Document); pairs.push(...linksList.reduce((pairs, link) => { if (link) { - let linkToDoc = LinkManager.Instance.findOppositeAnchor(link, dv.props.Document); + let linkToDoc = LinkManager.Instance.getOppositeAnchor(link, dv.props.Document); DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => { pairs.push({ a: dv, b: docView1, l: link }); }); @@ -100,6 +99,13 @@ export class DocumentManager { // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); + + // console.log("LINKED DOCUMENT VIEWS"); + // pairs.forEach(p => { + // console.log(StrCast(p.a.Document.title), p.a.props.Document[Id], StrCast(p.b.Document.title), p.b.props.Document[Id]); + // }); + + return pairs; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f4c8adc8e..1aacf2c53 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -46,7 +46,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () } export async function DragLinkAsDocument(dragEle: HTMLElement, x: number, y: number, linkDoc: Doc, sourceDoc: Doc) { - let draggeddoc = LinkManager.Instance.findOppositeAnchor(linkDoc, sourceDoc); + let draggeddoc = LinkManager.Instance.getOppositeAnchor(linkDoc, sourceDoc); let moddrag = await Cast(draggeddoc.annotationOn, Doc); let dragData = new DragManager.DocumentDragData(moddrag ? [moddrag] : [draggeddoc]); @@ -66,10 +66,10 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n // TODO: if not in same context then don't drag if (srcTarg) { - let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); + let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg); if (linkDocs) { draggedDocs = linkDocs.map(link => { - return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + return LinkManager.Instance.getOppositeAnchor(link, sourceDoc); }); } } @@ -236,10 +236,16 @@ export namespace DragManager { if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { return d; } else { - return Doc.MakeAlias(d); + // return d; + let r = Doc.MakeAlias(d); + // DocUtils.MakeLink(sourceDoc, r); + return r; } } else { - return Doc.MakeAlias(d); + // return d; + let r = Doc.MakeAlias(d); + // DocUtils.MakeLink(sourceDoc, r); + return r; } // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ? // Doc.MakeAlias(d) : d; @@ -282,10 +288,10 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } - export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { - runInAction(() => StartDragFunctions.map(func => func())); - StartDrag([ele], dragData, downX, downY, options); - } + // export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { + // runInAction(() => StartDragFunctions.map(func => func())); + // StartDrag([ele], dragData, downX, downY, options); + // } export let AbortDrag: () => void = emptyFunction; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 745255f31..82c3a9acd 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,9 +1,10 @@ import { observable, action } from "mobx"; -import { StrCast, Cast } from "../../new_fields/Types"; +import { StrCast, Cast, FieldValue } from "../../new_fields/Types"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { listSpec } from "../../new_fields/Schema"; import { List } from "../../new_fields/List"; import { Id } from "../../new_fields/FieldSymbols"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; /* @@ -23,6 +24,11 @@ import { Id } from "../../new_fields/FieldSymbols"; * - user defined kvps */ export class LinkManager { + // static Instance: LinkManager; + // private constructor() { + // LinkManager.Instance = this; + // } + private static _instance: LinkManager; public static get Instance(): LinkManager { return this._instance || (this._instance = new this()); @@ -30,25 +36,138 @@ export class LinkManager { private constructor() { } - @observable public allLinks: Array = []; // list of link docs - @observable public groupMetadataKeys: Map> = new Map(); - // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it hodls - @observable public linkProxies: Array = []; // list of linkbutton docs - used to visualize link when an anchors are not in the same context + public get LinkManagerDoc(): Doc | undefined { + return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc)); + } + // @observable public allLinks: Array = []; //List = new List([]); // list of link docs + // @observable public groupMetadataKeys: Map> = new Map(); + // map of group type to list of its metadata keys; serves as a dictionary of groups to what kind of metadata it holds + + public getAllLinks(): Doc[] { + return LinkManager.Instance.LinkManagerDoc ? LinkManager.Instance.LinkManagerDoc.allLinks ? DocListCast(LinkManager.Instance.LinkManagerDoc.allLinks) : [] : []; + } + + public addLink(linkDoc: Doc): boolean { + let linkList = LinkManager.Instance.getAllLinks(); + linkList.push(linkDoc); + console.log("link man doc", LinkManager.Instance.LinkManagerDoc); + if (LinkManager.Instance.LinkManagerDoc) { + LinkManager.Instance.LinkManagerDoc.allLinks = new List(linkList); + return true; + } + return false; + } + + public deleteLink(linkDoc: Doc): boolean { + let linkList = LinkManager.Instance.getAllLinks(); + let index = LinkManager.Instance.getAllLinks().indexOf(linkDoc); + if (index > -1) { + linkList.splice(index, 1); + if (LinkManager.Instance.LinkManagerDoc) { + LinkManager.Instance.LinkManagerDoc.allLinks = new List(linkList); + return true; + } + } + return false; + } // finds all links that contain the given anchor - public findAllRelatedLinks(anchor: Doc): Array { - return LinkManager.Instance.allLinks.filter(link => { + public getAllRelatedLinks(anchor: Doc): Doc[] {//List { + let related = LinkManager.Instance.getAllLinks().filter(link => { let protomatch1 = Doc.AreProtosEqual(anchor, Cast(link.anchor1, Doc, new Doc)); let protomatch2 = Doc.AreProtosEqual(anchor, Cast(link.anchor2, Doc, new Doc)); - // let idmatch1 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor1, Doc, new Doc)[Id]); - // let idmatch2 = StrCast(anchor[Id]) === StrCast(Cast(link.anchor2, Doc, new Doc)[Id]); - return protomatch1 || protomatch2;// || idmatch1 || idmatch2; + return protomatch1 || protomatch2; }); + return related; + } + + public addGroupType(groupType: string): boolean { + if (LinkManager.Instance.LinkManagerDoc) { + LinkManager.Instance.LinkManagerDoc[groupType] = new List([]); + let groupTypes = LinkManager.Instance.getAllGroupTypes(); + groupTypes.push(groupType); + LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List(groupTypes); + return true; + } + return false; + } + + // removes all group docs from all links with the given group type + public deleteGroupType(groupType: string): boolean { + if (LinkManager.Instance.LinkManagerDoc) { + if (LinkManager.Instance.LinkManagerDoc[groupType]) { + LinkManager.Instance.LinkManagerDoc[groupType] = undefined; + LinkManager.Instance.getAllLinks().forEach(linkDoc => { + LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); + LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); + }); + } + return true; + } else return false; + } + + public getAllGroupTypes(): string[] { + if (LinkManager.Instance.LinkManagerDoc) { + if (LinkManager.Instance.LinkManagerDoc.allGroupTypes) { + return Cast(LinkManager.Instance.LinkManagerDoc.allGroupTypes, listSpec("string"), []); + } else { + LinkManager.Instance.LinkManagerDoc.allGroupTypes = new List([]); + return []; + } + } + return []; + } + + // gets the groups associates with an anchor in a link + public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + return DocListCast(linkDoc.anchor1Groups); + } else { + return DocListCast(linkDoc.anchor2Groups); + } + } + + // sets the groups of the given anchor in the given link + public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) { + if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { + linkDoc.anchor1Groups = new List(groups); + } else { + linkDoc.anchor2Groups = new List(groups); + } + } + + public addGroupToAnchor(linkDoc: Doc, anchor: Doc, groupDoc: Doc, replace: boolean = false) { + let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + let index = groups.findIndex(gDoc => { + return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase(); + }); + if (index > -1 && replace) { + groups[index] = groupDoc; + } + if (index === -1) { + groups.push(groupDoc); + } + LinkManager.Instance.setAnchorGroups(linkDoc, anchor, groups); + } + + // removes group doc of given group type only from given anchor on given link + public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { + let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); + LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } + // public doesAnchorHaveGroup(linkDoc: Doc, anchor: Doc, groupDoc: Doc): boolean { + // let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); + // let index = groups.findIndex(gDoc => { + // return StrCast(groupDoc.type).toUpperCase() === StrCast(gDoc.type).toUpperCase(); + // }); + // return index > -1; + // } + // returns map of group type to anchor's links in that group type - public findRelatedGroupedLinks(anchor: Doc): Map> { - let related = this.findAllRelatedLinks(anchor); + public getRelatedGroupedLinks(anchor: Doc): Map> { + let related = this.getAllRelatedLinks(anchor); let anchorGroups = new Map>(); related.forEach(link => { let groups = LinkManager.Instance.getAnchorGroups(link, anchor); @@ -73,10 +192,41 @@ export class LinkManager { return anchorGroups; } + // public addMetadataKeyToGroup(groupType: string, key: string): boolean { + // if (LinkManager.Instance.LinkManagerDoc) { + // if (LinkManager.Instance.LinkManagerDoc[groupType]) { + // let keyList = LinkManager.Instance.findMetadataKeysInGroup(groupType); + // keyList.push(key); + // LinkManager.Instance.LinkManagerDoc[groupType] = new List(keyList); + // return true; + // } + // return false; + // } + // return false; + // } + + public getMetadataKeysInGroup(groupType: string): string[] { + if (LinkManager.Instance.LinkManagerDoc) { + return LinkManager.Instance.LinkManagerDoc[groupType] ? Cast(LinkManager.Instance.LinkManagerDoc[groupType], listSpec("string"), []) : []; + } + return []; + } + + public setMetadataKeysForGroup(groupType: string, keys: string[]): boolean { + if (LinkManager.Instance.LinkManagerDoc) { + // if (LinkManager.Instance.LinkManagerDoc[groupType]) { + LinkManager.Instance.LinkManagerDoc[groupType] = new List(keys); + return true; + // } + // return false; + } + return false; + } + // returns a list of all metadata docs associated with the given group type - public findAllMetadataDocsInGroup(groupType: string): Array { + public getAllMetadataDocsInGroup(groupType: string): Array { let md: Doc[] = []; - let allLinks = LinkManager.Instance.allLinks; + let allLinks = LinkManager.Instance.getAllLinks(); allLinks.forEach(linkDoc => { let anchor1Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc)); let anchor2Groups = LinkManager.Instance.getAnchorGroups(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc)); @@ -86,27 +236,9 @@ export class LinkManager { return md; } - // removes all group docs from all links with the given group type - public deleteGroup(groupType: string): void { - let deleted = LinkManager.Instance.groupMetadataKeys.delete(groupType); - if (deleted) { - LinkManager.Instance.allLinks.forEach(linkDoc => { - LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor1, Doc, new Doc), groupType); - LinkManager.Instance.removeGroupFromAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, new Doc), groupType); - }); - } - } - - // removes group doc of given group type only from given anchor on given link - public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { - let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); - LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); - } - // checks if a link with the given anchors exists - public doesLinkExist(anchor1: Doc, anchor2: Doc) { - let allLinks = LinkManager.Instance.allLinks; + public doesLinkExist(anchor1: Doc, anchor2: Doc): boolean { + let allLinks = LinkManager.Instance.getAllLinks(); let index = allLinks.findIndex(linkDoc => { return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor2)) || (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, new Doc), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, new Doc), anchor1)); @@ -115,7 +247,7 @@ export class LinkManager { } // finds the opposite anchor of a given anchor in a link - public findOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { + public getOppositeAnchor(linkDoc: Doc, anchor: Doc): Doc { if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { return Cast(linkDoc.anchor2, Doc, new Doc); } else { @@ -123,34 +255,17 @@ export class LinkManager { } } - // gets the groups associates with an anchor in a link - public getAnchorGroups(linkDoc: Doc, anchor: Doc): Array { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { - return DocListCast(linkDoc.anchor1Groups); - } else { - return DocListCast(linkDoc.anchor2Groups); - } - } - // sets the groups of the given anchor in the given link - public setAnchorGroups(linkDoc: Doc, anchor: Doc, groups: Doc[]) { - if (Doc.AreProtosEqual(anchor, Cast(linkDoc.anchor1, Doc, new Doc))) { - linkDoc.anchor1Groups = new List(groups); - } else { - linkDoc.anchor2Groups = new List(groups); - } - } - - @action - public addLinkProxy(proxy: Doc) { - LinkManager.Instance.linkProxies.push(proxy); - } + // @action + // public addLinkProxy(proxy: Doc) { + // LinkManager.Instance.linkProxies.push(proxy); + // } - public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { - let index = LinkManager.Instance.linkProxies.findIndex(p => { - return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; - }); - return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; - } + // public findLinkProxy(sourceViewId: string, targetViewId: string): Doc | undefined { + // let index = LinkManager.Instance.linkProxies.findIndex(p => { + // return StrCast(p.sourceViewId) === sourceViewId && StrCast(p.targetViewId) === targetViewId; + // }); + // return index > -1 ? LinkManager.Instance.linkProxies[index] : undefined; + // } } \ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 926273633..3a2752d7e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -573,7 +573,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let linkButton = null; if (SelectionManager.SelectedDocuments().length > 0) { let selFirst = SelectionManager.SelectedDocuments()[0]; - let linkCount = LinkManager.Instance.findAllRelatedLinks(selFirst.props.Document).length; + let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length; linkButton = (([]); + CurrentUserUtils.UserDocument.linkManagerDoc = linkManagerDoc; + } list.push(mainDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c82027da5..4140f8029 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -354,9 +354,9 @@ export class CollectionDockingView extends React.Component [LinkManager.Instance.findAllRelatedLinks(doc), doc.title], + tab.reactionDisposer = reaction(() => [LinkManager.Instance.getAllRelatedLinks(doc), doc.title], () => { - counter.innerHTML = LinkManager.Instance.findAllRelatedLinks(doc).length; + counter.innerHTML = LinkManager.Instance.getAllRelatedLinks(doc).length; tab.titleElement[0].textContent = doc.title; }, { fireImmediately: true }); tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0b922b3c4..7bc3ad124 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -73,7 +73,7 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - onPointerDown = (e: React.PointerEvent) => e.stopPropagation() + onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -115,7 +115,7 @@ class TreeView extends React.Component { return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); } @action - indent = () => this.props.addDocument(this.props.document) && this.delete(); + indent = () => this.props.addDocument(this.props.document) && this.delete() renderBullet() { let docList = Cast(this.props.document["data"], listSpec(Doc)); @@ -167,7 +167,7 @@ class TreeView extends React.Component { keyList.push(key); } }); - if (LinkManager.Instance.findAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); + if (LinkManager.Instance.getAllRelatedLinks(this.props.document).length > 0) keyList.push("links"); if (keyList.indexOf("data") !== -1) { keyList.splice(keyList.indexOf("data"), 1); } @@ -281,9 +281,9 @@ class TreeView extends React.Component { let ele: JSX.Element[] = []; let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); - let groups = LinkManager.Instance.findRelatedGroupedLinks(this.props.document); + let groups = LinkManager.Instance.getRelatedGroupedLinks(this.props.document); groups.forEach((groupLinkDocs, groupType) => { - let destLinks = groupLinkDocs.map(d => LinkManager.Instance.findOppositeAnchor(d, this.props.document)); + let destLinks = groupLinkDocs.map(d => LinkManager.Instance.getOppositeAnchor(d, this.props.document)); ele.push(
{groupType}:
@@ -325,7 +325,7 @@ class TreeView extends React.Component { addDocTab={this.props.addDocTab} setPreviewScript={emptyFunction}> -
+
; } } return
@@ -364,14 +364,14 @@ class TreeView extends React.Component { TreeView.AddDocToList(docList[i - 1], fieldKey, child); remove(child); } - } + }; let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); - } + }; let rowHeight = () => { let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym](); - } + }; return { let srcViews = this.documentAnchors(connection.a); let targetViews = this.documentAnchors(connection.b); - console.log(srcViews.length, targetViews.length); + // console.log(srcViews.length, targetViews.length); let possiblePairs: { a: Doc, b: Doc, }[] = []; srcViews.map(sv => { targetViews.map(tv => { - console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id)); + // console.log("PUSH", StrCast(sv.props.Document.title), StrCast(sv.props.Document.id), StrCast(tv.props.Document.title), StrCast(tv.props.Document.id)); possiblePairs.push({ a: sv.props.Document, b: tv.props.Document }); }); }); @@ -142,7 +142,6 @@ export class CollectionFreeFormLinksView extends React.Component diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1fc2cf770..7b185336b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -285,7 +285,7 @@ export class DocumentView extends DocComponent(Docu let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); - let linkedDocs = LinkManager.Instance.findAllRelatedLinks(this.props.Document); + let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = subBulletDocs ? [...subBulletDocs, ...expandedDocs] : expandedDocs; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; @@ -536,11 +536,6 @@ export class DocumentView extends DocComponent(Docu onPointerEnter = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = true; }; onPointerLeave = (e: React.PointerEvent): void => { this.props.Document.libraryBrush = false; }; - onDragOver = (e: React.DragEvent): void => { - this.props.Document.libraryBrush = true; - console.log("dragOver"); - }; - onDragLeave = (e: React.DragEvent): void => { this.props.Document.libraryBrush = false; }; isSelected = () => SelectionManager.IsSelected(this); @action select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; @@ -585,7 +580,6 @@ export class DocumentView extends DocComponent(Docu // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onDragOver={this.onDragOver} onDragLeave={this.onDragLeave} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} > {this.contents} diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss index 24bfd2c9f..6be2dcf60 100644 --- a/src/client/views/nodes/LinkButtonBox.scss +++ b/src/client/views/nodes/LinkButtonBox.scss @@ -1,18 +1,18 @@ -.linkBox-cont { - width: 200px; - height: 100px; - background-color: black; - text-align: center; - color: white; - padding: 10px; - border-radius: 5px; - position: relative; +// .linkBox-cont { +// width: 200px; +// height: 100px; +// background-color: black; +// text-align: center; +// color: white; +// padding: 10px; +// border-radius: 5px; +// position: relative; - .linkBox-cont-wrapper { - width: calc(100% - 20px); - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - } -} \ No newline at end of file +// .linkBox-cont-wrapper { +// width: calc(100% - 20px); +// position: absolute; +// left: 50%; +// top: 50%; +// transform: translate(-50%, -50%); +// } +// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx index 8a7c1ed8b..440847ead 100644 --- a/src/client/views/nodes/LinkButtonBox.tsx +++ b/src/client/views/nodes/LinkButtonBox.tsx @@ -1,63 +1,63 @@ -import React = require("react"); -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { computed, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { FieldView, FieldViewProps } from './FieldView'; -import "./LinkButtonBox.scss"; -import { DocumentView } from "./DocumentView"; -import { Doc } from "../../../new_fields/Doc"; -import { LinkButtonField } from "../../../new_fields/LinkButtonField"; -import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { DocumentManager } from "../../util/DocumentManager"; -import { Id } from "../../../new_fields/FieldSymbols"; - -library.add(faCaretUp); -library.add(faObjectGroup); -library.add(faStickyNote); -library.add(faFilePdf); -library.add(faFilm); - -@observer -export class LinkButtonBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } - - followLink = (): void => { - console.log("follow link???"); - let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); - let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); - if (targetView && targetView.props.ContainingCollectionView) { - CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); - } - } - - render() { - - let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); - let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); - - let text = "Could not find link"; - if (targetView) { - let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; - text = "Link to " + StrCast(targetView.props.Document.title) + context; - } - - let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); - let display = activeDvs.reduce((found, dv) => { - let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); - let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); - let match = matchSv || matchTv; - return match || found; - }, false); - - return ( -
-
-

{text}

-
-
- ); - } -} \ No newline at end of file +// import React = require("react"); +// import { library } from '@fortawesome/fontawesome-svg-core'; +// import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; +// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +// import { computed, observable, runInAction } from "mobx"; +// import { observer } from "mobx-react"; +// import { FieldView, FieldViewProps } from './FieldView'; +// import "./LinkButtonBox.scss"; +// import { DocumentView } from "./DocumentView"; +// import { Doc } from "../../../new_fields/Doc"; +// import { LinkButtonField } from "../../../new_fields/LinkButtonField"; +// import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; +// import { CollectionDockingView } from "../collections/CollectionDockingView"; +// import { DocumentManager } from "../../util/DocumentManager"; +// import { Id } from "../../../new_fields/FieldSymbols"; + +// library.add(faCaretUp); +// library.add(faObjectGroup); +// library.add(faStickyNote); +// library.add(faFilePdf); +// library.add(faFilm); + +// @observer +// export class LinkButtonBox extends React.Component { +// public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } + +// followLink = (): void => { +// console.log("follow link???"); +// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); +// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); +// if (targetView && targetView.props.ContainingCollectionView) { +// CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); +// } +// } + +// render() { + +// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); +// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); + +// let text = "Could not find link"; +// if (targetView) { +// let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; +// text = "Link to " + StrCast(targetView.props.Document.title) + context; +// } + +// let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); +// let display = activeDvs.reduce((found, dv) => { +// let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); +// let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); +// let match = matchSv || matchTv; +// return match || found; +// }, false); + +// return ( +//
+//
+//

{text}

+//
+//
+// ); +// } +// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 95199bae2..5f4f7d4f0 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -17,9 +17,8 @@ library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, fa interface GroupTypesDropdownProps { - groupId: string; groupType: string; - setGroup: (groupId: string, group: string) => void; + setGroupType: (group: string) => void; } // this dropdown could be generalized @observer @@ -32,20 +31,20 @@ class GroupTypesDropdown extends React.Component { @action createGroup = (groupType: string): void => { - this.props.setGroup(this.props.groupId, groupType); - LinkManager.Instance.groupMetadataKeys.set(groupType, []); + this.props.setGroupType(groupType); + LinkManager.Instance.addGroupType(groupType); } renderOptions = (): JSX.Element[] | JSX.Element => { if (this._searchTerm === "") return <>; - let allGroupTypes = Array.from(LinkManager.Instance.groupMetadataKeys.keys()); + let allGroupTypes = Array.from(LinkManager.Instance.getAllGroupTypes()); let groupOptions = allGroupTypes.filter(groupType => groupType.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); let exactFound = groupOptions.findIndex(groupType => groupType.toUpperCase() === this._searchTerm.toUpperCase()) > -1; let options = groupOptions.map(groupType => { return
{ this.props.setGroup(this.props.groupId, groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}
; + onClick={() => { this.props.setGroupType(groupType); this.setGroupType(groupType); this.setSearchTerm(""); }}>{groupType}
; }); // if search term does not already exist as a group type, give option to create new group type @@ -85,7 +84,7 @@ class LinkMetadataEditor extends React.Component { @action setMetadataKey = (value: string): void => { - let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType); // don't allow user to create existing key let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase()); @@ -98,12 +97,15 @@ class LinkMetadataEditor extends React.Component { } // set new value for key - let currIndex = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase()); + let currIndex = groupMdKeys.findIndex(key => { + console.log("finding index this", key.toUpperCase(), "that", this._key.toUpperCase()); + return StrCast(key).toUpperCase() === this._key.toUpperCase(); + }); if (currIndex === -1) console.error("LinkMetadataEditor: key was not found"); groupMdKeys[currIndex] = value; this._key = value; - LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); + LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys); } @action @@ -116,13 +118,13 @@ class LinkMetadataEditor extends React.Component { @action removeMetadata = (): void => { - let groupMdKeys = new Array(...LinkManager.Instance.groupMetadataKeys.get(this.props.groupType)!); + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType); let index = groupMdKeys.findIndex(key => key.toUpperCase() === this._key.toUpperCase()); if (index === -1) console.error("LinkMetadataEditor: key was not found"); groupMdKeys.splice(index, 1); - LinkManager.Instance.groupMetadataKeys.set(this.props.groupType, groupMdKeys); + LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, groupMdKeys); this._key = ""; } @@ -137,215 +139,176 @@ class LinkMetadataEditor extends React.Component { } } - -interface LinkEditorProps { +interface LinkGroupEditorProps { sourceDoc: Doc; linkDoc: Doc; - showLinks: () => void; + groupDoc: Doc; } @observer -export class LinkEditor extends React.Component { +export class LinkGroupEditor extends React.Component { - // map of temporary group id to the corresponding group doc - @observable private _groups: Map = new Map(); - - constructor(props: LinkEditorProps) { - super(props); + @action + setGroupType = (groupType: string): void => { + console.log("SET GROUP TYPE TO", groupType); + this.props.groupDoc.type = groupType; + console.log("GROUP TYPE HAS BEEN SET TO ", StrCast(this.props.groupDoc.type)); + } - let groups = new Map(); - let groupList = LinkManager.Instance.getAnchorGroups(props.linkDoc, props.sourceDoc); - groupList.forEach(groupDoc => { - let id = Utils.GenerateGuid(); - groups.set(id, groupDoc); - }); - this._groups = groups; + removeGroupFromLink = (groupType: string): void => { + LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); } - @action - deleteLink = (): void => { - let index = LinkManager.Instance.allLinks.indexOf(this.props.linkDoc); - LinkManager.Instance.allLinks.splice(index, 1); - this.props.showLinks(); + deleteGroup = (groupType: string): void => { + LinkManager.Instance.deleteGroupType(groupType); } - @action - addGroup = (): void => { - // new group only gets added if there is not already a group with type "new group" - let index = Array.from(this._groups.values()).findIndex(groupDoc => { - return groupDoc.type === "New Group"; - }); - if (index > -1) return; + copyGroup = (groupType: string): void => { + let sourceGroupDoc = this.props.groupDoc; + let sourceMdDoc = Cast(sourceGroupDoc.metadata, Doc, new Doc); - // create new metadata document for group - let mdDoc = Docs.TextDocument(); - mdDoc.proto!.anchor1 = this.props.sourceDoc.title; - mdDoc.proto!.anchor2 = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; + let destDoc = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + // let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); + let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - // create new group document - let groupDoc = Docs.TextDocument(); - groupDoc.proto!.type = "New Group"; - groupDoc.proto!.metadata = mdDoc; + // create new metadata doc with copied kvp + let destMdDoc = new Doc(); + destMdDoc.anchor1 = StrCast(sourceMdDoc.anchor2); + destMdDoc.anchor2 = StrCast(sourceMdDoc.anchor1); + keys.forEach(key => { + let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]); + destMdDoc[key] = val; + }); - this._groups.set(Utils.GenerateGuid(), groupDoc); + // create new group doc with new metadata doc + let destGroupDoc = new Doc(); + destGroupDoc.type = groupType; + destGroupDoc.metadata = destMdDoc; - let linkDoc = this.props.linkDoc.proto ? this.props.linkDoc.proto : this.props.linkDoc; - LinkManager.Instance.setAnchorGroups(linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); + LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, destDoc, destGroupDoc, true); } @action - setGroupType = (groupId: string, groupType: string): void => { - let groupDoc = this._groups.get(groupId); - if (groupDoc) { - groupDoc.proto!.type = groupType; - this._groups.set(groupId, groupDoc); - LinkManager.Instance.setAnchorGroups(this.props.linkDoc, this.props.sourceDoc, Array.from(this._groups.values())); - } - } - - removeGroupFromLink = (groupId: string, groupType: string): void => { - let groupDoc = this._groups.get(groupId); - if (!groupDoc) console.error("LinkEditor: group not found"); - LinkManager.Instance.removeGroupFromAnchor(this.props.linkDoc, this.props.sourceDoc, groupType); - this._groups.delete(groupId); - } - - deleteGroup = (groupId: string, groupType: string): void => { - let groupDoc = this._groups.get(groupId); - if (!groupDoc) console.error("LinkEditor: group not found"); - LinkManager.Instance.deleteGroup(groupType); - this._groups.delete(groupId); + addMetadata = (groupType: string): void => { + let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + // only add "new key" if there is no other key with value "new key"; prevents spamming + if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); + LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys); } - copyGroup = (groupId: string, groupType: string): void => { - let sourceGroupDoc = this._groups.get(groupId); - let sourceMdDoc = Cast(sourceGroupDoc!.metadata, Doc, new Doc); - let destDoc = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - let destGroupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, destDoc); - let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); - - // create new metadata doc with copied kvp - let destMdDoc = Docs.TextDocument(); - destMdDoc.proto!.anchor1 = StrCast(sourceMdDoc.anchor2); - destMdDoc.proto!.anchor2 = StrCast(sourceMdDoc.anchor1); - if (keys) { - keys.forEach(key => { - let val = sourceMdDoc[key] === undefined ? "" : StrCast(sourceMdDoc[key]); - destMdDoc[key] = val; + renderMetadata = (): JSX.Element[] => { + let metadata: Array = []; + let groupDoc = this.props.groupDoc; + let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); + let groupType = StrCast(groupDoc.type); + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + if (groupMdKeys) { + groupMdKeys.forEach((key, index) => { + metadata.push( + + ); }); } - - // create new group doc with new metadata doc - let destGroupDoc = Docs.TextDocument(); - destGroupDoc.proto!.type = groupType; - destGroupDoc.proto!.metadata = destMdDoc; - - // if group does not already exist on opposite anchor, create group doc - let index = destGroupList.findIndex(groupDoc => { StrCast(groupDoc.type).toUpperCase() === groupType.toUpperCase(); }); - if (index > -1) { - destGroupList[index] = destGroupDoc; - } else { - destGroupList.push(destGroupDoc); - } - - LinkManager.Instance.setAnchorGroups(this.props.linkDoc, destDoc, destGroupList); + return metadata; } - viewGroupAsTable = (groupId: string, groupType: string): JSX.Element => { - let keys = LinkManager.Instance.groupMetadataKeys.get(groupType); - let groupDoc = this._groups.get(groupId); - if (keys && groupDoc) { - let docs: Doc[] = LinkManager.Instance.findAllMetadataDocsInGroup(groupType); - let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys!], docs, { width: 200, height: 200, title: groupType + " table" })); - let ref = React.createRef(); - return
; - } else { - return ; - } + viewGroupAsTable = (groupType: string): JSX.Element => { + let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); + let createTable = action(() => Docs.SchemaDocument(["anchor1", "anchor2", ...keys], docs, { width: 500, height: 300, title: groupType + " table" })); + let ref = React.createRef(); + return
; } - renderGroup = (groupId: string, groupDoc: Doc): JSX.Element => { - let type = StrCast(groupDoc.type); - if ((type && LinkManager.Instance.groupMetadataKeys.get(type)) || type === "New Group") { - let buttons; - if (type === "New Group") { - buttons = ( - <> - - - - - - - ); - } else { - buttons = ( - <> - - - - - {this.viewGroupAsTable(groupId, type)} - - ); - } - - return ( -
-
-

type:

- -
- {this.renderMetadata(groupId)} -
- {buttons} -
-
+ render() { + let groupType = StrCast(this.props.groupDoc.type); + // if ((groupType && LinkManager.Instance.getMetadataKeysInGroup(groupType).length > 0) || groupType === "") { + let buttons; + if (groupType === "") { + buttons = ( + <> + + + + + + ); } else { - return <>; + buttons = ( + <> + + + + + {this.viewGroupAsTable(groupType)} + + ); } + + return ( +
+
+

type:

+ +
+ {this.renderMetadata()} +
+ {buttons} +
+
+ ); } + // else { + // return <>; + // } + // } +} + +interface LinkEditorProps { + sourceDoc: Doc; + linkDoc: Doc; + showLinks: () => void; +} +@observer +export class LinkEditor extends React.Component { @action - addMetadata = (groupType: string): void => { - console.log("ADD MD"); - let mdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); - if (mdKeys) { - // only add "new key" if there is no other key with value "new key"; prevents spamming - if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); - } else { - mdKeys = ["new key"]; - } - LinkManager.Instance.groupMetadataKeys.set(groupType, mdKeys); + deleteLink = (): void => { + LinkManager.Instance.deleteLink(this.props.linkDoc); + this.props.showLinks(); } - renderMetadata = (groupId: string): JSX.Element[] => { - let metadata: Array = []; - let groupDoc = this._groups.get(groupId); - if (groupDoc) { - let mdDoc = Cast(groupDoc.proto!.metadata, Doc, new Doc); - let groupType = StrCast(groupDoc.proto!.type); - let groupMdKeys = LinkManager.Instance.groupMetadataKeys.get(groupType); - if (groupMdKeys) { - groupMdKeys.forEach((key, index) => { - metadata.push( - - ); - }); - } - } - return metadata; + @action + addGroup = (): void => { + // create new metadata document for group + let mdDoc = new Doc(); + mdDoc.anchor1 = this.props.sourceDoc.title; + mdDoc.anchor2 = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc).title; + + // create new group document + let groupDoc = new Doc(); + groupDoc.type = ""; + groupDoc.metadata = mdDoc; + + LinkManager.Instance.addGroupToAnchor(this.props.linkDoc, this.props.sourceDoc, groupDoc); } render() { - let destination = LinkManager.Instance.findOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); + let destination = LinkManager.Instance.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - let groups: Array = []; - this._groups.forEach((groupDoc, groupId) => { - groups.push(this.renderGroup(groupId, groupDoc)); + let groupList = LinkManager.Instance.getAnchorGroups(this.props.linkDoc, this.props.sourceDoc); + console.log("NUM GROUPS", groupList.length); + let groups = groupList.map(groupDoc => { + return ; }); + + // let groups: Array = []; + // this._groups.forEach((groupDoc, groupId) => { + // groups.push(this.renderGroup(groupId, groupDoc)); + // }); + return (
diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index f96c7d2e4..04ca47db3 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -38,7 +38,7 @@ export class LinkMenu extends React.Component { render() { let sourceDoc = this.props.docView.props.Document; - let groups: Map = LinkManager.Instance.findRelatedGroupedLinks(sourceDoc); + let groups: Map = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); if (this._editingLink === undefined) { return (
diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index 229143d99..71326f703 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -42,10 +42,10 @@ export class LinkMenuGroup extends React.Component { document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); - let draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc)); + let draggedDocs = this.props.group.map(linkDoc => LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc)); let dragData = new DragManager.DocumentDragData(draggedDocs); - DragManager.StartLinkedDocumentDrag([this._drag.current], dragData, e.x, e.y, { + DragManager.StartLinkedDocumentDrag([this._drag.current], this.props.sourceDoc, dragData, e.x, e.y, { handlers: { dragComplete: action(emptyFunction), }, @@ -57,7 +57,7 @@ export class LinkMenuGroup extends React.Component { render() { let groupItems = this.props.group.map(linkDoc => { - let destination = LinkManager.Instance.findOppositeAnchor(linkDoc, this.props.sourceDoc); + let destination = LinkManager.Instance.getOppositeAnchor(linkDoc, this.props.sourceDoc); return ; }); @@ -69,6 +69,6 @@ export class LinkMenuGroup extends React.Component { {groupItems}
- ) + ); } } \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 42ef353b7..28694721d 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -53,8 +53,8 @@ export class LinkMenuItem extends React.Component { let mdRows: Array = []; if (groupDoc) { let mdDoc = Cast(groupDoc.metadata, Doc, new Doc); - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); - mdRows = keys!.map(key => { + let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); + mdRows = keys.map(key => { return (
{key}: {StrCast(mdDoc[key])}
); }); } @@ -88,7 +88,7 @@ export class LinkMenuItem extends React.Component { render() { - let keys = LinkManager.Instance.groupMetadataKeys.get(this.props.groupType); + let keys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType);//groupMetadataKeys.get(this.props.groupType); let canExpand = keys ? keys.length > 0 : false; return ( diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index fda788f2d..9b104184f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -234,23 +234,20 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { - console.log(key); const field = doc[key]; if (key === "proto" && copyProto) { - console.log(1); if (field instanceof Doc) { - console.log(2); copy[key] = Doc.MakeCopy(field); } } else { if (field instanceof RefField) { - console.log(3); + console.log("equals field, ref", key); copy[key] = field; } else if (field instanceof ObjectField) { - console.log(4); + console.log("copy field, object", key); copy[key] = ObjectField.MakeCopy(field); } else { - console.log(5); + console.log("equals field", key); copy[key] = field; } } diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts index 92e1ed922..e6d1de749 100644 --- a/src/new_fields/LinkButtonField.ts +++ b/src/new_fields/LinkButtonField.ts @@ -1,35 +1,35 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, primitive, createSimpleSchema, object } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString } from "./FieldSymbols"; -import { Doc } from "./Doc"; -import { DocumentView } from "../client/views/nodes/DocumentView"; +// import { Deserializable } from "../client/util/SerializationHelper"; +// import { serializable, primitive, createSimpleSchema, object } from "serializr"; +// import { ObjectField } from "./ObjectField"; +// import { Copy, ToScriptString } from "./FieldSymbols"; +// import { Doc } from "./Doc"; +// import { DocumentView } from "../client/views/nodes/DocumentView"; -export type LinkButtonData = { - sourceViewId: string, - targetViewId: string -}; +// export type LinkButtonData = { +// sourceViewId: string, +// targetViewId: string +// }; -const LinkButtonSchema = createSimpleSchema({ - sourceViewId: true, - targetViewId: true -}); +// const LinkButtonSchema = createSimpleSchema({ +// sourceViewId: true, +// targetViewId: true +// }); -@Deserializable("linkButton") -export class LinkButtonField extends ObjectField { - @serializable(object(LinkButtonSchema)) - readonly data: LinkButtonData; +// @Deserializable("linkButton") +// export class LinkButtonField extends ObjectField { +// @serializable(object(LinkButtonSchema)) +// readonly data: LinkButtonData; - constructor(data: LinkButtonData) { - super(); - this.data = data; - } +// constructor(data: LinkButtonData) { +// super(); +// this.data = data; +// } - [Copy]() { - return new LinkButtonField(this.data); - } +// [Copy]() { +// return new LinkButtonField(this.data); +// } - [ToScriptString]() { - return "invalid"; - } -} +// [ToScriptString]() { +// return "invalid"; +// } +// } -- cgit v1.2.3-70-g09d2 From e49fa36db6eab77bcd89c48969f555ae0579aa9a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 25 Jun 2019 19:03:25 -0400 Subject: fixed setup of documents to extend fields --- src/client/views/collections/CollectionBaseView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 5 +++-- src/client/views/nodes/FieldView.tsx | 3 ++- src/new_fields/Doc.ts | 10 ++++++++++ 5 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index bb5cad6f3..13c4a33a8 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -95,7 +95,7 @@ export class CollectionBaseView extends React.Component { value.push(doc); } } else { - Doc.SetOnPrototype(this.extDoc, this.extField, new List([doc])); + Doc.GetProto(this.extDoc)[this.extField] = new List([doc]); } return true; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index cc097f371..ab1dbc499 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -43,7 +43,7 @@ export class CollectionView extends React.Component { return (null); } - get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; } // bcz: ? Why do we need to compare Id's? + get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldExt === "annotations"; } // bcz: ? Why do we need to compare Id's? onContextMenu = (e: React.MouseEvent): void => { if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a9db64f81..9f4daf38d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -50,7 +50,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } - public get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldKey === "annotations"; } + public get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldExt === "annotations"; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } private panX = () => this.Document.panX || 0; private panY = () => this.Document.panY || 0; @@ -383,7 +383,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; if (this.dataDoc && this.props.fieldExt && this.dataDoc[this.props.fieldKey + "_ext"] === undefined) { - setTimeout(() => { console.log("Extending: " + this.dataDoc.title); let doc = new Doc(this.dataDoc[Id] + this.props.fieldKey, true); doc.title = "Extension"; this.dataDoc[this.props.fieldKey + "_ext"] = doc; }, 0); + console.log("Timeout " + this.dataDoc.title + " " + this.props.fieldKey); + setTimeout(() => Doc.MakeFieldExtension(this.dataDoc, this.props.fieldKey), 0); } return (
{ return

{field.date.toLocaleString()}

; } else if (field instanceof Doc) { - return

{field.title}

; + return

{field.title + " + " + field[Id]}

; // let returnHundred = () => 100; // return ( // Date: Tue, 25 Jun 2019 20:18:06 -0400 Subject: cleaned up version of templates with annotations. --- .../views/collections/CollectionBaseView.tsx | 11 ++++----- src/client/views/collections/CollectionSubView.tsx | 6 +++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +------- src/client/views/nodes/ImageBox.tsx | 4 ++-- src/new_fields/Doc.ts | 27 ++++++++++++++-------- 5 files changed, 30 insertions(+), 28 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 13c4a33a8..22e8259e7 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -1,16 +1,16 @@ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast, Opt } from '../../../new_fields/Doc'; +import { Doc } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { listSpec } from '../../../new_fields/Schema'; -import { Cast, FieldValue, NumCast, PromiseValue, StrCast, BoolCast } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, PromiseValue } from '../../../new_fields/Types'; +import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; import './CollectionBaseView.scss'; -import { DocumentManager } from '../../util/DocumentManager'; export enum CollectionViewType { Invalid, @@ -36,7 +36,6 @@ export interface CollectionViewProps extends FieldViewProps { contentRef?: React.Ref; } - @observer export class CollectionBaseView extends React.Component { @observable private static _safeMode = false; @@ -75,10 +74,10 @@ export class CollectionBaseView extends React.Component { } @computed get extDoc() { - return this.dataDoc && this.props.fieldExt && this.dataDoc[this.props.fieldKey + "_ext"] instanceof Doc ? this.dataDoc[this.props.fieldKey + "_ext"] as Doc : this.dataDoc; + return Doc.extDoc(this.dataDoc, this.props.fieldKey, this.props.fieldExt); } @computed get extField() { - return this.dataDoc && this.props.fieldExt && this.dataDoc[this.props.fieldKey + "_ext"] instanceof Doc ? this.props.fieldExt : this.props.fieldKey; + return Doc.extField(this.dataDoc, this.props.fieldKey, this.props.fieldExt); } @action.bound diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index caf6aa0c9..1b1f765ed 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -45,13 +45,15 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this.createDropTarget(ele); } + @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } @computed get extDoc() { - return this.props.DataDoc && this.props.fieldExt && this.props.DataDoc[this.props.fieldKey + "_ext"] instanceof Doc ? this.props.DataDoc[this.props.fieldKey + "_ext"] as Doc : this.props.DataDoc; + return Doc.extDoc(this.dataDoc, this.props.fieldKey, this.props.fieldExt); } @computed get extField() { - return this.props.DataDoc && this.props.fieldExt && this.props.DataDoc[this.props.fieldKey + "_ext"] instanceof Doc ? this.props.fieldExt : this.props.fieldKey; + return Doc.extField(this.dataDoc, this.props.fieldKey, this.props.fieldExt); } + get childDocs() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9f4daf38d..7489af1aa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -47,7 +47,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pwidth() { return this.props.PanelWidth(); } private get _pheight() { return this.props.PanelHeight(); } - @computed get dataDoc() { return this.props.DataDoc && BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } public get isAnnotationOverlay() { return this.props.fieldKey && this.props.fieldExt === "annotations"; } @@ -371,10 +370,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }); } - @computed get extDoc() { - return this.dataDoc && this.props.fieldExt && this.dataDoc[this.props.fieldKey + "_ext"] instanceof Doc ? this.dataDoc[this.props.fieldKey + "_ext"] as Doc : this.dataDoc; - } - private childViews = () => [ , ...this.views @@ -382,10 +377,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; - if (this.dataDoc && this.props.fieldExt && this.dataDoc[this.props.fieldKey + "_ext"] === undefined) { - console.log("Timeout " + this.dataDoc.title + " " + this.props.fieldKey); - setTimeout(() => Doc.MakeFieldExtension(this.dataDoc, this.props.fieldKey), 0); - } + if (this.props.fieldExt) Doc.UpdateFieldExtension(this.dataDoc, this.props.fieldKey); return (
(ImageD if (de.data instanceof DragManager.DocumentDragData) { de.data.droppedDocuments.forEach(action((drop: Doc) => { if (/*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) { - this.dataDoc[this.props.fieldKey] = new ImageField(drop.data.url); + Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url); e.stopPropagation(); } else { let layout = StrCast(drop.backgroundLayout); if (layout.indexOf(ImageBox.name) !== -1) { let imgData = this.dataDoc[this.props.fieldKey]; if (imgData instanceof ImageField) { - Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new List([imgData])); + Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new List([imgData]); } let imgList = Cast(this.dataDoc[this.props.fieldKey], listSpec(ImageField), [] as any[]); if (imgList) { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 621f87981..675278ed5 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -224,7 +224,7 @@ export namespace Doc { // gets the document's prototype or returns the document if it is a prototype export function GetProto(doc: Doc) { - return Doc.GetT(doc, "isPrototype", "boolean", true) || doc.isTemplate ? doc : (doc.proto || doc); + return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc); } export function allKeys(doc: Doc): string[] { @@ -249,16 +249,25 @@ export namespace Doc { return true; } - export function MakeFieldExtension(doc: Doc, fieldKey: string) { - let fieldExtension = new Doc(doc[Id] + fieldKey, true); - fieldExtension.title = "Extension of " + doc.title + "'s field:" + fieldKey; - let proto: Doc | undefined = doc; - while (proto && !Doc.IsPrototype(proto)) { - proto = proto.proto; + export function extDoc(doc: Doc, fieldKey: string, fieldExt: string) { + return doc && fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc; + } + export function extField(doc: Doc, fieldKey: string, fieldExt: string) { + return doc && fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? fieldExt : fieldKey; + } + export function UpdateFieldExtension(doc: Doc, fieldKey: string) { + if (doc && doc[fieldKey + "_ext"] === undefined) { + setTimeout(() => { + let fieldExtension = new Doc(doc[Id] + fieldKey, true); + fieldExtension.title = "Extension of " + doc.title + "'s field:" + fieldKey; + let proto: Doc | undefined = doc; + while (proto && !Doc.IsPrototype(proto)) { + proto = proto.proto; + } + (proto ? proto : doc)[fieldKey + "_ext"] = fieldExtension; + }, 0); } - (proto ? proto : doc)[fieldKey + "_ext"] = fieldExtension; } - export function MakeAlias(doc: Doc) { if (!GetT(doc, "isPrototype", "boolean", true)) { return Doc.MakeCopy(doc); -- cgit v1.2.3-70-g09d2 From 2d300b0cd3d02c900865c61eacd539efed5289e6 Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 20:18:14 -0400 Subject: fixed link metadata rendering bug --- src/client/documents/Documents.ts | 11 +- src/client/util/LinkManager.ts | 2 +- .../CollectionFreeFormLinkView.scss | 31 ----- .../CollectionFreeFormLinkView.tsx | 1 - .../CollectionFreeFormLinkWithProxyView.tsx | 131 --------------------- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 11 -- src/client/views/nodes/LinkButtonBox.scss | 18 --- src/client/views/nodes/LinkButtonBox.tsx | 63 ---------- src/client/views/nodes/LinkEditor.tsx | 45 ++++--- src/new_fields/LinkButtonField.ts | 35 ------ 11 files changed, 35 insertions(+), 316 deletions(-) delete mode 100644 src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx delete mode 100644 src/client/views/nodes/LinkButtonBox.scss delete mode 100644 src/client/views/nodes/LinkButtonBox.tsx delete mode 100644 src/new_fields/LinkButtonField.ts (limited to 'src/new_fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d2300e4d2..547687921 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -88,7 +88,7 @@ export namespace DocUtils { // let protoTarg = target.proto ? target.proto : target; export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { if (LinkManager.Instance.doesLinkExist(source, target)) { - console.log("LINK EXISTS"); return; + return; } UndoManager.RunInBatch(() => { @@ -186,11 +186,6 @@ export namespace Docs { { x: 0, y: 0, width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }); return iconProto; } - // function CreateLinkPrototype(): Doc { - // let linkProto = setupPrototypeOptions(linkProtoId, "LINK_PROTO", LinkButtonBox.LayoutString(), - // { x: 0, y: 0, width: 300 }); - // return linkProto; - // } function CreateTextPrototype(): Doc { let textProto = setupPrototypeOptions(textProtoId, "TEXT_PROTO", FormattedTextBox.LayoutString(), { x: 0, y: 0, width: 300, backgroundColor: "#f1efeb" }); @@ -277,9 +272,7 @@ export namespace Docs { export function IconDocument(icon: string, options: DocumentOptions = {}) { return CreateInstance(iconProto, new IconField(icon), options); } - // export function LinkButtonDocument(data: LinkButtonData, options: DocumentOptions = {}) { - // return CreateInstance(linkProto, new LinkButtonField(data), options); - // } + export function PdfDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(pdfProto, new PdfField(new URL(url)), options); } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 60de7fc52..db814082f 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -156,7 +156,7 @@ export class LinkManager { // removes group doc of given group type only from given anchor on given link public removeGroupFromAnchor(linkDoc: Doc, anchor: Doc, groupType: string) { let groups = LinkManager.Instance.getAnchorGroups(linkDoc, anchor); - let newGroups = groups.filter(groupDoc => StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase()); + let newGroups = groups.filter(groupDoc => { StrCast(groupDoc.type).toUpperCase() !== groupType.toUpperCase() }); LinkManager.Instance.setAnchorGroups(linkDoc, anchor, newGroups); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 239c2ce56..fc5212edd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -17,34 +17,3 @@ transform: translate(10000px,10000px); pointer-events: all; } - -.linkview-ele { - transform: translate(10000px,10000px); - pointer-events: all; - - &.linkview-line { - stroke: black; - stroke-width: 2px; - opacity: 0.5; - } -} - -.linkview-button { - width: 200px; - height: 100px; - border-radius: 5px; - padding: 10px; - position: relative; - background-color: black; - cursor: pointer; - - p { - width: calc(100% - 20px); - color: white; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - -} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 5dc3b5c16..b546d1b78 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -9,7 +9,6 @@ import v5 = require("uuid/v5"); export interface CollectionFreeFormLinkViewProps { A: Doc; B: Doc; - // LinkDoc: Doc; LinkDocs: Doc[]; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; removeDocument: (document: Doc) => boolean; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx deleted file mode 100644 index a4d122af2..000000000 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkWithProxyView.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc"; -import { BoolCast, NumCast, StrCast } from "../../../../new_fields/Types"; -import { InkingControl } from "../../InkingControl"; -import "./CollectionFreeFormLinkView.scss"; -import React = require("react"); -import v5 = require("uuid/v5"); -import { DocumentView } from "../../nodes/DocumentView"; -import { Docs } from "../../../documents/Documents"; -import { observable, action } from "mobx"; -import { CollectionDockingView } from "../CollectionDockingView"; -import { dropActionType, DragManager } from "../../../util/DragManager"; -import { emptyFunction } from "../../../../Utils"; -import { DocumentManager } from "../../../util/DocumentManager"; - -export interface CollectionFreeFormLinkViewProps { - sourceView: DocumentView; - targetView: DocumentView; - proxyDoc: Doc; - // addDocTab: (document: Doc, where: string) => void; -} - -@observer -export class CollectionFreeFormLinkWithProxyView extends React.Component { - - // @observable private _proxyX: number = NumCast(this.props.proxyDoc.x); - // @observable private _proxyY: number = NumCast(this.props.proxyDoc.y); - private _ref = React.createRef(); - private _downX: number = 0; - private _downY: number = 0; - @observable _x: number = 0; - @observable _y: number = 0; - // @observable private _proxyDoc: Doc = Docs.TextDocument(); // used for positioning - - @action - componentDidMount() { - let a2 = this.props.proxyDoc; - this._x = NumCast(a2.x) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2); - this._y = NumCast(a2.y) + (BoolCast(a2.isMinimized, false) ? 5 : NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2); - } - - - followButton = (e: React.PointerEvent): void => { - e.stopPropagation(); - let open = this.props.targetView.props.ContainingCollectionView ? this.props.targetView.props.ContainingCollectionView.props.Document : this.props.targetView.props.Document; - CollectionDockingView.Instance.AddRightSplit(open); - DocumentManager.Instance.jumpToDocument(this.props.targetView.props.Document, e.altKey); - } - - @action - setPosition(x: number, y: number) { - this._x = x; - this._y = y; - } - - startDragging(x: number, y: number) { - if (this._ref.current) { - let dragData = new DragManager.DocumentDragData([this.props.proxyDoc]); - - DragManager.StartLinkProxyDrag(this._ref.current, dragData, x, y, { - handlers: { - dragComplete: action(() => { - let a2 = this.props.proxyDoc; - let offset = NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; - let x = NumCast(a2.x);// + NumCast(a2.width) / NumCast(a2.zoomBasis, 1) / 2; - let y = NumCast(a2.y);// + NumCast(a2.height) / NumCast(a2.zoomBasis, 1) / 2; - this.setPosition(x, y); - - // this is a hack :'( theres prob a better way to make the input doc not render - let views = DocumentManager.Instance.getDocumentViews(this.props.proxyDoc); - views.forEach(dv => { - dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); - }); - }), - }, - hideSource: true //? - }); - } - } - - onPointerDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - - e.stopPropagation(); - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } - - onPointerMove = (e: PointerEvent): void => { - if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - this.startDragging(this._downX, this._downY); - } - e.stopPropagation(); - e.preventDefault(); - } - onPointerUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } - - render() { - let a1 = this.props.sourceView; - let x1 = NumCast(a1.Document.x) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.width) / NumCast(a1.Document.zoomBasis, 1) / 2); - let y1 = NumCast(a1.Document.y) + (BoolCast(a1.Document.isMinimized, false) ? 5 : NumCast(a1.Document.height) / NumCast(a1.Document.zoomBasis, 1) / 2); - - let context = this.props.targetView.props.ContainingCollectionView ? - (" in the context of " + StrCast(this.props.targetView.props.ContainingCollectionView.props.Document.title)) : ""; - let text = "link to " + StrCast(this.props.targetView.props.Document.title) + context; - - return ( - <> - - -
-

{text}

-
-
- - ); - } -} - -//onPointerDown={this.followButton} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 940b00a90..02396c3af 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -12,7 +12,6 @@ import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; import { IconBox } from "./IconBox"; -import { LinkButtonBox } from "./LinkButtonBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { VideoBox } from "./VideoBox"; @@ -104,7 +103,7 @@ export class DocumentContentsView extends React.Component(Docu var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - // // for linkbutton docs - // let isLinkButton = BoolCast(this.props.Document.isLinkButton); - // let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); - // let display = isLinkButton ? activeDvs.reduce((found, dv) => { - // let matchSv = this.props.Document.sourceViewId === StrCast(dv.props.Document[Id]); - // let matchTv = this.props.Document.targetViewId === StrCast(dv.props.Document[Id]); - // let match = matchSv || matchTv; - // return match || found; - // }, false) : true; - var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
(Docu width: nativeWidth, height: nativeHeight, transform: `scale(${scaling}, ${scaling})`, - // display: display ? "block" : "none" }} onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} diff --git a/src/client/views/nodes/LinkButtonBox.scss b/src/client/views/nodes/LinkButtonBox.scss deleted file mode 100644 index 6be2dcf60..000000000 --- a/src/client/views/nodes/LinkButtonBox.scss +++ /dev/null @@ -1,18 +0,0 @@ -// .linkBox-cont { -// width: 200px; -// height: 100px; -// background-color: black; -// text-align: center; -// color: white; -// padding: 10px; -// border-radius: 5px; -// position: relative; - -// .linkBox-cont-wrapper { -// width: calc(100% - 20px); -// position: absolute; -// left: 50%; -// top: 50%; -// transform: translate(-50%, -50%); -// } -// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkButtonBox.tsx b/src/client/views/nodes/LinkButtonBox.tsx deleted file mode 100644 index 440847ead..000000000 --- a/src/client/views/nodes/LinkButtonBox.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// import React = require("react"); -// import { library } from '@fortawesome/fontawesome-svg-core'; -// import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote } from '@fortawesome/free-solid-svg-icons'; -// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -// import { computed, observable, runInAction } from "mobx"; -// import { observer } from "mobx-react"; -// import { FieldView, FieldViewProps } from './FieldView'; -// import "./LinkButtonBox.scss"; -// import { DocumentView } from "./DocumentView"; -// import { Doc } from "../../../new_fields/Doc"; -// import { LinkButtonField } from "../../../new_fields/LinkButtonField"; -// import { Cast, StrCast, BoolCast } from "../../../new_fields/Types"; -// import { CollectionDockingView } from "../collections/CollectionDockingView"; -// import { DocumentManager } from "../../util/DocumentManager"; -// import { Id } from "../../../new_fields/FieldSymbols"; - -// library.add(faCaretUp); -// library.add(faObjectGroup); -// library.add(faStickyNote); -// library.add(faFilePdf); -// library.add(faFilm); - -// @observer -// export class LinkButtonBox extends React.Component { -// public static LayoutString() { return FieldView.LayoutString(LinkButtonBox); } - -// followLink = (): void => { -// console.log("follow link???"); -// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); -// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); -// if (targetView && targetView.props.ContainingCollectionView) { -// CollectionDockingView.Instance.AddRightSplit(targetView.props.ContainingCollectionView.props.Document); -// } -// } - -// render() { - -// let field = Cast(this.props.Document[this.props.fieldKey], LinkButtonField, new LinkButtonField({ sourceViewId: "-1", targetViewId: "-1" })); -// let targetView = DocumentManager.Instance.getDocumentViewById(field.data.targetViewId); - -// let text = "Could not find link"; -// if (targetView) { -// let context = targetView.props.ContainingCollectionView ? (" in the context of " + StrCast(targetView.props.ContainingCollectionView.props.Document.title)) : ""; -// text = "Link to " + StrCast(targetView.props.Document.title) + context; -// } - -// let activeDvs = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)); -// let display = activeDvs.reduce((found, dv) => { -// let matchSv = field.data.sourceViewId === StrCast(dv.props.Document[Id]); -// let matchTv = field.data.targetViewId === StrCast(dv.props.Document[Id]); -// let match = matchSv || matchTv; -// return match || found; -// }, false); - -// return ( -//
-//
-//

{text}

-//
-//
-// ); -// } -// } \ No newline at end of file diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index a6511c3fe..eed34b21f 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -71,10 +71,12 @@ class GroupTypesDropdown extends React.Component { interface LinkMetadataEditorProps { + id: string; groupType: string; mdDoc: Doc; mdKey: string; mdValue: string; + changeMdIdKey: (id: string, newKey: string) => void; } @observer class LinkMetadataEditor extends React.Component { @@ -86,8 +88,6 @@ class LinkMetadataEditor extends React.Component { setMetadataKey = (value: string): void => { let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(this.props.groupType); - // console.log("set", ...groupMdKeys, typeof (groupMdKeys[0])); - // don't allow user to create existing key let newIndex = groupMdKeys.findIndex(key => key.toUpperCase() === value.toUpperCase()); if (newIndex > -1) { @@ -105,6 +105,7 @@ class LinkMetadataEditor extends React.Component { if (currIndex === -1) console.error("LinkMetadataEditor: key was not found"); groupMdKeys[currIndex] = value; + this.props.changeMdIdKey(this.props.id, value); this._key = value; LinkManager.Instance.setMetadataKeysForGroup(this.props.groupType, [...groupMdKeys]); } @@ -133,7 +134,7 @@ class LinkMetadataEditor extends React.Component { return (
this.setMetadataKey(e.target.value)}>: - this.setMetadataValue(e.target.value)}> + this.setMetadataValue(e.target.value)}>
); @@ -148,6 +149,17 @@ interface LinkGroupEditorProps { @observer export class LinkGroupEditor extends React.Component { + private _metadataIds: Map = new Map(); + + constructor(props: LinkGroupEditorProps) { + super(props); + + let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(StrCast(props.groupDoc.type)); + groupMdKeys.forEach(key => { + this._metadataIds.set(key, Utils.GenerateGuid()); + }); + } + @action setGroupType = (groupType: string): void => { this.props.groupDoc.type = groupType; @@ -188,12 +200,18 @@ export class LinkGroupEditor extends React.Component { @action addMetadata = (groupType: string): void => { + this._metadataIds.set("new key", Utils.GenerateGuid()); let mdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); // only add "new key" if there is no other key with value "new key"; prevents spamming if (mdKeys.indexOf("new key") === -1) mdKeys.push("new key"); LinkManager.Instance.setMetadataKeysForGroup(groupType, mdKeys); } + // for key rendering purposes + changeMdIdKey = (id: string, newKey: string) => { + this._metadataIds.set(newKey, id); + } + renderMetadata = (): JSX.Element[] => { let metadata: Array = []; let groupDoc = this.props.groupDoc; @@ -201,25 +219,24 @@ export class LinkGroupEditor extends React.Component { let groupType = StrCast(groupDoc.type); let groupMdKeys = LinkManager.Instance.getMetadataKeysInGroup(groupType); - if (groupMdKeys) { - groupMdKeys.forEach((key, index) => { - metadata.push( - - ); - }); - } + groupMdKeys.forEach((key) => { + let val = StrCast(mdDoc[key]); + metadata.push( + + ); + }); return metadata; } viewGroupAsTable = (groupType: string): JSX.Element => { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); + let index = keys.indexOf(""); + if (index > -1) keys.splice(index, 1); let cols = ["anchor1", "anchor2", ...[...keys]]; - // keys.forEach(k => cols.push(k)); - // console.log("COLS", ...cols); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); - return
; + return
; } render() { @@ -233,7 +250,7 @@ export class LinkGroupEditor extends React.Component { - + ); } else { diff --git a/src/new_fields/LinkButtonField.ts b/src/new_fields/LinkButtonField.ts deleted file mode 100644 index e6d1de749..000000000 --- a/src/new_fields/LinkButtonField.ts +++ /dev/null @@ -1,35 +0,0 @@ -// import { Deserializable } from "../client/util/SerializationHelper"; -// import { serializable, primitive, createSimpleSchema, object } from "serializr"; -// import { ObjectField } from "./ObjectField"; -// import { Copy, ToScriptString } from "./FieldSymbols"; -// import { Doc } from "./Doc"; -// import { DocumentView } from "../client/views/nodes/DocumentView"; - -// export type LinkButtonData = { -// sourceViewId: string, -// targetViewId: string -// }; - -// const LinkButtonSchema = createSimpleSchema({ -// sourceViewId: true, -// targetViewId: true -// }); - -// @Deserializable("linkButton") -// export class LinkButtonField extends ObjectField { -// @serializable(object(LinkButtonSchema)) -// readonly data: LinkButtonData; - -// constructor(data: LinkButtonData) { -// super(); -// this.data = data; -// } - -// [Copy]() { -// return new LinkButtonField(this.data); -// } - -// [ToScriptString]() { -// return "invalid"; -// } -// } -- cgit v1.2.3-70-g09d2 From ca8a78de9957ad27d345ad51fdaee9dae3f096bd Mon Sep 17 00:00:00 2001 From: Fawn Date: Tue, 25 Jun 2019 20:44:34 -0400 Subject: can't link to containing collection --- src/client/documents/Documents.ts | 7 ++++--- src/client/util/DragManager.ts | 16 ++-------------- src/client/views/nodes/LinkEditor.tsx | 5 +++-- src/new_fields/Doc.ts | 3 --- 4 files changed, 9 insertions(+), 22 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 547687921..b11b5fdf2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,6 +35,7 @@ import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; +import { DocumentManager } from "../util/DocumentManager"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -87,9 +88,9 @@ export namespace DocUtils { // let protoSrc = source.proto ? source.proto : source; // let protoTarg = target.proto ? target.proto : target; export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default") { - if (LinkManager.Instance.doesLinkExist(source, target)) { - return; - } + if (LinkManager.Instance.doesLinkExist(source, target)) return; + let sv = DocumentManager.Instance.getDocumentView(source); + if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; UndoManager.RunInBatch(() => { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 4be3d82d3..55d8c570f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -63,8 +63,6 @@ export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: n let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - // TODO: if not in same context then don't drag - if (srcTarg) { let linkDocs = LinkManager.Instance.getAllRelatedLinks(srcTarg); if (linkDocs) { @@ -232,25 +230,20 @@ export namespace DragManager { (dropData: { [id: string]: any }) => { dropData.droppedDocuments = dragData.draggedDocuments.map(d => { let dv = DocumentManager.Instance.getDocumentView(d); - // console.log("DRAG", StrCast(d.title)); if (dv) { if (dv.props.ContainingCollectionView === SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) { return d; } else { - // return d; let r = Doc.MakeAlias(d); - // DocUtils.MakeLink(sourceDoc, r); + // DocUtils.MakeLink(r, sourceDoc); return r; } } else { - // return d; let r = Doc.MakeAlias(d); - // DocUtils.MakeLink(sourceDoc, r); + // DocUtils.MakeLink(r, sourceDoc); return r; } - // return (dv && dv.props.ContainingCollectionView !== SelectionManager.SelectedDocuments()[0].props.ContainingCollectionView) || !dv ? - // Doc.MakeAlias(d) : d; }); }); @@ -290,11 +283,6 @@ export namespace DragManager { StartDrag([ele], dragData, downX, downY, options); } - // export function StartLinkProxyDrag(ele: HTMLElement, dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { - // runInAction(() => StartDragFunctions.map(func => func())); - // StartDrag([ele], dragData, downX, downY, options); - // } - export let AbortDrag: () => void = emptyFunction; function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: { [id: string]: any }) => void) { diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index eed34b21f..232331204 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -133,8 +133,8 @@ class LinkMetadataEditor extends React.Component { render() { return (
- this.setMetadataKey(e.target.value)}>: - this.setMetadataValue(e.target.value)}> + this.setMetadataKey(e.target.value)}>: + this.setMetadataValue(e.target.value)}>
); @@ -221,6 +221,7 @@ export class LinkGroupEditor extends React.Component { groupMdKeys.forEach((key) => { let val = StrCast(mdDoc[key]); + console.log(key, val); metadata.push( ); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index f6fd44d61..d7411e63e 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -260,13 +260,10 @@ export namespace Doc { } } else { if (field instanceof RefField) { - console.log("equals field, ref", key); copy[key] = field; } else if (field instanceof ObjectField) { - console.log("copy field, object", key); copy[key] = ObjectField.MakeCopy(field); } else { - console.log("equals field", key); copy[key] = field; } } -- cgit v1.2.3-70-g09d2 From 5f3ba2363b74dc493b96ef2253560a48d3a7c711 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 25 Jun 2019 22:16:07 -0400 Subject: final cleanup for now --- src/client/views/DocumentDecorations.tsx | 9 +++----- .../views/collections/CollectionBaseView.tsx | 16 +++++--------- src/client/views/collections/CollectionSubView.tsx | 13 ++++++----- .../views/collections/CollectionTreeView.tsx | 8 ++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 +++++------ src/new_fields/Doc.ts | 25 +++++++++++++--------- 6 files changed, 36 insertions(+), 47 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e8ea7d5fc..b9537fca6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,10 +3,11 @@ import { faLink } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, WidthSym, HeightSym } from "../../new_fields/Doc"; +import { Doc } from "../../new_fields/Doc"; import { List } from "../../new_fields/List"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, StrCast, BoolCast } from "../../new_fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { URLField } from '../../new_fields/URLField'; import { emptyFunction, Utils } from "../../Utils"; import { Docs } from "../documents/Documents"; import { DocumentManager } from "../util/DocumentManager"; @@ -24,10 +25,6 @@ import { LinkMenu } from "./nodes/LinkMenu"; import { TemplateMenu } from "./TemplateMenu"; import { Template, Templates } from "./Templates"; import React = require("react"); -import { URLField, ImageField } from '../../new_fields/URLField'; -import { templateLiteral } from 'babel-types'; -import { CollectionViewType } from './collections/CollectionBaseView'; -import { ImageBox } from './nodes/ImageBox'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 4ef84ac66..a4a9ec994 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -59,7 +59,8 @@ export class CollectionBaseView extends React.Component { } } - @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } + @computed get dataDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } + @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; } active = (): boolean => { var isSelected = this.props.isSelected(); @@ -73,13 +74,6 @@ export class CollectionBaseView extends React.Component { this.props.whenActiveChanged(isActive); } - @computed get extDoc() { - return Doc.extDoc(this.dataDoc, this.props.fieldKey, this.props.fieldExt); - } - @computed get extField() { - return Doc.extField(this.dataDoc, this.props.fieldKey, this.props.fieldExt); - } - @action.bound addDocument(doc: Doc, allowDuplicates: boolean = false): boolean { var curPage = NumCast(this.props.Document.curPage, -1); @@ -88,13 +82,13 @@ export class CollectionBaseView extends React.Component { Doc.GetProto(doc).annotationOn = this.props.Document; } allowDuplicates = true; - const value = Cast(this.extDoc[this.extField], listSpec(Doc)); + const value = Cast(this.dataDoc[this.dataField], listSpec(Doc)); if (value !== undefined) { if (allowDuplicates || !value.some(v => v instanceof Doc && v[Id] === doc[Id])) { value.push(doc); } } else { - Doc.GetProto(this.extDoc)[this.extField] = new List([doc]); + Doc.GetProto(this.dataDoc)[this.dataField] = new List([doc]); } return true; } @@ -104,7 +98,7 @@ export class CollectionBaseView extends React.Component { let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); docView && SelectionManager.DeselectDoc(docView); //TODO This won't create the field if it doesn't already exist - const value = Cast(this.extDoc[this.extField], listSpec(Doc), []); + const value = Cast(this.dataDoc[this.dataField], listSpec(Doc), []); let index = value.reduce((p, v, i) => (v instanceof Doc && v[Id] === doc[Id]) ? i : p, -1); PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined) diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f1f1d9127..58251cd6e 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -2,9 +2,10 @@ import { action, computed } from "mobx"; import * as rp from 'request-promise'; import CursorField from "../../../new_fields/CursorField"; import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; -import { Cast, PromiseValue, BoolCast } from "../../../new_fields/Types"; +import { BoolCast, Cast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from "../../../server/RouteStore"; import { DocServer } from "../../DocServer"; @@ -13,12 +14,11 @@ import { DragManager } from "../../util/DragManager"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; import { FieldViewProps } from "../nodes/FieldView"; +import { FormattedTextBox } from "../nodes/FormattedTextBox"; import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import React = require("react"); -import { FormattedTextBox } from "../nodes/FormattedTextBox"; -import { Id } from "../../../new_fields/FieldSymbols"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; @@ -45,14 +45,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this.createDropTarget(ele); } - @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } - @computed get extDoc() { return Doc.extDoc(this.dataDoc, this.props.fieldKey, this.props.fieldExt); } - @computed get extField() { return Doc.extField(this.dataDoc, this.props.fieldKey, this.props.fieldExt); } + @computed get dataDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } + @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; } get childDocs() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extDoc : this.props.Document)[this.extField]); + return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.dataDoc : this.props.Document)[this.dataField]); } @action diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index eeacb02e8..3fe8f9ab5 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons'; +import { faAngleRight, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, computed } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; @@ -25,10 +25,6 @@ import { CollectionSchemaPreview } from './CollectionSchemaView'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { FormattedTextBox } from '../nodes/FormattedTextBox'; -import { ImageField } from '../../../new_fields/URLField'; -import { ImageBox } from '../nodes/ImageBox'; -import { CollectionView } from './CollectionView'; export interface TreeViewProps { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index cbd9e3de3..dc916a613 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym, DocListCastAsync } from "../../../../new_fields/Doc"; +import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; @@ -13,11 +13,13 @@ import { SelectionManager } from "../../../util/SelectionManager"; import { Transform } from "../../../util/Transform"; import { undoBatch } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; +import { ContextMenu } from "../../ContextMenu"; import { InkingCanvas } from "../../InkingCanvas"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentContentsView } from "../../nodes/DocumentContentsView"; import { DocumentViewProps, positionSchema } from "../../nodes/DocumentView"; import { pageSchema } from "../../nodes/ImageBox"; +import PDFMenu from "../../pdf/PDFMenu"; import { CollectionSubView } from "../CollectionSubView"; import { CollectionFreeFormLinksView } from "./CollectionFreeFormLinksView"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; @@ -25,10 +27,6 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); -import PDFMenu from "../../pdf/PDFMenu"; -import { ContextMenu } from "../../ContextMenu"; -import { Docs } from "../../../documents/Documents"; -import { thisExpression } from "babel-types"; export const panZoomSchema = createSchema({ panX: "number", @@ -377,7 +375,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; - if (this.props.fieldExt) Doc.UpdateFieldExtension(this.dataDoc, this.props.fieldKey); + if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); return (
- + {this.childViews} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 675278ed5..8825bc13a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -249,22 +249,27 @@ export namespace Doc { return true; } - export function extDoc(doc: Doc, fieldKey: string, fieldExt: string) { - return doc && fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc; + // + // Resolves a reference to a field by returning 'doc' if o field extension is specified, + // otherwise, it returns the extension document stored in doc._ext. + // This mechanism allows any fields to be extended with an extension document that can + // be used to capture field-specific metadata. For example, an image field can be extended + // to store annotations, ink, and other data. + // + export function resolvedFieldDataDoc(doc: Doc, fieldKey: string, fieldExt: string) { + return fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? doc[fieldKey + "_ext"] as Doc : doc; } - export function extField(doc: Doc, fieldKey: string, fieldExt: string) { - return doc && fieldExt && doc[fieldKey + "_ext"] instanceof Doc ? fieldExt : fieldKey; - } - export function UpdateFieldExtension(doc: Doc, fieldKey: string) { - if (doc && doc[fieldKey + "_ext"] === undefined) { + + export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) { + if (doc[fieldKey + "_ext"] === undefined) { setTimeout(() => { - let fieldExtension = new Doc(doc[Id] + fieldKey, true); - fieldExtension.title = "Extension of " + doc.title + "'s field:" + fieldKey; + let docExtensionForField = new Doc(doc[Id] + fieldKey, true); + docExtensionForField.title = "Extension of " + doc.title + "'s field:" + fieldKey; let proto: Doc | undefined = doc; while (proto && !Doc.IsPrototype(proto)) { proto = proto.proto; } - (proto ? proto : doc)[fieldKey + "_ext"] = fieldExtension; + (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField; }, 0); } } -- cgit v1.2.3-70-g09d2 From 084dd6d9b0ad51133025ba6bd2702fc44b1b6c31 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 26 Jun 2019 12:56:22 -0400 Subject: fixes to datadoc usage. fixed tree view for textboxes. --- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 10 +-- src/client/views/collections/CollectionSubView.tsx | 5 +- .../views/collections/CollectionTreeView.tsx | 81 +++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 +-- src/client/views/nodes/DocumentView.tsx | 19 ++--- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 6 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/new_fields/Doc.ts | 2 +- 13 files changed, 75 insertions(+), 69 deletions(-) (limited to 'src/new_fields') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b9537fca6..557db3639 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -176,7 +176,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let dragDocView = SelectionManager.SelectedDocuments()[0]; const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0); const [xoff, yoff] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top); - let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc)); + let dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document), SelectionManager.SelectedDocuments().map(dv => dv.props.DataDoc ? dv.props.DataDoc : dv.props.Document)); dragData.xOffset = xoff; dragData.yOffset = yoff; dragData.moveDocument = SelectionManager.SelectedDocuments()[0].props.moveDocument; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d26e24748..e48de27d7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -203,7 +203,7 @@ export class MainView extends React.Component { let mainCont = this.mainContainer; let content = !mainCont ? (null) : { } } - @computed get dataDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } + @computed get dataDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc ? this.props.DataDoc : this.props.Document : this.props.Document, this.props.fieldKey, this.props.fieldExt); } @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; } active = (): boolean => { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 796882724..a8061f9f1 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -437,15 +437,16 @@ export class CollectionSchemaPreview extends React.Component this.nativeWidth * this.contentScaling(); - private PanelHeight = () => this.nativeHeight * this.contentScaling(); + private PanelWidth = () => this.nativeWidth ? this.nativeWidth * this.contentScaling() : this.props.width(); + private PanelHeight = () => this.nativeHeight ? this.nativeHeight * this.contentScaling() : this.props.height(); private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()); - get centeringOffset() { return (this.props.width() - this.nativeWidth * this.contentScaling()) / 2; } + get centeringOffset() { return this.nativeWidth ? (this.props.width() - this.nativeWidth * this.contentScaling()) / 2 : 0; } @action onPreviewScriptChange = (e: React.ChangeEvent) => { this.props.setPreviewScript(e.currentTarget.value); } render() { + let self = this; let input = this.props.previewScript === undefined ? (null) :
; @@ -462,7 +463,8 @@ export class CollectionSchemaPreview extends React.Component(schemaCtor: (doc: Doc) => T) { this.createDropTarget(ele); } - @computed get dataDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } - @computed get dataField() { return this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey; } + @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey, this.props.fieldExt); } get childDocs() { //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.dataDoc : this.props.Document)[this.dataField]); + return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); } @action diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d33a4786c..a8324eba2 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -29,7 +29,7 @@ import React = require("react"); export interface TreeViewProps { document: Doc; - dataDoc: Doc; + dataDoc?: Doc; containingCollection: Doc; deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; @@ -66,14 +66,14 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { - let keys = Array.from(Object.keys(this.dataDoc)); - if (this.dataDoc.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(this.dataDoc.proto))); + let keys = Array.from(Object.keys(this.resolvedDataDoc)); + if (this.resolvedDataDoc.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } let keyList: string[] = []; keys.map(key => { - let docList = Cast(this.dataDoc[key], listSpec(Doc)); + let docList = Cast(this.resolvedDataDoc[key], listSpec(Doc)); if (docList && docList.length > 0) { keyList.push(key); } @@ -85,7 +85,7 @@ class TreeView extends React.Component { return keyList.length ? keyList[0] : "data"; } - @computed get dataDoc() { return BoolCast(this.props.document.isTemplate) ? this.props.dataDoc : this.props.document; } + @computed get resolvedDataDoc() { return BoolCast(this.props.document.isTemplate) && this.props.dataDoc ? this.props.dataDoc : this.props.document; } protected createTreeDropTarget = (ele: HTMLDivElement) => { this._treedropDisposer && this._treedropDisposer(); @@ -94,7 +94,7 @@ class TreeView extends React.Component { } } - @undoBatch delete = () => this.props.deleteDoc(this.dataDoc); + @undoBatch delete = () => this.props.deleteDoc(this.resolvedDataDoc); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, this.props.document, "onRight"); onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); @@ -126,7 +126,7 @@ class TreeView extends React.Component { @action remove = (document: Document, key: string): boolean => { - let children = Cast(this.dataDoc[key], listSpec(Doc), []); + let children = Cast(this.resolvedDataDoc[key], listSpec(Doc), []); if (children.indexOf(document) !== -1) { children.splice(children.indexOf(document), 1); return true; @@ -142,8 +142,8 @@ class TreeView extends React.Component { indent = () => this.props.addDocument(this.props.document) && this.delete() renderBullet() { - let docList = Cast(this.dataDoc[this.fieldKey], listSpec(Doc)); - let doc = Cast(this.dataDoc[this.fieldKey], Doc); + let docList = Cast(this.resolvedDataDoc[this.fieldKey], listSpec(Doc)); + let doc = Cast(this.resolvedDataDoc[this.fieldKey], Doc); let isDoc = doc instanceof Doc || docList; return
this._collapsed = !this._collapsed)}> {} @@ -161,15 +161,15 @@ class TreeView extends React.Component { editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} - SetValue={(value: string) => (Doc.GetProto(this.dataDoc)[key] = value) ? true : true} + SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc)[key] = value) ? true : true} OnFillDown={(value: string) => { - Doc.GetProto(this.dataDoc)[key] = value; + Doc.GetProto(this.resolvedDataDoc)[key] = value; let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; return this.props.addDocument(doc); @@ -178,13 +178,13 @@ class TreeView extends React.Component { />) @computed get keyList() { - let keys = Array.from(Object.keys(this.dataDoc)); - if (this.dataDoc.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(this.dataDoc.proto))); + let keys = Array.from(Object.keys(this.resolvedDataDoc)); + if (this.resolvedDataDoc.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.resolvedDataDoc.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } - let keyList: string[] = keys.reduce((l, key) => Cast(this.dataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]); - keys.map(key => Cast(this.dataDoc[key], Doc) instanceof Doc && keyList.push(key)); + let keyList: string[] = keys.reduce((l, key) => Cast(this.resolvedDataDoc[key], listSpec(Doc)) ? [...l, key] : l, [] as string[]); + keys.map(key => Cast(this.resolvedDataDoc[key], Doc) instanceof Doc && keyList.push(key)); if (keyList.indexOf(this.fieldKey) !== -1) { keyList.splice(keyList.indexOf(this.fieldKey), 1); } @@ -196,7 +196,7 @@ class TreeView extends React.Component { */ renderTitle() { let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId, true); + let onItemDown = SetupDrag(reference, () => this.resolvedDataDoc, this.move, this.props.dropAction, this.props.treeViewId, true); let headerElements = ( { {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document[this.fieldKey], listSpec(Doc), []) : []; - let openRight = dataDocs && dataDocs.indexOf(this.dataDoc) !== -1 ? (null) : ( + let openRight = dataDocs && dataDocs.indexOf(this.resolvedDataDoc) !== -1 ? (null) : (
); @@ -229,13 +229,13 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)) }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) }); ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, kvp, "onRight"); }, icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { - ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "inTab"), icon: "folder" }); - ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.dataDoc, "onRight"), icon: "caret-square-right" }); - if (DocumentManager.Instance.getDocumentViews(this.dataDoc).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.dataDoc).map(view => view.props.focus(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" }); + ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" }); + if (DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).length) { + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document)) }); } ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } else { @@ -252,9 +252,9 @@ class TreeView extends React.Component { let before = x[1] < bounds[1]; let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); if (de.data instanceof DragManager.DocumentDragData) { - let addDoc = (doc: Doc) => this.props.addDocument(doc, this.dataDoc, before); + let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before); if (inside) { - let docList = Cast(this.dataDoc.data, listSpec(Doc)); + let docList = Cast(this.resolvedDataDoc.data, listSpec(Doc)); if (docList !== undefined) { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } @@ -262,10 +262,10 @@ class TreeView extends React.Component { e.stopPropagation(); let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments); return (de.data.dropAction || de.data.userDropAction) ? - de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.dataDoc, before) || added, false) + de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before) || added, false) : (de.data.moveDocument) ? - movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.dataDoc, addDoc) || added, false) - : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.dataDoc, before), false); + movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.resolvedDataDoc, addDoc) || added, false) + : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before), false); } return false; } @@ -280,22 +280,23 @@ class TreeView extends React.Component { render() { let contentElement: (JSX.Element | null) = null; - let docList = Cast(this.dataDoc[this._chosenKey], listSpec(Doc)); + let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this._chosenKey, doc, addBefore, before); - let doc = Cast(this.dataDoc[this._chosenKey], Doc); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.resolvedDataDoc, this._chosenKey, doc, addBefore, before); + let doc = Cast(this.resolvedDataDoc[this._chosenKey], Doc); let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; if (!this._collapsed) { if (!this.props.document.embed) { contentElement =
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.dataDoc, this._chosenKey, addDoc, remDoc, this.move, + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this.props.document, this.props.dataDoc, this._chosenKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)}
; } else { + console.log("PW = " + this.props.panelWidth()); contentElement =
{ docs: Doc[], treeViewId: string, containingCollection: Doc, - dataDoc: Doc, + dataDoc: Doc | undefined, key: string, add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, remove: ((doc: Doc) => boolean), @@ -412,6 +413,8 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } + @computed get resolvedDataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; } + outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); @@ -428,11 +431,11 @@ export class CollectionTreeView extends CollectionSubView(Document) { onDrop={this.onTreeDrop} ref={this.createTreeDropTarget}> StrCast(this.props.DataDoc.title)} - SetValue={(value: string) => (Doc.GetProto(this.props.DataDoc).title = value) ? true : true} + GetValue={() => StrCast(this.resolvedDataDoc.title)} + SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc).title = value) ? true : true} OnFillDown={(value: string) => { Doc.GetProto(this.props.Document).title = value; let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a3ca02941..6672b3a07 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -163,7 +163,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return [[range[0][0] > x ? x : range[0][0], range[0][1] < xe ? xe : range[0][1]], [range[1][0] > y ? y : range[1][0], range[1][1] < ye ? ye : range[1][1]]]; }, [[minx, maxx], [miny, maxy]]); - let ink = Cast(this.dataDoc.ink, InkField); + let ink = Cast(this.extensionDoc.ink, InkField); if (ink && ink.inkData) { ink.inkData.forEach((value: StrokeData, key: string) => { let bounds = InkingCanvas.StrokeRect(value); @@ -295,8 +295,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getDocumentViewProps(layoutDoc: Doc): DocumentViewProps { + let datadoc = BoolCast(this.props.Document.isTemplate) || this.props.DataDoc === this.props.Document ? undefined : this.props.DataDoc; return { - DataDoc: BoolCast(this.props.Document.isTemplate) ? layoutDoc : this.props.DataDoc, + DataDoc: datadoc, Document: layoutDoc, addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, @@ -375,7 +376,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { render() { const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; const easing = () => this.props.Document.panTransformType === "Ease"; - if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); + if (this.props.fieldExt) Doc.UpdateDocumentExtensionForField(this.extensionDoc, this.props.fieldKey); return (
- + {this.childViews} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9c3479ec2..eb4f56af1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -67,7 +67,7 @@ const LinkDoc = makeInterface(linkSchema); export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Doc; - DataDoc: Doc; + DataDoc?: Doc; addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; @@ -209,10 +209,11 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); } + get dataDoc() { return this.props.DataDoc ? this.props.DataDoc : this.props.Document; } startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { if (this._mainCont.current) { let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; - let alldataConnected = [this.props.DataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; + let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); let dragData = new DragManager.DocumentDragData(allConnected, alldataConnected); const [xoff, yoff] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); @@ -271,7 +272,7 @@ export class DocumentView extends DocComponent(Docu if (this._doubleTap && this.props.renderDepth) { let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List(); - this.props.addDocTab(fullScreenAlias, this.props.DataDoc, "inTab"); + this.props.addDocTab(fullScreenAlias, this.dataDoc, "inTab"); SelectionManager.DeselectAll(); this.props.Document.libraryBrush = false; } @@ -348,7 +349,7 @@ export class DocumentView extends DocComponent(Docu this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.props.DataDoc]); + CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]); e.stopPropagation(); } else { if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. @@ -393,7 +394,7 @@ export class DocumentView extends DocComponent(Docu } } fullScreenClicked = (): void => { - CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.props.DataDoc); + CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(Doc.MakeAlias(this.props.Document), this.dataDoc); SelectionManager.DeselectAll(); } @@ -476,10 +477,10 @@ export class DocumentView extends DocComponent(Docu const cm = ContextMenu.Instance; let subitems: ContextMenuProps[] = []; subitems.push({ description: "Open Full Screen", event: this.fullScreenClicked, icon: "desktop" }); - subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "inTab"), icon: "folder" }); - subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" }); - subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight"), icon: "caret-square-right" }); - subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Tab", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Tab Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "inTab"), icon: "folder" }); + subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, this.dataDoc, "onRight"), icon: "caret-square-right" }); + subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 8a481144e..55f61ddff 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -30,7 +30,7 @@ export interface FieldViewProps { fieldExt: string; ContainingCollectionView: Opt; Document: Doc; - DataDoc: Doc; + DataDoc?: Doc; isSelected: () => boolean; select: (isCtrlPressed: boolean) => void; renderDepth: number; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0a1a86226..e1a496574 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -110,15 +110,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } - @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } + @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; } dispatchTransaction = (tx: Transaction) => { if (this._editorView) { const state = this._editorView.state.apply(tx); this._editorView.updateState(state); this._applyingChange = true; - Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey, new RichTextField(JSON.stringify(state.toJSON()))); - Doc.SetOnPrototype(this.dataDoc, this.props.fieldKey + "_text", state.doc.textBetween(0, state.doc.content.size, "\n\n")); + Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON())); + Doc.GetProto(this.dataDoc)[this.props.fieldKey + "_text"] = state.doc.textBetween(0, state.doc.content.size, "\n\n"); this._applyingChange = false; let title = StrCast(this.dataDoc.title); if (title && title.startsWith("-") && this._editorView) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0da774db1..0e6c1ee19 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -46,7 +46,7 @@ export class ImageBox extends DocComponent(ImageD private dropDisposer?: DragManager.DragDropDisposer; - @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } + @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; } protected createDropTarget = (ele: HTMLDivElement) => { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index f8bf5faf3..b863ee4fc 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -27,7 +27,7 @@ export class PDFBox extends DocComponent(PdfDocumen @observable private _alt = false; @observable private _scrollY: number = 0; - @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) ? this.props.DataDoc : this.props.Document; } + @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : this.props.Document; } private _reactionDisposer?: IReactionDisposer; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 8825bc13a..ddbbc1436 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -178,7 +178,7 @@ export namespace Doc { export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) { let hasProto = doc.proto instanceof Doc; let onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1; - let onProto = Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1; + let onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1; if (onDeleg || !hasProto || (!onProto && !defaultProto)) doc[key] = value; else doc.proto![key] = value; -- cgit v1.2.3-70-g09d2