From 2e745fca1bf2cea9fe3518bf85590ad2d871afe0 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Wed, 17 Jul 2019 13:03:10 -0400 Subject: cursor proto id check tweak --- src/client/views/collections/CollectionSubView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 4fd11add4..5d5189336 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -9,7 +9,7 @@ import { BoolCast, Cast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from "../../../server/RouteStore"; import { DocServer } from "../../DocServer"; -import { Docs, DocumentOptions } from "../../documents/Documents"; +import { Docs, DocumentOptions, DocumentType } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; @@ -74,7 +74,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { return; } // The following conditional detects a recurring bug we've seen on the server - if (proto[Id] === "collectionProto") { + if (proto[Id] === Docs.Prototypes.get(DocumentType.COL)[Id]) { alert("COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info..."); console.log(doc); console.log(proto); -- cgit v1.2.3-70-g09d2 From 5aa6bcdd4e23bb9e9d05181d0dc6b638e45e397b Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 17 Jul 2019 20:48:14 -0400 Subject: Moved DocServer.prepend and changed how corsProxy works --- src/Utils.ts | 13 +++++ src/client/DocServer.ts | 8 ---- src/client/util/History.ts | 2 +- .../util/Import & Export/DirectoryImportBox.tsx | 4 +- src/client/util/SearchUtil.ts | 3 +- src/client/util/TooltipTextMenu.tsx | 8 ++-- src/client/views/MainView.tsx | 4 +- src/client/views/SearchBox.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 7 +-- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.tsx | 5 +- src/client/views/nodes/ImageBox.tsx | 6 +-- src/client/views/nodes/VideoBox.tsx | 4 +- src/client/views/pdf/PDFViewer.tsx | 55 +++++----------------- src/client/views/search/SearchBox.tsx | 6 +-- src/mobile/ImageUpload.tsx | 5 +- .../authentication/models/current_user_utils.ts | 5 +- src/server/index.ts | 2 +- 19 files changed, 65 insertions(+), 81 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index e8a80bdc3..ac6d127cc 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -2,6 +2,7 @@ import v4 = require('uuid/v4'); import v5 = require("uuid/v5"); import { Socket } from 'socket.io'; import { Message } from './server/Message'; +import { RouteStore } from './server/RouteStore'; export class Utils { @@ -27,6 +28,18 @@ export class Utils { return { scale, translateX, translateY }; } + /** + * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the + * requested extension + * @param extension the specified sub-path to append to the window origin + */ + public static prepend(extension: string): string { + return window.location.origin + extension; + } + public static CorsProxy(url: string): string { + return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url); + } + public static CopyText(text: string) { var textArea = document.createElement("textarea"); textArea.value = text; diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 6737657c8..8c64d2b2f 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -47,14 +47,6 @@ export namespace DocServer { Utils.AddServerHandler(_socket, MessageStore.DeleteField, respondToDelete); Utils.AddServerHandler(_socket, MessageStore.DeleteFields, respondToDelete); } - /** - * A convenience method. Prepends the full path (i.e. http://localhost:1050) to the - * requested extension - * @param extension the specified sub-path to append to the window origin - */ - export function prepend(extension: string): string { - return window.location.origin + extension; - } function errorFunc(): never { throw new Error("Can't use DocServer without calling init first"); diff --git a/src/client/util/History.ts b/src/client/util/History.ts index cbf5b3fc8..e9ff21b22 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -129,7 +129,7 @@ export namespace HistoryUtil { function addStringifier(type: string, keys: string[], customStringifier?: (state: ParsedUrl, current: string) => string) { stringifiers[type] = state => { - let path = DocServer.prepend(`/${type}`); + let path = Utils.prepend(`/${type}`); if (customStringifier) { path = customStringifier(state, path); } diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index c096e9ceb..75b0b52a7 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -98,12 +98,12 @@ export default class DirectoryImportBox extends React.Component runInAction(() => this.remaining++); - let prom = fetch(DocServer.prepend(RouteStore.upload), { + let prom = fetch(Utils.prepend(RouteStore.upload), { method: 'POST', body: formData }).then(async (res: Response) => { (await res.json()).map(action((file: any) => { - let docPromise = Docs.Get.DocumentFromType(type, DocServer.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName }); + let docPromise = Docs.Get.DocumentFromType(type, Utils.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName }); docPromise.then(doc => { doc && docs.push(doc) && runInAction(() => this.remaining--); }); diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index abf1a7c32..ee5a83710 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -2,6 +2,7 @@ import * as rp from 'request-promise'; import { DocServer } from '../DocServer'; import { Doc } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; +import { Utils } from '../../Utils'; export namespace SearchUtil { export type HighlightingResult = { [id: string]: { [key: string]: string[] } }; @@ -29,7 +30,7 @@ export namespace SearchUtil { export function Search(query: string, returnDocs: false, options?: SearchParams): Promise; export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) { query = query || "*"; //If we just have a filter query, search for * as the query - const result: IdSearchResult = JSON.parse(await rp.get(DocServer.prepend("/search"), { + const result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), { qs: { ...options, q: query }, })); if (!returnDocs) { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 309d19016..a4c053de2 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -19,7 +19,7 @@ import { CollectionDockingView } from "../views/collections/CollectionDockingVie import { DocumentManager } from "./DocumentManager"; import { Id } from "../../new_fields/FieldSymbols"; import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; -import { SelectionManager } from "./SelectionManager"; +import { Utils } from "../../Utils"; //appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. export class TooltipTextMenu { @@ -213,8 +213,8 @@ export class TooltipTextMenu { let link = node && node.marks.find(m => m.type.name === "link"); if (link) { let href: string = link.attrs.href; - if (href.indexOf(DocServer.prepend("/doc/")) === 0) { - let docid = href.replace(DocServer.prepend("/doc/"), ""); + if (href.indexOf(Utils.prepend("/doc/")) === 0) { + let docid = href.replace(Utils.prepend("/doc/"), ""); DocServer.GetRefField(docid).then(action((f: Opt) => { if (f instanceof Doc) { if (DocumentManager.Instance.getDocumentView(f)) { @@ -254,7 +254,7 @@ export class TooltipTextMenu { if (docView && docView.props.ContainingCollectionView) { proto.sourceContext = docView.props.ContainingCollectionView.props.Document; } - linkDoc instanceof Doc && this.makeLink(DocServer.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab"); + linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab"); }), }, hideSource: false diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ce7369220..94a4835a1 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -16,7 +16,7 @@ import { listSpec } from '../../new_fields/Schema'; import { Cast, FieldValue, NumCast, BoolCast, StrCast } from '../../new_fields/Types'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; import { RouteStore } from '../../server/RouteStore'; -import { emptyFunction, returnOne, returnTrue } from '../../Utils'; +import { emptyFunction, returnOne, returnTrue, Utils } from '../../Utils'; import { DocServer } from '../DocServer'; import { Docs } from '../documents/Documents'; import { SetupDrag } from '../util/DragManager'; @@ -433,7 +433,7 @@ export class MainView extends React.Component { return [ this.isSearchVisible ?
: null,
-
+ ]; } diff --git a/src/client/views/SearchBox.tsx b/src/client/views/SearchBox.tsx index 1b9be841f..33cb63df5 100644 --- a/src/client/views/SearchBox.tsx +++ b/src/client/views/SearchBox.tsx @@ -13,6 +13,7 @@ import { Docs } from '../documents/Documents'; import { SetupDrag } from '../util/DragManager'; import { SearchItem } from './search/SearchItem'; import "./SearchBox.scss"; +import { Utils } from '../../Utils'; library.add(faSearch); library.add(faObjectGroup); @@ -47,7 +48,7 @@ export class SearchBox extends React.Component { @action getResults = async (query: string) => { - let response = await rp.get(DocServer.prepend('/search'), { + let response = await rp.get(Utils.prepend('/search'), { qs: { query } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a193ff677..ba7903419 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -465,7 +465,7 @@ export class CollectionDockingView extends React.Component boolean; @@ -164,7 +165,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } else { let path = window.location.origin + "/doc/"; if (text.startsWith(path)) { - let docid = text.replace(DocServer.prepend("/doc/"), "").split("?")[0]; + let docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0]; DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView @@ -193,7 +194,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (item.kind === "string" && item.type.indexOf("uri") !== -1) { let str: string; let prom = new Promise(resolve => e.dataTransfer.items[i].getAsString(resolve)) - .then(action((s: string) => rp.head(DocServer.prepend(RouteStore.corsProxy + "/" + (str = s))))) + .then(action((s: string) => rp.head(Utils.CorsProxy(str = s)))) .then(result => { let type = result["content-type"]; if (type) { @@ -219,7 +220,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { }).then(async (res: Response) => { (await res.json()).map(action((file: any) => { let full = { ...options, nativeWidth: type.indexOf("video") !== -1 ? 600 : 300, width: 300, title: dropFileName }; - let path = DocServer.prepend(file); + let path = Utils.prepend(file); Docs.Get.DocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc)); })); }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 245dd319d..af1cc079b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -557,13 +557,13 @@ export class DocumentView extends DocComponent(Docu }); cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); cm.addItem({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); - cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(DocServer.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); + cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" }); cm.addItem({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; try { - let stuff = await rp.get(DocServer.prepend(RouteStore.getUsers)); + let stuff = await rp.get(Utils.prepend(RouteStore.getUsers)); const users: User[] = JSON.parse(stuff); usersMenu = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({ description: email, event: async () => { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 066cc40e2..99801ecff 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -35,6 +35,7 @@ import "./FormattedTextBox.scss"; import React = require("react"); import { DateField } from '../../../new_fields/DateField'; import { thisExpression } from 'babel-types'; +import { Utils } from '../../../Utils'; library.add(faEdit); library.add(faSmile); @@ -311,8 +312,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe href = parent.childNodes[0].href ? parent.childNodes[0].href : parent.href; } if (href) { - if (href.indexOf(DocServer.prepend("/doc/")) === 0) { - this._linkClicked = href.replace(DocServer.prepend("/doc/"), "").split("?")[0]; + if (href.indexOf(Utils.prepend("/doc/")) === 0) { + this._linkClicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; if (this._linkClicked) { DocServer.GetRefField(this._linkClicked).then(async linkDoc => { if (linkDoc instanceof Doc) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index ebe3732e6..4ffd265d7 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -165,12 +165,12 @@ export class ImageBox extends DocComponent(ImageD recorder.ondataavailable = async function (e: any) { const formData = new FormData(); formData.append("file", e.data); - const res = await fetch(DocServer.prepend(RouteStore.upload), { + const res = await fetch(Utils.prepend(RouteStore.upload), { method: 'POST', body: formData }); const files = await res.json(); - const url = DocServer.prepend(files[0]); + const url = Utils.prepend(files[0]); // upload to server with known URL let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", x: NumCast(self.props.Document.x), y: NumCast(self.props.Document.y), width: 200, height: 32 }); audioDoc.embed = true; @@ -327,7 +327,7 @@ export class ImageBox extends DocComponent(ImageD let id = (this.props as any).id; // bcz: used to set id = "isExpander" in templates.tsx let nativeWidth = FieldValue(this.Document.nativeWidth, pw); let nativeHeight = FieldValue(this.Document.nativeHeight, 0); - let paths: string[] = [window.origin + RouteStore.corsProxy + "/" + "http://www.cs.brown.edu/~bcz/noImage.png"]; + let paths: string[] = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; // this._curSuffix = ""; // if (w > 20) { Doc.UpdateDocumentExtensionForField(this.dataDoc, this.props.fieldKey); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 8b8e500c4..0f2d18f6b 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -122,7 +122,7 @@ export class VideoBox extends DocComponent(VideoD public static async convertDataUri(imageUri: string, returnedFilename: string) { try { - let posting = DocServer.prepend(RouteStore.dataUriToImage); + let posting = Utils.prepend(RouteStore.dataUriToImage); const returnedUri = await rp.post(posting, { body: { uri: imageUri, @@ -164,7 +164,7 @@ export class VideoBox extends DocComponent(VideoD let filename = encodeURIComponent("snapshot" + this.props.Document.title + "_" + this.props.Document.curPage).replace(/\./g, ""); VideoBox.convertDataUri(dataUrl, filename).then(returnedFilename => { if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); + let url = Utils.prepend(returnedFilename); let imageSummary = Docs.Create.ImageDocument(url, { x: NumCast(this.props.Document.x) + width, y: NumCast(this.props.Document.y), width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 699a1ffd7..b7ded7e06 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -4,23 +4,18 @@ import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import * as rp from "request-promise"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; -import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types"; -import { emptyFunction } from "../../../Utils"; -import { DocServer } from "../../DocServer"; -import { Docs, DocUtils, DocumentOptions } from "../../documents/Documents"; -import { DocumentManager } from "../../util/DocumentManager"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction, Utils } from "../../../Utils"; +import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; -import { DocumentView } from "../nodes/DocumentView"; -import { PDFBox, handleBackspace } from "../nodes/PDFBox"; +import { PDFBox } from "../nodes/PDFBox"; import Page from "./Page"; import "./PDFViewer.scss"; import React = require("react"); -import PDFMenu from "./PDFMenu"; -import { UndoManager } from "../../util/UndoManager"; -import { CompileScript, CompiledScript, CompileResult } from "../../util/Scripting"; +import { CompileScript, CompileResult } from "../../util/Scripting"; import { ScriptField } from "../../../new_fields/ScriptField"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import Annotation from "./Annotation"; @@ -90,16 +85,12 @@ export class Viewer extends React.Component { private _annotationReactionDisposer?: IReactionDisposer; private _dropDisposer?: DragManager.DragDropDisposer; private _filterReactionDisposer?: IReactionDisposer; - private _activeReactionDisposer?: IReactionDisposer; private _viewer: React.RefObject; private _mainCont: React.RefObject; private _pdfViewer: any; // private _textContent: Pdfjs.TextContent[] = []; private _pdfFindController: any; private _searchString: string = ""; - private _rendered: boolean = false; - private _pageIndex: number = -1; - private _matchIndex: number = 0; constructor(props: IViewerProps) { super(props); @@ -135,23 +126,6 @@ export class Viewer extends React.Component { }, { fireImmediately: true }); - this._activeReactionDisposer = reaction( - () => this.props.parent.props.active(), - () => { - runInAction(() => { - if (!this.props.parent.props.active()) { - this._searching = false; - this._pdfFindController = null; - if (this._viewer.current) { - let cns = this._viewer.current.childNodes; - for (let i = cns.length - 1; i >= 0; i--) { - cns.item(i).remove(); - } - } - } - }); - } - ); if (this.props.parent.props.ContainingCollectionView) { this._filterReactionDisposer = reaction( @@ -346,7 +320,7 @@ export class Viewer extends React.Component { this._isPage[page] = "image"; const address = this.props.url; try { - let res = JSON.parse(await rp.get(DocServer.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); + let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${address.substring("files/".length, address.length - ".pdf".length)}-${page + 1}.PNG`))); runInAction(() => this._visibleElements[page] = ); @@ -476,7 +450,6 @@ export class Viewer extends React.Component { phraseSearch: true, query: searchString }); - this._rendered = true; }); container.addEventListener("pagerendered", () => { console.log("rendered"); @@ -488,7 +461,6 @@ export class Viewer extends React.Component { phraseSearch: true, query: searchString }); - this._rendered = true; }); } } @@ -563,7 +535,6 @@ export class Viewer extends React.Component { }); container.addEventListener("pagerendered", () => { console.log("rendered"); - this._rendered = true; }); this._pdfViewer.setDocument(this.props.pdf); this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer); @@ -703,17 +674,17 @@ class SimpleLinkService { externalLinkRel: any = null; pdf: any = null; - navigateTo(dest: any) { } + navigateTo() { } - getDestinationHash(dest: any) { return "#"; } + getDestinationHash() { return "#"; } - getAnchorUrl(hash: any) { return "#"; } + getAnchorUrl() { return "#"; } - setHash(hash: any) { } + setHash() { } - executeNamedAction(action: any) { } + executeNamedAction() { } - cachePageRef(pageNum: any, pageRef: any) { } + cachePageRef() { } get pagesCount() { return this.pdf ? this.pdf.numPages : 0; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 16c44225a..2214ac8af 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -9,12 +9,12 @@ import { Docs } from '../../documents/Documents'; import { NumCast, Cast } from '../../../new_fields/Types'; import { Doc } from '../../../new_fields/Doc'; import { SearchItem } from './SearchItem'; -import { DocServer } from '../../DocServer'; import * as rp from 'request-promise'; import { Id } from '../../../new_fields/FieldSymbols'; import { SearchUtil } from '../../util/SearchUtil'; import { RouteStore } from '../../../server/RouteStore'; import { FilterBox } from './FilterBox'; +import { Utils } from '../../../Utils'; @observer @@ -74,7 +74,7 @@ export class SearchBox extends React.Component { public static async convertDataUri(imageUri: string, returnedFilename: string) { try { - let posting = DocServer.prepend(RouteStore.dataUriToImage); + let posting = Utils.prepend(RouteStore.dataUriToImage); const returnedUri = await rp.post(posting, { body: { uri: imageUri, @@ -154,7 +154,7 @@ export class SearchBox extends React.Component { filteredDocs.forEach(doc => { const index = this._resultsSet.get(doc); const highlight = highlights[doc[Id]]; - const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)) : [] + const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)) : []; if (index === undefined) { this._resultsSet.set(doc, this._results.length); this._results.push([doc, hlights]); diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index a8f94b746..33a615cbf 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -11,6 +11,7 @@ import { listSpec } from '../new_fields/Schema'; import { List } from '../new_fields/List'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; +import { Utils } from '../Utils'; @@ -57,7 +58,7 @@ class Uploader extends React.Component { this.status = "getting user document"; - const res = await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)); + const res = await rp.get(Utils.prepend(RouteStore.getUserDocumentId)); if (!res) { throw new Error("No user id returned"); } @@ -104,6 +105,8 @@ class Uploader extends React.Component { } +DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload"); + ReactDOM.render(( ), diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index e796ccb43..1c52a3f11 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -12,6 +12,7 @@ import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { RouteStore } from "../../RouteStore"; +import { Utils } from "../../../Utils"; export class CurrentUserUtils { private static curr_email: string; @@ -74,7 +75,7 @@ export class CurrentUserUtils { } public static loadCurrentUser() { - return rp.get(DocServer.prepend(RouteStore.getCurrUser)).then(response => { + return rp.get(Utils.prepend(RouteStore.getCurrUser)).then(response => { if (response) { const result: { id: string, email: string } = JSON.parse(response); return result; @@ -87,7 +88,7 @@ export class CurrentUserUtils { public static async loadUserDocument({ id, email }: { id: string, email: string }) { this.curr_id = id; this.curr_email = email; - await rp.get(DocServer.prepend(RouteStore.getUserDocumentId)).then(id => { + await rp.get(Utils.prepend(RouteStore.getUserDocumentId)).then(id => { if (id) { return DocServer.GetRefField(id).then(async field => { if (field instanceof Doc) { diff --git a/src/server/index.ts b/src/server/index.ts index b3859aa4f..6b4e59bfc 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -417,7 +417,7 @@ app.get(RouteStore.reset, getReset); app.post(RouteStore.reset, postReset); app.use(RouteStore.corsProxy, (req, res) => - req.pipe(request(req.url.substring(1))).pipe(res)); + req.pipe(request(decodeURIComponent(req.url.substring(1)))).pipe(res)); app.get(RouteStore.delete, (req, res) => { if (release) { -- cgit v1.2.3-70-g09d2 From 828fdd80ab8af26f6e2ef521e05a71e7e500bf8c Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 24 Jul 2019 17:18:15 -0400 Subject: view specsss --- package.json | 1 + src/client/views/EditableView.tsx | 2 +- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 9 +- .../CollectionStackingViewFieldColumn.tsx | 44 +++- src/client/views/collections/CollectionSubView.tsx | 15 +- .../views/collections/CollectionViewChromes.scss | 113 ++++++++- .../views/collections/CollectionViewChromes.tsx | 261 ++++++++++++++++----- src/client/views/collections/KeyRestrictionRow.tsx | 45 ++++ 9 files changed, 428 insertions(+), 66 deletions(-) create mode 100644 src/client/views/collections/KeyRestrictionRow.tsx (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/package.json b/package.json index 4a15cbb2f..37052fde3 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "image-size": "^0.7.4", "imagesloaded": "^4.1.4", "jquery-awesome-cursor": "^0.3.1", + "js-datepicker": "^4.6.6", "jsonwebtoken": "^8.5.0", "jsx-to-string": "^1.4.0", "lodash": "^4.17.11", diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index a5bb40243..bd00d47b9 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -116,7 +116,7 @@ export class EditableView extends React.Component { return (
+ onClick={this.onClick}> {this.props.contents}
); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 94a4835a1..69602deed 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,5 +1,5 @@ import { IconName, library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowDown, faCloudUploadAlt, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faCaretUp, faLongArrowAltRight, faCloudUploadAlt, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, reaction, trace } from 'mobx'; import { observer } from 'mobx-react'; @@ -127,7 +127,9 @@ export class MainView extends React.Component { library.add(faCut); library.add(faCommentAlt); library.add(faThumbtack); + library.add(faLongArrowAltRight); library.add(faCheck); + library.add(faCaretUp); library.add(faArrowDown); library.add(faArrowUp); library.add(faCloudUploadAlt); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e4e6f92e3..a78a47ffb 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -249,6 +249,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return false; } + sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => { + let descending = BoolCast(this.props.Document.stackingHeadersSortDescending); + let firstEntry = descending ? b : a; + let secondEntry = descending ? a : b; + return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1; + } + render() { let headings = Array.from(this.Sections.keys()); let editableViewProps = { @@ -264,7 +271,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())], ["width = height", this.filteredChildren.filter(f => Math.abs(f[WidthSym]() - f[HeightSym]()) < 1)], ["height > width", this.filteredChildren.filter(f => f[WidthSym]() + 1 <= f[HeightSym]())]]. */} - {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).sort((a, b) => a[0].toString() > b[0].toString() ? 1 : -1). + {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). map(section => this.section(section[0], section[1] as Doc[])) : this.section(undefined, this.filteredChildren)} {this.props.Document.sectionFilter ? diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index fe24c63c7..582adc418 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -16,6 +16,8 @@ import "./CollectionStackingView.scss"; import { Docs } from "../../documents/Documents"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { CompileScript } from "../../util/Scripting"; interface CSVFieldColumnProps { @@ -35,6 +37,7 @@ export class CollectionStackingViewFieldColumn extends React.Component = React.createRef(); @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading; @@ -182,6 +185,43 @@ export class CollectionStackingViewFieldColumn extends React.Component { + let alias = Doc.MakeAlias(this.props.parent.props.Document); + let key = StrCast(this.props.parent.props.Document.sectionFilter); + let value = this.getValue(this._heading); + value = typeof value === "string" ? `"${value}"` : value; + let script = `return doc.${key} === ${value}`; + let compiled = CompileScript(script, { params: { doc: Doc.name } }); + if (compiled.compiled) { + let scriptField = new ScriptField(compiled); + alias.viewSpecScript = scriptField; + let dragData = new DragManager.DocumentDragData([alias], [alias.proto]); + DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY); + } + + e.stopPropagation(); + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.pointerUp); + } + + pointerUp = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + + document.removeEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.pointerUp); + } + + headerDown = (e: React.PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + + document.removeEventListener("pointermove", this.startDrag); + document.addEventListener("pointermove", this.startDrag); + document.removeEventListener("pointerup", this.pointerUp); + document.addEventListener("pointerup", this.pointerUp); + } + render() { let cols = this.props.cols(); let key = StrCast(this.props.parent.props.Document.sectionFilter); @@ -199,11 +239,11 @@ export class CollectionStackingViewFieldColumn extends React.Component {/* the default bucket (no key value) has a tooltip that describes what it is. Further, it does not have a color and cannot be deleted. */} -
boolean; @@ -54,7 +56,18 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { let self = this; //TODO tfs: This might not be what we want? //This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue) - return DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); + let docs = DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]); + let viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); + if (viewSpecScript) { + let script = viewSpecScript.script; + docs = docs.filter(d => { + let res = script.run({ doc: d }); + if (res.success) { + return res.result; + } + }); + } + return docs; } get childDocList() { //TODO tfs: This might not be what we want? diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index e5cb1b546..3103cd309 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -1,12 +1,120 @@ @import "../globalCssVariables"; +@import '~js-datepicker/dist/datepicker.min.css'; + +.collectionViewBaseChrome { + display: flex; + + .collectionViewBaseChrome-viewPicker { + font-size: 75%; + text-transform: uppercase; + letter-spacing: 2px; + background: rgb(238, 238, 238); + color: grey; + outline-color: black; + border: none; + padding: 12px 10px 11px 10px; + } + + .collectionViewBaseChrome-viewPicker:active { + outline-color: black; + } + + .collectionViewBaseChrome-collapse { + margin-right: 10px; + transition: all .5s; + } + + .collectionViewBaseChrome-viewSpecs { + margin-left: 10px; + display: grid; + + .collectionViewBaseChrome-viewSpecsInput { + padding: 12px 10px 11px 10px; + border: 0px; + color: grey; + text-align: center; + text-transform: uppercase; + letter-spacing: 2px; + outline-color: black; + font-size: 75%; + background: rgb(238, 238, 238); + height: 100%; + width: 150px; + } + + .collectionViewBaseChrome-viewSpecsMenu { + overflow: hidden; + transition: height .5s, display .5s; + position: absolute; + top: 60px; + z-index: 100; + display: flex; + flex-direction: column; + background: rgb(238, 238, 238); + box-shadow: grey 2px 2px 4px; + + .qs-datepicker { + left: unset; + right: 0; + } + + .collectionViewBaseChrome-viewSpecsMenu-row { + display: grid; + grid-template-columns: 150px 200px 150px; + margin-top: 10px; + margin-right: 10px; + + .collectionViewBaseChrome-viewSpecsMenu-rowLeft, + .collectionViewBaseChrome-viewSpecsMenu-rowMiddle, + .collectionViewBaseChrome-viewSpecsMenu-rowRight { + font-size: 75%; + letter-spacing: 2px; + color: grey; + margin-left: 10px; + padding: 5px; + border: none; + outline-color: black; + } + } + + .collectionViewBaseChrome-viewSpecsMenu-lastRow { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 10px; + margin: 10px; + } + } + } +} .collectionStackingViewChrome { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 1fr auto; padding-bottom: 10px; border-bottom: .5px solid lightgrey; margin: 10px; + .collectionStackingViewChrome-cont { + display: flex; + justify-content: space-between; + } + + .collectionStackingViewChrome-sort { + display: flex; + align-items: center; + justify-content: space-between; + + .collectionStackingViewChrome-sortIcon { + transition: transform .5s; + margin-left: 10px; + } + } + + button:hover { + transform: scale(1); + } + + .collectionStackingViewChrome-sectionFilter-cont { justify-self: right; display: flex; @@ -21,10 +129,9 @@ .collectionStackingViewChrome-sectionFilter { color: white; - width: fit-content; + width: 100px; text-align: center; background: rgb(238, 238, 238); - height: 37px; .editable-view-input, input, diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 52fee26bf..2edac384d 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -3,55 +3,189 @@ import { CollectionView } from "./CollectionView"; import "./CollectionViewChromes.scss"; import { CollectionViewType } from "./CollectionBaseView"; import { undoBatch } from "../../util/UndoManager"; -import { action, observable, runInAction, computed } from "mobx"; +import { action, observable, runInAction, computed, IObservable, IObservableValue } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../../new_fields/Doc"; import { DocLike } from "../MetadataEntryMenu"; -const higflyout = require("@hig/flyout"); -export const Flyout = higflyout.default; import * as Autosuggest from 'react-autosuggest'; import { EditableView } from "../EditableView"; -import { StrCast } from "../../../new_fields/Types"; +import { StrCast, NumCast, BoolCast, Cast } from "../../../new_fields/Types"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Utils } from "../../../Utils"; +import KeyRestrictionRow from "./KeyRestrictionRow"; +import { CompileScript } from "../../util/Scripting"; +import { ScriptField } from "../../../new_fields/ScriptField"; +const datepicker = require('js-datepicker'); interface CollectionViewChromeProps { CollectionView: CollectionView; } +let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); + +@observer class CollectionViewBaseChrome extends React.Component { + @observable private _viewSpecsOpen: boolean = false; + @observable private _dateWithinValue: string = ""; + @observable private _dateValue: Date = new Date(); + @observable private _keyRestrictions: [JSX.Element, string][] = []; + @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); } + + private _picker: any; + private _datePickerElGuid = Utils.GenerateGuid(); + + componentDidMount = () => { + this._picker = datepicker("#" + this._datePickerElGuid, { + disabler: (date: Date) => date > new Date(), + onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date), + dateSelected: new Date() + }); + + runInAction(() => { + this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]); + this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]); + }); + } + @undoBatch viewChanged = (e: React.ChangeEvent) => { //@ts-ignore - switch (e.target.selectedOptions[0].value) { - case "freeform": - this.props.CollectionView.props.Document.viewType = CollectionViewType.Freeform; - break; - case "schema": - this.props.CollectionView.props.Document.viewType = CollectionViewType.Schema; - break; - case "treeview": - this.props.CollectionView.props.Document.viewType = CollectionViewType.Tree; - break; - case "stacking": - this.props.CollectionView.props.Document.viewType = CollectionViewType.Stacking; - break; - case "masonry": - this.props.CollectionView.props.Document.viewType = CollectionViewType.Masonry; - break; - default: - break; + this.props.CollectionView.props.Document.viewType = parseInt(e.target.selectedOptions[0].value); + } + + @action + openViewSpecs = (e: React.SyntheticEvent) => { + this._viewSpecsOpen = true; + + //@ts-ignore + if (!e.target.classList[0].startsWith("qs")) { + this.closeDatePicker(); + } + + e.stopPropagation(); + document.removeEventListener("pointerdown", this.closeViewSpecs); + document.addEventListener("pointerdown", this.closeViewSpecs); + } + + @action closeViewSpecs = () => { this._viewSpecsOpen = false; document.removeEventListener("pointerdown", this.closeViewSpecs); }; + + @action + openDatePicker = (e: React.PointerEvent) => { + this.openViewSpecs(e); + if (this._picker) { + this._picker.alwaysShow = true; + this._picker.show(); + // TODO: calendar is offset when zoomed in/out + // this._picker.calendar.style.position = "absolute"; + // let transform = this.props.CollectionView.props.ScreenToLocalTransform(); + // let x = parseInt(this._picker.calendar.style.left) / transform.Scale; + // let y = parseInt(this._picker.calendar.style.top) / transform.Scale; + // this._picker.calendar.style.left = x; + // this._picker.calendar.style.top = y; + + e.stopPropagation(); } } + @action + addKeyRestriction = (e: React.MouseEvent) => { + let index = this._keyRestrictions.length; + this._keyRestrictions.push([ runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]); + + this.openViewSpecs(e); + } + + @action + applyFilter = (e: React.MouseEvent) => { + this.openViewSpecs(e); + + let keyRestrictionScript = `${this._keyRestrictions.map(i => i[1]) + .reduce((acc: string, value: string, i: number) => value ? `${acc} && ${value}` : acc)}`; + let yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0; + let monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0; + let weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0; + let dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7; + let lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset); + let upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1); + let dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`; + let fullScript = `return ${dateRestrictionScript} && ${keyRestrictionScript}`; + let compiled = CompileScript(fullScript, { params: { doc: Doc.name } }); + if (compiled.compiled) { + this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled); + } + } + + @action + closeDatePicker = () => { + if (this._picker) { + this._picker.alwaysShow = false; + this._picker.hide(); + } + document.removeEventListener("pointerdown", this.closeDatePicker); + } + render() { return (
- + + + + + +
+ +
+ {this._keyRestrictions.map(i => i[0])} +
+
+ CREATED WITHIN: +
+ + +
+
+ + +
+
+
) } @@ -62,6 +196,7 @@ export class CollectionStackingViewChrome extends React.Component => { @@ -109,41 +244,53 @@ export class CollectionStackingViewChrome extends React.Component { this.props.CollectionView.props.Document.stackingHeadersSortDescending = !this.props.CollectionView.props.Document.stackingHeadersSortDescending; } @action resetValue = () => { this._currentKey = this.sectionFilter; }; render() { return (
-
-
- Group items by: -
-
- this.sectionFilter} - autosuggestProps={ - { - resetValue: this.resetValue, - value: this._currentKey, - onChange: this.onKeyChange, - autosuggestProps: { - inputProps: - { - value: this._currentKey, - onChange: this.onKeyChange - }, - getSuggestionValue: this.getSuggestionValue, - suggestions: this.suggestions, - alwaysRenderSuggestions: true, - renderSuggestion: this.renderSuggestion, - onSuggestionsFetchRequested: this.onSuggestionFetch, - onSuggestionsClearRequested: this.onSuggestionClear - } - }} - SetValue={this.setValue} - contents={this.sectionFilter ? this.sectionFilter : "N/A"} - /> +
+ +
+
+ Group items by: +
+
+ this.sectionFilter} + autosuggestProps={ + { + resetValue: this.resetValue, + value: this._currentKey, + onChange: this.onKeyChange, + autosuggestProps: { + inputProps: + { + value: this._currentKey, + onChange: this.onKeyChange + }, + getSuggestionValue: this.getSuggestionValue, + suggestions: this.suggestions, + alwaysRenderSuggestions: true, + renderSuggestion: this.renderSuggestion, + onSuggestionsFetchRequested: this.onSuggestionFetch, + onSuggestionsClearRequested: this.onSuggestionClear + } + }} + oneLine + SetValue={this.setValue} + contents={this.sectionFilter ? this.sectionFilter : "N/A"} + /> +
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx new file mode 100644 index 000000000..8051a8359 --- /dev/null +++ b/src/client/views/collections/KeyRestrictionRow.tsx @@ -0,0 +1,45 @@ +import * as React from "react"; +import { observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; + +interface IKeyRestrictionProps { + contains: boolean; + script: (value: string) => void; +} + +@observer +export default class KeyRestrictionRow extends React.Component { + @observable private _key = ""; + @observable private _value = ""; + @observable private _contains = this.props.contains; + + render() { + if (this._key && this._value) { + let parsedValue: string | number = `"${this._value}"`; + let parsed = parseInt(this._value); + if (!isNaN(parsed)) { + parsedValue = parsed; + } + let scriptText = `(doc.${this._key} ${this._contains ? "===" : "!=="} ${parsedValue})`; + this.props.script(scriptText); + } + return ( +
+ runInAction(() => this._key = e.target.value)} + placeholder="KEY" /> + + runInAction(() => this._value = e.target.value)} + placeholder="VALUE" /> +
+ ) + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ede0719d96cc2551490398caf3e1f41fe7992931 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 24 Jul 2019 22:27:37 -0400 Subject: view specs almost entirely done, refactored some chrome impl --- .../views/collections/CollectionStackingView.scss | 1 + .../views/collections/CollectionStackingView.tsx | 6 +- src/client/views/collections/CollectionSubView.tsx | 1 + src/client/views/collections/CollectionView.tsx | 42 ++-- .../views/collections/CollectionViewChromes.scss | 156 +++++++-------- .../views/collections/CollectionViewChromes.tsx | 214 ++++++++++++--------- 6 files changed, 227 insertions(+), 193 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index e0ced8af4..f43340967 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -7,6 +7,7 @@ display: flex; overflow-y: auto; flex-wrap: wrap; + transition: top .5s; .collectionStackingView-docView-container { width: 45%; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index a78a47ffb..31ec80d76 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -8,7 +8,7 @@ import { BoolCast, NumCast, Cast, StrCast, FieldValue } from "../../../new_field import { emptyFunction, Utils } from "../../../Utils"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; -import { CollectionSubView } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { undoBatch } from "../../util/UndoManager"; import { DragManager } from "../../util/DragManager"; import { DocumentType } from "../../documents/Documents"; @@ -19,6 +19,7 @@ import { listSpec } from "../../../new_fields/Schema"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { List } from "../../../new_fields/List"; import { EditableView } from "../EditableView"; +import { CollectionViewProps } from "./CollectionBaseView"; let valuesCreated = 1; @@ -32,6 +33,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _columnStart: number = 0; @observable private cursor: CursorProperty = "grab"; get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); } + get chromeCollapsed() { return this.props.chromeCollapsed; } @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); } @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); } @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @@ -265,7 +267,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } // let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx); return ( -
e.stopPropagation()} > {/* {sectionFilter as boolean ? [ ["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())], diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 24c8adb35..98447e824 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -30,6 +30,7 @@ export interface CollectionViewProps extends FieldViewProps { moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; PanelWidth: () => number; PanelHeight: () => number; + chromeCollapsed: boolean; } export interface SubCollectionViewProps extends CollectionViewProps { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ba9780c24..e2f652908 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -17,7 +17,8 @@ import { CollectionStackingView } from './CollectionStackingView'; import { CollectionTreeView } from "./CollectionTreeView"; import { StrCast, PromiseValue } from '../../../new_fields/Types'; import { DocumentType } from '../../documents/Documents'; -import { CollectionStackingViewChrome } from './CollectionViewChromes'; +import { CollectionStackingViewChrome, CollectionViewBaseChrome } from './CollectionViewChromes'; +import { observable } from 'mobx'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh); @@ -32,40 +33,39 @@ library.add(faImage); @observer export class CollectionView extends React.Component { + @observable private _collapsed = false; + public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); } private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; switch (this.isAnnotationOverlay ? CollectionViewType.Freeform : type) { - case CollectionViewType.Schema: return (); - case CollectionViewType.Docking: return (); - case CollectionViewType.Tree: return (); - case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } - case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } + case CollectionViewType.Schema: return (); + case CollectionViewType.Docking: return (); + case CollectionViewType.Tree: return (); + case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } + case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } case CollectionViewType.Freeform: default: - return (); + return (); } return (null); } - private Chrome = (type: CollectionViewType) => { - if (this.isAnnotationOverlay || this.props.Document === CurrentUserUtils.UserDocument.sidebar) { - return (null); - } - - switch (type) { - case CollectionViewType.Stacking: return (); - default: - return null; - } + private collapse = (value: boolean) => { + this._collapsed = value; } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { - return [ - this.Chrome(type), - this.SubViewHelper(type, renderProps) - ] + if (this.isAnnotationOverlay || this.props.Document === CurrentUserUtils.UserDocument.sidebar) { + return [(null), this.SubViewHelper(type, renderProps)]; + } + else { + return [ + (), + this.SubViewHelper(type, renderProps) + ]; + } } get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 3103cd309..d37228bde 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -1,98 +1,104 @@ @import "../globalCssVariables"; @import '~js-datepicker/dist/datepicker.min.css'; -.collectionViewBaseChrome { - display: flex; - - .collectionViewBaseChrome-viewPicker { - font-size: 75%; - text-transform: uppercase; - letter-spacing: 2px; - background: rgb(238, 238, 238); - color: grey; - outline-color: black; - border: none; - padding: 12px 10px 11px 10px; - } - - .collectionViewBaseChrome-viewPicker:active { - outline-color: black; - } - - .collectionViewBaseChrome-collapse { - margin-right: 10px; - transition: all .5s; - } +.collectionViewChrome { + display: grid; + grid-template-columns: 1fr auto; + padding-bottom: 10px; + border-bottom: .5px solid lightgrey; + margin: 10px; + position: relative; + z-index: 9001; + transition: top .5s; - .collectionViewBaseChrome-viewSpecs { - margin-left: 10px; - display: grid; + .collectionViewBaseChrome { + display: flex; - .collectionViewBaseChrome-viewSpecsInput { - padding: 12px 10px 11px 10px; - border: 0px; - color: grey; - text-align: center; + .collectionViewBaseChrome-viewPicker { + font-size: 75%; text-transform: uppercase; letter-spacing: 2px; - outline-color: black; - font-size: 75%; background: rgb(238, 238, 238); - height: 100%; - width: 150px; + color: grey; + outline-color: black; + border: none; + padding: 12px 10px 11px 10px; + margin-left: 50px; } - .collectionViewBaseChrome-viewSpecsMenu { - overflow: hidden; - transition: height .5s, display .5s; + .collectionViewBaseChrome-viewPicker:active { + outline-color: black; + } + + .collectionViewBaseChrome-collapse { + transition: all .5s; position: absolute; - top: 60px; - z-index: 100; - display: flex; - flex-direction: column; - background: rgb(238, 238, 238); - box-shadow: grey 2px 2px 4px; + width: 40px; + } + + .collectionViewBaseChrome-viewSpecs { + margin-left: 10px; + display: grid; - .qs-datepicker { - left: unset; - right: 0; + .collectionViewBaseChrome-viewSpecsInput { + padding: 12px 10px 11px 10px; + border: 0px; + color: grey; + text-align: center; + text-transform: uppercase; + letter-spacing: 2px; + outline-color: black; + font-size: 75%; + background: rgb(238, 238, 238); + height: 100%; + width: 150px; } - .collectionViewBaseChrome-viewSpecsMenu-row { - display: grid; - grid-template-columns: 150px 200px 150px; - margin-top: 10px; - margin-right: 10px; - - .collectionViewBaseChrome-viewSpecsMenu-rowLeft, - .collectionViewBaseChrome-viewSpecsMenu-rowMiddle, - .collectionViewBaseChrome-viewSpecsMenu-rowRight { - font-size: 75%; - letter-spacing: 2px; - color: grey; - margin-left: 10px; - padding: 5px; - border: none; - outline-color: black; + .collectionViewBaseChrome-viewSpecsMenu { + overflow: hidden; + transition: height .5s, display .5s; + position: absolute; + top: 60px; + z-index: 100; + display: flex; + flex-direction: column; + background: rgb(238, 238, 238); + box-shadow: grey 2px 2px 4px; + + .qs-datepicker { + left: unset; + right: 0; + } + + .collectionViewBaseChrome-viewSpecsMenu-row { + display: grid; + grid-template-columns: 150px 200px 150px; + margin-top: 10px; + margin-right: 10px; + + .collectionViewBaseChrome-viewSpecsMenu-rowLeft, + .collectionViewBaseChrome-viewSpecsMenu-rowMiddle, + .collectionViewBaseChrome-viewSpecsMenu-rowRight { + font-size: 75%; + letter-spacing: 2px; + color: grey; + margin-left: 10px; + padding: 5px; + border: none; + outline-color: black; + } } - } - .collectionViewBaseChrome-viewSpecsMenu-lastRow { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 10px; - margin: 10px; + .collectionViewBaseChrome-viewSpecsMenu-lastRow { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 10px; + margin: 10px; + } } } } -} -.collectionStackingViewChrome { - display: grid; - grid-template-columns: 1fr auto; - padding-bottom: 10px; - border-bottom: .5px solid lightgrey; - margin: 10px; .collectionStackingViewChrome-cont { display: flex; diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 2edac384d..24d13d5cb 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -19,16 +19,19 @@ const datepicker = require('js-datepicker'); interface CollectionViewChromeProps { CollectionView: CollectionView; + type: CollectionViewType; + collapse?: (value: boolean) => any; } let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); @observer -class CollectionViewBaseChrome extends React.Component { +export class CollectionViewBaseChrome extends React.Component { @observable private _viewSpecsOpen: boolean = false; @observable private _dateWithinValue: string = ""; @observable private _dateValue: Date = new Date(); @observable private _keyRestrictions: [JSX.Element, string][] = []; + @observable private _collapsed: boolean = false; @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); } private _picker: any; @@ -124,68 +127,92 @@ class CollectionViewBaseChrome extends React.Component { + this._collapsed = !this._collapsed; + this.props.collapse(this._collapsed); + } + + subChrome = () => { + + switch (this.props.type) { + case CollectionViewType.Stacking: return ( + ); + default: + return null; + } + } + render() { return ( -
- - -
- -
- {this._keyRestrictions.map(i => i[0])} -
-
- CREATED WITHIN: +
+
+ + +
+ +
+ {this._keyRestrictions.map(i => i[0])} +
+
+ CREATED WITHIN:
- - -
-
-
+
+ - +
+ {this.subChrome()}
) } @@ -249,48 +276,45 @@ export class CollectionStackingViewChrome extends React.Component - -
- -
-
- Group items by: -
-
- this.sectionFilter} - autosuggestProps={ - { - resetValue: this.resetValue, - value: this._currentKey, - onChange: this.onKeyChange, - autosuggestProps: { - inputProps: - { - value: this._currentKey, - onChange: this.onKeyChange - }, - getSuggestionValue: this.getSuggestionValue, - suggestions: this.suggestions, - alwaysRenderSuggestions: true, - renderSuggestion: this.renderSuggestion, - onSuggestionsFetchRequested: this.onSuggestionFetch, - onSuggestionsClearRequested: this.onSuggestionClear - } - }} - oneLine - SetValue={this.setValue} - contents={this.sectionFilter ? this.sectionFilter : "N/A"} - /> +
+ +
+ +
+
+ Group items by:
+
+ this.sectionFilter} + autosuggestProps={ + { + resetValue: this.resetValue, + value: this._currentKey, + onChange: this.onKeyChange, + autosuggestProps: { + inputProps: + { + value: this._currentKey, + onChange: this.onKeyChange + }, + getSuggestionValue: this.getSuggestionValue, + suggestions: this.suggestions, + alwaysRenderSuggestions: true, + renderSuggestion: this.renderSuggestion, + onSuggestionsFetchRequested: this.onSuggestionFetch, + onSuggestionsClearRequested: this.onSuggestionClear + } + }} + oneLine + SetValue={this.setValue} + contents={this.sectionFilter ? this.sectionFilter : "N/A"} + />
-- cgit v1.2.3-70-g09d2 From 7d468c19c28a48314ea993d84e5995364e88fa86 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 26 Jul 2019 02:21:09 -0400 Subject: added a MakeBackground and FitToBox menu items. fixed problems with fitToBox and frozen dimensions. fixed issue with drop() on free forms. --- .../views/collections/CollectionBaseView.scss | 2 +- .../views/collections/CollectionBaseView.tsx | 6 ++-- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 32 +++++++++++++++------- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.scss | 1 - src/client/views/nodes/DocumentView.tsx | 7 +++++ src/client/views/nodes/ImageBox.tsx | 2 +- 8 files changed, 37 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/views/collections/CollectionBaseView.scss b/src/client/views/collections/CollectionBaseView.scss index 34bcb705e..583e6f6ca 100644 --- a/src/client/views/collections/CollectionBaseView.scss +++ b/src/client/views/collections/CollectionBaseView.scss @@ -6,7 +6,7 @@ border-radius: 0 0 $border-radius $border-radius; box-sizing: border-box; border-radius: inherit; - pointer-events: all; width:100%; height:100%; + overflow: auto; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index c39caec24..67112ae7c 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -126,7 +126,6 @@ export class CollectionBaseView extends React.Component { let self = this; let targetDataDoc = this.props.Document; if (Doc.AreProtosEqual(targetDataDoc, targetCollection)) { - //if (Doc.AreProtosEqual(this.extensionDoc, targetCollection)) { return true; } if (this.removeDocument(doc)) { @@ -146,7 +145,10 @@ export class CollectionBaseView extends React.Component { const viewtype = this.collectionViewType; return (
{viewtype !== undefined ? this.props.children(viewtype, props) : (null)} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 2ddefb3c0..cbc9e61eb 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -104,7 +104,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } else if (de.data.moveDocument) { let movedDocs = de.data.options === this.props.Document[Id] ? de.data.draggedDocuments : de.data.droppedDocuments; added = movedDocs.reduce((added: boolean, d) => - de.data.moveDocument(d, /*this.props.DataDoc ? this.props.DataDoc :*/ this.props.Document, this.props.addDocument) || added, false); + de.data.moveDocument(d, this.props.Document, this.props.addDocument) || added, false); } else { added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6500b3273..9c4275388 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,9 +34,11 @@ import { CompileScript } from "../../../util/Scripting"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faTable, faPaintBrush, faAsterisk } from "@fortawesome/free-solid-svg-icons"; +import { faTable, faPaintBrush, faAsterisk, faExpandArrowsAlt, faCompressArrowsAlt } from "@fortawesome/free-solid-svg-icons"; +import { undo } from "prosemirror-history"; +import { number } from "prop-types"; -library.add(faEye, faTable, faPaintBrush); +library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt); export const panZoomSchema = createSchema({ panX: "number", @@ -58,24 +60,29 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pheight() { return this.props.PanelHeight(); } private inkKey = "ink"; + get prevScaling() { + return (this.props as any).ContentScaling && this.Document.nativeWidth && this.fitToBox ? (this.props as any).ContentScaling() : 1; + } + @computed get contentBounds() { - let bounds = this.props.fitToBox && !NumCast(this.nativeWidth) ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; + let bounds = (this.fitToBox) && !this.nativeWidth ? Doc.ComputeContentBounds(DocListCast(this.props.Document.data)) : undefined; return { panX: bounds ? (bounds.x + bounds.r) / 2 : this.Document.panX || 0, panY: bounds ? (bounds.y + bounds.b) / 2 : this.Document.panY || 0, - scale: bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1 + scale: (bounds ? Math.min(this.props.PanelHeight() / (bounds.b - bounds.y), this.props.PanelWidth() / (bounds.r - bounds.x)) : this.Document.scale || 1) / this.prevScaling }; } - @computed get nativeWidth() { return this.Document.nativeWidth || 0; } - @computed get nativeHeight() { return this.Document.nativeHeight || 0; } + @computed get fitToBox() { return this.props.fitToBox || this.props.Document.fitToBox; } + @computed get nativeWidth() { return this.fitToBox ? 0 : this.Document.nativeWidth || 0; } + @computed get nativeHeight() { return this.fitToBox ? 0 : this.Document.nativeHeight || 0; } public get isAnnotationOverlay() { return this.props.fieldExt ? true : false; } // fieldExt will be "" or "annotation". should maybe generalize this, or make it more specific (ie, 'annotation' instead of 'fieldExt') private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } private panX = () => this.contentBounds.panX; private panY = () => this.contentBounds.panY; private zoomScaling = () => this.contentBounds.scale; - 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 centeringShiftX = () => !this.nativeWidth ? this._pwidth / 2 / this.prevScaling : 0; // shift so pan position is at center of window for non-overlay collections + private centeringShiftY = () => !this.nativeHeight ? this._pheight / 2 / this.prevScaling : 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()); private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY()); @@ -109,11 +116,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { + let xf = this.getTransform(); if (super.drop(e, de)) { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.droppedDocuments.length) { - let dragDoc = de.data.droppedDocuments[0]; - let [xp, yp] = this.getTransform().transformPoint(de.x, de.y); + let [xp, yp] = xf.transformPoint(de.x, de.y); let x = xp - de.data.xOffset; let y = yp - de.data.yOffset; let dropX = NumCast(de.data.droppedDocuments[0].x); @@ -491,6 +498,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } onContextMenu = () => { + ContextMenu.Instance.addItem({ + description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, + event: undoBatch(async () => this.props.Document.fitToBox = !this.fitToBox), + icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" + }); ContextMenu.Instance.addItem({ description: "Arrange contents in grid", icon: "table", diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b765517a2..d96e93aeb 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -365,7 +365,7 @@ export class MarqueeView extends React.Component marqueeSelect() { let selRect = this.Bounds; let selection: Doc[] = []; - this.props.activeDocuments().map(doc => { + this.props.activeDocuments().filter(doc => !doc.isBackground).map(doc => { var z = NumCast(doc.zoomBasis, 1); var x = NumCast(doc.x); var y = NumCast(doc.y); diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 3a4b46b7e..7c72fb6e6 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -4,7 +4,6 @@ position: inherit; top: 0; left:0; - pointer-events: all; // background: $light-color; //overflow: hidden; transform-origin: left top; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4a5e20533..9d2dd3121 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -514,6 +514,11 @@ export class DocumentView extends DocComponent(Docu } proto.ignoreAspect = !BoolCast(proto.ignoreAspect, false); } + @undoBatch + @action + makeBackground = (): void => { + this.props.Document.isBackground = true; + } @undoBatch @action @@ -544,6 +549,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); + cm.addItem({ description: "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); cm.addItem({ description: "Make Portal", event: () => { @@ -648,6 +654,7 @@ export class DocumentView extends DocComponent(Docu
(ImageD if (field instanceof ImageField) paths = [this.choosePath(field.url)]; paths.push(...altpaths); // } - let interactive = InkingControl.Instance.selectedTool ? "" : "-interactive"; + let interactive = InkingControl.Instance.selectedTool || this.props.Document.isBackground ? "" : "-interactive"; let rotation = NumCast(this.dataDoc.rotation, 0); let aspect = (rotation % 180) ? this.dataDoc[HeightSym]() / this.dataDoc[WidthSym]() : 1; let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; -- cgit v1.2.3-70-g09d2 From 7c3b56a2e4552308014aa011493b33e3b054be94 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Tue, 30 Jul 2019 16:33:27 -0400 Subject: fixed some scripting stuff on view specs --- src/client/views/collections/CollectionSubView.tsx | 3 +++ src/client/views/collections/CollectionViewChromes.scss | 3 ++- src/client/views/collections/CollectionViewChromes.tsx | 9 +++++++-- src/client/views/collections/KeyRestrictionRow.tsx | 6 +++++- 4 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a15ed8f94..7386eaba6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -66,6 +66,9 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (res.success) { return res.result; } + else { + console.log(res.error); + } }); } return docs; diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 989315194..c062c3261 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -37,9 +37,10 @@ } .collectionViewBaseChrome-collapse { - transition: all .5s; + transition: all .5s opacity 0.2s; position: absolute; width: 40px; + transform-origin: top left; } .collectionViewBaseChrome-viewSpecs { diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 2bffe3cc0..0827cfef9 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -142,7 +142,7 @@ export class CollectionViewBaseChrome extends React.Component
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx index 9baa250a6..1b59547d8 100644 --- a/src/client/views/collections/KeyRestrictionRow.tsx +++ b/src/client/views/collections/KeyRestrictionRow.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; +import { Doc } from "../../../new_fields/Doc"; interface IKeyRestrictionProps { contains: boolean; @@ -23,7 +24,10 @@ export default class KeyRestrictionRow extends React.Component Date: Wed, 31 Jul 2019 00:05:39 -0400 Subject: added alt-drag-drop for applying template to items in a collection --- src/client/views/collections/CollectionSubView.tsx | 15 ++++++++++----- src/client/views/nodes/ImageBox.tsx | 12 +----------- src/new_fields/Doc.ts | 13 +++++++++++++ 3 files changed, 24 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections/CollectionSubView.tsx') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7386eaba6..077f3f941 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,13 +1,15 @@ import { action, computed } from "mobx"; import * as rp from 'request-promise'; import CursorField from "../../../new_fields/CursorField"; -import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; +import { ScriptField } from "../../../new_fields/ScriptField"; import { BoolCast, Cast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from "../../../server/RouteStore"; +import { Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocumentOptions, DocumentType } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; @@ -19,10 +21,6 @@ import { CollectionPDFView } from "./CollectionPDFView"; import { CollectionVideoView } from "./CollectionVideoView"; import { CollectionView } from "./CollectionView"; import React = require("react"); -import { MainView } from "../MainView"; -import { Utils } from "../../../Utils"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { CompileScript } from "../../util/Scripting"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; @@ -115,6 +113,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @action protected drop(e: Event, de: DragManager.DropEvent): boolean { if (de.data instanceof DragManager.DocumentDragData) { + if (de.mods === "AltKey" && de.data.draggedDocuments.length) { + this.childDocs.map(doc => + Doc.ApplyTemplateTo(de.data.draggedDocuments[0], doc, undefined) + ); + e.stopPropagation(); + return true; + } let added = false; if (de.data.dropAction || de.data.userDropAction) { added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d) || added, false); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index dbe545048..b60ef41fd 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -93,17 +93,7 @@ export class ImageBox extends DocComponent(ImageD if (de.data instanceof DragManager.DocumentDragData) { de.data.droppedDocuments.forEach(action((drop: Doc) => { if (de.mods === "CtrlKey") { - let temp = Doc.MakeDelegate(drop); - this.props.Document.nativeWidth = Doc.GetProto(this.props.Document).nativeWidth = undefined; - this.props.Document.nativeHeight = Doc.GetProto(this.props.Document).nativeHeight = undefined; - this.props.Document.width = drop.width; - this.props.Document.height = drop.height; - Doc.GetProto(this.props.Document).type = DocumentType.TEMPLATE; - if (this.props.DataDoc && this.props.DataDoc.layout === this.props.Document) { - this.props.DataDoc.layout = temp; - } else { - this.props.Document.layout = temp; - } + Doc.ApplyTemplateTo(drop, this.props.Document, this.props.DataDoc); e.stopPropagation(); } else 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); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 59314783b..dab0f9070 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -462,6 +462,19 @@ export namespace Doc { otherdoc.type = DocumentType.TEMPLATE; return otherdoc; } + export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) { + let temp = Doc.MakeDelegate(templateDoc); + target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined; + target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined; + target.width = templateDoc.width; + target.height = templateDoc.height; + Doc.GetProto(target).type = DocumentType.TEMPLATE; + if (targetData && targetData.layout === target) { + targetData.layout = temp; + } else { + target.layout = temp; + } + } export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) { // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) -- cgit v1.2.3-70-g09d2