From b285803c4e8c37302f6e02624a6127667d628305 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 25 Jun 2019 19:49:54 -0400 Subject: Youtube Api Exploration --- src/client/documents/Documents.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ab61b915c..fd4532807 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -23,7 +23,7 @@ import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; import { OmitKeys } from "../../Utils"; -import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField"; +import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; import { Cast, NumCast } from "../../new_fields/Types"; @@ -35,6 +35,8 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; +import { createInstance } from "@react-pdf/renderer"; +import { YoutubeBox } from "../apis/youtube/YoutubeBox"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -113,6 +115,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; + let youtubeProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; const pdfProtoId = "pdfProto"; @@ -123,9 +126,10 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; + const youtubeProtoId = "youtubeProto"; export function initProtos(): Promise { - return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId]).then(fields => { + return DocServer.GetRefFields([textProtoId, histoProtoId, collProtoId, imageProtoId, webProtoId, kvpProtoId, videoProtoId, audioProtoId, pdfProtoId, iconProtoId, youtubeProtoId]).then(fields => { textProto = fields[textProtoId] as Doc || CreateTextPrototype(); histoProto = fields[histoProtoId] as Doc || CreateHistogramPrototype(); collProto = fields[collProtoId] as Doc || CreateCollectionPrototype(); @@ -136,6 +140,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); + youtubeProto = fields[youtubeProtoId] as Doc || CreateYoutubePrototype(); }); } @@ -183,6 +188,13 @@ export namespace Docs { { x: 0, y: 0, width: 300, height: 300 }); return webProto; } + function CreateYoutubePrototype(): Doc { + let webProto = setupPrototypeOptions(youtubeProtoId, "YOUTUBE_PROTO", YoutubeBox.LayoutString(), + { x: 0, y: 0, width: 300, height: 300 }); + return webProto; + } + + function CreateCollectionPrototype(): Doc { let collProto = setupPrototypeOptions(collProtoId, "COLLECTION_PROTO", CollectionView.LayoutString("data"), { panX: 0, panY: 0, scale: 1, width: 500, height: 500 }); @@ -238,6 +250,10 @@ export namespace Docs { // doc.SetText(KeyStore.OverlayLayout, FixedCaption()); // return doc; } + export function YoutubeDocument(url: string, options: DocumentOptions = {}) { + return CreateInstance(youtubeProto, new YoutubeField(new URL(url)), options); + } + export function VideoDocument(url: string, options: DocumentOptions = {}) { return CreateInstance(videoProto, new VideoField(new URL(url)), options); } -- cgit v1.2.3-70-g09d2 From 516c0b35bbb7a184931ab0fe1d54086d8052bda4 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 17 Jul 2019 15:15:49 -0400 Subject: changed image proxying stuff --- src/client/documents/Documents.ts | 5 +++-- src/client/views/nodes/ImageBox.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index af2b95659..3c248760b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -311,8 +311,9 @@ export namespace Docs { } export function ImageDocument(url: string, options: DocumentOptions = {}) { - let inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), new ImageField(new URL(url)), { title: path.basename(url), ...options }); - requestImageSize(window.origin + RouteStore.corsProxy + "/" + url) + let imgField = new ImageField(new URL(url)); + let inst = InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options }); + requestImageSize(imgField.url.href) .then((size: any) => { let aspect = size.height / size.width; if (!inst.proto!.nativeWidth) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index a3e098fd8..ebe3732e6 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -259,7 +259,7 @@ export class ImageBox extends DocComponent(ImageD _curSuffix = "_m"; resize(srcpath: string, layoutdoc: Doc) { - requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath) + requestImageSize(srcpath) .then((size: any) => { let aspect = size.height / size.width; let rotation = NumCast(this.dataDoc.rotation) % 180; -- cgit v1.2.3-70-g09d2 From ae07ba8fb410752ea98702219247ce5f89d1758b Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 19 Jul 2019 14:42:39 -0400 Subject: fixes for templates and stacking views --- src/client/documents/Documents.ts | 3 ++- src/client/views/collections/CollectionStackingView.tsx | 5 +++-- src/new_fields/Doc.ts | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3c248760b..11df6c152 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -55,7 +55,8 @@ export enum DocumentType { ICON = "icon", IMPORT = "import", LINK = "link", - LINKDOC = "linkdoc" + LINKDOC = "linkdoc", + TEMPLATE = "template" } export interface DocumentOptions { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4424cffe1..039bd7b3a 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -258,8 +258,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let sectionFilter = StrCast(this.props.Document.sectionFilter); let fields = new Map(); sectionFilter && this.filteredChildren.map(d => { - if (!fields.has(d[sectionFilter] as object)) fields.set(d[sectionFilter] as object, [d]); - else fields.get(d[sectionFilter] as object)!.push(d); + let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object; + if (!fields.has(sectionValue)) fields.set(sectionValue, [d]); + else fields.get(sectionValue)!.push(d); }); return (
fieldTemplate.proto = proto); } -- cgit v1.2.3-70-g09d2 From 5e9bcf2e35415fd0ab4dec4f0141511cd4d312d0 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 19 Jul 2019 16:03:09 -0400 Subject: split masonry collection view into its own view type. --- src/client/documents/Documents.ts | 4 ++ .../views/collections/CollectionBaseView.tsx | 3 +- .../views/collections/CollectionStackingView.tsx | 58 +++++++--------------- src/client/views/collections/CollectionView.tsx | 10 ++-- 4 files changed, 32 insertions(+), 43 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 11df6c152..47ed33adf 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -410,6 +410,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); } + export function MasonryDocument(documents: Array, options: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Masonry }); + } + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 2603dc60b..72faf52c4 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -18,7 +18,8 @@ export enum CollectionViewType { Schema, Docking, Tree, - Stacking + Stacking, + Masonry } export interface CollectionRenderProps { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 039bd7b3a..0e5f9a321 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -6,7 +6,6 @@ import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { BoolCast, NumCast, Cast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, Utils } from "../../../Utils"; -import { ContextMenu } from "../ContextMenu"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; @@ -15,16 +14,14 @@ import { DragManager } from "../../util/DragManager"; import { DocumentType } from "../../documents/Documents"; import { Transform } from "../../util/Transform"; import { CursorProperty } from "csstype"; -import { COLLECTION_BORDER_WIDTH } from "../../views/globalCssVariables.scss"; -import { string } from "prop-types"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); _heightDisposer?: IReactionDisposer; - _gridSize = 1; _docXfs: any[] = []; + _columnStart: number = 0; @observable private cursor: CursorProperty = "grab"; @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); } @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); } @@ -33,24 +30,25 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } + @computed get Sections() { + let sectionFilter = StrCast(this.props.Document.sectionFilter); + let fields = new Map(); + sectionFilter && this.filteredChildren.map(d => { + let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object; + if (!fields.has(sectionValue)) fields.set(sectionValue, [d]); + else fields.get(sectionValue)!.push(d); + }); + return fields; + } componentDidMount() { this._heightDisposer = reaction(() => [this.yMargin, this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])], - () => { - if (this.singleColumn) { - let sectionFilter = StrCast(this.props.Document.sectionFilter); - let fields = new Map(); - sectionFilter && this.filteredChildren.map(d => { - if (!fields.has(d[sectionFilter] as object)) fields.set(d[sectionFilter] as object, [d]); - else fields.get(d[sectionFilter] as object)!.push(d); - }); - (this.props.Document.height = fields.size * 50 + this.filteredChildren.reduce((height, d, i) => - height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin)); - } - } + () => this.singleColumn && + (this.props.Document.height = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => + height + this.getDocHeight(d) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap), this.yMargin)) , { fireImmediately: true }); } componentWillUnmount() { - if (this._heightDisposer) this._heightDisposer(); + this._heightDisposer && this._heightDisposer(); } @action @@ -98,7 +96,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return (nw && nh) ? wid * aspect : d[HeightSym](); } - offsetTransform(doc: Doc, translateX: number, translateY: number) { let outerXf = Utils.GetScreenTransform(this._masonryGridRef!); let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); @@ -133,7 +130,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } else { let dref = React.createRef(); let dxf = () => this.getDocTransform(layoutDoc, dref.current!); - let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); + let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); this._docXfs.push({ dxf: dxf, width: width, height: height }); return
{this.getDisplayDoc(layoutDoc, d, dxf)} @@ -142,7 +139,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { }); } - _columnStart: number = 0; columnDividerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); @@ -156,7 +152,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let dragPos = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0]; let delta = dragPos - this._columnStart; this._columnStart = dragPos; - this.props.Document.columnWidth = this.columnWidth + delta; } @@ -172,14 +167,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
; } - onContextMenu = (e: React.MouseEvent): void => { - if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // 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: "Toggle multi-column", - event: () => this.props.Document.singleColumn = !BoolCast(this.props.Document.singleColumn, true), icon: "file-pdf" - }); - } - } @undoBatch @action @@ -247,7 +234,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { position: "relative", gridGap: this.gridGap, gridTemplateColumns: this.singleColumn ? undefined : templatecols, - gridAutoRows: this.singleColumn ? undefined : `${this._gridSize}px` + gridAutoRows: this.singleColumn ? undefined : "0px" }} > {this.children(docList)} @@ -255,21 +242,14 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
; } render() { - let sectionFilter = StrCast(this.props.Document.sectionFilter); - let fields = new Map(); - sectionFilter && this.filteredChildren.map(d => { - let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object; - if (!fields.has(sectionValue)) fields.set(sectionValue, [d]); - else fields.get(sectionValue)!.push(d); - }); return (
e.stopPropagation()} > + ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > {/* {sectionFilter as boolean ? [ ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())], ["width = height", this.filteredChildren.filter(f => Math.abs(f[WidthSym]() - f[HeightSym]()) < 1)], ["height > width", this.filteredChildren.filter(f => f[WidthSym]() + 1 <= f[HeightSym]())]]. */} - {sectionFilter ? Array.from(fields.entries()). + {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()). map(section => this.section(section[0].toString(), section[1] as Doc[])) : this.section("", this.filteredChildren)}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7e9d35d3d..045c8531e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faProjectDiagram, faSignature, faSquare, faTh, faImage, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; +import { faProjectDiagram, faSignature, faColumns, faSquare, faTh, faImage, faThList, faTree, faEllipsisV } from '@fortawesome/free-solid-svg-icons'; import { observer } from "mobx-react"; import * as React from 'react'; import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; @@ -25,6 +25,8 @@ library.add(faSquare); library.add(faProjectDiagram); library.add(faSignature); library.add(faThList); +library.add(faColumns); +library.add(faEllipsisV); library.add(faImage); @observer @@ -37,7 +39,8 @@ export class CollectionView extends React.Component { case CollectionViewType.Schema: return (); case CollectionViewType.Docking: return (); case CollectionViewType.Tree: return (); - case CollectionViewType.Stacking: return (); + case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } + case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } case CollectionViewType.Freeform: default: return (); @@ -57,7 +60,8 @@ export class CollectionView extends React.Component { } subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" }); subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" }); - subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "th-list" }); + subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "ellipsis-v" }); + subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" }); ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems }); ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => { -- cgit v1.2.3-70-g09d2 From 6da55377db662a8fda3a241e7c9d115d33baff83 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 19 Jul 2019 18:33:17 -0400 Subject: json to documents conversion function --- src/client/documents/Documents.ts | 87 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3c248760b..5a6eff04c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -439,6 +439,93 @@ export namespace Docs { export namespace Get { + /** + * This function takes any valid JSON(-like) data, i.e. parsed or unparsed, and at arbitrarily + * deep levels of nesting, converts the data and structure into nested documents with the appropriate fields. + * + * After building a hierarchy within / below a top-level document, it then returns that top-level parent. + * + * @param input for convenience and flexibility, either a valid JSON string to be parsed, + * or the result of any JSON.parse() call. + * @param title an optional title to give to the highest parent document in the hierarchy + */ + export function DocumentHierarchyFromJson(input: any, title?: string): Opt; + export function DocumentHierarchyFromJson(input: string, title?: string): Opt { + // preliminary check - making a document out of null or undefined is meaningless + if (input === null || input === undefined) { + return undefined; + } + let parsed: any = input; + // if we've received a string, treat it like valid JSON and try to parse it into an object. + if (typeof input === "string") { + try { + parsed = JSON.parse(input); + } catch (e) { + // if this fails, the string is invalid JSON, so we should assume that the input is the + // result of a JSON.parse() call that returned a regular string value to be stored. + parsed = input; + } + } else if (!["object", "boolean", "number"].includes(typeof input)) { + // since the caller might also pass in the results of a JSON.parse() call, input + // might be an object, an array (still typeof object), a boolean or a number. + // anything else (a function, etc. that is passed in naively as any) is meaningless for this operation + return undefined; + } + let converted: Doc; + if (typeof parsed === "object" && !(parsed instanceof Array)) { + // JavaScript object: this gets converted directly to a document, which is a also a list of key value pairs + converted = convertObject(parsed); + } else { + // Array, a boolean, a string or a number: this gets stored as a field in a wrapper document, since no key value structure exists + (converted = new Doc).json = valueOf(parsed); + } + // if we passed in a title, assign it + title && (converted.title = title); + return converted; + } + + const convertObject = (object: any): Doc => { + // create the document that will store this object's data + let target = new Doc(); + // for each value of the document, recursively convert it to a document or other field + // and store the field at the appropriate key in the document + Object.keys(object).map(key => { + let result = valueOf(object[key]); + // if the result is undefined, ignore it + result && (target[key] = result); + }); + return target; + }; + + const convertList = (list: Array): List => { + // create the list (Field implementation) that will store this Array's data + let thisLevel = new List(); + // for each element in the list, recursively convert it to a document or other field + // and push the field to the list + list.map(item => { + let result = valueOf(item); + // if the result is undefined, ignore it + result && thisLevel.push(result); + }); + return thisLevel; + }; + + const valueOf = (data: any): Opt => { + if (data === null || data === undefined) { + return undefined; + } + if (typeof data === "object") { + // recursively convert the object or array to the appropriate field value and return it + return data instanceof Array ? convertList(data) : convertObject(data); + } else if (["string", "number", "boolean"].includes(typeof data)) { + // no real conversion necessary - just return the data, already a valid field, to be stored + return data; + } else { + // any other type cannot be stored in JSON, but ya never know + throw new Error(`How did ${data} end up in JSON?`); + } + }; + export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise> { let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; if (type.indexOf("image") !== -1) { -- cgit v1.2.3-70-g09d2 From 2899cbc887398688b82dd284ee482cdb136c6452 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 22 Jul 2019 00:38:50 -0400 Subject: json conversion tweaks --- src/Utils.ts | 16 +++++- src/client/documents/Documents.ts | 102 ++++++++++++++++++-------------------- src/new_fields/InkField.ts | 4 +- 3 files changed, 64 insertions(+), 58 deletions(-) (limited to 'src/client/documents') diff --git a/src/Utils.ts b/src/Utils.ts index ac6d127cc..8df67df5d 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -146,7 +146,7 @@ export type Without = Pick>; export type Predicate = (entry: [K, V]) => boolean; -export function deepCopy(source: Map, predicate?: Predicate) { +export function DeepCopy(source: Map, predicate?: Predicate) { let deepCopy = new Map(); let entries = source.entries(), next = entries.next(); while (!next.done) { @@ -157,4 +157,18 @@ export function deepCopy(source: Map, predicate?: Predicate) { next = entries.next(); } return deepCopy; +} + +export namespace JSONUtils { + + export function tryParse(source: string) { + let results: any; + try { + results = JSON.parse(source); + } catch (e) { + results = source; + } + return results; + } + } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5a6eff04c..de049be5a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -21,7 +21,7 @@ import { AggregateFunction } from "../northstar/model/idea/idea"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; -import { OmitKeys } from "../../Utils"; +import { OmitKeys, JSONUtils } from "../../Utils"; import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField"; import { HtmlField } from "../../new_fields/HtmlField"; import { List } from "../../new_fields/List"; @@ -439,91 +439,83 @@ export namespace Docs { export namespace Get { + const primitives = ["string", "number", "boolean"]; + /** * This function takes any valid JSON(-like) data, i.e. parsed or unparsed, and at arbitrarily * deep levels of nesting, converts the data and structure into nested documents with the appropriate fields. * * After building a hierarchy within / below a top-level document, it then returns that top-level parent. * + * If we've received a string, treat it like valid JSON and try to parse it into an object. If this fails, the + * string is invalid JSON, so we should assume that the input is the result of a JSON.parse() + * call that returned a regular string value to be stored as a Field. + * + * If we've received something other than a string, since the caller might also pass in the results of a + * JSON.parse() call, valid input might be an object, an array (still typeof object), a boolean or a number. + * Anything else (like a function, etc. passed in naively as any) is meaningless for this operation. + * + * All TS/JS objects get converted directly to documents, directly preserving the key value structure. Everything else, + * lacking the key value structure, gets stored as a field in a wrapper document. + * * @param input for convenience and flexibility, either a valid JSON string to be parsed, * or the result of any JSON.parse() call. * @param title an optional title to give to the highest parent document in the hierarchy */ - export function DocumentHierarchyFromJson(input: any, title?: string): Opt; - export function DocumentHierarchyFromJson(input: string, title?: string): Opt { - // preliminary check - making a document out of null or undefined is meaningless - if (input === null || input === undefined) { - return undefined; - } - let parsed: any = input; - // if we've received a string, treat it like valid JSON and try to parse it into an object. - if (typeof input === "string") { - try { - parsed = JSON.parse(input); - } catch (e) { - // if this fails, the string is invalid JSON, so we should assume that the input is the - // result of a JSON.parse() call that returned a regular string value to be stored. - parsed = input; - } - } else if (!["object", "boolean", "number"].includes(typeof input)) { - // since the caller might also pass in the results of a JSON.parse() call, input - // might be an object, an array (still typeof object), a boolean or a number. - // anything else (a function, etc. that is passed in naively as any) is meaningless for this operation + export function DocumentHierarchyFromJson(input: any, title?: string): Opt { + if (input === null || ![...primitives, "object"].includes(typeof input)) { return undefined; } + let parsed: any = typeof input === "string" ? JSONUtils.tryParse(input) : input; let converted: Doc; if (typeof parsed === "object" && !(parsed instanceof Array)) { - // JavaScript object: this gets converted directly to a document, which is a also a list of key value pairs - converted = convertObject(parsed); + converted = convertObject(parsed, title); } else { - // Array, a boolean, a string or a number: this gets stored as a field in a wrapper document, since no key value structure exists - (converted = new Doc).json = valueOf(parsed); + (converted = new Doc).json = toField(parsed); } - // if we passed in a title, assign it title && (converted.title = title); return converted; } - const convertObject = (object: any): Doc => { - // create the document that will store this object's data - let target = new Doc(); - // for each value of the document, recursively convert it to a document or other field - // and store the field at the appropriate key in the document - Object.keys(object).map(key => { - let result = valueOf(object[key]); - // if the result is undefined, ignore it - result && (target[key] = result); - }); + /** + * For each value of the object, recursively convert it to its appropriate field value + * and store the field at the appropriate key in the document if it is not undefined + * @param object the object to convert + * @returns the object mapped from JSON to field values, where each mapping + * might involve arbitrary recursion (since toField might itself call convertObject) + */ + const convertObject = (object: any, title?: string): Doc => { + let target = new Doc(), result: Opt; + Object.keys(object).map(key => (result = toField(object[key], key)) && (target[key] = result)); + title && (target.title = title); return target; }; + /** + * For each element in the list, recursively convert it to a document or other field + * and push the field to the list if it is not undefined + * @param list the list to convert + * @returns the list mapped from JSON to field values, where each mapping + * might involve arbitrary recursion (since toField might itself call convertList) + */ const convertList = (list: Array): List => { - // create the list (Field implementation) that will store this Array's data - let thisLevel = new List(); - // for each element in the list, recursively convert it to a document or other field - // and push the field to the list - list.map(item => { - let result = valueOf(item); - // if the result is undefined, ignore it - result && thisLevel.push(result); - }); - return thisLevel; + let target = new List(), result: Opt; + list.map(item => (result = toField(item)) && target.push(result)); + return target; }; - const valueOf = (data: any): Opt => { + + const toField = (data: any, title?: string): Opt => { if (data === null || data === undefined) { return undefined; } - if (typeof data === "object") { - // recursively convert the object or array to the appropriate field value and return it - return data instanceof Array ? convertList(data) : convertObject(data); - } else if (["string", "number", "boolean"].includes(typeof data)) { - // no real conversion necessary - just return the data, already a valid field, to be stored + if (primitives.includes(typeof data)) { return data; - } else { - // any other type cannot be stored in JSON, but ya never know - throw new Error(`How did ${data} end up in JSON?`); } + if (typeof data === "object") { + return data instanceof Array ? convertList(data) : convertObject(data, title); + } + throw new Error(`How did ${data} of type ${typeof data} end up in JSON?`); }; export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise> { diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 4e3b7abe0..39c6c8ce3 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; import { ObjectField } from "./ObjectField"; import { Copy, ToScriptString } from "./FieldSymbols"; -import { deepCopy } from "../Utils"; +import { DeepCopy } from "../Utils"; export enum InkTool { None, @@ -39,7 +39,7 @@ export class InkField extends ObjectField { } [Copy]() { - return new InkField(deepCopy(this.inkData)); + return new InkField(DeepCopy(this.inkData)); } [ToScriptString]() { -- cgit v1.2.3-70-g09d2 From 8db50c6ba0be83b85c896043da53e40c17523e90 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 22 Jul 2019 13:45:32 -0400 Subject: more missing stuff .. bad merge? --- src/client/documents/Documents.ts | 3 ++- src/client/views/nodes/FormattedTextBox.tsx | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index de049be5a..7563fda20 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -55,7 +55,8 @@ export enum DocumentType { ICON = "icon", IMPORT = "import", LINK = "link", - LINKDOC = "linkdoc" + LINKDOC = "linkdoc", + TEMPLATE = "template" } export interface DocumentOptions { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 99801ecff..0a79677e2 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -233,10 +233,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`; }, field2 => { - if (StrCast(this.props.Document.layout).indexOf("\"" + this.props.fieldKey + "\"") !== -1) { // bcz: UGH! why is this needed... something is happening out of order. test with making a collection, then adding a text note and converting that to a template field. - this._editorView && !this._applyingChange && - this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2))); - } + this._editorView && !this._applyingChange && + this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2))); } ); -- cgit v1.2.3-70-g09d2 From 0591b8f1e60d1285fd9aac3e61160824948a166b Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 17:33:31 -0400 Subject: Everything related to search stored --- src/client/apis/youtube/YoutubeBox.tsx | 123 +++++++++++++++++++----------- src/client/documents/Documents.ts | 2 +- src/server/youtubeApi/youtubeApiSample.js | 4 - 3 files changed, 81 insertions(+), 48 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 373eee5c4..e630c11ae 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -16,6 +16,16 @@ import { Doc, DocListCastAsync } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; +interface VideoTemplate { + thumbnailUrl: string; + videoTitle: string; + videoId: string; + duration: string; + channelTitle: string; + viewCount: string; + publishDate: string; + videoDescription: string; +} @observer export class YoutubeBox extends React.Component { @@ -28,58 +38,71 @@ export class YoutubeBox extends React.Component { @observable lisOfBackUp: JSX.Element[] = []; @observable videoIds: string | undefined; @observable videoDetails: any[] = []; + @observable curVideoTemplates: VideoTemplate[] = []; public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } async componentWillMount() { //DocServer.getYoutubeChannels(); - //DocServer.getYoutubeVideoDetails("Ks-_Mh1QhMc, 1NmvhSmN2uM", (results: any[]) => console.log("Details results: ", results)); - let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); let castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc); let awaitedBackUp = await castedSearchBackUp; + let castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc); + let awaitedDetails = await castedDetailBackUp; - console.log("Backup results: ", awaitedBackUp); - console.log("Original Backup results: ", castedBackUpDocs); - - let json = Cast(awaitedBackUp!.json, Doc); - let jsonList = await DocListCastAsync(json); - console.log("Fucked up list: ", jsonList); - for (let video of jsonList!) { - let videoId = await Cast(video.id, Doc); - let id = StrCast(videoId!.videoId); - console.log("ID: ", id); - } + let jsonList = await DocListCastAsync(awaitedBackUp!.json); + let jsonDetailList = await DocListCastAsync(awaitedDetails!.json); - if (!castedBackUpDocs) { - this.props.Document.cachedSearch = castedBackUpDocs = new List(); - } - if (castedBackUpDocs.length !== 0) { - + if (jsonList!.length !== 0) { runInAction(() => this.searchResultsFound = true); + let index = 0; + for (let video of jsonList!) { + + let videoId = await Cast(video.id, Doc); + let id = StrCast(videoId!.videoId); + let snippet = await Cast(video.snippet, Doc); + let videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title)); + let thumbnail = await Cast(snippet!.thumbnails, Doc); + let thumbnailMedium = await Cast(thumbnail!.medium, Doc); + let thumbnailUrl = StrCast(thumbnailMedium!.url); + let videoDescription = StrCast(snippet!.description); + let pusblishDate = (this.roundPublishTime2(StrCast(snippet!.publishedAt)))!; + let channelTitle = StrCast(snippet!.channelTitle); + let duration: string; + let viewCount: string; + if (jsonDetailList!.length !== 0) { + let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc); + let statistics = await Cast(jsonDetailList![index].statistics, Doc); + duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration)); + viewCount = this.abbreviateViewCount(NumCast(statistics!.viewCount))!; + } + index = index + 1; + let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; + runInAction(() => this.curVideoTemplates.push(newTemplate)); + + // runInAction(() => this.lisOfBackUp.push(( + //
  • this.embedVideoOnClick(id, videoTitle)} key={Utils.GenerateGuid() + id}> + //
    + //
    + // + // {duration} + //
    + //
    + // {videoTitle} + // {channelTitle} + // {viewCount} + // {pusblishDate} + //

    {videoDescription}

    + + //
    + //
    + //
  • ) + // )); - for (let videoBackUp of castedBackUpDocs) { - let curBackUp = await videoBackUp; - let videoId = StrCast(curBackUp.videoId); - let videoTitle = StrCast(curBackUp.videoTitle); - let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); - runInAction(() => this.lisOfBackUp.push(( -
  • this.embedVideoOnClick(videoId, videoTitle)} - key={Utils.GenerateGuid()} - > - - {videoTitle} -
  • ) - )); } - - } - - } _ignore = 0; @@ -134,11 +157,10 @@ export class YoutubeBox extends React.Component { @action processVideoDetails = (videoDetails: any[]) => { this.videoDetails = videoDetails; - console.log("Detail Res: ", this.videoDetails); + this.props.Document.cachedDetails = Docs.Get.DocumentHierarchyFromJson(videoDetails, "detailBackUp"); } backUpSearchResults = (videos: any[]) => { - console.log("Res: ", videos); this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp"); let newCachedList = new List(); this.props.Document.cachedSearch = newCachedList; @@ -164,7 +186,6 @@ export class YoutubeBox extends React.Component { let videoYearDif = curDate.getFullYear() - date.getFullYear(); let videoMonthDif = curDate.getMonth() - date.getMonth(); let videoDayDif = curDate.getDay() - date.getDay(); - console.log("video day dif: ", videoDayDif, " first day: ", curDate.getDay(), " second day: ", date.getDay()); let videoHoursDif = curDate.getHours() - date.getHours(); let videoMinutesDif = curDate.getMinutes() - date.getMinutes(); let videoSecondsDif = curDate.getSeconds() - date.getSeconds(); @@ -182,7 +203,6 @@ export class YoutubeBox extends React.Component { return videoSecondsDif + " seconds ago"; } - console.log("Date : ", date); } roundPublishTime2 = (publishTime: string) => { @@ -290,8 +310,26 @@ export class YoutubeBox extends React.Component { ; })} ; - } else if (this.lisOfBackUp.length !== 0) { - return
      {this.lisOfBackUp}
    ; + } else if (this.curVideoTemplates.length !== 0) { + return
      + {this.curVideoTemplates.map((video: VideoTemplate) => { + return
    • this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}> +
      +
      + + {video.duration} +
      +
      + {video.videoTitle} + {video.channelTitle} + {video.viewCount} + {video.publishDate} +

      {video.videoDescription}

      +
      +
      +
    • ; + })} +
    ; } } else { return (null); @@ -301,7 +339,6 @@ export class YoutubeBox extends React.Component { @action embedVideoOnClick = (videoId: string, filteredTitle: string) => { let embeddedUrl = "https://www.youtube.com/embed/" + videoId; - console.log("EmbeddedUrl: ", embeddedUrl); this.selectedVideoUrl = embeddedUrl; let addFunction = this.props.addDocument!; let newVideoX = NumCast(this.props.Document.x); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 191be9b7d..333e9859b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -498,7 +498,7 @@ export namespace Docs { const convertObject = (object: any, title?: string): Doc => { let target = new Doc(), result: Opt; Object.keys(object).map(key => (result = toField(object[key], key)) && (target[key] = result)); - title && (target.title = title); + title && !target.title && (target.title = title); return target; }; diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js index cf41a33e7..4fede08aa 100644 --- a/src/server/youtubeApi/youtubeApiSample.js +++ b/src/server/youtubeApi/youtubeApiSample.js @@ -23,8 +23,6 @@ module.exports.readApiKey = (callback) => { module.exports.authorizedGetChannel = (apiKey) => { //this didnt get called - console.log("I get called ", apiKey); - console.log(TOKEN_PATH); // Authorize a client with the loaded credentials, then call the YouTube API. authorize(JSON.parse(apiKey), getChannel); } @@ -157,7 +155,6 @@ function getSampleVideos(auth, args) { return; } let videos = response.data.items; - console.log('Videos found: ' + videos[0].id.videoId, " ", unescape(videos[0].snippet.title)); args.callBack(videos); }); } @@ -174,7 +171,6 @@ function getVideoDetails(auth, args) { return; } let videoDetails = response.data.items; - console.log('Video Details founds: ', videoDetails); args.callBack(videoDetails); }); } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 189cf09a7c81e8b4b2f5313cf093097cc9eba03e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 24 Jul 2019 00:16:52 -0400 Subject: more cleanup --- src/client/documents/Documents.ts | 3 ++- src/new_fields/Doc.ts | 53 ++++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 27 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7563fda20..a5b1d6573 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -56,7 +56,8 @@ export enum DocumentType { IMPORT = "import", LINK = "link", LINKDOC = "linkdoc", - TEMPLATE = "template" + TEMPLATE = "template", + EXTENSION = "extension" } export interface DocumentOptions { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5a9a0eab4..2a697e621 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -314,20 +314,22 @@ export namespace Doc { } export function UpdateDocumentExtensionForField(doc: Doc, fieldKey: string) { - let extensionDoc = doc[fieldKey + "_ext"]; - if (extensionDoc === undefined) { + let docExtensionForField = doc[fieldKey + "_ext"] as Doc; + if (docExtensionForField === undefined) { setTimeout(() => { - let docExtensionForField = new Doc(doc[Id] + fieldKey, true); - docExtensionForField.title = fieldKey + "[" + StrCast(doc.title).match(/\.\.\.[0-9]*/) + "].ext"; - docExtensionForField.extendsDoc = doc; + docExtensionForField = new Doc(doc[Id] + fieldKey, true); + docExtensionForField.title = fieldKey + ".ext"; + docExtensionForField.extendsDoc = doc; // this is used by search to map field matches on the extension doc back to the document it extends. + docExtensionForField.type = DocumentType.EXTENSION; let proto: Doc | undefined = doc; while (proto && !Doc.IsPrototype(proto)) { proto = proto.proto; } (proto ? proto : doc)[fieldKey + "_ext"] = docExtensionForField; }, 0); - } else if (extensionDoc instanceof Doc && extensionDoc.extendsDoc === undefined) { - setTimeout(() => (extensionDoc as Doc).extendsDoc = doc, 0); + } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already + docExtensionForField.extendsDoc === undefined && setTimeout(() => (docExtensionForField as Doc).extendsDoc = doc, 0); + docExtensionForField.type === undefined && setTimeout(() => (docExtensionForField as Doc).type = DocumentType.EXTENSION, 0); } } export function MakeAlias(doc: Doc) { @@ -344,13 +346,16 @@ export namespace Doc { // for individual layout properties to be overridden in the expanded layout. // export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) { - let resolvedDataDoc = (layoutDoc !== dataDoc) ? dataDoc : undefined; - if (!dataDoc || !(layoutDoc && !(Cast(layoutDoc.layout, Doc) instanceof Doc) && - resolvedDataDoc && resolvedDataDoc !== layoutDoc)) { - return false; - } - return true; + return BoolCast(layoutDoc.isTemplate) && dataDoc && layoutDoc !== dataDoc && !(layoutDoc.layout instanceof Doc); } + + // + // Returns an expanded template layout for a target data document. + // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc + // using the template layout doc's id as the field key. + // If it doesn't find the expanded layout, then it makes a delegate of the template layout and + // saves it on the data doc indexed by the template layout's id + // export function expandTemplateLayout(templateLayoutDoc: Doc, dataDoc?: Doc) { if (!WillExpandTemplateLayout(templateLayoutDoc, dataDoc) || !dataDoc) return templateLayoutDoc; // if we have a data doc that doesn't match the layout, then we're rendering a template. @@ -361,19 +366,14 @@ export namespace Doc { if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } - // expandedTemplateLayout = dataDoc[templateLayoutDoc.title + templateLayoutDoc[Id]]; - expandedTemplateLayout = dataDoc["Layout[" + templateLayoutDoc[Id] + "]"]; + let expandedLayoutFieldKey = "Layout[" + templateLayoutDoc[Id] + "]"; + expandedTemplateLayout = dataDoc[expandedLayoutFieldKey]; if (expandedTemplateLayout instanceof Doc) { return expandedTemplateLayout; } - if (expandedTemplateLayout === undefined && BoolCast(templateLayoutDoc.isTemplate)) { - setTimeout(() => { - let expandedDoc = Doc.MakeDelegate(templateLayoutDoc); - //expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; - expandedDoc.title = templateLayoutDoc.title + "[" + StrCast(dataDoc.title).match(/\.\.\.[0-9]*/) + "]"; - expandedDoc.isExpandedTemplate = templateLayoutDoc; - dataDoc["Layout[" + templateLayoutDoc[Id] + "]"] = expandedDoc; - }, 0); + if (expandedTemplateLayout === undefined) { + setTimeout(() => + dataDoc[expandedLayoutFieldKey] = Doc.MakeDelegate(templateLayoutDoc, undefined, templateLayoutDoc.title + ".layout"), 0); } return templateLayoutDoc; // use the templateLayout when it's not a template or the expandedTemplate is pending. } @@ -402,12 +402,13 @@ export namespace Doc { return copy; } - export function MakeDelegate(doc: Doc, id?: string): Doc; - export function MakeDelegate(doc: Opt, id?: string): Opt; - export function MakeDelegate(doc: Opt, id?: string): Opt { + export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc; + export function MakeDelegate(doc: Opt, id?: string, title?: string): Opt; + export function MakeDelegate(doc: Opt, id?: string, title?: string): Opt { if (doc) { const delegate = new Doc(id, true); delegate.proto = doc; + title && (delegate.title = title); return delegate; } return undefined; -- cgit v1.2.3-70-g09d2 From 74593e4370b554072ebbdef9a683a04a1bcbbde1 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 24 Jul 2019 22:10:00 -0400 Subject: Added a Button document --- src/client/documents/Documents.ts | 17 +++++- src/client/views/MainView.tsx | 5 +- src/client/views/nodes/ButtonBox.scss | 10 ++++ src/client/views/nodes/ButtonBox.tsx | 74 +++++++++++++++++++++++++ src/client/views/nodes/DocumentContentsView.tsx | 3 +- 5 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 src/client/views/nodes/ButtonBox.scss create mode 100644 src/client/views/nodes/ButtonBox.tsx (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a5b1d6573..3859f2255 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -38,6 +38,7 @@ import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting } from "../util/Scripting"; +import { ButtonBox } from "../views/nodes/ButtonBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -56,6 +57,7 @@ export enum DocumentType { IMPORT = "import", LINK = "link", LINKDOC = "linkdoc", + BUTTON = "button", TEMPLATE = "template", EXTENSION = "extension" } @@ -162,6 +164,9 @@ export namespace Docs { data: new List(), layout: { view: EmptyBox }, options: {} + }], + [DocumentType.BUTTON, { + layout: { view: ButtonBox }, }] ]); @@ -277,7 +282,7 @@ export namespace Docs { * only when creating a DockDocument from the current user's already existing * main document. */ - export function InstanceFromProto(proto: Doc, data: Field, options: DocumentOptions, delegId?: string) { + export function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string) { const { omit: protoProps, extract: delegateProps } = OmitKeys(options, delegateKeys); if (!("author" in protoProps)) { @@ -306,9 +311,11 @@ export namespace Docs { * @param options initial values to apply to this new delegate * @param value the data to store in this new delegate */ - function MakeDataDelegate(proto: Doc, options: DocumentOptions, value: D) { + function MakeDataDelegate(proto: Doc, options: DocumentOptions, value?: D) { const deleg = Doc.MakeDelegate(proto); - deleg.data = value; + if (value !== undefined) { + deleg.data = value; + } return Doc.assign(deleg, options); } @@ -411,6 +418,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); } + export function ButtonDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}) }); + } + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 53be0278e..61a013963 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowDown, faCloudUploadAlt, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faCloudUploadAlt, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat, faBolt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, reaction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -131,6 +131,7 @@ export class MainView extends React.Component { library.add(faArrowDown); library.add(faArrowUp); library.add(faCloudUploadAlt); + library.add(faBolt); this.initEventListeners(); this.initAuthenticationRouters(); } @@ -378,10 +379,12 @@ export class MainView extends React.Component { let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); let addTreeNode = action(() => CurrentUserUtils.UserDocument); let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" })); + let addButtonDocument = action(() => Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })); let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })); let btns: [React.RefObject, IconName, string, () => Doc][] = [ [React.createRef(), "object-group", "Add Collection", addColNode], + [React.createRef(), "bolt", "Add Button", addButtonDocument], // [React.createRef(), "clone", "Add Docking Frame", addDockingNode], [React.createRef(), "cloud-upload-alt", "Import Directory", addImportCollectionNode], ]; diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss new file mode 100644 index 000000000..97cc91128 --- /dev/null +++ b/src/client/views/nodes/ButtonBox.scss @@ -0,0 +1,10 @@ +.buttonBox-outerDiv { + width: 100%; + height: 100%; + pointer-events: all; +} + +.buttonBox-mainButton { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx new file mode 100644 index 000000000..622595cdc --- /dev/null +++ b/src/client/views/nodes/ButtonBox.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { FieldViewProps, FieldView } from './FieldView'; +import { createSchema, makeInterface } from '../../../new_fields/Schema'; +import { ScriptField } from '../../../new_fields/ScriptField'; +import { DocComponent } from '../DocComponent'; +import { ContextMenu } from '../ContextMenu'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faEdit } from '@fortawesome/free-regular-svg-icons'; +import { emptyFunction } from '../../../Utils'; +import { ScriptBox } from '../ScriptBox'; +import { CompileScript } from '../../util/Scripting'; +import { OverlayView } from '../OverlayView'; +import { Doc } from '../../../new_fields/Doc'; + +import './ButtonBox.scss'; +import { observer } from 'mobx-react'; + +library.add(faEdit); + +const ButtonSchema = createSchema({ + onClick: ScriptField, + text: "string" +}); + +type ButtonDocument = makeInterface<[typeof ButtonSchema]>; +const ButtonDocument = makeInterface(ButtonSchema); + +@observer +export class ButtonBox extends DocComponent(ButtonDocument) { + public static LayoutString() { return FieldView.LayoutString(ButtonBox); } + + onClick = (e: React.MouseEvent) => { + const onClick = this.Document.onClick; + if (!onClick) { + return; + } + e.stopPropagation(); + e.preventDefault(); + onClick.script.run({ this: this.props.Document }); + } + + onContextMenu = () => { + ContextMenu.Instance.addItem({ + description: "Edit OnClick script", icon: "edit", event: () => { + let overlayDisposer: () => void = emptyFunction; + const script = this.Document.onClick; + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { this: Doc.name }, + typecheck: false + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + this.Document.onClick = new ScriptField(script); + overlayDisposer(); + }} />; + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnClick` }); + } + }); + } + + render() { + return ( +
    + +
    + ); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index ef65c12cf..91d4fb524 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -11,6 +11,7 @@ import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import { ImageBox } from "./ImageBox"; +import { ButtonBox } from "./ButtonBox"; import { IconBox } from "./IconBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; @@ -97,7 +98,7 @@ export class DocumentContentsView extends React.Component 7) return (null); if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null); return Date: Sat, 27 Jul 2019 17:03:20 -0400 Subject: schema headers changed to schemaheaderfields, a bug with udpating the headers (in CollectionSchemaView>ChangeColumn) --- src/client/documents/Documents.ts | 13 +-- src/client/util/DragManager.ts | 5 +- src/client/views/MainView.tsx | 3 +- .../views/collections/CollectionSchemaCells.tsx | 30 +++++- .../views/collections/CollectionSchemaHeaders.tsx | 9 +- .../CollectionSchemaMovableTableHOC.tsx | 7 +- .../views/collections/CollectionSchemaView.scss | 15 +++ .../views/collections/CollectionSchemaView.tsx | 103 +++++++++++++-------- .../views/collections/CollectionStackingView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +- src/client/views/nodes/LinkEditor.tsx | 3 +- src/client/views/nodes/LinkMenuGroup.tsx | 3 +- src/new_fields/SchemaHeaderField.ts | 55 +++++++++-- 13 files changed, 186 insertions(+), 65 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 76ac34d85..e6fe1b8b3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -39,6 +39,7 @@ import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; +import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -84,7 +85,7 @@ export interface DocumentOptions { curPage?: number; documentText?: string; borderRounding?: string; - schemaColumns?: List; + schemaColumns?: List; dockingConfig?: string; dbDoc?: Doc; // [key: string]: Opt; @@ -403,23 +404,23 @@ export namespace Docs { } export function FreeformDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Freeform }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform }); } - export function SchemaDocument(schemaColumns: string[], documents: Array, options: DocumentOptions) { + export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); } export function TreeDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree }); } export function StackingDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking }); } export function MasonryDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Masonry }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0299b1d90..47d3c313d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -8,6 +8,7 @@ import * as globalCssVariables from "../views/globalCssVariables.scss"; import { DocumentManager } from "./DocumentManager"; import { LinkManager } from "./LinkManager"; import { SelectionManager } from "./SelectionManager"; +import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -290,10 +291,10 @@ export namespace DragManager { // for column dragging in schema view export class ColumnDragData { - constructor(colKey: string) { + constructor(colKey: SchemaHeaderField) { this.colKey = colKey; } - colKey: string; + colKey: SchemaHeaderField; [id: string]: any; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0e04b5e7e..282db244e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import { PreviewCursor } from './PreviewCursor'; import { FilterBox } from './search/FilterBox'; import { CollectionTreeView } from './collections/CollectionTreeView'; import { ClientUtils } from '../util/ClientUtils'; +import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField'; @observer export class MainView extends React.Component { @@ -375,7 +376,7 @@ export class MainView extends React.Component { let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; // let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); - let addSchemaNode = action(() => Docs.Create.SchemaDocument(["title"], [], { width: 200, height: 200, title: "a schema collection" })); + let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title")], [], { width: 200, height: 200, title: "a schema collection" })); //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 4a618a436..c91f1017b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -21,7 +21,12 @@ import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_field import { Docs } from "../../documents/Documents"; import { DocumentContentsView } from "../nodes/DocumentContentsView"; import { SelectionManager } from "../../util/SelectionManager"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faExpand } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; +library.add(faExpand); export interface CellProps { row: number; @@ -38,6 +43,7 @@ export interface CellProps { changeFocusedCellByIndex: (row: number, col: number) => void; setIsEditing: (isEditing: boolean) => void; isEditable: boolean; + setPreviewDoc: (doc: Doc) => void; } @observer @@ -92,7 +98,7 @@ export class CollectionSchemaCell extends React.Component { this._document[fieldKey] = de.data.draggedDocuments[0]; } else { - let coll = Docs.Create.SchemaDocument(["title"], de.data.draggedDocuments, {}); + let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title")], de.data.draggedDocuments, {}); this._document[fieldKey] = coll; } e.stopPropagation(); @@ -106,6 +112,18 @@ export class CollectionSchemaCell extends React.Component { } } + expandDoc = (e: React.PointerEvent) => { + let field = this.props.rowProps.original[this.props.rowProps.column.id as string]; + let doc = FieldValue(Cast(field, Doc)); + + console.log("Expanding doc", StrCast(doc!.title)); + this.props.setPreviewDoc(doc!); + + // this.props.changeFocusedCellByIndex(this.props.row, this.props.col); + + e.stopPropagation(); + } + renderCellWithType(type: string | undefined) { let dragRef: React.RefObject = React.createRef(); @@ -157,6 +175,15 @@ export class CollectionSchemaCell extends React.Component { if (this.props.isFocused && this.props.isEditable) className += " focused"; if (this.props.isFocused && !this.props.isEditable) className += " inactive"; + let doc = FieldValue(Cast(field, Doc)); + if (type === "document") console.log("doc", typeof field); + let fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc); + let docExpander = ( +
    + +
    + ); + return (
    @@ -193,6 +220,7 @@ export class CollectionSchemaCell extends React.Component { val && val.forEach(doc => this.applyToDoc(doc, run)); }} />
    + {fieldIsDoc ? docExpander : null}
    ); diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index d1d0674c4..9fc28eafa 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -10,11 +10,12 @@ import { ColumnType } from "./CollectionSchemaView"; import { emptyFunction } from "../../../Utils"; import { contains } from "typescript-collections/dist/lib/arrays"; import { faFile } from "@fortawesome/free-regular-svg-icons"; +import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile); export interface HeaderProps { - keyValue: string; + keyValue: SchemaHeaderField; possibleKeys: string[]; existingKeys: string[]; keyType: ColumnType; @@ -33,14 +34,14 @@ export class CollectionSchemaHeader extends React.Component { this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify"; return ( -
    +
    {this.props.keyValue}
    } + menuButtonContent={
    {this.props.keyValue.heading}
    } addNew={false} onSelect={this.props.onSelect} setIsEditing={this.props.setIsEditing} diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 0ddd56180..2349e42ca 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -12,14 +12,15 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { DocumentManager } from "../../util/DocumentManager"; +import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; library.add(faGripVertical, faTrash); export interface MovableColumnProps { columnRenderer: TableCellRenderer; - columnValue: string; - allColumns: string[]; - reorderColumns: (toMove: string, relativeTo: string, before: boolean, columns: string[]) => void; + columnValue: SchemaHeaderField; + allColumns: SchemaHeaderField[]; + reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void; ScreenToLocalTransform: () => Transform; } export class MovableColumn extends React.Component { diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 91c6e8b3c..e0de76247 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -379,6 +379,7 @@ button.add-column { .collectionSchemaView-cellWrapper { height: 100%; padding: 4px; + position: relative; &:focus { outline: none; @@ -405,6 +406,20 @@ button.add-column { height: 100%; // word-wrap: break-word; } + + &:hover .collectionSchemaView-cellContents-docExpander { + display: block; + } +} + +.collectionSchemaView-cellContents-docExpander { + height: 30px; + width: 30px; + display: none; + position: absolute; + top: 0; + right: 0; + background-color: lightgray; } .doc-drag-over { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index c6550560a..176872503 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -38,6 +38,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { DocumentManager } from "../../util/DocumentManager"; import { ImageBox } from "../nodes/ImageBox"; import { ComputedField } from "../../../new_fields/ScriptField"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; library.add(faCog, faPlus, faSortUp, faSortDown); @@ -284,11 +285,13 @@ export class SchemaTable extends React.Component { @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); } - @computed get columns() { return Cast(this.props.Document.schemaColumns, listSpec("string"), []); } - set columns(columns: string[]) { this.props.Document.schemaColumns = new List(columns); } + @computed get columns() { + return Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField), []); + } + set columns(columns: SchemaHeaderField[]) { this.props.Document.schemaColumns = new List(columns); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column[] { - let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.toUpperCase() === key.toUpperCase()) === -1); + let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); let columns: Column[] = []; let tableIsFocused = this.props.isFocused(this.props.Document); let focusedRow = this._focusedCell.row; @@ -321,9 +324,9 @@ export class SchemaTable extends React.Component { let header = c.heading)} keyType={this.getColumnType(col)} - typeConst={columnTypes.get(col) !== undefined} + typeConst={col.type !== undefined || columnTypes.get(col.heading) !== undefined} onSelect={this.changeColumns} setIsEditing={this.setHeaderIsEditing} deleteColumn={this.deleteColumn} @@ -334,16 +337,16 @@ export class SchemaTable extends React.Component { return { Header: , - accessor: (doc: Doc) => doc ? doc[col] : 0, - id: col, + accessor: (doc: Doc) => doc ? doc[col.heading] : 0, + id: col.heading, Cell: (rowProps: CellInfo) => { - let row = rowProps.index; - let column = this.columns.indexOf(rowProps.column.id!); - let isFocused = focusedRow === row && focusedCol === column && tableIsFocused; + let rowIndex = rowProps.index; + let columnIndex = this.columns.map(c => c.heading).indexOf(rowProps.column.id!); + let isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; let props: CellProps = { - row: row, - col: column, + row: rowIndex, + col: columnIndex, rowProps: rowProps, isFocused: isFocused, changeFocusedCellByIndex: this.changeFocusedCellByIndex, @@ -356,6 +359,7 @@ export class SchemaTable extends React.Component { moveDocument: this.props.moveDocument, setIsEditing: this.setCellIsEditing, isEditable: isEditable, + setPreviewDoc: this.props.setPreviewDoc }; let colType = this.getColumnType(col); @@ -394,6 +398,15 @@ export class SchemaTable extends React.Component { // } // return this.props.Document; // } + constructor(props: SchemaTableProps) { + super(props); + // convert old schema columns (list of strings) into new schema columns (list of schema header fields) + let oldSchemaColumns = Cast(this.props.Document.schemaColumns, listSpec("string"), []); + if (oldSchemaColumns && oldSchemaColumns.length) { + let newSchemaColumns = oldSchemaColumns.map(i => typeof i === "string" ? new SchemaHeaderField(i) : i); + this.props.Document.schemaColumns = new List(newSchemaColumns); + } + } componentDidMount() { document.addEventListener("keydown", this.onKeyDown); @@ -440,7 +453,7 @@ export class SchemaTable extends React.Component { let row = rowInfo.index; //@ts-ignore - let col = this.columns.indexOf(column!.id); + let col = this.columns.map(c => c.heading).indexOf(column!.id); // let col = column ? this.columns.indexOf(column!) : -1; let isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document); // let column = this.columns.indexOf(column.id!); @@ -502,6 +515,11 @@ export class SchemaTable extends React.Component { if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected()) { let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this.changeFocusedCellByDirection(direction); + + let doc = this.props.dataDoc ? this.props.dataDoc : this.props.Document; + let children = Cast(doc[this.props.fieldKey], listSpec(Doc), []); + const pdoc = FieldValue(children[this._focusedCell.row]); + pdoc && this.props.setPreviewDoc(pdoc); } } @@ -532,8 +550,8 @@ export class SchemaTable extends React.Component { this._focusedCell = { row: this._focusedCell.row + 1 === children.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col }; break; } - const pdoc = FieldValue(children[this._focusedCell.row]); - pdoc && this.props.setPreviewDoc(pdoc); + // const pdoc = FieldValue(children[this._focusedCell.row]); + // pdoc && this.props.setPreviewDoc(pdoc); } @action @@ -544,8 +562,8 @@ export class SchemaTable extends React.Component { this._focusedCell = { row: row, col: col }; this.props.setFocused(this.props.Document); - const fdoc = FieldValue(children[this._focusedCell.row]); - fdoc && this.props.setPreviewDoc(fdoc); + // const fdoc = FieldValue(children[this._focusedCell.row]); + // fdoc && this.props.setPreviewDoc(fdoc); } createRow = () => { @@ -561,25 +579,25 @@ export class SchemaTable extends React.Component { @action createColumn = () => { let index = 0; - let found = this.columns.findIndex(col => col.toUpperCase() === "New field".toUpperCase()) > -1; + let found = this.columns.findIndex(col => col.heading.toUpperCase() === "New field".toUpperCase()) > -1; if (!found) { - this.columns.push("New field"); + this.columns.push(new SchemaHeaderField("New field")); return; } while (found) { index++; - found = this.columns.findIndex(col => col.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; + found = this.columns.findIndex(col => col.heading.toUpperCase() === ("New field (" + index + ")").toUpperCase()) > -1; } - this.columns.push("New field (" + index + ")"); + this.columns.push(new SchemaHeaderField("New field (" + index + ")")); } @action deleteColumn = (key: string) => { - let list = Cast(this.props.Document.schemaColumns, listSpec("string")); + let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); if (list === undefined) { - this.props.Document.schemaColumns = list = new List([]); + this.props.Document.schemaColumns = list = new List([]); } else { - const index = list.indexOf(key); + const index = list.map(c => c.heading).indexOf(key); if (index > -1) { list.splice(index, 1); } @@ -588,26 +606,37 @@ export class SchemaTable extends React.Component { @action changeColumns = (oldKey: string, newKey: string, addNew: boolean) => { - let list = Cast(this.props.Document.schemaColumns, listSpec("string")); + let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); if (list === undefined) { - this.props.Document.schemaColumns = list = new List([newKey]); + this.props.Document.schemaColumns = list = new List([new SchemaHeaderField(newKey)]); } else { if (addNew) { - this.columns.push(newKey); + this.columns.push(new SchemaHeaderField(newKey)); } else { - const index = list.indexOf(oldKey); + const index = list.map(c => c.heading).indexOf(oldKey); if (index > -1) { - list[index] = newKey; + list[index] = new SchemaHeaderField(newKey); } } } } - getColumnType = (key: string): ColumnType => { - if (columnTypes.get(key)) return columnTypes.get(key)!; + getColumnType = (column: SchemaHeaderField): ColumnType => { + // added functionality to convert old column type stuff to new column type stuff -syip + if (column.type && column.type !== 0) { + return column.type; + } + if (columnTypes.get(column.heading)) { + column.type = columnTypes.get(column.heading)!; + return columnTypes.get(column.heading)!; + } const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc)); - if (!typesDoc) return ColumnType.Any; - return NumCast(typesDoc[key]); + if (!typesDoc) { + column.type = ColumnType.Any; + return ColumnType.Any; + } + column.type = NumCast(typesDoc[column.heading]); + return NumCast(typesDoc[column.heading]); } setColumnType = (key: string, type: ColumnType): void => { @@ -624,11 +653,11 @@ export class SchemaTable extends React.Component { } @action - setColumns = (columns: string[]) => { + setColumns = (columns: SchemaHeaderField[]) => { this.columns = columns; } - reorderColumns = (toMove: string, relativeTo: string, before: boolean, columnsValues: string[]) => { + reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => { let columns = [...columnsValues]; let oldIndex = columns.indexOf(toMove); let relIndex = columns.indexOf(relativeTo); @@ -661,7 +690,7 @@ export class SchemaTable extends React.Component { //TODO Types untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); - this.columns.forEach(key => keys[key] = true); + this.columns.forEach(key => keys[key.heading] = true); return Array.from(Object.keys(keys)); } @@ -727,7 +756,7 @@ export class SchemaTable extends React.Component { csv = csv.substr(0, csv.length - 1) + "\n"; let self = this; DocListCast(this.props.Document.data).map(doc => { - csv += self.columns.reduce((val, col) => val + (doc[col] ? doc[col]!.toString() : "0") + ",", ""); + csv += self.columns.reduce((val, col) => val + (doc[col.heading] ? doc[col.heading]!.toString() : "0") + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; }); csv.substring(0, csv.length - 1); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 7677f53c1..e897c5676 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -16,7 +16,7 @@ import { Transform } from "../../util/Transform"; import { CursorProperty } from "csstype"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { listSpec } from "../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; import { List } from "../../../new_fields/List"; import { EditableView } from "../EditableView"; import { CollectionViewProps } from "./CollectionBaseView"; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b765517a2..2e54a9736 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -19,6 +19,7 @@ import { CollectionViewType } from "../CollectionBaseView"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; import "./MarqueeView.scss"; import React = require("react"); +import { SchemaHeaderField, RandomPastel } from "../../../../new_fields/SchemaHeaderField"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -134,7 +135,7 @@ export class MarqueeView extends React.Component doc.width = 200; docList.push(doc); } - let newCol = Docs.Create.SchemaDocument([...(groupAttr ? ["_group"] : []), ...columns.filter(c => c)], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); this.props.addDocument(newCol, false); } diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index afde85b69..0ea948c81 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -11,6 +11,7 @@ import { faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTim import { library } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SetupDrag } from "../../util/DragManager"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; library.add(faArrowLeft, faEllipsisV, faTable, faTrash, faCog, faExchangeAlt, faTimes, faPlus); @@ -289,7 +290,7 @@ export class LinkGroupEditor extends React.Component { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]]; + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index ae97bed2f..3637807ad 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -14,6 +14,7 @@ import { Docs } from "../../documents/Documents"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { UndoManager } from "../../util/UndoManager"; import { StrCast } from "../../../new_fields/Types"; +import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; interface LinkMenuGroupProps { sourceDoc: Doc; @@ -70,7 +71,7 @@ export class LinkMenuGroup extends React.Component { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]]; + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts index 284de3023..a6df31e81 100644 --- a/src/new_fields/SchemaHeaderField.ts +++ b/src/new_fields/SchemaHeaderField.ts @@ -3,13 +3,42 @@ import { serializable, createSimpleSchema, primitive } from "serializr"; import { ObjectField } from "./ObjectField"; import { Copy, ToScriptString, OnUpdate } from "./FieldSymbols"; import { scriptingGlobal, Scripting } from "../client/util/Scripting"; +import { ColumnType } from "../client/views/collections/CollectionSchemaView"; export const PastelSchemaPalette = new Map([ - ["purple", "#f5b5fc"], - ["green", "#96F7D2"], - ["yellow", "#F0F696"], - ["red", "#FCB1B1"] -]) + ["pink1", "#FFB4E8"], + ["pink2", "#ff9cee"], + ["pink3", "#ffccf9"], + ["pink4", "#fcc2ff"], + ["pink5", "#f6a6ff"], + ["purple1", "#b28dff"], + ["purple2", "#c5a3ff"], + ["purple3", "#d5aaff"], + ["purple4", "#ecd4ff"], + ["purple5", "#fb34ff"], + ["purple6", "#dcd3ff"], + ["purple7", "#a79aff"], + ["purple8", "#b5b9ff"], + ["purple9", "#97a2ff"], + ["bluegreen1", "#afcbff"], + ["bluegreen2", "#aff8db"], + ["bluegreen3", "#c4faf8"], + ["bluegreen4", "#85e3ff"], + ["bluegreen5", "#ace7ff"], + ["bluegreen6", "#6eb5ff"], + ["bluegreen7", "#bffcc6"], + ["bluegreen8", "#dbffd6"], + ["yellow1", "#f3ffe3"], + ["yellow2", "#e7ffac"], + ["yellow3", "#ffffd1"], + ["yellow4", "#fff5ba"], + ["red1", "#ffc9de"], + ["red2", "#ffabab"], + ["red3", "#ffbebc"], + ["red4", "#ffcbc1"], +]); + +export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)]; @scriptingGlobal @Deserializable("schemaheader") @@ -17,12 +46,19 @@ export class SchemaHeaderField extends ObjectField { @serializable(primitive()) heading: string; color: string; + type: number; - constructor(heading: string = "", color: string = Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * 4)]) { + constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType) { super(); this.heading = heading; this.color = color; + if (type) { + this.type = type; + } + else { + this.type = 0; + } } setHeading(heading: string) { @@ -35,8 +71,13 @@ export class SchemaHeaderField extends ObjectField { this[OnUpdate](); } + setType(type: ColumnType) { + this.type = type; + this[OnUpdate](); + } + [Copy]() { - return new SchemaHeaderField(this.heading, this.color); + return new SchemaHeaderField(this.heading, this.color, this.type); } [ToScriptString]() { -- cgit v1.2.3-70-g09d2 From 92b848b79eaf90235fed550bc96b6ca982bd07df Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sat, 27 Jul 2019 21:23:12 -0400 Subject: asdfkjlsdafds --- src/client/documents/Documents.ts | 6 ++--- src/client/views/nodes/FormattedTextBox.tsx | 38 +++++++++++++++++++++++------ src/client/views/nodes/PDFBox.tsx | 18 ++++++++------ src/client/views/pdf/PDFViewer.tsx | 29 +++++++++++++++++++--- 4 files changed, 70 insertions(+), 21 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e6fe1b8b3..ee1b9fd0d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -582,12 +582,12 @@ export namespace Docs { export namespace DocUtils { export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", tags: string = "Default", sourceContext?: Doc) { - if (LinkManager.Instance.doesLinkExist(source, target)) return; + if (LinkManager.Instance.doesLinkExist(source, target)) return undefined; let sv = DocumentManager.Instance.getDocumentView(source); if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; - if (target === CurrentUserUtils.UserDocument) return; + if (target === CurrentUserUtils.UserDocument) return undefined; - let linkDoc; + let linkDoc: Doc | undefined; UndoManager.RunInBatch(() => { linkDoc = Docs.Create.TextDocument({ width: 100, height: 30, borderRounding: "100%" }); linkDoc.type = DocumentType.LINK; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e982cdfdd..364dc1c83 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -15,7 +15,7 @@ import { RichTextField } from "../../../new_fields/RichTextField"; import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types"; import { DocServer } from "../../DocServer"; -import { Docs } from '../../documents/Documents'; +import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from "../../util/DragManager"; import buildKeymap from "../../util/ProsemirrorExampleTransfer"; @@ -300,7 +300,34 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => { - return false; + let cbe = event as ClipboardEvent; + let docId: string; + if (!cbe.clipboardData) { + return false; + } + let linkId: string; + docId = cbe.clipboardData.getData("dash/pdfOrigin"); + if (!docId) { + return false; + } + + DocServer.GetRefField(docId).then(doc => { + if (!(doc instanceof Doc)) { + return; + } + let link = DocUtils.MakeLink(this.props.Document, doc); + if (link) { + cbe.clipboardData!.setData("dash/linkDoc", link[Id]); + linkId = link[Id]; + let frag = addMarkToFrag(slice.content); + slice = new Slice(frag, slice.openStart, slice.openEnd); + var tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); + } + }); + + return true; + function addMarkToFrag(frag: Fragment) { const nodes: Node[] = []; frag.forEach(node => nodes.push(addLinkMark(node))); @@ -313,7 +340,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); - const link = view.state.schema.mark(view.state.schema.marks.link, { href: "http://localhost:1050/doc/[link document id]", location: "onRight" }); + const link = view.state.schema.mark(view.state.schema.marks.link, { href: `http://localhost:1050/doc/${linkId}`, location: "onRight" }); if (linkIndex !== -1) { marks.splice(linkIndex, 1, link); } else { @@ -321,11 +348,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } return node.mark(marks); } - let frag = addMarkToFrag(slice.content); - slice = new Slice(frag, slice.openStart, slice.openEnd); - var tr = view.state.tr.replaceSelection(slice); - view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); - return true; } private setupEditor(config: any, doc: Doc, fieldKey: string) { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 282312dca..72d835fce 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -66,23 +66,28 @@ export class PDFBox extends DocComponent(PdfDocumen this._keyRef = React.createRef(); this._valueRef = React.createRef(); this._scriptRef = React.createRef(); - - document.addEventListener("keydown", this.onKeyDown); } componentDidMount() { if (this.props.setPdfBox) this.props.setPdfBox(this); + + document.removeEventListener("copy", this.copy); + document.addEventListener("copy", this.copy); } componentWillUnmount() { this._reactionDisposer && this._reactionDisposer(); + document.removeEventListener("copy", this.copy); } - onKeyDown = (e: KeyboardEvent) => { - if (e.ctrlKey && e.keyCode === KeyCodes.C) { + private copy = (e: ClipboardEvent) => { + if (this.props.active()) { let text = this.selectionText; - text += `${Utils.GenerateDeterministicGuid("pdf paste")}/${this.props.Document[Id]}`; - navigator.clipboard.writeText(text); + if (e.clipboardData) { + e.clipboardData.setData("text/plain", text); + e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); + e.preventDefault(); + } } } @@ -254,7 +259,6 @@ export class PDFBox extends DocComponent(PdfDocumen let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
    { if (this._mainCont.current) { this._dropDisposer = this._mainCont.current && DragManager.MakeDropTarget(this._mainCont.current, { handlers: { drop: this.drop.bind(this) } }); } + + document.removeEventListener("paste", this.paste); + document.addEventListener("paste", this.paste); } componentWillUnmount = () => { @@ -163,6 +167,25 @@ export class Viewer extends React.Component { this._annotationReactionDisposer && this._annotationReactionDisposer(); this._filterReactionDisposer && this._filterReactionDisposer(); this._dropDisposer && this._dropDisposer(); + document.removeEventListener("paste", this.paste); + } + + paste = (e: ClipboardEvent) => { + if (e.clipboardData) { + if (e.clipboardData.getData("dash/pdfOrigin") === this.props.parent.props.Document[Id]) { + let linkDocId = e.clipboardData.getData("dash/linkDoc"); + if (linkDocId) { + DocServer.GetRefField(linkDocId).then(async (link) => { + if (!(link instanceof Doc)) { + return; + } + let proto = Doc.GetProto(link); + let source = await Cast(proto.anchor1, Doc); + proto.anchor2 = this.makeAnnotationDocument(source, 0, "#0390fc", false); + }); + } + } + } } scrollTo(y: number) { @@ -213,7 +236,7 @@ export class Viewer extends React.Component { } @action - makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { + makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string, createLink: boolean = true): Doc => { let annoDocs: Doc[] = []; let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); @@ -242,7 +265,7 @@ export class Viewer extends React.Component { mainAnnoDoc.y = Math.max(minY, 0); mainAnnoDoc.annotations = new List(annoDocs); - if (sourceDoc) { + if (sourceDoc && createLink) { DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); } this._savedAnnotations.clear(); @@ -611,7 +634,7 @@ export class Viewer extends React.Component {
    {this._visibleElements}
    -
    +
    console.log("gello world")} style={{ transform: "scale(1.5)", transformOrigin: "top left" }} />
    Date: Sun, 28 Jul 2019 22:58:32 -0400 Subject: templating fixes, context menu undo streamlining --- src/.DS_Store | Bin 6148 -> 6148 bytes src/client/documents/Documents.ts | 5 ++--- src/client/views/ContextMenuItem.tsx | 4 ++-- src/client/views/collections/CollectionTreeView.tsx | 10 +++++----- src/client/views/collections/CollectionView.tsx | 14 +++++++------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 17 ++++++++++++----- src/scraping/buxton/scraper.py | 4 ++-- 8 files changed, 31 insertions(+), 25 deletions(-) (limited to 'src/client/documents') diff --git a/src/.DS_Store b/src/.DS_Store index 071dafa1e..c544bc837 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3859f2255..30a114e33 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -163,7 +163,6 @@ export namespace Docs { [DocumentType.LINKDOC, { data: new List(), layout: { view: EmptyBox }, - options: {} }], [DocumentType.BUTTON, { layout: { view: ButtonBox }, @@ -414,8 +413,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Tree }); } - export function StackingDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: CollectionViewType.Stacking }); + export function StackingDocument(documents: Array, options: DocumentOptions, viewType: CollectionViewType = CollectionViewType.Stacking) { + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(["title"]), ...options, viewType: viewType }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 4ff0fe388..a1787e78f 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -37,13 +37,13 @@ export class ContextMenuItem extends React.Component) => { + handleEvent = async (e: React.MouseEvent) => { if ("event" in this.props) { let batch: UndoManager.Batch | undefined; if (this.props.undoable !== false) { batch = UndoManager.StartBatch(`Context menu event: ${this.props.description}`); } - this.props.event(); + await this.props.event(); batch && batch.end(); this.props.closeMenu && this.props.closeMenu(); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1c7fe4bee..3eab109bc 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -237,10 +237,10 @@ class TreeView extends React.Component { 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, true)), icon: "camera" }); } - ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); + ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); } else { - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.dataDoc)), icon: "caret-square-right" }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); } ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); @@ -520,8 +520,8 @@ export class CollectionTreeView extends CollectionSubView(Document) { onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout if (!e.isPropagationStopped() && this.props.Document.workspaceLibrary) { // excludeFromLibrary means this is the user document - ContextMenu.Instance.addItem({ description: "Create Workspace", event: undoBatch(() => MainView.Instance.createNewWorkspace()), icon: "plus" }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.remove(this.props.Document)), icon: "minus" }); + ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index b4f29755c..e3f5d1eb8 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -53,14 +53,14 @@ export class CollectionView extends React.Component { 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 let subItems: ContextMenuProps[] = []; - subItems.push({ description: "Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Freeform), icon: "signature" }); + subItems.push({ description: "Freeform", event: () => this.props.Document.viewType = CollectionViewType.Freeform, icon: "signature" }); if (CollectionBaseView.InSafeMode()) { - ContextMenu.Instance.addItem({ description: "Test Freeform", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Invalid), icon: "project-diagram" }); + ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } - subItems.push({ description: "Schema", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Schema), icon: "th-list" }); - subItems.push({ description: "Treeview", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Tree), icon: "tree" }); - subItems.push({ description: "Stacking", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Stacking), icon: "ellipsis-v" }); - subItems.push({ description: "Masonry", event: undoBatch(() => this.props.Document.viewType = CollectionViewType.Masonry), icon: "columns" }); + subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" }); + subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" }); + subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" }); + subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" }); switch (this.props.Document.viewType) { case CollectionViewType.Freeform: { subItems.push({ description: "Custom", icon: "fingerprint", event: CollectionFreeFormView.AddCustomLayout(this.props.Document, this.props.fieldKey) }); @@ -68,7 +68,7 @@ export class CollectionView extends React.Component { } } ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" }); - ContextMenu.Instance.addItem({ description: "Apply Template", event: undoBatch(() => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight")), icon: "project-diagram" }); + ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d07fc7f80..a6152e1b7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -496,7 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, - event: undoBatch(async () => this.props.Document.fitToBox = !this.fitToBox), + event: async () => this.props.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 77824b4ff..b0fcc4202 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -20,6 +20,7 @@ import { RichTextField } from "../../../new_fields/RichTextField"; import { ImageField } from "../../../new_fields/URLField"; import { SelectionManager } from "../../util/SelectionManager"; import { listSpec } from "../../../new_fields/Schema"; +import { CollectionViewType } from "../collections/CollectionBaseView"; export type KVPScript = { script: CompiledScript; @@ -195,6 +196,9 @@ export class KeyValueBox extends React.Component { } let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey); + if (!fieldTemplate) { + return; + } let previousViewType = fieldTemplate.viewType; Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc)); previousViewType && (fieldTemplate.viewType = previousViewType); @@ -211,14 +215,17 @@ export class KeyValueBox extends React.Component { return Docs.Create.StackingDocument([], options); } let first = await Cast(data[0], Doc); - if (!first) { + if (!first || !first.data) { return Docs.Create.StackingDocument([], options); } - switch (first.type) { - case "image": - return Docs.Create.StackingDocument([], options); - case "text": + switch (first.data.constructor) { + case RichTextField: return Docs.Create.TreeDocument([], options); + case ImageField: + return Docs.Create.StackingDocument([], options, CollectionViewType.Masonry); + default: + console.log(`Template for ${first.data.constructor} not supported!`); + return undefined; } } else if (data instanceof ImageField) { return Docs.Create.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options); diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 182b22a1a..1ff0e3b31 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -139,7 +139,7 @@ def write_text_doc(content): data_doc = { "_id": data_doc_guid, "fields": { - "proto": protofy("commonImportProto"), + "proto": protofy("textProto"), "data": { "Data": '{"doc":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"' + content + '"}]}]},"selection":{"type":"text","anchor":1,"head":1}' + '}', "__type": "RichTextField" @@ -235,7 +235,7 @@ def parse_document(file_name: str): count += 1 view_guids.append(write_image(pure_name, image)) copyfile(dir_path + "/" + image, dir_path + - "/" + image.replace(".", "_o.", 1)) + "/" + image.replace(".", "_o.", 1)) os.rename(dir_path + "/" + image, dir_path + "/" + image.replace(".", "_m.", 1)) print(f"extracted {count} images...") -- cgit v1.2.3-70-g09d2 From f1cb6a2212b11ba6d18dfa2e800b2c8e4ad94a88 Mon Sep 17 00:00:00 2001 From: fawn Date: Mon, 29 Jul 2019 14:28:25 -0400 Subject: made hit box on col resizer smaller and hit box on coll expander bigger --- src/client/documents/Documents.ts | 8 ++--- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionSchemaCells.tsx | 2 +- .../views/collections/CollectionSchemaView.scss | 39 +++++++++++++++------- .../views/collections/CollectionSchemaView.tsx | 6 ++-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/LinkEditor.tsx | 2 +- src/client/views/nodes/LinkMenuGroup.tsx | 2 +- src/new_fields/SchemaHeaderField.ts | 4 +-- 9 files changed, 41 insertions(+), 26 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ee1b9fd0d..01e3ced5d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -404,7 +404,7 @@ export namespace Docs { } export function FreeformDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Freeform }); } export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { @@ -412,15 +412,15 @@ export namespace Docs { } export function TreeDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Tree }); } export function StackingDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Stacking }); } export function MasonryDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Masonry }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f5a6715e5..d4c0711a2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -375,7 +375,7 @@ export class MainView extends React.Component { let imgurl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"; // let addDockingNode = action(() => Docs.Create.StandardCollectionDockingDocument([{ doc: addColNode(), initialWidth: 200 }], { width: 200, height: 200, title: "a nested docking freeform collection" })); - let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title")], [], { width: 200, height: 200, title: "a schema collection" })); + let addSchemaNode = action(() => Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], [], { width: 200, height: 200, title: "a schema collection" })); //let addTreeNode = action(() => Docs.TreeDocument([CurrentUserUtils.UserDocument], { width: 250, height: 400, title: "Library:" + CurrentUserUtils.email, dropAction: "alias" })); // let addTreeNode = action(() => Docs.TreeDocument(this._northstarSchemas, { width: 250, height: 400, title: "northstar schemas", dropAction: "copy" })); let addColNode = action(() => Docs.Create.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" })); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 194765880..e06a5c66b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -108,7 +108,7 @@ export class CollectionSchemaCell extends React.Component { this._document[fieldKey] = de.data.draggedDocuments[0]; } else { - let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title")], de.data.draggedDocuments, {}); + let coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "f1efeb")], de.data.draggedDocuments, {}); this._document[fieldKey] = coll; } e.stopPropagation(); diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 053d6452c..749b9a364 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -97,15 +97,6 @@ // margin-right: -30px; } - .rt-resizable-header { - padding: 0; - height: 30px; - - &:last-child { - overflow: visible; - } - } - .rt-resizable-header-content { height: 100%; overflow: visible; @@ -198,8 +189,22 @@ } .rt-resizer { - width: 20px; - right: -10px; + width: 8px; + right: -4px; + } + + .rt-resizable-header { + padding: 0; + height: 30px; + } + + .rt-resizable-header:last-child { + overflow: visible; + border: 3px solid red !important; + + .rt-resizer { + width: 5px !important; + } } } @@ -318,7 +323,7 @@ button.add-column { input[type="radio"] { display: none; } - + .columnMenu-colorPicker { width: 20px; height: 20px; @@ -497,4 +502,14 @@ button.add-column { .collectionSchemaView-expander { height: 100%; + min-height: 30px; + position: relative; + color: gray; + + svg { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 53dd9523b..ece638ec7 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -635,15 +635,15 @@ export class SchemaTable extends React.Component { let list = Cast(this.props.Document.schemaColumns, listSpec(SchemaHeaderField)); if (list === undefined) { console.log("change columns new"); - this.props.Document.schemaColumns = list = new List([new SchemaHeaderField(newKey, "f1efeb")]); + this.props.Document.schemaColumns = list = new List([new SchemaHeaderField(newKey, "#f1efeb")]); } else { console.log("change column"); if (addNew) { - this.columns.push(new SchemaHeaderField(newKey, "f1efeb")); + this.columns.push(new SchemaHeaderField(newKey, "#f1efeb")); } else { const index = list.map(c => c.heading).indexOf(oldKey); if (index > -1) { - list[index] = new SchemaHeaderField(newKey, "f1efeb"); + list[index] = new SchemaHeaderField(newKey, "#f1efeb"); } } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1c767e012..7decadbe9 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -135,7 +135,7 @@ export class MarqueeView extends React.Component doc.width = 200; docList.push(doc); } - let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + let newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); this.props.addDocument(newCol, false); } diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 0ea948c81..ecb3e9db4 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -290,7 +290,7 @@ export class LinkGroupEditor extends React.Component { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); diff --git a/src/client/views/nodes/LinkMenuGroup.tsx b/src/client/views/nodes/LinkMenuGroup.tsx index 0cb216aa6..e04044266 100644 --- a/src/client/views/nodes/LinkMenuGroup.tsx +++ b/src/client/views/nodes/LinkMenuGroup.tsx @@ -72,7 +72,7 @@ export class LinkMenuGroup extends React.Component { let keys = LinkManager.Instance.getMetadataKeysInGroup(groupType); let index = keys.indexOf(""); if (index > -1) keys.splice(index, 1); - let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c)); + let cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); let docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); let createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); let ref = React.createRef(); diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts index d124a3907..475296d5c 100644 --- a/src/new_fields/SchemaHeaderField.ts +++ b/src/new_fields/SchemaHeaderField.ts @@ -48,12 +48,12 @@ export class SchemaHeaderField extends ObjectField { color: string; type: number; - constructor(heading: string = "", color?: string, type?: ColumnType) { + constructor(heading: string = "", color: string, type?: ColumnType) { console.log("CREATING SCHEMA HEADER FIELD"); super(); this.heading = heading; - this.color = color === "" || color === undefined ? RandomPastel() : color; + this.color = color === undefined ? "#000" : color; if (type) { this.type = type; } -- cgit v1.2.3-70-g09d2 From d87020047c1953b212257236a25036df3903dd47 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Mon, 29 Jul 2019 16:20:20 -0400 Subject: chrome default to collapsed --- src/client/documents/Documents.ts | 11 ++++++----- src/client/views/collections/CollectionView.tsx | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 18709499a..d1b3071ed 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -82,6 +82,7 @@ export interface DocumentOptions { backgroundColor?: string; dropAction?: dropActionType; backgroundLayout?: string; + chromeStatus?: string; curPage?: number; documentText?: string; borderRounding?: string; @@ -403,23 +404,23 @@ export namespace Docs { } export function FreeformDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Freeform }); } export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); } export function TreeDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Tree }); } export function StackingDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Stacking }); } export function MasonryDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title")]), ...options, viewType: CollectionViewType.Masonry }); } export function ButtonDocument(options?: DocumentOptions) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 55e014c66..b7ac8768f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -35,7 +35,7 @@ library.add(faImage, faEye); @observer export class CollectionView extends React.Component { - @observable private _collapsed = false; + @observable private _collapsed = true; private _reactionDisposer: IReactionDisposer | undefined; -- cgit v1.2.3-70-g09d2 From 46843c105c5fb3dfed3bab9283ae4b2917eade4e Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 1 Aug 2019 15:20:40 -0400 Subject: Fixed Doc.MakeCopy promise stuff --- src/client/documents/Documents.ts | 4 ++++ src/new_fields/Doc.ts | 12 +++++++----- src/new_fields/Proxy.ts | 29 +++++++++++++++++++++++++++++ src/new_fields/ScriptField.ts | 12 +++++++----- src/new_fields/util.ts | 5 +---- 5 files changed, 48 insertions(+), 14 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2a1f63d59..63b4d4e32 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -41,6 +41,8 @@ import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; import { Scripting } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; +import { ComputedField } from "../../new_fields/ScriptField"; +import { ProxyField } from "../../new_fields/Proxy"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -193,6 +195,8 @@ export namespace Docs { * haven't been initialized, the newly initialized prototype document. */ export async function initialize(): Promise { + ProxyField.initPlugin(); + ComputedField.initPlugin(); // non-guid string ids for each document prototype let prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); // fetch the actual prototype documents from the server diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 84b8589dd..6e2c5e697 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -12,7 +12,7 @@ import { scriptingGlobal } from "../client/util/Scripting"; import { List } from "./List"; import { DocumentType } from "../client/documents/Documents"; import { ComputedField } from "./ScriptField"; -import { PrefetchProxy } from "./Proxy"; +import { PrefetchProxy, ProxyField } from "./Proxy"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -420,7 +420,7 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { - const field = doc[key]; + const field = ProxyField.WithoutProxy(() => doc[key]); if (key === "proto" && copyProto) { if (field instanceof Doc) { copy[key] = Doc.MakeCopy(field); @@ -431,7 +431,7 @@ export namespace Doc { } else if (field instanceof ObjectField) { copy[key] = ObjectField.MakeCopy(field); } else if (field instanceof Promise) { - field.then(f => (copy[key] === undefined) && (copy[key] = f)); //TODO what should we do here? + debugger; //This shouldn't happend... } else { copy[key] = field; } @@ -525,8 +525,10 @@ export namespace Doc { } export function UseDetailLayout(d: Doc) { runInAction(async () => { - let detailLayout1 = await PromiseValue(d.detailedLayout); - let detailLayout = await PromiseValue(d.detailedLayout); + const dl1 = d.detailedLayout; + let detailLayout1 = await PromiseValue(dl1); + const dl2 = d.detailedLayout; + let detailLayout = await PromiseValue(dl2); if (detailLayout) { d.layout = detailLayout; d.nativeWidth = d.nativeHeight = undefined; diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index b3e8d6467..c6292e37c 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -7,6 +7,7 @@ import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { Id, Copy, ToScriptString } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; +import { Plugins } from "./util"; @Deserializable("proxy") export class ProxyField extends ObjectField { @@ -68,6 +69,34 @@ export class ProxyField extends ObjectField { } } +export namespace ProxyField { + let useProxy = true; + export function DisableProxyFields() { + useProxy = false; + } + + export function EnableProxyFields() { + useProxy = true; + } + + export function WithoutProxy(fn: () => T) { + DisableProxyFields(); + try { + return fn(); + } finally { + EnableProxyFields(); + } + } + + export function initPlugin() { + Plugins.addGetterPlugin((doc, _, value) => { + if (useProxy && value instanceof ProxyField) { + return { value: value.value() }; + } + }); + } +} + function prefetchValue(proxy: PrefetchProxy) { return proxy.value() as any; } diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index 6d52525b8..83fb52d07 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -137,9 +137,11 @@ export namespace ComputedField { } } - Plugins.addGetterPlugin((doc, _, value) => { - if (useComputed && value instanceof ComputedField) { - return { value: value.value(doc), shouldReturn: true }; - } - }); + export function initPlugin() { + Plugins.addGetterPlugin((doc, _, value) => { + if (useComputed && value instanceof ComputedField) { + return { value: value.value(doc), shouldReturn: true }; + } + }); + } } \ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 2ebfb9e71..c6f693f7f 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -14,7 +14,7 @@ function _readOnlySetter(): never { export interface GetterResult { value: FieldResult; - shouldReturn: boolean; + shouldReturn?: boolean; } export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined; const getterPlugins: GetterPlugin[] = []; @@ -103,9 +103,6 @@ export function getter(target: any, prop: string | symbol | number, receiver: an function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any { receiver = receiver || target[SelfProxy]; let field = target.__fields[prop]; - if (field instanceof ProxyField) { - return field.value(); - } for (const plugin of getterPlugins) { const res = plugin(receiver, prop, field); if (res === undefined) continue; -- cgit v1.2.3-70-g09d2 From e0fb04a7748666a41a7cd500dcdd23027ef12b6f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 2 Aug 2019 12:56:37 -0400 Subject: links --- src/client/documents/Documents.ts | 7 +++++- src/client/util/LinkManager.ts | 13 ++++++++-- .../views/collections/CollectionTreeView.tsx | 28 +++++++++++++++------- src/client/views/nodes/KeyValuePair.tsx | 17 ++++++------- 4 files changed, 43 insertions(+), 22 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 63b4d4e32..818383765 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -38,7 +38,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; -import { Scripting } from "../util/Scripting"; +import { Scripting, CompileScript } from "../util/Scripting"; import { ButtonBox } from "../views/nodes/ButtonBox"; import { SchemaHeaderField, RandomPastel } from "../../new_fields/SchemaHeaderField"; import { ComputedField } from "../../new_fields/ScriptField"; @@ -623,6 +623,11 @@ export namespace DocUtils { LinkManager.Instance.addLink(linkDoc); + let script = `return links(self)};`; + let computed = CompileScript(script, { params: { this: "Doc" }, capturedVariables: { self: source }, typecheck: false }); + computed.compiled && (Doc.GetProto(source).links = new ComputedField(computed)); + computed.compiled && (Doc.GetProto(target).links = new ComputedField(computed)); + }, "make link"); return linkDoc; } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index a647f22c1..448a8e9cf 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -6,6 +6,7 @@ import { List } from "../../new_fields/List"; import { Id } from "../../new_fields/FieldSymbols"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { Docs } from "../documents/Documents"; +import { Scripting } from "./Scripting"; /* @@ -42,7 +43,12 @@ export class LinkManager { } public getAllLinks(): Doc[] { - return LinkManager.Instance.LinkManagerDoc ? LinkManager.Instance.LinkManagerDoc.allLinks ? DocListCast(LinkManager.Instance.LinkManagerDoc.allLinks) : [] : []; + let ldoc = LinkManager.Instance.LinkManagerDoc; + if (ldoc) { + let docs = DocListCast(ldoc.allLinks); + return docs; + } + return []; } public addLink(linkDoc: Doc): boolean { @@ -242,4 +248,7 @@ export class LinkManager { return Cast(linkDoc.anchor1, Doc, null); } } -} \ No newline at end of file +} +Scripting.addGlobal(function links(doc: any) { + return new List(LinkManager.Instance.getAllRelatedLinks(doc)); +}); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7f5d78313..02b2583cd 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -27,6 +27,7 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { ComputedField } from '../../../new_fields/ScriptField'; import { KeyValueBox } from '../nodes/KeyValueBox'; +import { exportNamedDeclaration } from 'babel-types'; export interface TreeViewProps { @@ -79,11 +80,17 @@ class TreeView extends React.Component { return splits.length > 1 ? splits[1].split("\"")[0] : "data"; } @computed get childDocs() { - let layout = this.props.document.layout as Doc; + let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined; return (this.props.dataDoc ? Cast(this.props.dataDoc[this.fieldKey], listSpec(Doc)) : undefined) || (layout ? Cast(layout[this.fieldKey], listSpec(Doc)) : undefined) || Cast(this.props.document[this.fieldKey], listSpec(Doc)); } + @computed get childLinks() { + let layout = this.props.document.layout instanceof Doc ? this.props.document.layout : undefined; + return (this.props.dataDoc ? Cast(this.props.dataDoc.links, listSpec(Doc)) : undefined) || + (layout instanceof Doc ? Cast(layout.links, listSpec(Doc)) : undefined) || + Cast(this.props.document.links, listSpec(Doc)); + } @computed get resolvedDataDoc() { if (this.props.dataDoc === undefined && this.props.document.layout instanceof Doc) { // if there is no dataDoc (ie, we're not rendering a template layout), but this document @@ -276,13 +283,15 @@ class TreeView extends React.Component { noOverlays = (doc: Doc) => ({ title: "", caption: "" }); @computed get renderContent() { - if (this.treeViewExpandedView === this.fieldKey) { - let remDoc = (doc: Doc) => this.remove(doc, this.fieldKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc, addBefore, before); - return
      - {!this.childDocs ? (null) : - TreeView.GetChildElements(this.childDocs as Doc[], this.props.treeViewId, this.props.document.layout as Doc, - this.resolvedDataDoc, this.fieldKey, addDoc, remDoc, this.move, + const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined; + if (expandKey !== undefined) { + let remDoc = (doc: Doc) => this.remove(doc, expandKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.dataDoc, expandKey, doc, addBefore, before); + let docs = expandKey === "links" ? this.childLinks : this.childDocs; + return
        + {!docs ? (null) : + TreeView.GetChildElements(docs as Doc[], this.props.treeViewId, this.props.document.layout as Doc, + this.resolvedDataDoc, expandKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth)}
      ; @@ -334,7 +343,8 @@ class TreeView extends React.Component { onPointerDown={action(() => { this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? "fields" : this.treeViewExpandedView === "fields" && this.props.document.layout ? "layout" : - this.childDocs ? this.fieldKey : "fields"; + this.treeViewExpandedView === "layout" && this.props.document.links ? "links" : + this.childDocs ? this.fieldKey : "fields"; this._collapsed = false; })}> {this.treeViewExpandedView} diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 3775f0f47..534a42efc 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -1,22 +1,19 @@ import { action, observable } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app -import { emptyFunction, returnFalse, returnZero, returnTrue, returnOne } from '../../../Utils'; -import { CompileScript, CompiledScript, ScriptOptions } from "../../util/Scripting"; +import { Doc, Field } from '../../../new_fields/Doc'; +import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; import { Transform } from '../../util/Transform'; +import { undoBatch } from '../../util/UndoManager'; +import { CollectionDockingView } from '../collections/CollectionDockingView'; +import { ContextMenu } from '../ContextMenu'; import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from './FieldView'; +import { KeyValueBox } from './KeyValueBox'; import "./KeyValueBox.scss"; import "./KeyValuePair.scss"; import React = require("react"); -import { Doc, Opt, Field } from '../../../new_fields/Doc'; -import { FieldValue } from '../../../new_fields/Types'; -import { KeyValueBox } from './KeyValueBox'; -import { DragManager, SetupDrag } from '../../util/DragManager'; -import { ContextMenu } from '../ContextMenu'; -import { Docs } from '../../documents/Documents'; -import { CollectionDockingView } from '../collections/CollectionDockingView'; -import { undoBatch } from '../../util/UndoManager'; // Represents one row in a key value plane -- cgit v1.2.3-70-g09d2 From e7e0c506121c3074447df2fd6556fff7b2990d2a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 2 Aug 2019 14:44:29 -0400 Subject: fixed loading of link compute field --- src/client/documents/Documents.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 818383765..17e474e0e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -623,11 +623,10 @@ export namespace DocUtils { LinkManager.Instance.addLink(linkDoc); - let script = `return links(self)};`; - let computed = CompileScript(script, { params: { this: "Doc" }, capturedVariables: { self: source }, typecheck: false }); + let script = `return links(this)};`; + let computed = CompileScript(script, { params: { this: "Doc" }, typecheck: false }); computed.compiled && (Doc.GetProto(source).links = new ComputedField(computed)); computed.compiled && (Doc.GetProto(target).links = new ComputedField(computed)); - }, "make link"); return linkDoc; } -- cgit v1.2.3-70-g09d2 From df93a00ed3047abeee90f3ba4858cf7339ce046b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 2 Aug 2019 17:14:42 -0400 Subject: removed proto from links --- src/client/documents/Documents.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2a1f63d59..5d1b6b382 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -599,9 +599,8 @@ export namespace DocUtils { let linkDoc: Doc | undefined; UndoManager.RunInBatch(() => { - linkDoc = Docs.Create.TextDocument({ width: 100, height: 30, borderRounding: "100%" }); - linkDoc.type = DocumentType.LINK; - let linkDocProto = Doc.GetProto(linkDoc); + let linkDocProto = new Doc(); + linkDocProto.type = DocumentType.LINK; linkDocProto.targetContext = targetContext; linkDocProto.sourceContext = sourceContext; @@ -617,7 +616,7 @@ export namespace DocUtils { linkDocProto.anchor2Page = target.curPage; linkDocProto.anchor2Groups = new List([]); - LinkManager.Instance.addLink(linkDoc); + LinkManager.Instance.addLink(linkDocProto); }, "make link"); return linkDoc; -- cgit v1.2.3-70-g09d2 From 753615c80d4cf08605ebaaeeaf0a44a0fd88d898 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 3 Aug 2019 15:21:10 -0400 Subject: working version of clustering --- src/Utils.ts | 2 + src/client/documents/Documents.ts | 3 +- src/client/util/DragManager.ts | 8 +- src/client/views/MainView.tsx | 4 +- src/client/views/TemplateMenu.tsx | 5 +- .../views/collections/CollectionDockingView.tsx | 3 +- .../views/collections/CollectionSchemaView.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 142 +++++++++++++-------- .../collections/collectionFreeForm/MarqueeView.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 +- src/client/views/nodes/DocumentView.tsx | 17 +-- .../views/presentationview/PresentationElement.tsx | 3 +- src/client/views/search/SearchItem.tsx | 3 +- 13 files changed, 125 insertions(+), 81 deletions(-) (limited to 'src/client/documents') diff --git a/src/Utils.ts b/src/Utils.ts index 8df67df5d..502540eb0 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -140,6 +140,8 @@ export function returnOne() { return 1; } export function returnZero() { return 0; } +export function returnEmptyString() { return ""; } + export function emptyFunction() { } export type Without = Pick>; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 09bafcf43..07e38a4c0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -84,6 +84,7 @@ export interface DocumentOptions { templates?: List; viewType?: number; backgroundColor?: string; + defaultBackgroundColor?: string; dropAction?: dropActionType; backgroundLayout?: string; chromeStatus?: string; @@ -124,7 +125,7 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.TEXT, { layout: { view: FormattedTextBox }, - options: { height: 150, backgroundColor: "#f1efeb" } + options: { height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" } }], [DocumentType.HIST, { layout: { view: HistogramBox, collectionView: [CollectionView, data] as CollectionViewType }, diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0b5c785a4..a7aaaed7c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -404,7 +404,8 @@ export namespace DragManager { hideSource = options.hideSource(); } } - eles.map(ele => (ele.hidden = hideSource)); + eles.map(ele => (ele.hidden = hideSource) && + (ele.parentElement && ele.parentElement.className.indexOf("collectionFreeFormDocumentView") !== -1 && (ele.parentElement.hidden = hideSource))); let lastX = downX; let lastY = downY; @@ -434,7 +435,10 @@ export namespace DragManager { let hideDragElements = () => { dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); - eles.map(ele => (ele.hidden = false)); + eles.map(ele => { + ele.hidden = false; + (ele.parentElement && ele.parentElement.className.indexOf("collectionFreeFormDocumentView") !== -1 && (ele.parentElement.hidden = false)); + }); }; let endDrag = () => { document.removeEventListener("pointermove", moveHandler, true); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 2ecf5fd85..53f589684 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -15,7 +15,7 @@ import { listSpec } from '../../new_fields/Schema'; import { Cast, FieldValue, NumCast, BoolCast, StrCast } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnOne, returnTrue, Utils } from '../../Utils'; +import { emptyFunction, returnOne, returnTrue, Utils, returnEmptyString } from '../../Utils'; import { DocServer } from '../DocServer'; import { Docs } from '../documents/Documents'; import { SetupDrag } from '../util/DragManager'; @@ -270,6 +270,7 @@ export class MainView extends React.Component { PanelWidth={this.getPWidth} PanelHeight={this.getPHeight} renderDepth={0} + backgroundColor={returnEmptyString} selectOnLoad={false} focus={emptyFunction} parentActive={returnTrue} @@ -334,6 +335,7 @@ export class MainView extends React.Component { renderDepth={0} selectOnLoad={false} focus={emptyFunction} + backgroundColor={returnEmptyString} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index e654a0644..6dd908445 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -60,12 +60,9 @@ export class TemplateMenu extends React.Component { let de = new DragManager.DocumentDragData([topDoc], [undefined]); de.moveDocument = topDocView.props.moveDocument; let xf = newDocView.ContentDiv!.getBoundingClientRect(); - console.log("ex = " + ex + " " + xf.left + " " + (ex - xf.left)); DragManager.StartDocumentDrag([newDocView.ContentDiv!], de, ex, ey, { offsetX: (ex - xf.left), offsetY: (ey - xf.top), - handlers: { - dragComplete: () => { }, - }, + handlers: { dragComplete: () => { }, }, hideSource: false }); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 588102f01..f559480ed 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -10,7 +10,7 @@ import { Id } from '../../../new_fields/FieldSymbols'; import { FieldId } from "../../../new_fields/RefField"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { emptyFunction, returnTrue, Utils, returnOne } from "../../../Utils"; +import { emptyFunction, returnTrue, Utils, returnOne, returnEmptyString } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { DocumentManager } from '../../util/DocumentManager'; import { DragLinksAsDocuments, DragManager } from "../../util/DragManager"; @@ -607,6 +607,7 @@ export class DockedFrameRenderer extends React.Component { parentActive={returnTrue} whenActiveChanged={emptyFunction} focus={emptyFunction} + backgroundColor={returnEmptyString} addDocTab={this.addDocTab} ContainingCollectionView={undefined} zoomToScale={emptyFunction} diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 9efd0d3ec..8218877ba 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -6,7 +6,7 @@ import { action, computed, observable, trace, untracked } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, Column, RowInfo, ResizedChangeFunction, Resize } from "react-table"; import "react-table/react-table.css"; -import { emptyFunction, returnOne } from "../../../Utils"; +import { emptyFunction, returnOne, returnEmptyString } from "../../../Utils"; import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; @@ -999,6 +999,7 @@ export class CollectionSchemaPreview extends React.Component r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); } - _groupingBorder = 100; + _clusterDistance = 75; bounsdSelect(doc: Doc, doc2: Doc) { - var x2 = NumCast(doc2.x) - this._groupingBorder; - var y2 = NumCast(doc2.y) - this._groupingBorder; - var w2 = NumCast(doc2.width) + this._groupingBorder; - var h2 = NumCast(doc2.height) + this._groupingBorder; - var x = NumCast(doc.x) - this._groupingBorder; - var y = NumCast(doc.y) - this._groupingBorder; - var w = NumCast(doc.width) + this._groupingBorder; - var h = NumCast(doc.height) + this._groupingBorder; - if (this.intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 })) { + var x2 = NumCast(doc2.x) - this._clusterDistance; + var y2 = NumCast(doc2.y) - this._clusterDistance; + var w2 = NumCast(doc2.width) + this._clusterDistance; + var h2 = NumCast(doc2.height) + this._clusterDistance; + var x = NumCast(doc.x) - this._clusterDistance; + var y = NumCast(doc.y) - this._clusterDistance; + var w = NumCast(doc.width) + this._clusterDistance; + var h = NumCast(doc.height) + this._clusterDistance; + if (doc.z === doc2.z && this.intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 })) { return true; } return false; @@ -197,36 +199,83 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return false; } + tryDragCluster(e: PointerEvent) { + let probe = this.getTransform().transformPoint(e.clientX, e.clientY); + let cluster = this.childDocs.reduce((cluster, cd) => { + let cx = NumCast(cd.x) - this._clusterDistance; + let cy = NumCast(cd.y) - this._clusterDistance; + let cw = NumCast(cd.width) + 2 * this._clusterDistance; + let ch = NumCast(cd.height) + 2 * this._clusterDistance; + if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) + return NumCast(cd.cluster); + return cluster; + }, -1); + if (cluster !== -1) { + let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster); + this.selectDocuments(eles); + let clusterDocs = SelectionManager.SelectedDocuments(); + SelectionManager.DeselectAll(); + let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined)); + de.moveDocument = this.props.moveDocument; + const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0); + const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top); + de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined; + de.xOffset = xoff; + de.yOffset = yoff; + DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, { + handlers: { dragComplete: action(emptyFunction) }, + hideSource: !de.dropAction + }); + return true; + } + + return false; + } + @observable sets: (Doc[])[] = []; @action updateClusters() { - let sets: (Doc[])[] = [] + this.sets.length = 0; this.childDocs.map(c => { let included = [] - for (let i = 0; i < sets.length; i++) { - for (let j = 0; j < sets[i].length; j++) { - if (this.bounsdSelect(c, sets[i][j])) { + for (let i = 0; i < this.sets.length; i++) { + for (let j = 0; j < this.sets[i].length; j++) { + if (this.bounsdSelect(c, this.sets[i][j])) { included.push(i); break; } } } if (included.length === 0) - sets.push([c]); + this.sets.push([c]); else if (included.length === 1) - sets[included[0]].push(c); + this.sets[included[0]].push(c); else { - sets[included[0]].push(c); + this.sets[included[0]].push(c); for (let s = 1; s < included.length; s++) { - sets[included[0]].push(...sets[included[s]]); - sets[included[s]].length = 0; + this.sets[included[0]].push(...this.sets[included[s]]); + this.sets[included[s]].length = 0; } } }); - for (let s = 0; s < sets.length; s++) { - for (let i = 0; i < sets[s].length; i++) { - Doc.GetProto(sets[s][i]).cluster = s; + for (let s = 0; s < this.sets.length; s++) { + for (let i = 0; i < this.sets[s].length; i++) { + this.sets[s][i].cluster = s; + } + } + } + + getClusterColor = (doc: Doc) => { + if (this.props.Document.useClusters) { + let cluster = NumCast(doc.cluster); + let set = this.sets.length > cluster ? this.sets[NumCast(doc.cluster)] : undefined; + let colors = ["#da42429e", "#31ea318c", "#8c4000", "#4a7ae2c4", "#d809ff", "#ff7601", "#1dffff", "yellow", "#1b8231f2", "#000000ad"]; + let clusterColor = colors[cluster % colors.length]; + for (let i = 0; set && i < set.length; i++) { + if (set[i].backgroundColor && set[i].backgroundColor !== set[i].defaultBackgroundColor) clusterColor = StrCast(set[i].backgroundColor); } + return clusterColor; } + return ""; } @action @@ -249,34 +298,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { if (!e.cancelBubble) { - let probe = this.getTransform().transformPoint(e.clientX, e.clientY); - let cluster = this.childDocs.reduce((cluster, cd) => { - let cx = NumCast(cd.x) - this._groupingBorder; - let cy = NumCast(cd.y) - this._groupingBorder; - let cw = NumCast(cd.width) + 2 * this._groupingBorder; - let ch = NumCast(cd.height) + 2 * this._groupingBorder; - if (this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) - return NumCast(cd.cluster); - return cluster; - }, -1); - if (cluster !== -1) { - let eles = this.childDocs.filter(cd => NumCast(cd.cluster) === cluster); - this.selectDocuments(eles); - let clusterDocs = SelectionManager.SelectedDocuments(); - SelectionManager.DeselectAll(); - let de = new DragManager.DocumentDragData(eles, eles.map(d => undefined)); - de.moveDocument = this.props.moveDocument; - const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0); - const [xoff, yoff] = this.getTransform().transformDirection(e.x - left, e.y - top); - de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined; - de.xOffset = xoff; - de.yOffset = yoff; - DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, e.clientX, e.clientY, { - handlers: { - dragComplete: action(emptyFunction) - }, - hideSource: !de.dropAction - }); + if (this.props.Document.useClusters && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); document.removeEventListener("pointermove", this.onPointerMove); @@ -493,6 +515,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, focus: this.focusDocument, + backgroundColor: this.getClusterColor, parentActive: this.props.active, whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, @@ -516,6 +539,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, focus: this.focusDocument, + backgroundColor: returnEmptyString, parentActive: this.props.active, whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, @@ -625,6 +649,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { event: async () => this.props.Document.fitToBox = !this.fitToBox, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); + layoutItems.push({ + description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`, + event: async () => { + Docs.Prototypes.get(DocumentType.TEXT).defaultBackgroundColor = "#f1efeb"; + Docs.Prototypes.get(DocumentType.COL).defaultBackgroundColor = "white"; + this.props.Document.useClusters = !this.props.Document.useClusters; + }, + icon: !this.props.Document.useClusters ? "expand-arrows-alt" : "compress-arrows-alt" + }); layoutItems.push({ description: "Arrange contents in grid", icon: "table", @@ -700,10 +733,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ...this.views ] private overlayChildViews = () => { - console.log(this.overlayViews.length); - return [ - ...this.overlayViews - ]; + return [...this.overlayViews]; } public static AddCustomLayout(doc: Doc, dataKey: string): () => void { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b9ee588dd..ff96bd993 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -278,6 +278,7 @@ export class MarqueeView extends React.Component panX: 0, panY: 0, backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", + defaultBackgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", width: bounds.width, height: bounds.height, title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection", diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3b6c443c2..ee596c841 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -8,6 +8,7 @@ import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView" import "./DocumentView.scss"; import React = require("react"); import { Doc } from "../../../new_fields/Doc"; +import { returnEmptyString } from "../../../Utils"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { x?: number; @@ -69,6 +70,11 @@ export class CollectionFreeFormDocumentView extends DocComponent this.clusterColor; + render() { const hasPosition = this.props.x !== undefined || this.props.y !== undefined; return ( @@ -77,7 +83,10 @@ export class CollectionFreeFormDocumentView extends DocComponent void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; zoomToScale: (scale: number) => void; + backgroundColor: (doc: Doc) => string; getScale: () => number; animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; ChromeHeight?: () => number; @@ -675,12 +676,9 @@ export class DocumentView extends DocComponent(Docu // to determine the render JSX string, otherwise the layout field should directly contain a JSX layout string. return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; } + render() { - if (this.Document.hidden) { - return null; - } - let self = this; - let backgroundColor = StrCast(this.layoutDoc.backgroundColor); + let backgroundColor = this.props.backgroundColor(this.props.Document) || StrCast(this.layoutDoc.backgroundColor); let foregroundColor = StrCast(this.layoutDoc.color); var nativeWidth = this.nativeWidth > 0 && !BoolCast(this.props.Document.ignoreAspect) ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; @@ -695,8 +693,6 @@ export class DocumentView extends DocComponent(Docu }); } let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("(Docu color: foregroundColor, outlineColor: "maroon", outlineStyle: "dashed", - boxShadow: this.layoutDoc.isBackground ? - `0px 0px 50px 50px ${groupCol}` : - `${groupCol} ${StrCast(this.props.Document.boxShadow, `0vw 0vw ${50 / this.props.ContentScaling()}px`)}`, outlineWidth: BoolCast(this.layoutDoc.libraryBrush) && !StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", marginLeft: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? @@ -717,7 +710,7 @@ export class DocumentView extends DocComponent(Docu border: BoolCast(this.layoutDoc.libraryBrush) && StrCast(Doc.GetProto(this.props.Document).borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", - background: this.layoutDoc.isBackground ? groupCol : backgroundColor, + background: backgroundColor, width: nativeWidth, height: nativeHeight, transform: `scale(${this.props.ContentScaling()})`, diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index 11f3eb846..e2d8daea9 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils, returnFalse, emptyFunction, returnOne } from "../../../Utils"; +import { Utils, returnFalse, emptyFunction, returnOne, returnEmptyString } from "../../../Utils"; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; import { ContextMenu } from "../ContextMenu"; @@ -843,6 +843,7 @@ export default class PresentationElement extends React.Component 350} PanelHeight={() => 90} focus={emptyFunction} + backgroundColor={returnEmptyString} selectOnLoad={false} parentActive={returnFalse} whenActiveChanged={returnFalse} diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 0390359b3..1b9bba5c6 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -7,7 +7,7 @@ import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnFalse, returnOne, Utils } from "../../../Utils"; +import { emptyFunction, returnFalse, returnOne, Utils, returnEmptyString } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { SetupDrag, DragManager } from "../../util/DragManager"; @@ -223,6 +223,7 @@ export class SearchItem extends React.Component { PanelWidth={returnXDimension} PanelHeight={returnYDimension} focus={emptyFunction} + backgroundColor={returnEmptyString} selectOnLoad={false} parentActive={returnFalse} whenActiveChanged={returnFalse} -- cgit v1.2.3-70-g09d2 From fd047fa87334b948a4a0b00ccf9bd02ded03ea36 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 4 Aug 2019 10:16:46 -0400 Subject: fixed MakeLink (& text linking) --- src/client/documents/Documents.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/client/documents') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 07e38a4c0..e804d5440 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -602,9 +602,8 @@ export namespace DocUtils { if (sv && sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === target) return; if (target === CurrentUserUtils.UserDocument) return undefined; - let linkDoc: Doc | undefined; + let linkDocProto = new Doc(); UndoManager.RunInBatch(() => { - let linkDocProto = new Doc(); linkDocProto.type = DocumentType.LINK; linkDocProto.targetContext = targetContext; @@ -628,7 +627,7 @@ export namespace DocUtils { computed.compiled && (Doc.GetProto(source).links = new ComputedField(computed)); computed.compiled && (Doc.GetProto(target).links = new ComputedField(computed)); }, "make link"); - return linkDoc; + return linkDocProto; } } -- cgit v1.2.3-70-g09d2