From d98ab1aae1510f1cf1c0f4a32553a7971a660b66 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 1 Jul 2019 12:13:47 -0400 Subject: Couple of undo things --- src/client/views/collections/CollectionTreeView.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 927aa363f..f5dd76b71 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -251,6 +251,8 @@ class TreeView extends React.Component { e.stopPropagation(); } } + + @undoBatch treeDrop = (e: Event, de: DragManager.DropEvent) => { let x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y); let rect = this._header!.current!.getBoundingClientRect(); -- cgit v1.2.3-70-g09d2 From d101b95f647f39fe50a97d332cb107ae84d25759 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 1 Jul 2019 14:30:31 -0400 Subject: Added a tooltip --- src/client/views/collections/CollectionDockingView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 13b0579d2..a97fd1e69 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -383,7 +383,7 @@ export class CollectionDockingView extends React.Component { e.preventDefault(); e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From 055b823ac472c3831d9cca9629cd2c505310c664 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 1 Jul 2019 22:02:56 -0400 Subject: added library view pullout --- src/.DS_Store | Bin 6148 -> 6148 bytes src/client/views/Main.scss | 16 +++ src/client/views/MainView.tsx | 117 ++++++++++++++++----- .../views/collections/CollectionDockingView.tsx | 10 +- 4 files changed, 116 insertions(+), 27 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/.DS_Store b/src/.DS_Store index ff00f097e..fc746835f 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 0271edcd2..59c70c674 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -224,4 +224,20 @@ ul#add-options-list { display: inline-block; padding: 0; } +} + +.mainView-libraryFlyout { + height: 100%; + position: absolute; +} + +.mainView-libraryHandle { + opacity: 0.6; + width: 20px; + height: 40px; + top: 50%; + border-radius: 20px; + position: absolute; + z-index: 1; + background: gray; } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d28e3b837..654175523 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -160,12 +160,13 @@ export class MainView extends React.Component { } } + @action createNewWorkspace = async (id?: string) => { const list = Cast(CurrentUserUtils.UserDocument.data, listSpec(Doc)); if (list) { let freeformDoc = Docs.FreeformDocument([], { x: 0, y: 400, width: this.pwidth * .7, height: this.pheight, title: `WS collection ${list.length + 1}` }); - var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(CurrentUserUtils.UserDocument, CurrentUserUtils.UserDocument, 150), CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] }; + var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] }; let mainDoc = Docs.DockDocument([CurrentUserUtils.UserDocument, freeformDoc], JSON.stringify(dockingLayout), { title: `Workspace ${list.length + 1}` }, id); if (!CurrentUserUtils.UserDocument.linkManagerDoc) { let linkManagerDoc = new Doc(); @@ -224,40 +225,106 @@ export class MainView extends React.Component { getPHeight = () => { return this.pheight; } - @computed - get mainContent() { + + @observable flyoutWidth: number = 250; + @computed get dockingContent() { + let flyoutWidth = this.flyoutWidth; let mainCont = this.mainContainer; - let content = !mainCont ? (null) : - ; let castRes = mainCont ? FieldValue(Cast(mainCont.presentationView, listSpec(Doc))) : undefined; return {({ measureRef }) => -
- {content} +
+ {!mainCont ? (null) : + } {castRes ? : null}
} ; } + _downsize = 0; + onPointerDown = (e: React.PointerEvent) => { + this._downsize = e.clientX; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + e.preventDefault(); + } + @action + onPointerMove = (e: PointerEvent) => { + this.flyoutWidth = e.clientX; + } + @action + onPointerUp = (e: PointerEvent) => { + if (Math.abs(e.clientX - this._downsize) < 4) { + if (this.flyoutWidth < 5) this.flyoutWidth = 250; + else this.flyoutWidth = 0; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + @computed + get mainContent() { + let addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => { + if (doc.dockingConfig) { + this.openWorkspace(doc); + } else { + // CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); + } + } + let flyout = addDocTab(doc, undefined, "onRight")} + removeDocument={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={returnOne} + PanelWidth={this.getPWidth} + PanelHeight={this.getPHeight} + renderDepth={0} + selectOnLoad={false} + focus={emptyFunction} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> + ; + return
+
+ +
+
+ {flyout} +
+ {this.dockingContent} +
; + } + selected = (tool: InkTool) => { if (!InkingControl.Instance || InkingControl.Instance.selectedTool === InkTool.None) return { display: "none" }; if (InkingControl.Instance.selectedTool === tool) { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a97fd1e69..ad5472a4a 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -456,8 +456,14 @@ export class CollectionDockingView extends React.Component + + {({ measureRef }) => +
+ + } + ); } -- cgit v1.2.3-70-g09d2 From 16822d88f981e7a86729d942a92b6ad8590680e4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 1 Jul 2019 22:32:44 -0400 Subject: fixed title template view issue --- src/client/views/TemplateMenu.tsx | 8 ++++++++ src/client/views/Templates.tsx | 4 ++-- src/client/views/collections/CollectionDockingView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 5 +++++ 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index d63f45e5a..1b32f0ddd 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -64,6 +64,13 @@ export class TemplateMenu extends React.Component { } } + @undoBatch + @action + clearTemplates = (event: React.MouseEvent) => { + this.props.docs.map(d => d.clearTemplates()); + Array.from(this.props.templates.keys()).map(t => this.props.templates.set(t, false)); + } + @action componentWillReceiveProps(nextProps: TemplateMenuProps) { // this._templates = nextProps.templates; @@ -84,6 +91,7 @@ export class TemplateMenu extends React.Component {
this.toggleTemplateActivity()}>+
    {templateMenu} +
); diff --git a/src/client/views/Templates.tsx b/src/client/views/Templates.tsx index 5bb8d454a..4843a70a6 100644 --- a/src/client/views/Templates.tsx +++ b/src/client/views/Templates.tsx @@ -50,7 +50,7 @@ export namespace Templates { export const Title = new Template("Title", TemplatePosition.InnerTop, `
- {props.DataDoc.title} + {props.Document.title}
{layout}
@@ -58,7 +58,7 @@ export namespace Templates {
` ); export const Header = new Template("Header", TemplatePosition.InnerTop, - `< div style = "display:flex; flex-direction:column; height:100%;" > + `
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index ad5472a4a..8724216f5 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -269,7 +269,7 @@ export class CollectionDockingView extends React.Component(Docu } this.templates = this.templates; } + @action + clearTemplates = () => { + this.templates.length = 0; + this.templates = this.templates; + } @undoBatch @action -- cgit v1.2.3-70-g09d2 From b3bdb02cf0e0f9d9fe00bb2b7e54f5e81d6abf47 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 2 Jul 2019 13:17:00 -0400 Subject: pdf fix --- .../collectionFreeForm/CollectionFreeFormView.tsx | 3 ++ src/client/views/pdf/PDFViewer.tsx | 37 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b1aba10bf..169dbe540 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -195,6 +195,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // if (!this.props.active()) { // return; // } + if (this.props.Document.type === "pdf") { + return; + } let childSelected = this.childDocs.some(doc => { var dv = DocumentManager.Instance.getDocumentView(doc); return dv && SelectionManager.IsSelected(dv) ? true : false; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 35bf1c4d7..23a4713d4 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -540,6 +540,43 @@ export class Viewer extends React.Component { console.log("rendered"); this._rendered = true; }); +<<<<<<< Updated upstream +======= + let options = { + bar: this._searchCont.current, + toggleButton: this._searchToggle.current, + findField: this._findField.current, + highlightAllCheckbox: this._highlightAll.current, + caseSensitiveCheckbox: this._caseSensitivity.current, + entireWordCheckbox: this._entireWord.current, + findMsg: this._findMsg.current, + findResultsCount: this._findResults.current, + findPreviousButton: this._previousButton.current, + findNextButton: this._nextButton.current, + } + let findBar = new PDFFindBar.PDFFindBar(options, this._eventBus); + this._eventBus.on("find", (evt: any) => { + // this._pdfFindController.executeCommand('find', { + // query: "the", + // phraseSearch: true, + // caseSensitive: false, + // highlightAll: true, + // findPrevious: undefined + // }); + this._pdfFindController.executeCommand('find' + evt.type, { + query: evt.query, + phraseSearch: evt.phraseSearch, + caseSensitive: evt.caseSensitive, + entireWord: evt.entireWord, + highlightAll: evt.highlightAll, + findPrevious: evt.findPrevious + }); + }); + this._eventBus.on("updatefindcontrolstate", (evt: any) => { + // console.log("hello"); + findBar.updateUIState(evt.state, evt.previous, evt.matchesCount); + }) +>>>>>>> Stashed changes pdfViewer.setDocument(this.props.pdf); this._pdfFindController = new PDFJSViewer.PDFFindController(pdfViewer); // this._pdfFindController._linkService = pdfLinkService; -- cgit v1.2.3-70-g09d2 From 9334c8d12df76df60eedcc093b986f42d1f7f67d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 2 Jul 2019 14:03:36 -0400 Subject: Refactored some search stuff and fixed treeview flyout min X --- src/client/util/SearchUtil.ts | 28 ++++++++++++++++------ src/client/views/MainView.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 4 ++-- src/client/views/search/SearchBox.tsx | 15 +----------- src/client/views/search/SearchItem.tsx | 4 ++-- src/server/Search.ts | 8 +++---- 6 files changed, 31 insertions(+), 30 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 27d27a3b8..ac2eb72e7 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -4,27 +4,41 @@ import { Doc } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; export namespace SearchUtil { - export function Search(query: string, returnDocs: true): Promise; - export function Search(query: string, returnDocs: false): Promise; + export interface IdSearchResult { + ids: string[]; + numFound: number; + } + + export interface DocSearchResult { + docs: Doc[]; + numFound: number; + } + + export function Search(query: string, returnDocs: true): Promise; + export function Search(query: string, returnDocs: false): Promise; export async function Search(query: string, returnDocs: boolean) { - const ids = JSON.parse(await rp.get(DocServer.prepend("/search"), { + const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), { qs: { query } })); if (!returnDocs) { - return ids; + return result; } + const { ids, numFound } = result; const docMap = await DocServer.GetRefFields(ids); - return ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); + const docs = ids.map((id: string) => docMap[id]).filter((doc: any) => doc instanceof Doc); + return { docs, numFound }; } export async function GetAliasesOfDocument(doc: Doc): Promise { const proto = await Doc.GetT(doc, "proto", Doc, true); const protoId = (proto || doc)[Id]; - return Search(`proto_i:"${protoId}"`, true); + const result = await Search(`proto_i:"${protoId}"`, true); + return result.docs; // return Search(`{!join from=id to=proto_i}id:${protoId}`, true); } export async function GetViewsOfDocument(doc: Doc): Promise { - return Search(`proto_i:"${doc[Id]}"`, true); + const results = await Search(`proto_i:"${doc[Id]}"`, true); + return results.docs; } } \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 787e240b9..04ecc47cf 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -272,7 +272,7 @@ export class MainView extends React.Component { } @action onPointerMove = (e: PointerEvent) => { - this.flyoutWidth = e.clientX; + this.flyoutWidth = Math.max(e.clientX, 0); } @action onPointerUp = (e: PointerEvent) => { diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index b29a30069..c0f489cd8 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -23,9 +23,9 @@ export class SelectorContextMenu extends React.Component { async fetchDocuments() { let aliases = (await SearchUtil.GetAliasesOfDocument(this.props.Document)).filter(doc => doc !== this.props.Document); - const docs = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); + const { docs } = await SearchUtil.Search(`data_l:"${this.props.Document[Id]}"`, true); const map: Map = new Map; - const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); + const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true).then(result => result.docs))); allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); docs.forEach(doc => map.delete(doc)); runInAction(() => { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index a2556133b..1f6835c26 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -102,20 +102,7 @@ export class SearchBox extends React.Component { @action getResults = async (query: string) => { - let response = await rp.get(DocServer.prepend('/search'), { - qs: { - query - } - }); - let res: string[] = JSON.parse(response); - const fields = await DocServer.GetRefFields(res); - const docs: Doc[] = []; - for (const id of res) { - const field = fields[id]; - if (field instanceof Doc) { - docs.push(field); - } - } + const { docs } = await SearchUtil.Search(query, true); return FilterBox.Instance.filterDocsByType(docs); } diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index f8a0c7b16..5a7bdd24d 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -48,9 +48,9 @@ export class SelectorContextMenu extends React.Component { async fetchDocuments() { let aliases = (await SearchUtil.GetViewsOfDocument(this.props.doc)).filter(doc => doc !== this.props.doc); - const docs = await SearchUtil.Search(`data_l:"${this.props.doc[Id]}"`, true); + const { docs } = await SearchUtil.Search(`data_l:"${this.props.doc[Id]}"`, true); const map: Map = new Map; - const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true))); + const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search(`data_l:"${doc[Id]}"`, true).then(result => result.docs))); allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); docs.forEach(doc => map.delete(doc)); runInAction(() => { diff --git a/src/server/Search.ts b/src/server/Search.ts index 2db2b242c..e2a74b737 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -27,11 +27,11 @@ export class Search { start: start } })); - const fields = searchResults.response.docs; - const ids = fields.map((field: any) => field.id); - return ids; + const { docs, numFound } = searchResults.response; + const ids = docs.map((field: any) => field.id); + return { ids, numFound }; } catch { - return []; + return { ids: [], numFound: -1 }; } } -- cgit v1.2.3-70-g09d2 From a8664face6f19cd2373bd928c2a2b3043bb6278d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Tue, 2 Jul 2019 19:02:51 -0400 Subject: Added arrange script to freeform views --- src/client/views/MainView.tsx | 4 +- src/client/views/OverlayView.tsx | 46 ++++++++++++++++++++++ src/client/views/ScriptBox.tsx | 36 +++++++++++++++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 41 ++++++++++++++++++- .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 ++++-- src/client/views/nodes/KeyValueBox.tsx | 12 ++++-- 6 files changed, 141 insertions(+), 10 deletions(-) create mode 100644 src/client/views/OverlayView.tsx create mode 100644 src/client/views/ScriptBox.tsx (limited to 'src/client/views/collections') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 04ecc47cf..cc39dc04c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu'; import { InkTool } from '../../new_fields/InkField'; import _ from "lodash"; import KeyManager from './GlobalKeyHandler'; +import { OverlayView } from './OverlayView'; @observer export class MainView extends React.Component { @@ -291,7 +292,7 @@ export class MainView extends React.Component { } else { CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); } - } + }; let flyout = +
); } diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx new file mode 100644 index 000000000..72f1068ce --- /dev/null +++ b/src/client/views/OverlayView.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { observer } from "mobx-react"; +import { observable, action } from "mobx"; + +export type OverlayDisposer = () => void; + +export type OverlayElementOptions = { + x: number; + y: number; + width?: number; + height?: number; +}; + +@observer +export class OverlayView extends React.Component { + public static Instance: OverlayView; + @observable.shallow + private _elements: { ele: JSX.Element, options: OverlayElementOptions }[] = []; + + constructor(props: any) { + super(props); + if (!OverlayView.Instance) { + OverlayView.Instance = this; + } + } + + @action + addElement(ele: JSX.Element, options: OverlayElementOptions): OverlayDisposer { + const eleWithPosition = { ele, options }; + this._elements.push(eleWithPosition); + return action(() => { + const index = this._elements.indexOf(eleWithPosition); + if (index !== -1) this._elements.splice(index, 1); + }); + } + + render() { + return ( +
+ {this._elements.map(({ ele, options: { x, y, width, height } }) => ( +
{ele}
+ ))} +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx new file mode 100644 index 000000000..aea9d52a4 --- /dev/null +++ b/src/client/views/ScriptBox.tsx @@ -0,0 +1,36 @@ +import * as React from "react"; +import { observer } from "mobx-react"; +import { observable, action } from "mobx"; + +export interface ScriptBoxProps { + onSave: (text: string, onError: (error: string) => void) => void; + onCancel?: () => void; +} + +@observer +export class ScriptBox extends React.Component { + @observable + private _scriptText: string = ""; + + @action + onChange = (e: React.ChangeEvent) => { + this._scriptText = e.target.value; + } + + @action + onError = (error: string) => { + console.log(error); + } + + render() { + return ( +
+
+ + +
+ +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 169dbe540..23c0fdd0d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -27,6 +27,10 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); +import { ScriptField } from "../../../../new_fields/ScriptField"; +import { OverlayView } from "../../OverlayView"; +import { ScriptBox } from "../../ScriptBox"; +import { CompileScript } from "../../../util/Scripting"; export const panZoomSchema = createSchema({ @@ -388,6 +392,18 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } + getCalculatedPositions(doc: Doc, index: number, collection: Doc): { x?: number, y?: number, width?: number, height?: number } | undefined { + const script = Cast(this.props.Document.arrangeScript, ScriptField); + if (!script) { + return undefined; + } + const result = script.script.run({ doc, index, collection }); + if (!result.success) { + return undefined; + } + return result.result; + } + @computed.struct get views() { let curPage = FieldValue(this.Document.curPage, -1); @@ -397,7 +413,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (Math.round(page) === Math.round(curPage) || page === -1) { let minim = BoolCast(doc.isMinimized, false); if (minim === undefined || !minim) { - prev.push(); + const pos = this.getCalculatedPositions(doc, prev.length, this.Document) || {}; + prev.push(); } } return prev; @@ -438,6 +455,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } }); + ContextMenu.Instance.addItem({ + description: "Add freeform arrangement", + event: () => { + let overlayDisposer: () => void; + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { + doc: "Doc", index: "number", collection: "Doc" + }, + requiredType: "{x: number, y: number, width?: number, height?: number}", + typecheck: false + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + this.props.Document.arrangeScript = new ScriptField(script); + overlayDisposer(); + }} />; + overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 }); + } + }); } private childViews = () => [ diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 858959d81..8556817cc 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -9,6 +9,10 @@ import "./DocumentView.scss"; import React = require("react"); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { + x?: number; + y?: number; + width?: number; + height?: number; } const schema = createSchema({ @@ -23,13 +27,13 @@ const FreeformDocument = makeInterface(schema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent(FreeformDocument) { @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) scale(${this.zoom}) `; } - @computed get X() { return FieldValue(this.Document.x, 0); } - @computed get Y() { return FieldValue(this.Document.y, 0); } + @computed get X() { return this.props.x !== undefined ? this.props.x : this.Document.x || 0; } + @computed get Y() { return this.props.y !== undefined ? this.props.y : this.Document.y || 0; } + @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.width !== undefined ? this.props.width : this.Document.width || 0; } + @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : this.props.height !== undefined ? this.props.height : this.Document.height || 0; } @computed get zoom(): number { return 1 / FieldValue(this.Document.zoomBasis, 1); } @computed get nativeWidth(): number { return FieldValue(this.Document.nativeWidth, 0); } @computed get nativeHeight(): number { return FieldValue(this.Document.nativeHeight, 0); } - @computed get width(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.width, 0); } - @computed get height(): number { return BoolCast(this.props.Document.willMaximize) ? 0 : FieldValue(this.Document.height, 0); } set width(w: number) { this.Document.width = w; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 0e798d291..9407d742c 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -9,7 +9,7 @@ import { KeyValuePair } from "./KeyValuePair"; import React = require("react"); import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Doc, Field, FieldResult } from "../../../new_fields/Doc"; -import { ComputedField } from "../../../new_fields/ScriptField"; +import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; import { SetupDrag } from "../../util/DragManager"; import { Docs } from "../../documents/Documents"; import { RawDataOperationParameters } from "../../northstar/model/idea/idea"; @@ -50,7 +50,7 @@ export class KeyValueBox extends React.Component { let eq = value.startsWith("="); let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; - let dubEq = value.startsWith(":="); + let dubEq = value.startsWith(":=") ? 1 : value.startsWith(";=") ? 2 : 0; value = dubEq ? value.substr(2) : value; let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } }; if (dubEq) options.typecheck = false; @@ -58,8 +58,12 @@ export class KeyValueBox extends React.Component { if (!script.compiled) { return false; } - let field = new ComputedField(script); - if (!dubEq) { + let field: Field; + if (dubEq === 1) { + field = new ComputedField(script); + } else if (dubEq === 2) { + field = new ScriptField(script); + } else { let res = script.run({ this: target }); if (!res.success) return false; field = res.result; -- cgit v1.2.3-70-g09d2 From c619bad0281ea6f248c48b8d418f324f67f530dd Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 3 Jul 2019 12:00:59 -0400 Subject: added to fitToBox to treeviews. fixed some fitToBox issues. moved notifications button to library tree view. --- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- .../dash-nodes/HistogramBoxPrimitives.tsx | 2 - src/client/views/Main.scss | 4 +- src/client/views/MainView.tsx | 84 ++++++++-------------- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 38 +++++++++- .../views/collections/CollectionVideoView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 5 +- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/LinkEditor.tsx | 1 - src/client/views/search/SearchItem.tsx | 26 +++---- src/new_fields/Doc.ts | 12 ++++ 13 files changed, 98 insertions(+), 89 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index a60eaea85..b81eafbee 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -36,7 +36,7 @@ export class HistogramBox extends React.Component { @computed public get HistogramResult(): HistogramResult { return this.HistoOp.Result as HistogramResult; } @observable public SizeConverter: SizeConverter = new SizeConverter(); - @computed get createOperationParamsCache() { trace(); return this.HistoOp.CreateOperationParameters(); } + @computed get createOperationParamsCache() { return this.HistoOp.CreateOperationParameters(); } @computed get BinRanges() { return this.HistogramResult ? this.HistogramResult.binRanges : undefined; } @computed get ChartType() { return !this.BinRanges ? ChartType.SinglePoint : this.BinRanges[0] instanceof AggregateBinRange ? diff --git a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx index 350987695..5a16b3782 100644 --- a/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx +++ b/src/client/northstar/dash-nodes/HistogramBoxPrimitives.tsx @@ -62,7 +62,6 @@ export class HistogramBoxPrimitives extends React.Component); } render() { - trace(); return
{this.xaxislines} {this.yaxislines} diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 08f5cb8a5..44c1cb9fc 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -111,8 +111,8 @@ button:hover { //toolbar stuff #toolbar { position: absolute; - bottom: 62px; - left: 24px; + right: 8px; + top: 5px; .toolbar-button { display: block; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 19a04595a..1542fedef 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,44 +1,43 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faArrowDown, faArrowUp, faCheck, faPenNib, faThumbtack, faRedoAlt, faTable, faTree, faUndoAlt, faBell, faCommentAlt, faCut, faExclamation } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faArrowUp, faBell, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, observable, runInAction, trace } from 'mobx'; +import { action, computed, configure, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker, SketchPicker } from 'react-color'; import "normalize.css"; import * as React from 'react'; +import { SketchPicker } from 'react-color'; import Measure from 'react-measure'; import * as request from 'request'; +import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; +import { Id } from '../../new_fields/FieldSymbols'; +import { InkTool } from '../../new_fields/InkField'; +import { List } from '../../new_fields/List'; +import { listSpec } from '../../new_fields/Schema'; +import { Cast, FieldValue } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnTrue, Utils, returnOne, returnZero } from '../../Utils'; -import { Docs, DocTypes } from '../documents/Documents'; -import { SetupDrag, DragManager } from '../util/DragManager'; +import { emptyFunction, returnOne, returnTrue } from '../../Utils'; +import { DocServer } from '../DocServer'; +import { Docs } from '../documents/Documents'; +import { SetupDrag } from '../util/DragManager'; +import { HistoryUtil } from '../util/History'; import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; -import { PresentationView } from './presentationview/PresentationView'; +import { CollectionBaseView } from './collections/CollectionBaseView'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { ContextMenu } from './ContextMenu'; import { DocumentDecorations } from './DocumentDecorations'; +import KeyManager from './GlobalKeyHandler'; import { InkingControl } from './InkingControl'; import "./Main.scss"; import { MainOverlayTextBox } from './MainOverlayTextBox'; import { DocumentView } from './nodes/DocumentView'; +import { OverlayView } from './OverlayView'; +import PDFMenu from './pdf/PDFMenu'; +import { PresentationView } from './presentationview/PresentationView'; import { PreviewCursor } from './PreviewCursor'; import { FilterBox } from './search/FilterBox'; -import { SelectionManager } from '../util/SelectionManager'; -import { FieldResult, Field, Doc, Opt, DocListCast } from '../../new_fields/Doc'; -import { Cast, FieldValue, StrCast, PromiseValue } from '../../new_fields/Types'; -import { DocServer } from '../DocServer'; -import { listSpec } from '../../new_fields/Schema'; -import { Id } from '../../new_fields/FieldSymbols'; -import { HistoryUtil } from '../util/History'; -import { CollectionBaseView } from './collections/CollectionBaseView'; -import { List } from '../../new_fields/List'; -import PDFMenu from './pdf/PDFMenu'; -import { InkTool } from '../../new_fields/InkField'; -import _ from "lodash"; -import KeyManager from './GlobalKeyHandler'; -import { OverlayView } from './OverlayView'; +import { CollectionTreeView } from './collections/CollectionTreeView'; @observer export class MainView extends React.Component { @@ -184,8 +183,6 @@ export class MainView extends React.Component { } } - @observable _notifsCol: Opt; - @action openWorkspace = async (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; @@ -197,18 +194,12 @@ export class MainView extends React.Component { if (col) { const l = Cast(col.data, listSpec(Doc)); if (l) { - runInAction(() => this._notifsCol = col); + runInAction(() => CollectionTreeView.NotifsCol = col); } } }, 100); } - openNotifsCol = () => { - if (this._notifsCol && CollectionDockingView.Instance) { - CollectionDockingView.Instance.AddRightSplit(this._notifsCol, undefined); - } - } - onDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -284,24 +275,25 @@ export class MainView extends React.Component { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); } + flyoutWidthFunc = () => this.flyoutWidth; + addDocTabFunc = (doc: Doc) => { + if (doc.dockingConfig) { + this.openWorkspace(doc); + } else { + CollectionDockingView.Instance.AddRightSplit(doc, undefined); + } + }; @computed get flyout() { - let addDocTab = (doc: Doc, dataDoc: Doc | undefined, location: string) => { - if (doc.dockingConfig) { - this.openWorkspace(doc); - } else { - CollectionDockingView.Instance.AddRightSplit(doc, dataDoc); - } - }; return addDocTab(doc, undefined, "onRight")} + addDocTab={this.addDocTabFunc} removeDocument={undefined} ScreenToLocalTransform={Transform.Identity} ContentScaling={returnOne} - PanelWidth={this.getPWidth} + PanelWidth={this.flyoutWidthFunc} PanelHeight={this.getPHeight} renderDepth={0} selectOnLoad={false} @@ -403,23 +395,9 @@ export class MainView extends React.Component { /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ @computed get miscButtons() { - const length = this._notifsCol ? DocListCast(this._notifsCol.data).length : 0; - const notifsRef = React.createRef(); - const dragNotifs = action(() => this._notifsCol!); let logoutRef = React.createRef(); return [ -
-
- -
0 ? { "display": "initial" } : { "display": "none" }}> - {length} -
-
-
, this.isSearchVisible ?
: null,
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b0d46953c..d06d1dab6 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -334,7 +334,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get reactTable() { - trace(); let previewWidth = this.previewWidth() + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; return doc) { } render() { - trace(); return (
this.onDrop(e, {})} onContextMenu={this.onContextMenu} ref={this.createTarget}> @@ -404,6 +402,7 @@ interface CollectionSchemaPreviewProps { Document?: Doc; DataDocument?: Doc; childDocs?: Doc[]; + fitToBox?: () => number[]; renderDepth: number; width: () => number; height: () => number; @@ -472,6 +471,7 @@ export class CollectionSchemaPreview extends React.Component { } ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); + e.preventDefault(); } } @@ -312,6 +314,11 @@ class TreeView extends React.Component { return ele; } + fitToBox = () => { + let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); + let bounds = Doc.ComputeContentBounds(layoutDoc); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.props.panelHeight() / (bounds.b - bounds.y), this.props.panelWidth() / (bounds.r - bounds.x))]; + } render() { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); @@ -333,6 +340,7 @@ class TreeView extends React.Component { Document={layoutDoc} DataDocument={this.resolvedDataDoc} renderDepth={this.props.renderDepth} + fitToBox={this.fitToBox} width={docWidth} height={layoutDoc[HeightSym]} getTransform={this.docTransform} @@ -456,6 +464,31 @@ export class CollectionTreeView extends CollectionSubView(Document) { outerXf = () => Utils.GetScreenTransform(this._mainEle!); onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + + @observable static NotifsCol: Opt; + + openNotifsCol = () => { + if (CollectionTreeView.NotifsCol && CollectionDockingView.Instance) { + CollectionDockingView.Instance.AddRightSplit(CollectionTreeView.NotifsCol, undefined); + } + } + @computed get notifsButton() { + const length = CollectionTreeView.NotifsCol ? DocListCast(CollectionTreeView.NotifsCol.data).length : 0; + const notifsRef = React.createRef(); + const dragNotifs = action(() => CollectionTreeView.NotifsCol!); + return
+
+ +
0 ? { "display": "initial" } : { "display": "none" }}> + {length} +
+
+
; + } + render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); @@ -480,6 +513,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { TreeView.loadId = doc[Id]; Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} /> + {this.props.Document.excludeFromLibrary ? this.notifsButton : (null)}
    { TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.Document, this.props.DataDoc, this.props.fieldKey, addDoc, this.remove, diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index c1a6ca44e..1984965ba 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -122,7 +122,6 @@ export class CollectionVideoView extends React.Component { } render() { - trace(); return ( {this.subView} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 23c0fdd0d..cb7be3f28 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,9 +54,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed get nativeHeight() { return this.Document.nativeHeight || 0; } public get isAnnotationOverlay() { return this.props.fieldKey === "annotations" || this.props.fieldExt === "annotations"; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } - private panX = () => this.props.fitToBox ? this.props.fitToBox[0] : this.Document.panX || 0; - private panY = () => this.props.fitToBox ? this.props.fitToBox[1] : this.Document.panY || 0; - private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox[2] : this.Document.scale || 1; + private panX = () => this.props.fitToBox ? this.props.fitToBox()[0] : this.Document.panX || 0; + private panY = () => this.props.fitToBox ? this.props.fitToBox()[1] : this.Document.panY || 0; + private zoomScaling = () => this.props.fitToBox ? this.props.fitToBox()[2] : this.Document.scale || 1; private centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 : 0; // shift so pan position is at center of window for non-overlay collections private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 : 0;// shift so pan position is at center of window for non-overlay collections private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3673cb0e2..8b75d0d25 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -71,7 +71,7 @@ export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; - fitToBox?: number[]; + fitToBox?: () => number[]; addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; removeDocument?: (doc: Doc) => boolean; moveDocument?: (doc: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; @@ -573,8 +573,7 @@ export class DocumentView extends DocComponent(Docu @computed get nativeWidth() { return this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.Document.nativeHeight || 0; } @computed get contents() { - return ( - ); + return (); } render() { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 3f5a2e744..c5fc6c65a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -31,7 +31,7 @@ export interface FieldViewProps { fieldKey: string; fieldExt: string; leaveNativeSize?: boolean; - fitToBox?: number[]; + fitToBox?: () => number[]; ContainingCollectionView: Opt; Document: Doc; DataDoc?: Doc; diff --git a/src/client/views/nodes/LinkEditor.tsx b/src/client/views/nodes/LinkEditor.tsx index 22da732cf..e6cc50620 100644 --- a/src/client/views/nodes/LinkEditor.tsx +++ b/src/client/views/nodes/LinkEditor.tsx @@ -271,7 +271,6 @@ export class LinkGroupEditor extends React.Component { ); } - trace(); return (
    diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 5a7bdd24d..129d71b3b 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -101,34 +101,23 @@ export class SearchItem extends React.Component { @observable _useIcons = true; @observable _displayDim = 50; + fitToBox = () => { + let bounds = Doc.ComputeContentBounds(this.props.doc); + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / Math.max((bounds.b - bounds.y), (bounds.r - bounds.x)), this._displayDim]; + } @computed public get DocumentIcon() { - let layoutresult = StrCast(this.props.doc.type); if (!this._useIcons) { - let renderDoc = this.props.doc; - let box: number[] = []; - if (layoutresult.indexOf(DocTypes.COL) !== -1) { - renderDoc = Doc.MakeDelegate(renderDoc); - let bounds = DocListCast(renderDoc.data).reduce((bounds, doc) => { - var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; - let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; - return { - x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), - r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) - }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); - box = [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Number(SEARCH_THUMBNAIL_SIZE) / (bounds.r - bounds.x), this._displayDim]; - } let returnXDimension = () => this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); let returnYDimension = () => this._displayDim; - let scale = () => returnXDimension() / NumCast(renderDoc.nativeWidth, returnXDimension()); + let scale = () => returnXDimension() / NumCast(this.props.doc.nativeWidth, returnXDimension()); return
    { this._useIcons = !this._useIcons; this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE); })} onPointerEnter={action(() => this._displayDim = this._useIcons ? 50 : Number(SEARCH_THUMBNAIL_SIZE))} onPointerLeave={action(() => this._displayDim = 50)} > {
    ; } + let layoutresult = StrCast(this.props.doc.type); let button = layoutresult.indexOf(DocTypes.PDF) !== -1 ? faFilePdf : layoutresult.indexOf(DocTypes.IMG) !== -1 ? faImage : layoutresult.indexOf(DocTypes.TEXT) !== -1 ? faStickyNote : diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 734a90731..29d35e19f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -249,6 +249,18 @@ export namespace Doc { return true; } + export function ComputeContentBounds(doc: Doc) { + let bounds = DocListCast(doc.data).reduce((bounds, doc) => { + var [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; + let [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; + return { + x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), + r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) + }; + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + return bounds; + } + // // Resolves a reference to a field by returning 'doc' if o field extension is specified, // otherwise, it returns the extension document stored in doc._ext. -- cgit v1.2.3-70-g09d2 From 3cdc6f11e262e289b6070d6fa75a59fff7e59b1e Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 3 Jul 2019 13:42:20 -0400 Subject: Added state to layout scripts --- src/client/views/OverlayView.tsx | 9 +-- src/client/views/ScriptBox.scss | 17 ++++++ src/client/views/ScriptBox.tsx | 10 +++- .../collectionFreeForm/CollectionFreeFormView.tsx | 69 +++++++++++++--------- 4 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 src/client/views/ScriptBox.scss (limited to 'src/client/views/collections') diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 72f1068ce..f8fc94274 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import { observer } from "mobx-react"; import { observable, action } from "mobx"; +import { Utils } from "../../Utils"; export type OverlayDisposer = () => void; @@ -15,7 +16,7 @@ export type OverlayElementOptions = { export class OverlayView extends React.Component { public static Instance: OverlayView; @observable.shallow - private _elements: { ele: JSX.Element, options: OverlayElementOptions }[] = []; + private _elements: { ele: JSX.Element, id: string, options: OverlayElementOptions }[] = []; constructor(props: any) { super(props); @@ -26,7 +27,7 @@ export class OverlayView extends React.Component { @action addElement(ele: JSX.Element, options: OverlayElementOptions): OverlayDisposer { - const eleWithPosition = { ele, options }; + const eleWithPosition = { ele, options, id: Utils.GenerateGuid() }; this._elements.push(eleWithPosition); return action(() => { const index = this._elements.indexOf(eleWithPosition); @@ -37,8 +38,8 @@ export class OverlayView extends React.Component { render() { return (
    - {this._elements.map(({ ele, options: { x, y, width, height } }) => ( -
    {ele}
    + {this._elements.map(({ ele, options: { x, y, width, height }, id }) => ( +
    {ele}
    ))}
    ); diff --git a/src/client/views/ScriptBox.scss b/src/client/views/ScriptBox.scss new file mode 100644 index 000000000..28326624a --- /dev/null +++ b/src/client/views/ScriptBox.scss @@ -0,0 +1,17 @@ +.scriptBox-outerDiv { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} + +.scriptBox-toolbar { + width: 100%; +} + +.scriptBox-textArea { + width: 100%; + height: 100%; + box-sizing: border-box; + resize: none; +} \ No newline at end of file diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index aea9d52a4..fa236c2da 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -2,15 +2,23 @@ import * as React from "react"; import { observer } from "mobx-react"; import { observable, action } from "mobx"; +import "./ScriptBox.scss"; + export interface ScriptBoxProps { onSave: (text: string, onError: (error: string) => void) => void; onCancel?: () => void; + initialText?: string; } @observer export class ScriptBox extends React.Component { @observable - private _scriptText: string = ""; + private _scriptText: string; + + constructor(props: ScriptBoxProps) { + super(props); + this._scriptText = props.initialText || ""; + } @action onChange = (e: React.ChangeEvent) => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 23c0fdd0d..edd2fb1b0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -28,7 +28,7 @@ import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); import { ScriptField } from "../../../../new_fields/ScriptField"; -import { OverlayView } from "../../OverlayView"; +import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; @@ -36,7 +36,9 @@ import { CompileScript } from "../../../util/Scripting"; export const panZoomSchema = createSchema({ panX: "number", panY: "number", - scale: "number" + scale: "number", + arrangeScript: ScriptField, + arrangeInit: ScriptField, }); type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof positionSchema, typeof pageSchema]>; @@ -392,28 +394,34 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } - getCalculatedPositions(doc: Doc, index: number, collection: Doc): { x?: number, y?: number, width?: number, height?: number } | undefined { - const script = Cast(this.props.Document.arrangeScript, ScriptField); - if (!script) { - return undefined; - } - const result = script.script.run({ doc, index, collection }); + getCalculatedPositions(doc: Doc, index: number, collection: Doc, script: ScriptField, state: any): { x?: number, y?: number, width?: number, height?: number, state?: any } { + const result = script.script.run({ doc, index, collection, state }); if (!result.success) { - return undefined; + return {}; } - return result.result; + return result.result === undefined ? {} : result.result; } @computed.struct get views() { let curPage = FieldValue(this.Document.curPage, -1); + const initScript = this.Document.arrangeInit; + const script = this.Document.arrangeScript; + let state: any = undefined; + if (initScript) { + const initResult = initScript.script.run(); + if (initResult.success) { + state = initResult.result; + } + } let docviews = this.childDocs.reduce((prev, doc) => { if (!(doc instanceof Doc)) return prev; var page = NumCast(doc.page, -1); if (Math.round(page) === Math.round(curPage) || page === -1) { let minim = BoolCast(doc.isMinimized, false); if (minim === undefined || !minim) { - const pos = this.getCalculatedPositions(doc, prev.length, this.Document) || {}; + const pos = script ? this.getCalculatedPositions(doc, prev.length, this.Document, script, state) : {}; + state = pos.state === undefined ? state : pos.state; prev.push(); } } @@ -458,23 +466,28 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContextMenu.Instance.addItem({ description: "Add freeform arrangement", event: () => { - let overlayDisposer: () => void; - let scriptingBox = overlayDisposer()} onSave={(text, onError) => { - const script = CompileScript(text, { - params: { - doc: "Doc", index: "number", collection: "Doc" - }, - requiredType: "{x: number, y: number, width?: number, height?: number}", - typecheck: false - }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - return; - } - this.props.Document.arrangeScript = new ScriptField(script); - overlayDisposer(); - }} />; - overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 }); + let addOverlay = (key: "arrangeScript" | "arrangeInit", options: OverlayElementOptions, params?: Record, requiredType?: string) => { + let overlayDisposer: () => void; + const script = this.Document[key]; + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + let scriptingBox = overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params, + requiredType, + typecheck: false + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + this.Document[key] = new ScriptField(script); + overlayDisposer(); + }} />; + overlayDisposer = OverlayView.Instance.addElement(scriptingBox, options); + }; + addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300 }, undefined, undefined); + addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300 }, { doc: "Doc", index: "number", collection: "Doc", state:"any" }, "{x: number, y: number, width?: number, height?: number}"); } }); } -- cgit v1.2.3-70-g09d2 From c77dfd3fcb2df644fcf2c177db7e6e376ca4c3a9 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 3 Jul 2019 14:29:53 -0400 Subject: quick and dirty arrange transition animations --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 5 ++++- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index cb7be3f28..6b0cb5728 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCastAsync, HeightSym, WidthSym } from "../../../../new_fields/Doc"; +import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; @@ -458,6 +458,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ContextMenu.Instance.addItem({ description: "Add freeform arrangement", event: () => { + const docs = DocListCast(this.Document[this.props.fieldKey]); + docs.map(d => d.transition = "transform 1s"); let overlayDisposer: () => void; let scriptingBox = overlayDisposer()} onSave={(text, onError) => { const script = CompileScript(text, { @@ -473,6 +475,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } this.props.Document.arrangeScript = new ScriptField(script); overlayDisposer(); + setTimeout(() => docs.map(d => d.transition = undefined), 1200); }} />; overlayDisposer = OverlayView.Instance.addElement(scriptingBox, { x: 100, y: 100, width: 200, height: 200 }); } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3f9270597..30cf74f3e 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,7 +1,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { BoolCast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { BoolCast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; import { DocumentView, DocumentViewProps, positionSchema } from "./DocumentView"; @@ -86,6 +86,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Wed, 3 Jul 2019 17:29:31 -0400 Subject: Debug commit --- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/search/FilterBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 19 ++++++++++++++++--- src/debug/Viewer.tsx | 7 +++++++ 4 files changed, 25 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ef3868df6..0cc31f031 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -236,7 +236,6 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) }); ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" }); @@ -246,6 +245,7 @@ class TreeView extends React.Component { } ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } else { + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) }); ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index 23a1b31d8..02789fb27 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -58,7 +58,7 @@ export class FilterBox extends React.Component { componentDidMount = () => { document.addEventListener("pointerdown", (e) => { if (!e.defaultPrevented && e.timeStamp !== this._pointerTime) { - SearchBox.Instance.closeSearch(); + // SearchBox.Instance.closeSearch(); } }); } diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 129d71b3b..7d45f9dfb 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 } from "../../../Utils"; +import { emptyFunction, returnFalse, returnOne, Utils } from "../../../Utils"; import { DocTypes } from "../../documents/Documents"; import { DocumentManager } from "../../util/DocumentManager"; import { SetupDrag } from "../../util/DragManager"; @@ -21,6 +21,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { SearchBox } from "./SearchBox"; import "./SearchItem.scss"; import "./SelectorContextMenu.scss"; +import { ContextMenu } from "../ContextMenu"; export interface SearchItemProps { doc: Doc; @@ -178,7 +179,7 @@ export class SearchItem extends React.Component { } @action - pointerDown = (e: React.PointerEvent) => SearchBox.Instance.openSearch(e) + pointerDown = (e: React.PointerEvent) => { e.preventDefault; e.button === 0 && SearchBox.Instance.openSearch(e); } highlightDoc = (e: React.PointerEvent) => { if (this.props.doc.type === DocTypes.LINK) { @@ -214,9 +215,21 @@ export class SearchItem extends React.Component { } } + onContextMenu = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({ + description: "Copy ID", event: () => { + Utils.CopyText(this.props.doc[Id]); + } + }); + ContextMenu.Instance.displayMenu(e.clientX, e.clientY); + } + render() { return ( -
    +
    diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index b22300d0b..e6d9e031a 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -10,6 +10,13 @@ import { List } from '../new_fields/List'; import { URLField } from '../new_fields/URLField'; import { EditableView } from '../client/views/EditableView'; import { CompileScript } from '../client/util/Scripting'; +import { DateField } from '../new_fields/DateField'; +import { ScriptField } from '../new_fields/ScriptField'; + +DateField; +URLField; +ScriptField; + function applyToDoc(doc: { [index: string]: FieldResult }, key: string, scriptString: string): boolean; function applyToDoc(doc: { [index: number]: FieldResult }, key: number, scriptString: string): boolean; -- cgit v1.2.3-70-g09d2 From 20e57b499d309052d4a12ef2d6a2288eec8bf04f Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 3 Jul 2019 19:05:41 -0400 Subject: Couple of fixes --- src/client/views/collections/CollectionDockingView.tsx | 3 +++ src/client/views/search/FilterBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8724216f5..8b6f5b6a6 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -455,6 +455,9 @@ export class CollectionDockingView extends React.Component 0) { + return
    Nested workspaces can't be rendered
    ; + } return ( {({ measureRef }) => diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index 02789fb27..23a1b31d8 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -58,7 +58,7 @@ export class FilterBox extends React.Component { componentDidMount = () => { document.addEventListener("pointerdown", (e) => { if (!e.defaultPrevented && e.timeStamp !== this._pointerTime) { - // SearchBox.Instance.closeSearch(); + SearchBox.Instance.closeSearch(); } }); } diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 7d45f9dfb..601f4032d 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -179,7 +179,7 @@ export class SearchItem extends React.Component { } @action - pointerDown = (e: React.PointerEvent) => { e.preventDefault; e.button === 0 && SearchBox.Instance.openSearch(e); } + pointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.button === 0 && SearchBox.Instance.openSearch(e); } highlightDoc = (e: React.PointerEvent) => { if (this.props.doc.type === DocTypes.LINK) { -- cgit v1.2.3-70-g09d2 From 41733ff406b639635aaccb1cd2b03ecf1f902b17 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 3 Jul 2019 19:22:16 -0400 Subject: Added params to init script and added docs param to layout script --- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba41b5afe..4b7d462e5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -394,8 +394,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }; } - getCalculatedPositions(doc: Doc, index: number, collection: Doc, script: ScriptField, state: any): { x?: number, y?: number, width?: number, height?: number, state?: any } { - const result = script.script.run({ doc, index, collection, state }); + getCalculatedPositions(script: ScriptField, params: { doc: Doc, index: number, collection: Doc, docs: Doc[], state: any }): { x?: number, y?: number, width?: number, height?: number, state?: any } { + const result = script.script.run(params); if (!result.success) { return {}; } @@ -408,19 +408,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const initScript = this.Document.arrangeInit; const script = this.Document.arrangeScript; let state: any = undefined; + const docs = this.childDocs; if (initScript) { - const initResult = initScript.script.run(); + const initResult = initScript.script.run({ docs, collection: this.Document }); if (initResult.success) { state = initResult.result; } } - let docviews = this.childDocs.reduce((prev, doc) => { + let docviews = docs.reduce((prev, doc) => { if (!(doc instanceof Doc)) return prev; var page = NumCast(doc.page, -1); if (Math.round(page) === Math.round(curPage) || page === -1) { let minim = BoolCast(doc.isMinimized, false); if (minim === undefined || !minim) { - const pos = script ? this.getCalculatedPositions(doc, prev.length, this.Document, script, state) : {}; + const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : {}; state = pos.state === undefined ? state : pos.state; prev.push(); } @@ -489,8 +490,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }} />; overlayDisposer = OverlayView.Instance.addElement(scriptingBox, options); }; - addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300 }, undefined, undefined); - addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300 }, { doc: "Doc", index: "number", collection: "Doc", state: "any" }, "{x: number, y: number, width?: number, height?: number}"); + addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300 }, { collection: "Doc", docs: "Doc[]" }, undefined); + addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300 }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); } }); } -- cgit v1.2.3-70-g09d2 From 74e4909a77ac143ecdb1d038ad182aae9c710129 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 3 Jul 2019 22:15:25 -0400 Subject: implemented directory import routine --- src/client/documents/Documents.ts | 57 ++++++++- src/client/util/Import & Export/ImageImporter.tsx | 67 +++++++++++ src/client/util/Import & Export/ImportBox.tsx | 134 +++++++++++++++++++++ src/client/util/SelectionManager.ts | 2 +- src/client/views/MainView.tsx | 3 + src/client/views/collections/CollectionSubView.tsx | 46 +------ src/client/views/nodes/DocumentContentsView.tsx | 3 +- 7 files changed, 265 insertions(+), 47 deletions(-) create mode 100644 src/client/util/Import & Export/ImageImporter.tsx create mode 100644 src/client/util/Import & Export/ImportBox.tsx (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7d7a1f02a..5d637dd3a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -36,6 +36,7 @@ import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; +import ImportBox from "../util/Import & Export/ImportBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -51,7 +52,8 @@ export enum DocTypes { KVP = "kvp", VID = "video", AUDIO = "audio", - LINK = "link" + LINK = "link", + IMPORT = "import" } export interface DocumentOptions { @@ -127,6 +129,7 @@ export namespace Docs { let audioProto: Doc; let pdfProto: Doc; let iconProto: Doc; + let importProto: Doc; // let linkProto: Doc; const textProtoId = "textProto"; const histoProtoId = "histoProto"; @@ -138,6 +141,7 @@ export namespace Docs { const videoProtoId = "videoProto"; const audioProtoId = "audioProto"; const iconProtoId = "iconProto"; + const importProtoId = "importProto"; // const linkProtoId = "linkProto"; export function initProtos(): Promise { @@ -152,6 +156,7 @@ export namespace Docs { audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype(); pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype(); iconProto = fields[iconProtoId] as Doc || CreateIconPrototype(); + importProto = fields[importProtoId] as Doc || CreateImportPrototype(); }); } @@ -174,6 +179,11 @@ export namespace Docs { return imageProto; } + function CreateImportPrototype(): Doc { + let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); + return importProto; + } + function CreateHistogramPrototype(): Doc { let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"), { x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST }); @@ -261,6 +271,10 @@ export namespace Docs { return CreateInstance(audioProto, new AudioField(new URL(url)), options); } + export function DirectoryImportDocument(options: DocumentOptions = {}) { + return CreateInstance(importProto, "", options); + } + export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) { return CreateInstance(histoProto, new HistogramField(histoOp), options); } @@ -333,6 +347,47 @@ export namespace Docs { return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } + export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> { + let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; + if (type.indexOf("image") !== -1) { + ctor = Docs.ImageDocument; + } + if (type.indexOf("video") !== -1) { + ctor = Docs.VideoDocument; + } + if (type.indexOf("audio") !== -1) { + ctor = Docs.AudioDocument; + } + if (type.indexOf("pdf") !== -1) { + ctor = Docs.PdfDocument; + options.nativeWidth = 1200; + } + if (type.indexOf("excel") !== -1) { + ctor = Docs.DBDocument; + options.dropAction = "copy"; + } + if (type.indexOf("html") !== -1) { + if (path.includes(window.location.hostname)) { + let s = path.split('/'); + let id = s[s.length - 1]; + DocServer.GetRefField(id).then(field => { + if (field instanceof Doc) { + let alias = Doc.MakeAlias(field); + alias.x = options.x || 0; + alias.y = options.y || 0; + alias.width = options.width || 300; + alias.height = options.height || options.width || 300; + addDocument && addDocument(alias, false); + } + }); + return undefined; + } + ctor = Docs.WebDocument; + options = { height: options.width, ...options, title: path, nativeWidth: undefined }; + } + return ctor ? ctor(path, options) : undefined; + } + export function CaptionDocument(doc: Doc) { const captionDoc = Doc.MakeAlias(doc); captionDoc.overlayLayout = FixedCaption(); diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx new file mode 100644 index 000000000..d664f6487 --- /dev/null +++ b/src/client/util/Import & Export/ImageImporter.tsx @@ -0,0 +1,67 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action } from "mobx"; +import { Docs } from "../../documents/Documents"; +import { FieldViewProps } from "../../views/nodes/FieldView"; + +interface ImageImporterProps { + addSchema: (imageDocs: Doc[]) => void; +} + +export default class BulkImporter extends React.Component { + private selector = React.createRef(); + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files) return; + + for (let i = 0; i < files.length; i++) { + let target = files.item(i); + + if (target === null) { + continue; + } + + let type = target.type; + let formData = new FormData(); + formData.append('file', target); + let dropFileName = target ? target.name : "-empty-"; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = window.location.origin + file; + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" }); + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", "true"); + this.selector.current!.setAttribute("webkitdirectory", "true"); + } + + render() { + return ( +
    + +
    + ); + } + +} \ No newline at end of file diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx new file mode 100644 index 000000000..630911710 --- /dev/null +++ b/src/client/util/Import & Export/ImportBox.tsx @@ -0,0 +1,134 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action, observable } from "mobx"; +import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; +import Measure, { ContentRect } from "react-measure"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp } from '@fortawesome/free-solid-svg-icons'; +import { Docs, DocumentOptions } from "../../documents/Documents"; + +interface ImageImporterProps { + addSchema: (imageDocs: Doc[]) => void; +} + +export default class ImportBox extends React.Component { + @observable private top = 0; + @observable private left = 0; + private dimensions = 50; + + constructor(props: FieldViewProps) { + super(props); + library.add(faArrowUp); + } + + public static LayoutString() { return FieldView.LayoutString(ImportBox); } + + private selector = React.createRef(); + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files || files.length === 0) return; + + let directory = (files.item(0) as any).webkitRelativePath.split("/", 1); + + for (let i = 0; i < files.length; i++) { + let uploaded_file = files.item(i); + + if (!uploaded_file) { + continue; + } + + let formData = new FormData(); + formData.append('file', uploaded_file); + let dropFileName = uploaded_file ? uploaded_file.name : "-empty-"; + let type = uploaded_file.type; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = DocServer.prepend(file); + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let doc = this.props.Document; + let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") }; + let parent = this.props.ContainingCollectionView; + if (parent) { + let importContainer = Docs.StackingDocument(docs, options); + Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); + this.props.removeDocument && this.props.removeDocument(doc); + } + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", "true"); + this.selector.current!.setAttribute("webkitdirectory", "true"); + } + + @action + preserveCentering = (rect: ContentRect) => { + let bounds = rect.offset!; + if (bounds.width === 0 || bounds.height === 0) { + return; + } + let offset = this.dimensions / 2; + this.left = bounds.width / 2 - offset; + this.top = bounds.height / 2 - offset; + } + + render() { + let dimensions = 50; + return ( + + {({ measureRef }) => +
    + +
    ; } - titleClicked = (e: React.MouseEvent) => { - if (this._collapsed) return false; - else { - this.props.document.embed = !BoolCast(this.props.document.embed); - return true; - } - } static loadId = ""; editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} @@ -236,18 +231,19 @@ class TreeView extends React.Component { onWorkspaceContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped()) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { let kvp = Docs.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.addItem({ description: (BoolCast(this.props.document.embed) ? "Collapse" : "Expand") + " inline", event: () => this.props.document.embed = !BoolCast(this.props.document.embed), icon: "expand" }); if (NumCast(this.props.document.viewType) !== CollectionViewType.Docking) { ContextMenu.Instance.addItem({ description: "Open Tab", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "inTab"), icon: "folder" }); ContextMenu.Instance.addItem({ description: "Open Right", event: () => this.props.addDocTab(this.props.document, this.resolvedDataDoc, "onRight"), icon: "caret-square-right" }); if (DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).length) { - ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).map(view => view.props.focus(this.props.document, true)) }); + ContextMenu.Instance.addItem({ description: "Focus", event: () => DocumentManager.Instance.getDocumentViews(this.resolvedDataDoc).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)) }); + ContextMenu.Instance.addItem({ description: "Delete Item", event: undoBatch(() => this.props.deleteDoc(this.props.document)), icon: "trash-alt" }); } else { - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)) }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); + ContextMenu.Instance.addItem({ description: "Open as Workspace", event: undoBatch(() => MainView.Instance.openWorkspace(this.resolvedDataDoc)), 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 Fields", event: () => { let kvp = Docs.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); e.stopPropagation(); e.preventDefault(); @@ -268,6 +264,8 @@ class TreeView extends React.Component { e.stopPropagation(); } if (de.data instanceof DragManager.DocumentDragData) { + e.stopPropagation(); + if (de.data.draggedDocuments[0] === this.props.document) return true; let addDoc = (doc: Doc) => this.props.addDocument(doc, this.resolvedDataDoc, before); if (inside) { let docList = Cast(this.resolvedDataDoc.data, listSpec(Doc)); @@ -275,7 +273,6 @@ class TreeView extends React.Component { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } - e.stopPropagation(); let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments); return (de.data.dropAction || de.data.userDropAction) ? de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.resolvedDataDoc, before) || added, false) @@ -314,18 +311,37 @@ class TreeView extends React.Component { return ele; } - fitToBox = () => { + @computed get docBounds() { + if (StrCast(this.props.document.type).indexOf(DocTypes.COL) === -1) return undefined; let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); - let bounds = Doc.ComputeContentBounds(layoutDoc); - return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.props.panelHeight() / (bounds.b - bounds.y), this.props.panelWidth() / (bounds.r - bounds.x))]; + return Doc.ComputeContentBounds(layoutDoc); + } + docWidth = () => { + let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); + if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 5)); + return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; } + docHeight = () => { + let bounds = this.docBounds; + return Math.min(this.MAX_EMBED_HEIGHT, (() => { + let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); + if (aspect) return this.docWidth() * aspect; + if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); + return NumCast(this.props.document.height) ? NumCast(this.props.document.height) : 50; + })()); + } + fitToBox = () => { + let bounds = this.docBounds!; + return [(bounds.x + bounds.r) / 2, (bounds.y + bounds.b) / 2, Math.min(this.docHeight() / (bounds.b - bounds.y), this.docWidth() / (bounds.r - bounds.x))]; + } + render() { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.resolvedDataDoc[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.resolvedDataDoc, this._chosenKey, doc, addBefore, before); let doc = Cast(this.resolvedDataDoc[this._chosenKey], Doc); - let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; + if (!this._collapsed) { if (!this.props.document.embed) { contentElement =
      @@ -335,14 +351,14 @@ class TreeView extends React.Component {
    ; } else { let layoutDoc = Doc.expandTemplateLayout(this.props.document, this.props.dataDoc); - contentElement =
    + contentElement =
    this.cleanupInteractions(false); e.stopPropagation(); } - if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e" || e.key === "p") { + if (e.key === "c" || e.key === "s" || e.key === "S" || e.key === "e") { this._commandExecuted = true; e.stopPropagation(); e.preventDefault(); @@ -277,7 +277,7 @@ export class MarqueeView extends React.Component width: bounds.width, height: bounds.height, ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined, - title: e.key === "s" || e.key === "S" ? "-summary-" : e.key === "p" ? "-summary-" : "a nested collection", + title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection", }); this.marqueeInkDelete(inkData); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8b75d0d25..69dc31fec 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -589,8 +589,10 @@ export class DocumentView extends DocComponent(Docu style={{ outlineColor: "maroon", outlineStyle: "dashed", - outlineWidth: BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush) ? + outlineWidth: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && !NumCast(this.props.Document.borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", + border: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && NumCast(this.props.Document.borderRounding) ? + `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", background: backgroundColor, width: nativeWidth, -- cgit v1.2.3-70-g09d2 From 575ee72cd6ddd65d6c02c6f50a3266f632b1b858 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 00:17:15 -0400 Subject: highlights current workspace in treeview --- src/client/views/collections/CollectionDockingView.tsx | 2 ++ src/client/views/collections/CollectionTreeView.tsx | 3 ++- src/client/views/nodes/DocumentView.tsx | 4 ++-- src/client/views/presentationview/PresentationElement.tsx | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8b6f5b6a6..c5f8fb728 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -240,6 +240,7 @@ export class CollectionDockingView extends React.Component this.setupGoldenLayout(), 1); + this.props.Document.workspaceBrush = true; } this._ignoreStateChange = ""; }, { fireImmediately: true }); @@ -249,6 +250,7 @@ export class CollectionDockingView extends React.Component void = () => { try { + this.props.Document.workspaceBrush = false; this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); this._goldenLayout.unbind('stackCreated', this.stackCreated); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 622ba37d1..3db786043 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -217,7 +217,8 @@ class TreeView extends React.Component { return <>
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 69dc31fec..f751a45fe 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -589,9 +589,9 @@ export class DocumentView extends DocComponent(Docu style={{ outlineColor: "maroon", outlineStyle: "dashed", - outlineWidth: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && !NumCast(this.props.Document.borderRounding) ? + outlineWidth: BoolCast(this.props.Document.libraryBrush) && !NumCast(this.props.Document.borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", - border: (BoolCast(this.props.Document.libraryBrush) || BoolCast(this.props.Document.protoBrush)) && NumCast(this.props.Document.borderRounding) ? + border: BoolCast(this.props.Document.libraryBrush) && NumCast(this.props.Document.borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", background: backgroundColor, diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index d63c0b066..6896ee452 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -377,7 +377,7 @@ export default class PresentationElement extends React.Component { p.gotoDocument(p.index, NumCast(this.props.mainDocument.selectedDoc)); e.stopPropagation(); }}> -- cgit v1.2.3-70-g09d2 From e56a29faab91c5059af41e0b2abd79e46eacce0c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 00:37:06 -0400 Subject: fixed edit font size for tree view --- src/client/views/EditableView.tsx | 3 ++- src/client/views/collections/CollectionTreeView.tsx | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 97a2d19dd..f7aa6cc94 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -25,6 +25,7 @@ export interface EditableProps { */ contents: any; fontStyle?: string; + fontSize?: number; height?: number; display?: string; oneLine?: boolean; @@ -80,7 +81,7 @@ export class EditableView extends React.Component { if (this._editing) { return this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} - style={{ display: this.props.display }} />; + style={{ display: this.props.display, fontSize: this.props.fontSize }} />; } else { return (
    { contents={StrCast(this.props.document[key])} height={36} fontStyle={style} + fontSize={12} GetValue={() => StrCast(this.props.document[key])} SetValue={(value: string) => (Doc.GetProto(this.resolvedDataDoc)[key] = value) ? true : true} OnFillDown={(value: string) => { -- cgit v1.2.3-70-g09d2 From d479f5e6bcdb7d1a0edb2f8cf366549abe2a910b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 14:08:00 -0400 Subject: added text font color setting. adding interactive border radius. moved main toolbar. --- src/client/documents/Documents.ts | 4 +-- src/client/util/DragManager.ts | 1 + src/client/util/RichTextSchema.tsx | 11 ++++++++ src/client/util/TooltipTextMenu.tsx | 3 +- src/client/views/DocumentDecorations.scss | 22 +++++++++++++++ src/client/views/DocumentDecorations.tsx | 32 ++++++++++++++++++++++ src/client/views/InkingControl.tsx | 3 ++ src/client/views/Main.scss | 2 +- src/client/views/MainOverlayTextBox.tsx | 5 ++++ src/client/views/MainView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 16 ++++------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 15 ++++++---- src/client/views/nodes/DocumentView.tsx | 4 +-- src/client/views/nodes/FormattedTextBox.tsx | 11 +++++++- 14 files changed, 106 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7a976e7d7..2bddf053a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -77,7 +77,7 @@ export interface DocumentOptions { backgroundLayout?: string; curPage?: number; documentText?: string; - borderRounding?: number; + borderRounding?: string; schemaColumns?: List; dockingConfig?: string; dbDoc?: Doc; @@ -93,7 +93,7 @@ export namespace DocUtils { if (target === CurrentUserUtils.UserDocument) return; UndoManager.RunInBatch(() => { - let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); + let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: "100%" }); linkDoc.type = DocTypes.LINK; let linkDocProto = Doc.GetProto(linkDoc); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 7dc48fb78..d4b1bc8f4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -315,6 +315,7 @@ export namespace DragManager { scaleYs.push(scaleY); let dragElement = ele.cloneNode(true) as HTMLElement; dragElement.style.opacity = "0.7"; + dragElement.style.borderRadius = getComputedStyle(ele).borderRadius; dragElement.style.position = "absolute"; dragElement.style.margin = "0"; dragElement.style.top = "0"; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 63c879d67..2a57180d3 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -348,6 +348,17 @@ export const marks: { [index: string]: MarkSpec } = { }] }, + pFontColor: { + attrs: { + color: { default: "yellow" } + }, + parseDOM: [{ style: 'background: #d9dbdd' }], + toDOM: (node) => { + return ['span', { + style: `color: ${node.attrs.color}` + }]; + } + }, /** FONT SIZES */ pFontSize: { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 9f8d0b2f6..e3e26d1f4 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -111,7 +111,8 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p32, 32); this.fontSizeToNum.set(schema.marks.p48, 48); this.fontSizeToNum.set(schema.marks.p72, 72); - //this.fontSizeToNum.set(schema.marks.pFontSize,schema.marks.pFontSize.) + this.fontSizeToNum.set(schema.marks.pFontSize, 10); + this.fontSizeToNum.set(schema.marks.pFontSize, 10); this.fontSizes = Array.from(this.fontSizeToNum.keys()); //list types diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index ba9f32d7d..2d430512b 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -26,6 +26,14 @@ $linkGap : 3px; opacity: 0.8; } + .documentDecorations-radius { + pointer-events: auto; + background: black; + opacity: 0.8; + transform: translate(10px, 10px); + grid-row: 4; + } + #documentDecorations-topLeftResizer, #documentDecorations-leftResizer, #documentDecorations-bottomLeftResizer { @@ -44,11 +52,25 @@ $linkGap : 3px; grid-column-start: 5; grid-column-end: 7; } + + #documentDecorations-borderRadius{ + grid-column-start: 5; + grid-column-end: 7; + border-radius: 100%; + .borderRadiusTooltip{ + width:10px; + height:10px; + position:absolute; + } + } #documentDecorations-topLeftResizer, #documentDecorations-bottomRightResizer { cursor: nwse-resize; } + #documentDecorations-bottomRightResizer { + grid-row:4; + } #documentDecorations-topRightResizer, #documentDecorations-bottomLeftResizer { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3df520428..c7990647a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -341,6 +341,37 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> iconDoc.y = where[1] + NumCast(selView.props.Document.y); } + _radiusDown = [0, 0]; + @action + onRadiusDown = (e: React.PointerEvent): void => { + e.stopPropagation(); + if (e.button === 0) { + this._radiusDown = [e.clientX, e.clientY]; + this._isPointerDown = true; + this._resizeUndo = UndoManager.StartBatch("DocDecs set radius"); + document.removeEventListener("pointermove", this.onRadiusMove); + document.removeEventListener("pointerup", this.onRadiusUp); + document.addEventListener("pointermove", this.onRadiusMove); + document.addEventListener("pointerup", this.onRadiusUp); + } + } + + onRadiusMove = (e: PointerEvent): void => { + let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1])); + SelectionManager.SelectedDocuments().map(dv => Doc.GetProto(dv.props.Document).borderRounding = `${Math.min(100, dist)}%`); + e.stopPropagation(); + e.preventDefault(); + } + + onRadiusUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isPointerDown = false; + this._resizeUndo && this._resizeUndo.end(); + document.removeEventListener("pointermove", this.onRadiusMove); + document.removeEventListener("pointerup", this.onRadiusUp); + } + @action onPointerDown = (e: React.PointerEvent): void => { e.stopPropagation(); @@ -705,6 +736,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
    e.preventDefault()}>
    e.preventDefault()}>
    e.preventDefault()}>
    +
    e.preventDefault()}>
    {linkButton}
    diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 0461d7299..c7f7bdb66 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -10,6 +10,8 @@ import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; import { undoBatch, UndoManager } from "../util/UndoManager"; import { StrCast } from "../../new_fields/Types"; +import { FormattedTextBox } from "./nodes/FormattedTextBox"; +import { MainOverlayTextBox } from "./MainOverlayTextBox"; library.add(faPen, faHighlighter, faEraser, faBan); @@ -42,6 +44,7 @@ export class InkingControl extends React.Component { switchColor = action((color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { + if (MainOverlayTextBox.Instance.SetColor(color.hex)) return; let selected = SelectionManager.SelectedDocuments(); let oldColors = selected.map(view => { let targetDoc = view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 44c1cb9fc..b85a8040a 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -144,7 +144,7 @@ button:hover { #add-nodes-menu { position: absolute; bottom: 22px; - left: 24px; + left: 250px; > label { background: $dark-color; diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 7d15702a2..d8aaea259 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -36,6 +36,11 @@ export class MainOverlayTextBox extends React.Component this._outerdiv && this._tooltip && !this._outerdiv.contains(this._tooltip) && this._outerdiv.appendChild(this._tooltip); } + public SetColor(color: string) { + return this._textBox && this._textBox.setFontColor(color); + } + + constructor(props: MainOverlayTextBoxProps) { super(props); this._textProxyDiv = React.createRef(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1542fedef..61ccf4c1d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -354,7 +354,7 @@ export class MainView extends React.Component { [React.createRef(), "tree", "Add Tree", addTreeNode], ]; - return < div id="add-nodes-menu" > + return < div id="add-nodes-menu" style={{ left: this.flyoutWidth + 5 }} > diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 31c0fe499..4850c6218 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -44,14 +44,12 @@ export class MarqueeView extends React.Component _commandExecuted = false; @action - cleanupInteractions = (all: boolean = false, rem_keydown: boolean = true) => { + cleanupInteractions = (all: boolean = false) => { if (all) { document.removeEventListener("pointerup", this.onPointerUp, true); document.removeEventListener("pointermove", this.onPointerMove, true); } - if (rem_keydown) { - document.removeEventListener("keydown", this.marqueeCommand, true); - } + document.removeEventListener("keydown", this.marqueeCommand, true); this._visible = false; } @@ -191,12 +189,9 @@ export class MarqueeView extends React.Component SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document); } this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]); - mselect.length ? this.cleanupInteractions(true, false) : this.cleanupInteractions(true); - } - else { - //console.log("invisible"); - this.cleanupInteractions(true); } + //console.log("invisible"); + this.cleanupInteractions(true); if (e.altKey) { e.preventDefault(); @@ -266,13 +261,12 @@ export class MarqueeView extends React.Component } let ink = Cast(this.props.container.props.Document.ink, InkField); let inkData = ink ? ink.inkData : undefined; - let zoomBasis = NumCast(this.props.container.props.Document.scale, 1); let newCollection = Docs.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panX: 0, panY: 0, - borderRounding: e.key === "e" ? -1 : undefined, + borderRounding: e.key === "e" ? "100%" : undefined, backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white", width: bounds.width, height: bounds.height, diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 30cf74f3e..1c00687ed 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -70,11 +70,14 @@ export class CollectionFreeFormDocumentView extends DocComponent { - let br = NumCast(this.props.Document.borderRounding); - return br >= 0 ? br : - NumCast(this.props.Document.nativeWidth) === 0 ? - Math.min(this.props.PanelWidth(), this.props.PanelHeight()) - : Math.min(this.Document.nativeWidth || 0, this.Document.nativeHeight || 0); + let br = StrCast(this.props.Document.borderRounding); + if (br.endsWith("%")) { + let percent = Number(br.substr(0, br.length - 1)) / 100; + let nativeDim = Math.min(NumCast(this.props.Document.nativeWidth), NumCast(this.props.Document.nativeHeight)); + let minDim = percent * (nativeDim ? nativeDim : Math.min(this.props.PanelWidth(), this.props.PanelHeight())); + return minDim; + } + return undefined; } render() { @@ -84,7 +87,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu style={{ outlineColor: "maroon", outlineStyle: "dashed", - outlineWidth: BoolCast(this.props.Document.libraryBrush) && !NumCast(this.props.Document.borderRounding) ? + outlineWidth: BoolCast(this.props.Document.libraryBrush) && !StrCast(this.props.Document.borderRounding) ? `${this.props.ScreenToLocalTransform().Scale}px` : "0px", - border: BoolCast(this.props.Document.libraryBrush) && NumCast(this.props.Document.borderRounding) ? + border: BoolCast(this.props.Document.libraryBrush) && StrCast(this.props.Document.borderRounding) ? `dashed maroon ${this.props.ScreenToLocalTransform().Scale}px` : undefined, borderRadius: "inherit", background: backgroundColor, diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 1eeb04755..bf6f4c764 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -97,6 +97,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return ""; } + @undoBatch + public setFontColor(color: string) { + if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false; + let colorMark = this._editorView!.state.schema.mark(this._editorView!.state.schema.marks.pFontColor, { color: color }); + this._editorView!.dispatch(this._editorView!.state.tr.addMark(this._editorView!.state.selection.from, + this._editorView!.state.selection.to, colorMark)); + return true; + } + constructor(props: FieldViewProps) { super(props); if (this.props.outer_div) { @@ -418,7 +427,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } render() { let style = this.props.isOverlay ? "scroll" : "hidden"; - let rounded = NumCast(this.props.Document.borderRounding) < 0 ? "-rounded" : ""; + let rounded = StrCast(this.props.Document.borderRounding) === "100%" ? "-rounded" : ""; let interactive = InkingControl.Instance.selectedTool ? "" : "interactive"; return (
    Date: Thu, 4 Jul 2019 14:15:11 -0400 Subject: from last --- src/client/views/collections/CollectionSchemaView.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index d06d1dab6..568949efb 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -461,13 +461,28 @@ export class CollectionSchemaPreview extends React.Component) => { this.props.setPreviewScript(e.currentTarget.value); } + @computed get borderRounding() { + let br = StrCast(this.props.Document!.borderRounding); + if (br.endsWith("%")) { + let percent = Number(br.substr(0, br.length - 1)) / 100; + let nativeDim = Math.min(NumCast(this.props.Document!.nativeWidth), NumCast(this.props.Document!.nativeHeight)); + let minDim = percent * (nativeDim ? nativeDim : Math.min(this.PanelWidth(), this.PanelHeight())); + return minDim; + } + return undefined; + } render() { let input = this.props.previewScript === undefined ? (null) :
    ; return (
    {!this.props.Document || !this.props.width ? (null) : ( -
    +
    Date: Thu, 4 Jul 2019 18:20:54 -0400 Subject: added dropping and reordering within Stacking views. needs GUI feedback. --- src/client/util/DragManager.ts | 2 +- .../views/collections/CollectionStackingView.tsx | 58 +++++++++++++++++++++- src/client/views/collections/CollectionSubView.tsx | 9 +++- src/client/views/nodes/ImageBox.tsx | 4 +- 4 files changed, 68 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index d4b1bc8f4..ce1f9d890 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -448,7 +448,7 @@ export namespace DragManager { x: e.x, y: e.y, data: dragData, - mods: e.altKey ? "AltKey" : "" + mods: e.altKey ? "AltKey" : e.ctrlKey ? "CtrlKey" : "" } }) ); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b10907937..6b4eddec9 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -11,6 +11,8 @@ import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; import { resolve } from "bluebird"; +import { undoBatch } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -96,8 +98,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
    ; }); } + docXfs: any[] = [] @computed get children() { + this.docXfs.length = 0; return this.childDocs.filter(d => !d.isMinimized).map((d, i) => { let aspect = d.nativeHeight ? NumCast(d.nativeWidth) / NumCast(d.nativeHeight) : undefined; let dref = React.createRef(); @@ -105,6 +109,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; let height = () => aspect ? width() / aspect : d[HeightSym](); let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); + this.docXfs.push({ dxf: dxf, width: width, height: height }); return (
    doc) { }); } } + + @undoBatch + @action + drop = (e: Event, de: DragManager.DropEvent) => { + let targInd = -1; + let where = [de.x, de.y]; + if (de.data instanceof DragManager.DocumentDragData) { + this.docXfs.map((cd, i) => { + let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); + let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height()); + if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { + targInd = i; + } + }) + } + if (super.drop(e, de)) { + if (targInd !== -1) { + let newDoc = de.data.droppedDocuments[0]; + let docs = this.childDocList; + if (docs) { + let srcInd = docs.indexOf(newDoc); + docs.splice(srcInd, 1); + docs.splice(targInd > srcInd ? targInd - 1 : targInd, 0, newDoc); + } + } + } + return false; + } + @undoBatch + @action + onDrop = (e: React.DragEvent): void => { + let where = [e.clientX, e.clientY]; + let targInd = -1; + this.docXfs.map((cd, i) => { + let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); + let pos1 = cd.dxf().inverse().transformPoint(cd.width(), cd.height()); + if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) { + targInd = i; + } + }) + super.onDrop(e, {}, () => { + if (targInd !== -1) { + let newDoc = this.childDocs[this.childDocs.length - 1]; + let docs = this.childDocList; + if (docs) { + docs.splice(docs.length - 1, 1); + docs.splice(targInd, 0, newDoc); + } + } + }); + } render() { let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.childDocs.filter(d => !d.isMinimized).length, Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); let templatecols = ""; for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `; return ( -
    e.stopPropagation()} > +
    e.stopPropagation()} >
    (schemaCtor: (doc: Doc) => T) { //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) return DocListCast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); } + get childDocList() { + //TODO tfs: This might not be what we want? + //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) + return Cast((BoolCast(this.props.Document.isTemplate) ? this.extensionDoc : this.props.Document)[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey], listSpec(Doc)); + } @action protected async setCursorPosition(position: [number, number]) { @@ -145,7 +150,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action - protected onDrop(e: React.DragEvent, options: DocumentOptions): void { + protected onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) { if (e.ctrlKey) { e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl return; @@ -255,7 +260,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } if (promises.length) { - Promise.all(promises).finally(() => batch.end()); + Promise.all(promises).finally(() => { completed && completed(); batch.end(); }); } else { batch.end(); } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 06bf65f73..dd4e4262d 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -73,7 +73,7 @@ export class ImageBox extends DocComponent(ImageD if (de.mods === "AltKey" && /*this.dataDoc !== this.props.Document &&*/ drop.data instanceof ImageField) { Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(drop.data.url); e.stopPropagation(); - } else { + } else if (de.mods === "CtrlKey") { if (this.extensionDoc !== this.dataDoc) { let layout = StrCast(drop.backgroundLayout); if (layout.indexOf(ImageBox.name) !== -1) { @@ -205,6 +205,8 @@ export class ImageBox extends DocComponent(ImageD requestImageSize(window.origin + RouteStore.corsProxy + "/" + srcpath) .then((size: any) => { let aspect = size.height / size.width; + let rotation = NumCast(this.dataDoc.rotation) % 180; + if (rotation === 90 || rotation === 270) aspect = 1 / aspect; if (Math.abs(layoutdoc[HeightSym]() / layoutdoc[WidthSym]() - aspect) > 0.01) { setTimeout(action(() => { layoutdoc.height = layoutdoc[WidthSym]() * aspect; -- cgit v1.2.3-70-g09d2 From 1b02511185e22349e68bab1f6b8ce74b874a3ba5 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 4 Jul 2019 19:54:27 -0400 Subject: Made some changes to clean up some GoldenLayout stuff --- src/client/goldenLayout.js | 11 ++++ src/client/util/DragManager.ts | 1 + src/client/views/_nodeModuleOverrides.scss | 18 +++---- .../views/collections/CollectionDockingView.tsx | 58 ++++++++++------------ src/client/views/nodes/DocumentView.tsx | 20 ++++---- src/client/views/search/SearchItem.tsx | 2 +- 6 files changed, 57 insertions(+), 53 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 54c9c6068..28c009645 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -2271,6 +2271,17 @@ this._dragListener.on('dragStop', this._createDragListener, this); }, + destroy: function () { + this._dragListener.destroy(); + this._element = null; + this._itemConfig = null; + this._dragListener = null; + const index = this._layoutManager._dragSources.indexOf(this); + if (index > -1) { + this._layoutManager._dragSources.splice(index, 1); + } + }, + /** * Callback for the DragListener's dragStart event * diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ce1f9d890..2fd83ae56 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -43,6 +43,7 @@ export function SetupDrag( e.stopPropagation(); e.preventDefault(); if (e.shiftKey && CollectionDockingView.Instance) { + e.persist(); CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]); } else { document.addEventListener("pointermove", onRowMove); diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss index 3594ac9f4..b8a7db034 100644 --- a/src/client/views/_nodeModuleOverrides.scss +++ b/src/client/views/_nodeModuleOverrides.scss @@ -2,21 +2,21 @@ // goldenlayout stuff div .lm_header { - background: $dark-color; + background: $dark-color; } .lm_tab { - margin-top: 0.6em !important; - padding-top: 0.5em !important; - min-height: 1.35em; - padding-bottom: 0px; - border-radius: 5px; - font-family: $sans-serif !important; + // margin-top: 0.6em !important; + // padding-top: 0.5em !important; + // min-height: 1.35em; + // padding-bottom: 0px; + // border-radius: 5px; + font-family: $sans-serif !important; } .lm_header .lm_controls { - right: 1em !important; + right: 1em !important; } // @TODO the ril__navgiation buttons in the img gallery are a lil messed up but I can't figure out -// why. Low priority for now but it's bugging me. --Julie +// why. Low priority for now but it's bugging me. --Julie \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c5f8fb728..f146ec62b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -62,12 +62,30 @@ export class CollectionDockingView extends React.Component - this.AddRightSplit(dragDoc, dragDataDocs ? dragDataDocs[i] : undefined, true).contentItems[0].tab._dragListener. - onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); + public StartOtherDrag(e: any, dragDocs: Doc[], dragDataDocs: (Doc | undefined)[] = []) { + let config: any; + if (dragDocs.length === 1) { + config = CollectionDockingView.makeDocumentConfig(dragDocs[0], dragDataDocs[0]); + } else { + config = { + type: 'row', + content: dragDocs.map((doc, i) => { + CollectionDockingView.makeDocumentConfig(doc, dragDataDocs[i]); + }) + }; + } + const div = document.createElement("div"); + const dragSource = this._goldenLayout.createDragSource(div, config); + dragSource._dragListener.on("dragStop", () => { + dragSource.destroy(); + }); + dragSource._dragListener.onMouseDown(e); + // dragSource.destroy(); + // this.hack = true; + // this.undohack = UndoManager.StartBatch("goldenDrag"); + // dragDocs.map((dragDoc, i) => + // this.AddRightSplit(dragDoc, dragDataDocs[i], true).contentItems[0].tab._dragListener. + // onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 })); } @action @@ -299,33 +317,7 @@ export class CollectionDockingView extends React.Component) => (sourceDoc instanceof Doc) && DragLinksAsDocuments(tab, x, y, sourceDoc))); - } else - if ((className === "lm_title" || className === "lm_tab lm_active") && e.shiftKey) { - e.stopPropagation(); - e.preventDefault(); - let x = e.clientX; - let y = e.clientY; - let docid = (e.target as any).DashDocId; - let datadocid = (e.target as any).DashDataDocId; - let tab = (e.target as any).parentElement as HTMLElement; - let glTab = (e.target as any).Tab; - if (glTab && glTab.contentItem && glTab.contentItem.parent) { - glTab.contentItem.parent.setActiveContentItem(glTab.contentItem); - } - DocServer.GetRefField(docid).then(action(async (f: Opt) => { - if (f instanceof Doc) { - let dataDoc = (datadocid !== docid) ? await DocServer.GetRefField(datadocid) : f; - DragManager.StartDocumentDrag([tab], new DragManager.DocumentDragData([f], [dataDoc instanceof Doc ? dataDoc : f]), x, y, - { - handlers: { - dragComplete: emptyFunction, - }, - hideSource: false, - withoutShiftDrag: true - }); - } - })); - } + } if (className === "lm_drag_handle" || className === "lm_close" || className === "lm_maximise" || className === "lm_minimise" || className === "lm_close_tab") { this._flush = true; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a84cac37f..2610d0e6d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -362,16 +362,16 @@ export class DocumentView extends DocComponent(Docu this._downX = e.clientX; this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; - if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]); - e.stopPropagation(); - } else { - if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); - } + // if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { + // CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]); + // e.stopPropagation(); + // } else { + if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + // } } onPointerMove = (e: PointerEvent): void => { if (!e.cancelBubble && this.active) { diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 79a06c81a..606ac4e85 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -231,7 +231,7 @@ export class SearchItem extends React.Component { onPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); - e.preventDefault(); + // e.preventDefault(); const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc; DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, { handlers: { dragComplete: emptyFunction }, -- cgit v1.2.3-70-g09d2 From ad87140f4a52984d754c06fcb55e66a0cc935ce2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 4 Jul 2019 23:18:13 -0400 Subject: fixed shift-dragging of documents --- src/client/util/DragManager.ts | 1 - src/client/views/DocumentDecorations.tsx | 1 - src/client/views/collections/CollectionDockingView.tsx | 3 --- src/client/views/collections/CollectionSchemaView.tsx | 1 - src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 1 - src/client/views/search/SearchItem.tsx | 1 - 7 files changed, 1 insertion(+), 9 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2fd83ae56..f9492f452 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -41,7 +41,6 @@ export function SetupDrag( let onItemDown = async (e: React.PointerEvent) => { if (e.button === 0) { e.stopPropagation(); - e.preventDefault(); if (e.shiftKey && CollectionDockingView.Instance) { e.persist(); CollectionDockingView.Instance.StartOtherDrag(e, [await docFunc()]); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c7990647a..48b89ff4e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -172,7 +172,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> document.addEventListener("pointermove", this.onBackgroundMove); document.addEventListener("pointerup", this.onBackgroundUp); e.stopPropagation(); - e.preventDefault(); } @action diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f146ec62b..19d07ecdc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -321,9 +321,6 @@ export class CollectionDockingView extends React.Component doc) { onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { if (this.props.isSelected()) e.stopPropagation(); - else e.preventDefault(); } } diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index dd4e4262d..5da52f4c2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -95,7 +95,7 @@ export class ImageBox extends DocComponent(ImageD onPointerDown = (e: React.PointerEvent): void => { if (e.shiftKey && e.ctrlKey) { e.stopPropagation(); // allows default system drag drop of images with shift+ctrl only - } else e.preventDefault(); + } // if (Date.now() - this._lastTap < 300) { // if (e.buttons === 1) { // this._downX = e.clientX; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 1239b498f..264d3c1f7 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -119,7 +119,6 @@ export class VideoBox extends DocComponent(VideoD }); } onPointerDown = (e: React.PointerEvent) => { - e.preventDefault(); e.stopPropagation(); } diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 606ac4e85..51f0ed607 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -231,7 +231,6 @@ export class SearchItem extends React.Component { onPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); - // e.preventDefault(); const doc = Doc.IsPrototype(this.props.doc) ? Doc.MakeDelegate(this.props.doc) : this.props.doc; DragManager.StartDocumentDrag([e.currentTarget], new DragManager.DocumentDragData([doc], []), e.clientX, e.clientY, { handlers: { dragComplete: emptyFunction }, -- cgit v1.2.3-70-g09d2 From aedd283fb9f9eff4145e27658bc6647982256032 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 5 Jul 2019 00:34:19 -0400 Subject: beginning implementation of key value tags for all imported documents --- src/client/documents/Documents.ts | 12 +- .../util/Import & Export/DirectoryImportBox.scss | 0 .../util/Import & Export/DirectoryImportBox.tsx | 181 +++++++++++++++++++++ src/client/util/Import & Export/ImageImporter.tsx | 67 -------- src/client/util/Import & Export/ImportBox.tsx | 134 --------------- src/client/util/request-image-size.js | 6 +- src/client/views/MainView.tsx | 1 - src/client/views/collections/CollectionSubView.tsx | 4 +- src/client/views/nodes/DocumentContentsView.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 4 +- 10 files changed, 198 insertions(+), 215 deletions(-) create mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss create mode 100644 src/client/util/Import & Export/DirectoryImportBox.tsx delete mode 100644 src/client/util/Import & Export/ImageImporter.tsx delete mode 100644 src/client/util/Import & Export/ImportBox.tsx (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5d637dd3a..26b5498a2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -36,7 +36,7 @@ import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; import { LinkManager } from "../util/LinkManager"; import { DocumentManager } from "../util/DocumentManager"; -import ImportBox from "../util/Import & Export/ImportBox"; +import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -180,7 +180,7 @@ export namespace Docs { } function CreateImportPrototype(): Doc { - let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); + let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", DirectoryImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT }); return importProto; } @@ -347,7 +347,7 @@ export namespace Docs { return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } - export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> { + export async function getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise> { let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; if (type.indexOf("image") !== -1) { ctor = Docs.ImageDocument; @@ -370,17 +370,17 @@ export namespace Docs { if (path.includes(window.location.hostname)) { let s = path.split('/'); let id = s[s.length - 1]; - DocServer.GetRefField(id).then(field => { + return DocServer.GetRefField(id).then(field => { if (field instanceof Doc) { let alias = Doc.MakeAlias(field); alias.x = options.x || 0; alias.y = options.y || 0; alias.width = options.width || 300; alias.height = options.height || options.width || 300; - addDocument && addDocument(alias, false); + return alias; } + return undefined; }); - return undefined; } ctor = Docs.WebDocument; options = { height: options.width, ...options, title: path, nativeWidth: undefined }; diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx new file mode 100644 index 000000000..2d77f6ae6 --- /dev/null +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -0,0 +1,181 @@ +import "fs"; +import React = require("react"); +import { Doc } from "../../../new_fields/Doc"; +import { DocServer } from "../../DocServer"; +import { RouteStore } from "../../../server/RouteStore"; +import { action, observable, runInAction } from "mobx"; +import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; +import Measure, { ContentRect } from "react-measure"; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons'; +import { Docs, DocumentOptions } from "../../documents/Documents"; +import { EditableView } from "../../views/EditableView"; + +export default class DirectoryImportBox extends React.Component { + private selector = React.createRef(); + @observable private top = 0; + @observable private left = 0; + private dimensions = 50; + + @observable private key = "Key"; + @observable private value = "Value"; + + public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); } + + constructor(props: FieldViewProps) { + super(props); + library.add(faArrowUp, faTag); + } + + updateKey = (newKey: string) => { + runInAction(() => this.key = newKey); + console.log("KEY ", this.key); + return true; + } + + updateValue = (newValue: string) => { + runInAction(() => this.value = newValue); + console.log("VALUE ", this.value); + return true; + } + + handleSelection = async (e: React.ChangeEvent) => { + let promises: Promise[] = []; + let docs: Doc[] = []; + + let files = e.target.files; + if (!files || files.length === 0) return; + + let directory = (files.item(0) as any).webkitRelativePath.split("/", 1); + + for (let i = 0; i < files.length; i++) { + let uploaded_file = files.item(i); + + if (!uploaded_file) { + continue; + } + + let formData = new FormData(); + formData.append('file', uploaded_file); + let dropFileName = uploaded_file ? uploaded_file.name : "-empty-"; + let type = uploaded_file.type; + + let prom = fetch(DocServer.prepend(RouteStore.upload), { + method: 'POST', + body: formData + }).then(async (res: Response) => { + (await res.json()).map(action((file: any) => { + let path = DocServer.prepend(file); + console.log(path); + let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName }); + docPromise.then(doc => doc && docs.push(doc)); + })); + }); + promises.push(prom); + } + + await Promise.all(promises); + + let doc = this.props.Document; + let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") }; + let parent = this.props.ContainingCollectionView; + if (parent) { + let importContainer = Docs.StackingDocument(docs, options); + Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer); + this.props.removeDocument && this.props.removeDocument(doc); + } + } + + componentDidMount() { + this.selector.current!.setAttribute("directory", ""); + this.selector.current!.setAttribute("webkitdirectory", ""); + } + + @action + preserveCentering = (rect: ContentRect) => { + let bounds = rect.offset!; + if (bounds.width === 0 || bounds.height === 0) { + return; + } + let offset = this.dimensions / 2; + this.left = bounds.width / 2 - offset; + this.top = bounds.height / 2 - offset; + } + + render() { + let dimensions = 50; + let keyValueStyle = { paddingLeft: 5, width: "50%" }; + return ( + + {({ measureRef }) => +
    + +