From 291d2fb28fa878eff190daf813854d9c0477ccb6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 14 Aug 2020 17:29:55 -0400 Subject: cleaned up invocation of RichTextMenu --- src/client/views/DocumentDecorations.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 03746a1d2..3f16dd7e7 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -659,8 +659,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (bounds.y > bounds.b) { bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight); } - var offset = 0; - let useRotation = seldoc.rootDoc.type === DocumentType.INK; + let offset = 0; + const useRotation = seldoc.rootDoc.type === DocumentType.INK; return (
Date: Fri, 14 Aug 2020 21:39:18 -0400 Subject: fixed default documents that were missing the 'system' tag. fixed catalog to have default worksapce documents in it. --- src/client/util/CurrentUserUtils.ts | 3 --- src/client/views/DocumentDecorations.tsx | 9 ++++----- src/client/views/MainView.tsx | 8 ++++++-- src/client/views/search/SearchBox.tsx | 7 +++---- src/fields/ScriptField.ts | 1 + 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aff80da75..8931495dc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -861,9 +861,6 @@ export class CurrentUserUtils { title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true })); } - if (doc.activePresentation === undefined) { - doc.activePresentation = Doc.MakeCopy(doc.emptyPresentation as Doc, true); - } } // Sharing sidebar is where shared documents are contained diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3f16dd7e7..e546ca858 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -659,7 +659,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (bounds.y > bounds.b) { bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight); } - let offset = 0; const useRotation = seldoc.rootDoc.type === DocumentType.INK; return (
@@ -674,10 +673,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
{maximizeIcon} {titleArea} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5a2488bb6..f7ff895a5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -254,6 +254,8 @@ export class MainView extends React.Component { @action createNewWorkspace = async (id?: string) => { + const myCatalog = Doc.UserDoc().myCatalog as Doc; + const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; const workspaceCount = DocListCast(workspaces.data).length + 1; const freeformOptions: DocumentOptions = { @@ -264,8 +266,10 @@ export class MainView extends React.Component { title: "Untitled Collection", }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); - + const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Workspace ${workspaceCount}` }, id, "row"); + Doc.AddDocToList(myCatalog, "data", freeformDoc); + Doc.AddDocToList(myCatalog, "data", presentation); + Doc.UserDoc().activePresentation = presentation; const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`); diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 770a03cb1..084449d04 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -604,13 +604,12 @@ export class SearchBox extends ViewBoxBaseComponent `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`; // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello - const query = [baseExpr, includeDeleted, includeIcons, typeExpr].join(" AND ").replace(/AND $/, ""); + const query = [baseExpr, includeDeleted, typeExpr].join(" AND ").replace(/AND $/, ""); return query; } diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 9391f56ac..1fb71fefb 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -98,6 +98,7 @@ export class ScriptField extends ObjectField { if (script?.options.capturedVariables) { const doc = Doc.assign(new Doc, script.options.capturedVariables); + doc.system = true; this.captures = new ProxyField(doc); } this.setterscript = setterscript; -- cgit v1.2.3-70-g09d2 From 963f335eea91e0e3b78ca844af8028c611ad865b Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 18 Aug 2020 10:11:28 -0400 Subject: fixed selecting horizontal/vertical lines --- src/client/util/InteractionUtils.tsx | 4 ++-- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/InkingStroke.tsx | 18 +++++++++++------- 3 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index ae3b3e064..13c4fac25 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -140,7 +140,7 @@ export namespace InteractionUtils { export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string, - dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) { + dash: string | undefined, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) { let pts: { X: number; Y: number; }[] = []; if (shape) { //if any of the shape are true pts = makePolygon(shape, points); @@ -182,7 +182,7 @@ export namespace InteractionUtils { const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${(pt.X - left - width / 2) * scalex + width / 2}, ${(pt.Y - top - width / 2) * scaley + width / 2} `, ""); - const dashArray = String(Number(width) * Number(dash)); + const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined; const defGuid = Utils.GenerateGuid(); const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth))); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e546ca858..37de08a6f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -603,7 +603,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined; const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; - if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 2 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { + if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { return (null); } const canDelete = SelectionManager.SelectedDocuments().some(docView => { diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 41311ed86..3376fcd97 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -103,11 +103,15 @@ export class InkingStroke extends ViewBoxBaseComponent p.X); const ys = data.map(p => p.Y); - const left = Math.min(...xs) - strokeWidth / 2; - const top = Math.min(...ys) - strokeWidth / 2; - const right = Math.max(...xs) + strokeWidth / 2; - const bottom = Math.max(...ys) + strokeWidth / 2; - const width = Math.max(right - left); + const lineTop = Math.min(...ys); + const lineBot = Math.max(...ys); + const lineLft = Math.min(...xs); + const lineRgt = Math.max(...xs); + const left = lineLft - strokeWidth / 2; + const top = lineTop - strokeWidth / 2; + const right = lineRgt + strokeWidth / 2; + const bottom = lineBot + strokeWidth / 2; + const width = Math.max(1, right - left); const height = Math.max(1, bottom - top); const scaleX = width === strokeWidth ? 1 : (this.props.PanelWidth() - strokeWidth) / (width - strokeWidth); const scaleY = height === strokeWidth ? 1 : (this.props.PanelHeight() - strokeWidth) / (height - strokeWidth); @@ -116,12 +120,12 @@ export class InkingStroke extends ViewBoxBaseComponent 1 && lineRgt - lineLft > 1, false); const hpoints = InteractionUtils.CreatePolyline(data, left, top, this.props.isSelected() && strokeWidth > 5 ? strokeColor : "transparent", strokeWidth, (strokeWidth + 15), StrCast(this.layoutDoc.strokeBezier), StrCast(this.layoutDoc.fillColor, "transparent"), - "none", "none", "0", scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true); + "none", "none", undefined, scaleX, scaleY, "", this.props.active() ? "visiblepainted" : "none", false, true); //points for adding const apoints = InteractionUtils.CreatePoints(data, left, top, strokeColor, strokeWidth, strokeWidth, -- cgit v1.2.3-70-g09d2 From a6ad9d55396013bd76b2f6f05c5633c71518e595 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 18 Aug 2020 10:44:15 -0400 Subject: added pinning of multiple documents at a time to prensetation view --- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 8748b1880..7effc4aa0 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -199,7 +199,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV return !targetDoc ? (null) :
{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}
}>
DockedFrameRenderer.PinDoc(targetDoc, isPinned)}> + onClick={e => this.props.views().map(view => view && DockedFrameRenderer.PinDoc(view.props.Document, isPinned))}>
; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 37de08a6f..535e86457 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,34 +1,30 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; +import { faAngleDoubleLeft, faAngleDoubleRight, faAngleLeft, faAngleRight, faArrowAltCircleDown, faArrowAltCircleUp, faCaretUp, faCheckCircle, faCloudUploadAlt, faExternalLinkAlt, faFilePdf, faFilm, faImage, faLink, faObjectGroup, faPause, faShare, faStickyNote, faStopCircle, faSyncAlt, faTag, faTextHeight, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, reaction, runInAction, get } from "mobx"; +import { Tooltip } from '@material-ui/core'; +import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DataSym, Field, WidthSym, HeightSym, AclEdit, AclAdmin } from "../../fields/Doc"; +import { AclAdmin, AclEdit, DataSym, Doc, Field } from "../../fields/Doc"; import { Document } from '../../fields/documentSchemas'; +import { HtmlField } from '../../fields/HtmlField'; +import { InkField } from "../../fields/InkField"; import { ScriptField } from '../../fields/ScriptField'; -import { Cast, StrCast, NumCast } from "../../fields/Types"; -import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils"; +import { Cast, NumCast } from "../../fields/Types"; +import { GetEffectiveAcl } from '../../fields/util'; +import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from "../../Utils"; import { DocUtils } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; +import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from "../util/UndoManager"; +import { CollectionDockingView } from './collections/CollectionDockingView'; +import FormatShapePane from './collections/collectionFreeForm/FormatShapePane'; import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import e = require('express'); -import { CollectionDockingView } from './collections/CollectionDockingView'; -import { SnappingManager } from '../util/SnappingManager'; -import { HtmlField } from '../../fields/HtmlField'; -import { InkField } from "../../fields/InkField"; -import { Tooltip } from '@material-ui/core'; -import { GetEffectiveAcl } from '../../fields/util'; -import { DocumentIcon } from './nodes/DocumentIcon'; -import { render } from 'react-dom'; -import { createLessThan } from 'typescript'; -import FormatShapePane from './collections/collectionFreeForm/FormatShapePane'; -import { PropertiesView } from './collections/collectionFreeForm/PropertiesView'; library.add(faCaretUp); library.add(faObjectGroup); -- cgit v1.2.3-70-g09d2 From 913cd0d93c0ef2d138606e146d9b3133d6a25026 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 18 Aug 2020 21:26:26 -0400 Subject: fixed snapping to work when dragging an alias to allow snapping to original --- src/client/views/DocumentDecorations.tsx | 2 +- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 +++--- src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 535e86457..0cc492ee9 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -360,7 +360,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY; this.Interacting = true; this._resizeUndo = UndoManager.StartBatch("DocDecs resize"); - SelectionManager.SelectedDocuments()[0].props.setupDragLines?.(); + SelectionManager.SelectedDocuments()[0].props.setupDragLines?.(e.ctrlKey || e.shiftKey); } this._snapX = e.pageX; this._snapY = e.pageY; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b595ec2ac..5d6d7924e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1315,7 +1315,7 @@ export class CollectionFreeFormView extends CollectionSubView { + setupDragLines = (snapToDraggedDoc: boolean = false) => { const activeDocs = this.getActiveDocuments(); if (activeDocs.length > 50) { DragManager.SetSnapLines([], []); @@ -1337,7 +1337,7 @@ export class CollectionFreeFormView extends CollectionSubView !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => { + snappableDocs.filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => { const { left, top, width, height } = docDims(doc); const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top); const docSize = this.getTransform().inverse().transformDirection(width, height); @@ -1349,7 +1349,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (SnappingManager.GetIsDragging()) { - this.setupDragLines(); + this.setupDragLines(e.ctrlKey || e.shiftKey); } e.stopPropagation(); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 168a0df38..0939ccdf7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -69,7 +69,7 @@ export interface DocumentViewProps { removeDocument?: (doc: Doc | Doc[]) => boolean; moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; ScreenToLocalTransform: () => Transform; - setupDragLines?: () => void; + setupDragLines?: (snapToDraggedDoc: boolean) => void; renderDepth: number; ContentScaling: () => number; PanelWidth: () => number; -- cgit v1.2.3-70-g09d2 From 36630b9aa2e1c4710a69a4fdf4ec98c3f5bca92c Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 21 Aug 2020 16:05:52 -0400 Subject: trying scenes instead of workspaces --- src/client/util/CurrentUserUtils.ts | 40 ++++++------- src/client/util/History.ts | 2 +- src/client/util/SettingsManager.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.scss | 2 +- src/client/views/MainView.tsx | 50 ++++++++-------- .../views/collections/CollectionDockingView.tsx | 12 ++-- .../views/collections/CollectionTreeView.tsx | 8 +-- src/client/views/collections/CollectionView.tsx | 2 +- .../CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 10 ++-- .../views/nodes/formattedText/DashDocView.tsx | 4 +- .../views/nodes/formattedText/RichTextSchema.tsx | 4 +- src/mobile/MobileInterface.tsx | 70 +++++++++++----------- 15 files changed, 105 insertions(+), 106 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8dd7b033b..4b1c48bd3 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -36,7 +36,7 @@ export class CurrentUserUtils { @computed public static get UserDocument() { return Doc.UserDoc(); } @observable public static GuestTarget: Doc | undefined; - @observable public static GuestWorkspace: Doc | undefined; + @observable public static GuestScene: Doc | undefined; @observable public static GuestMobile: Doc | undefined; @observable public static propertiesWidth: number = 0; @@ -511,7 +511,7 @@ export class CurrentUserUtils { this.setupSharingSidebar(doc); // sets up the right sidebar collection for mobile upload documents and sharing return [ { title: "Sharing", target: Cast(doc["sidebar-sharing"], Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc["sidebar-sharing"] as Doc }, - { title: "Workspace", target: Cast(doc["sidebar-workspaces"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, + { title: "Scenes", target: Cast(doc["sidebar-scenes"], Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' }, { title: "Archive", target: Cast(doc["sidebar-recentlyClosed"], Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, { title: "Import", target: Cast(doc["sidebar-import"], Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, @@ -592,7 +592,7 @@ export class CurrentUserUtils { // SEts up mobile buttons for inside mobile menu static setupMobileButtons(doc?: Doc, buttons?: string[]) { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ - { title: "WORKSPACES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Workspaces from your mobile, and navigate through all of your documents. " }, + { title: "SCENES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Scenes from your mobile, and navigate through all of your documents. " }, { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." }, { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." }, { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." }, @@ -689,7 +689,7 @@ export class CurrentUserUtils { } static setupLibrary(userDoc: Doc) { - return CurrentUserUtils.setupWorkspaces(userDoc); + return CurrentUserUtils.setupScenes(userDoc); } // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. @@ -724,28 +724,28 @@ export class CurrentUserUtils { } } - static async setupWorkspaces(doc: Doc) { - // setup workspaces library item - await doc.myWorkspaces; - if (doc.myWorkspaces === undefined) { - doc.myWorkspaces = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "WORKSPACES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true + static async setupScenes(doc: Doc) { + // setup scenes library item + await doc.myScenes; + if (doc.myScenes === undefined) { + doc.myScenes = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "SCENES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true })); } - if (doc["sidebar-workspaces"] === undefined) { - const newWorkspace = ScriptField.MakeScript(`createNewWorkspace()`); - (doc.myWorkspaces as Doc).contextMenuScripts = new List([newWorkspace!]); - (doc.myWorkspaces as Doc).contextMenuLabels = new List(["Create New Workspace"]); + if (doc["sidebar-scenes"] === undefined) { + const newScene = ScriptField.MakeScript(`createNewScene()`); + (doc.myScenes as Doc).contextMenuScripts = new List([newScene!]); + (doc.myScenes as Doc).contextMenuLabels = new List(["Create New Scene"]); - const workspaces = doc.myWorkspaces as Doc; + const scenes = doc.myScenes as Doc; - doc["sidebar-workspaces"] = new PrefetchProxy(Docs.Create.TreeDocument([workspaces], { + doc["sidebar-scenes"] = new PrefetchProxy(Docs.Create.TreeDocument([scenes], { treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, treeViewOpen: true, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })) as any as Doc; } - return doc.myWorkspaces as any as Doc; + return doc.myScenes as any as Doc; } static setupCatalog(doc: Doc) { @@ -821,7 +821,7 @@ export class CurrentUserUtils { static async setupSidebarButtons(doc: Doc) { CurrentUserUtils.setupSidebarContainer(doc); await CurrentUserUtils.setupToolsBtnPanel(doc); - CurrentUserUtils.setupWorkspaces(doc); + CurrentUserUtils.setupScenes(doc); CurrentUserUtils.setupCatalog(doc); CurrentUserUtils.setupRecentlyClosed(doc); CurrentUserUtils.setupUserDoc(doc); @@ -1002,8 +1002,8 @@ export class CurrentUserUtils { } } -Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }, - "creates a new workspace when called"); +Scripting.addGlobal(function createNewScene() { return MainView.Instance.createNewScene(); }, + "creates a new scene when called"); Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 7b7d4b835..aed887055 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -197,7 +197,7 @@ export namespace HistoryUtil { await Promise.all(Object.keys(init).map(id => initDoc(id, init[id]))); } if (field instanceof Doc) { - MainView.Instance.openWorkspace(field, true); + MainView.Instance.openScene(field, true); } } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 5642c5a42..b2131c9b2 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -158,7 +158,7 @@ export default class SettingsManager extends React.Component<{}> {
Settings
{Doc.CurrentUserEmail}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0cc492ee9..fdce8bf71 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -596,7 +596,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return ; } render() { - const darkScheme = Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimgray" : undefined; + const darkScheme = Cast(Doc.UserDoc().activeScene, Doc, null)?.darkScheme ? "dimgray" : undefined; const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index a05a2b858..fb80bfc0d 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -313,7 +313,7 @@ } -.mainView-workspace { +.mainView-scene { height: 200px; position: relative; display: flex; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6bbe09974..08ac69a38 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -77,10 +77,10 @@ export class MainView extends React.Component { @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = false; @observable public flyoutWidth: number = 0; - private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeWorkspace, Doc, null)?.darkScheme); } + private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeScene, Doc, null)?.darkScheme); } @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeWorkspace, Doc)) : CurrentUserUtils.GuestWorkspace; } + @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeScene, Doc)) : CurrentUserUtils.GuestScene; } @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } @computed public get searchDoc() { return Cast(this.userDoc["search-panel"], Doc) as Doc; } @@ -224,12 +224,12 @@ export class MainView extends React.Component { } initAuthenticationRouters = async () => { - // Load the user's active workspace, or create a new one if initial session after signup + // Load the user's active scene, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; if (received && !this.userDoc) { reaction( () => CurrentUserUtils.GuestTarget, - target => target && this.createNewWorkspace(), + target => target && this.createNewScene(), { fireImmediately: true } ); } else { @@ -242,21 +242,21 @@ export class MainView extends React.Component { }), ); } - const doc = this.userDoc && await Cast(this.userDoc.activeWorkspace, Doc); + const doc = this.userDoc && await Cast(this.userDoc.activeScene, Doc); if (doc) { - this.openWorkspace(doc); + this.openScene(doc); } else { - this.createNewWorkspace(); + this.createNewScene(); } } } @action - createNewWorkspace = async (id?: string) => { + createNewScene = async (id?: string) => { const myCatalog = Doc.UserDoc().myCatalog as Doc; const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); - const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; - const workspaceCount = DocListCast(workspaces.data).length + 1; + const scenes = Cast(this.userDoc.myScenes, Doc) as Doc; + const sceneCount = DocListCast(scenes.data).length + 1; const freeformOptions: DocumentOptions = { x: 0, y: 400, @@ -265,28 +265,28 @@ export class MainView extends React.Component { title: "Untitled Collection", }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Workspace ${workspaceCount}` }, id, "row"); + const sceneDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Scene ${sceneCount}` }, id, "row"); Doc.AddDocToList(myCatalog, "data", freeformDoc); Doc.AddDocToList(myCatalog, "data", presentation); Doc.UserDoc().activePresentation = presentation; const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`); - workspaceDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, copyWorkspace!]); - workspaceDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Workspace"]); + const copyScene = ScriptField.MakeScript(`copyScene()`); + sceneDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, copyScene!]); + sceneDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Scene"]); - Doc.AddDocToList(workspaces, "data", workspaceDoc); + Doc.AddDocToList(scenes, "data", sceneDoc); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => this.openWorkspace(workspaceDoc), 0); + setTimeout(() => this.openScene(sceneDoc), 0); } @action - openWorkspace = (doc: Doc, fromHistory = false) => { + openScene = (doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; - if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest workspace + if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest scene !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); - this.userDoc ? (this.userDoc.activeWorkspace = doc) : (CurrentUserUtils.GuestWorkspace = doc); + this.userDoc ? (this.userDoc.activeScene = doc) : (CurrentUserUtils.GuestScene = doc); } const state = this._urlState; if (state.sharing === true && !this.userDoc) { @@ -438,7 +438,7 @@ export class MainView extends React.Component { flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { return where === "close" ? CollectionDockingView.CloseRightSplit(doc) : - doc.dockingConfig ? this.openWorkspace(doc) : + doc.dockingConfig ? this.openScene(doc) : CollectionDockingView.AddRightSplit(doc, libraryPath); } sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1); @@ -977,12 +977,12 @@ export class MainView extends React.Component { } Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); }); Scripting.addGlobal(function toggleComicMode() { Doc.UserDoc().fontFamily = "Comic Sans MS"; Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); -Scripting.addGlobal(function copyWorkspace() { - const copiedWorkspace = Doc.MakeCopy(Cast(Doc.UserDoc().activeWorkspace, Doc, null), true); - const workspaces = Cast(Doc.UserDoc().myWorkspaces, Doc, null); - Doc.AddDocToList(workspaces, "data", copiedWorkspace); +Scripting.addGlobal(function copyScene() { + const copiedScene = Doc.MakeCopy(Cast(Doc.UserDoc().activeScene, Doc, null), true); + const scenes = Cast(Doc.UserDoc().myScenes, Doc, null); + Doc.AddDocToList(scenes, "data", copiedScene); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) - setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0); + setTimeout(() => MainView.Instance.openScene(copiedScene), 0); }); Scripting.addGlobal(function importDocument() { return MainView.Instance.importDocument(); }, "imports files from device directly into the import sidebar"); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 43da0d3cf..6338e69a4 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -96,7 +96,7 @@ export class CollectionDockingView extends React.Component this.setupGoldenLayout(), 1); - DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false); - this.props.Document.workspaceBrush = true; + DocListCast((Doc.UserDoc().myScenes as Doc).data).map(d => d.sceneBrush = false); + this.props.Document.sceneBrush = true; } this._ignoreStateChange = ""; }, { fireImmediately: true }); @@ -421,7 +421,7 @@ export class CollectionDockingView extends React.Component void = () => { try { - this.props.Document.workspaceBrush = false; + this.props.Document.sceneBrush = false; this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); this._goldenLayout.unbind('stackCreated', this.stackCreated); @@ -668,7 +668,7 @@ export class CollectionDockingView extends React.Component 0) { - return
Nested workspaces can't be rendered
; + return
Nested scenes can't be rendered
; } return ; } diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index f79a68b5a..e95b28d6c 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -16,7 +16,6 @@ import { DocumentType } from '../documents/DocumentTypes'; import { SelectionManager } from '../util/SelectionManager'; import { undoBatch } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import './collections/ParentDocumentSelector.scss'; import { GoogleRef } from "./nodes/formattedText/FormattedTextBox"; import './PropertiesButtons.scss'; import React = require("react"); diff --git a/src/client/views/PropertiesDocContextSelector.scss b/src/client/views/PropertiesDocContextSelector.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx new file mode 100644 index 000000000..b382298f3 --- /dev/null +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -0,0 +1,58 @@ +import { IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import { Doc } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; +import { NumCast, StrCast } from "../../fields/Types"; +import { CollectionViewType } from "./collections/CollectionView"; +import { CollectionDockingView } from "./collections/CollectionDockingView"; +import './PropertiesDocContextSelector.scss'; +import { SearchUtil } from "../util/SearchUtil"; + +type PropertiesDocContextSelectorProps = { + Document: Doc, + Stack?: any, + hideTitle?: boolean, + addDocTab(doc: Doc, location: string): void +}; + +@observer +export class PropertiesDocContextSelector extends React.Component { + @observable private _docs: { col: Doc, target: Doc }[] = []; + @observable private _otherDocs: { col: Doc, target: Doc }[] = []; + _reaction: IReactionDisposer | undefined; + + componentDidMount() { this._reaction = reaction(() => this.props.Document, () => this.fetchDocuments(), { fireImmediately: true }); } + componentWillUnmount() { this._reaction?.(); } + async fetchDocuments() { + const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); + const containerProtoSets = await Promise.all(aliases.map(async alias => ((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs))); + const containerProtos = containerProtoSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); + const containerSets = await Promise.all(Array.from(containerProtos.keys()).map(async container => SearchUtil.GetAliasesOfDocument(container))); + const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); + const doclayoutSets = await Promise.all(Array.from(containers.keys()).map(async (dp) => SearchUtil.GetAliasesOfDocument(dp))); + const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()).keys()); + runInAction(() => { + this._docs = doclayouts.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).filter(doc => !Doc.IsSystem(doc)).map(doc => ({ col: doc, target: this.props.Document })); + this._otherDocs = []; + }); + } + + getOnClick = (col: Doc, target: Doc) => { + col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; + if (col._viewType === CollectionViewType.Freeform) { + col._panX = NumCast(target.x) + NumCast(target._width) / 2; + col._panY = NumCast(target.y) + NumCast(target._height) / 2; + } + this.props.addDocTab(col, "inTab"); + } + + render() { + return
+ {this.props.hideTitle ? (null) :

Contexts:

} + {this._docs.map(doc =>

this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}

)} + {this._otherDocs.length ?
: null} + {this._otherDocs.map(doc =>

this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}

)} +
; + } +} \ No newline at end of file diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss new file mode 100644 index 000000000..254afeb0a --- /dev/null +++ b/src/client/views/PropertiesView.scss @@ -0,0 +1,779 @@ +.propertiesView { + + background-color: rgb(205, 205, 205); + height: 100%; + font-family: "Noto Sans"; + cursor: auto; + + overflow-x: hidden; + overflow-y: scroll; + + .propertiesView-title { + background-color: rgb(159, 159, 159); + text-align: center; + padding-top: 12px; + padding-bottom: 12px; + display: flex; + font-size: 18px; + font-weight: bold; + justify-content: center; + + .propertiesView-title-icon { + width: 20px; + height: 20px; + padding-left: 38px; + margin-top: -5px; + align-items: flex-end; + margin-left: auto; + margin-right: 10px; + + &:hover { + color: grey; + cursor: pointer; + } + + } + + } + + .propertiesView-name { + border-bottom: 1px solid black; + padding: 8.5px; + font-size: 12.5px; + + &:hover { + cursor: text; + } + } + + .propertiesView-settings { + border-bottom: 1px solid black; + //padding: 8.5px; + font-size: 12.5px; + font-weight: bold; + + .propertiesView-settings-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-settings-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-settings-content { + margin-left: 12px; + padding-bottom: 10px; + padding-top: 8px; + } + + } + + .propertiesView-sharing { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-sharing-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-sharing-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-sharing-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + + .propertiesView-acls-checkbox { + float: right; + height: 20px; + margin-top: -20px; + margin-right: -15; + + .propertiesView-acls-checkbox-text { + font-size: 7px; + margin-top: -10px; + margin-left: 6px; + } + } + + .change-buttons { + display: flex; + + button { + width: 5; + height: 5; + } + + input { + width: 100%; + } + } + } + } + + .propertiesView-appearance { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-appearance-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-appearance-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-appearance-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + + .propertiesView-transform { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-transform-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-transform-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-transform-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } + + .notify-button { + padding: 2px; + width: 12px; + height: 12px; + background-color: black; + border-radius: 10px; + padding-left: 2px; + padding-right: 2px; + margin-top: 2px; + margin-left: 3px; + + .notify-button-icon { + width: 6px; + height: 6.5px; + margin-left: .5px; + } + + &:hover { + background-color: rgb(158, 158, 158); + cursor: pointer; + } + } + + .expansion-button-icon { + width: 11px; + height: 11px; + color: black; + margin-left: 27px; + + &:hover { + color: rgb(131, 131, 131); + cursor: pointer; + } + } + + .propertiesView-sharingTable { + + // whatever's commented out - add it back in when adding the buttons + + // border: 1.5px solid black; + border: 1px solid black; + padding: 5px; // remove when adding buttons + border-radius: 6px; // remove when adding buttons + margin-right: 10px; // remove when adding buttons + // width: 100%; + // display: inline-table; + background-color: #ececec; + max-height: 130px; + overflow-y: scroll; + width: 92%; + + .propertiesView-sharingTable-item { + + display: flex; + // padding: 5px; + padding: 3px; + align-items: center; + border-bottom: 0.5px solid grey; + + &:hover .propertiesView-sharingTable-item-name { + overflow-x: unset; + white-space: unset; + overflow-wrap: break-word; + } + + .propertiesView-sharingTable-item-name { + font-weight: bold; + width: 95px; + overflow-x: hidden; + display: inline-block; + text-overflow: ellipsis; + white-space: nowrap; + } + + .propertiesView-sharingTable-item-permission { + display: flex; + align-items: flex-end; + margin-left: auto; + + .permissions-select { + border: none; + background-color: inherit; + width: 75px; + //text-align: justify; // for Edge + //text-align-last: end; + + &:hover { + cursor: pointer; + } + } + } + + &:last-child { + border-bottom: none; + } + } + } + + .propertiesView-fields { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-fields-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-fields-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + + } + + .propertiesView-fields-checkbox { + float: right; + height: 20px; + margin-top: -9px; + + .propertiesView-fields-checkbox-text { + font-size: 7px; + margin-top: -10px; + margin-left: 6px; + } + } + + .propertiesView-fields-content { + font-size: 10px; + margin-left: 2px; + padding: 10px; + + &:hover { + cursor: pointer; + } + } + } + + .field { + display: flex; + font-size: 7px; + background-color: #e8e8e8; + padding-right: 3px; + border: 0.5px solid grey; + border-radius: 5px; + padding-left: 3px; + } + + .uneditable-field { + display: flex; + overflow-y: visible; + margin-bottom: 2px; + + &:hover { + cursor: auto; + } + } + .propertiesView-contexts { + + .propertiesView-contexts-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-contexts-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-contexts-content { + overflow: hidden; + padding: 10px; + } + + } + + .propertiesView-layout { + + .propertiesView-layout-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-layout-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-layout-content { + overflow: hidden; + padding: 10px; + } + + } + + .propertiesView-presTrails { + border-bottom: 1px solid black; + //padding: 8.5px; + + .propertiesView-presTrails-title { + font-weight: bold; + font-size: 12.5px; + padding: 4px; + display: flex; + color: white; + padding-left: 8px; + background-color: rgb(51, 51, 51); + + &:hover { + cursor: pointer; + } + + .propertiesView-presTrails-title-icon { + float: right; + justify-items: right; + align-items: flex-end; + margin-left: auto; + margin-right: 9px; + + &:hover { + cursor: pointer; + } + } + } + + .propertiesView-presTrails-content { + font-size: 10px; + padding: 10px; + margin-left: 5px; + } + } +} + +.inking-button { + + display: flex; + + .inking-button-points { + background-color: #333333; + padding: 7px; + border-radius: 7px; + margin-right: 32px; + width: 32; + height: 32; + padding-top: 9px; + margin-left: 18px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inking-button-lock { + background-color: #333333; + padding: 7px; + border-radius: 7px; + margin-right: 32px; + width: 32; + height: 32; + padding-top: 9px; + padding-left: 10px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inking-button-rotate { + background-color: #333333; + padding: 7px; + border-radius: 7px; + width: 32; + height: 32; + padding-top: 9px; + padding-left: 10px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } +} + +.inputBox-duo { + display: flex; +} + +.inputBox { + + margin-top: 10px; + display: flex; + height: 19px; + margin-right: 15px; + + .inputBox-title { + font-size: 12px; + padding-right: 5px; + } + + .inputBox-input { + font-size: 10px; + width: 50px; + margin-right: 1px; + border-radius: 3px; + + &:hover { + cursor: pointer; + } + } + + .inputBox-button { + + .inputBox-button-up { + background-color: #333333; + height: 9px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 1.5px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + .inputBox-button-down { + background-color: #333333; + height: 9px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1px; + padding-bottom: 1px; + border-radius: 1.5px; + + &:hover { + background: rgb(131, 131, 131); + transform: scale(1.05); + cursor: pointer; + } + } + + } +} + +.color-palette { + width: 160px; + height: 360; +} + +.strokeAndFill { + display: flex; + margin-top: 10px; + + .fill { + margin-right: 40px; + display: flex; + padding-bottom: 7px; + margin-left: 35px; + + .fill-title { + font-size: 12px; + margin-right: 2px; + } + + .fill-button { + padding-top: 2px; + margin-top: -1px; + } + } + + .stroke { + display: flex; + + .stroke-title { + font-size: 12px; + } + + .stroke-button { + padding-top: 2px; + margin-left: 2px; + margin-top: -1px; + } + } +} + +.propertiesView-presSelected { + border-top: solid 1px darkgrey; + width: 100%; + padding-top: 5px; + font-family: Roboto; + font-weight: 500; + display: inline-flex; + + .propertiesView-selectedList { + border-left: solid 1px darkgrey; + margin-left: 10px; + padding-left: 5px; + + .selectedList-items { + font-size: 12; + font-weight: 300; + margin-top: 1; + } + } +} + +.widthAndDash { + + .width { + .width-top { + display: flex; + + .width-title { + font-size: 12; + margin-right: 20px; + margin-left: 35px; + text-align: center; + } + + .width-input { + margin-right: 30px; + margin-top: -10px; + } + } + + .width-range { + margin-right: 1px; + margin-bottom: 6; + } + } + + .arrows { + + display: flex; + margin-bottom: 3px; + margin-left: 4px; + + .arrows-head { + + display: flex; + margin-right: 35px; + + .arrows-head-title { + font-size: 10; + } + + .arrows-head-input { + margin-left: 6px; + margin-top: 2px; + } + } + + .arrows-tail { + display: flex; + + .arrows-tail-title { + font-size: 10; + } + + .arrows-tail-input { + margin-left: 6px; + margin-top: 2px; + } + } + } + + .dashed { + + display: flex; + margin-left: 64px; + margin-bottom: 6px; + + .dashed-title { + font-size: 10; + } + + .dashed-input { + margin-left: 6px; + margin-top: 2px; + } + } +} + +.editable-title { + border: none; + padding: 6px; + padding-bottom: 2px; + background: #eeeeee; + border-top: 1px solid; + border-left: 1px solid; + + &:hover { + border: 0.75px solid rgb(122, 28, 28); + } +} + + +.properties-flyout { + grid-column: 2/4; +} \ No newline at end of file diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx new file mode 100644 index 000000000..fef0a3e00 --- /dev/null +++ b/src/client/views/PropertiesView.tsx @@ -0,0 +1,1089 @@ +import React = require("react"); +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Checkbox, Tooltip } from "@material-ui/core"; +import { action, computed, observable } from "mobx"; +import { observer } from "mobx-react"; +import { ColorState, SketchPicker } from "react-color"; +import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, Field, HeightSym, WidthSym } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; +import { InkField } from "../../fields/InkField"; +import { ComputedField } from "../../fields/ScriptField"; +import { Cast, NumCast, StrCast } from "../../fields/Types"; +import { GetEffectiveAcl, SharingPermissions } from "../../fields/util"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../Utils"; +import { DocumentType } from "../documents/DocumentTypes"; +import { DocumentManager } from "../util/DocumentManager"; +import { SelectionManager } from "../util/SelectionManager"; +import { SharingManager } from "../util/SharingManager"; +import { Transform } from "../util/Transform"; +import { undoBatch, UndoManager } from "../util/UndoManager"; +import { CollectionDockingView } from "./collections/CollectionDockingView"; +import { EditableView } from "./EditableView"; +import { FormatShapePane } from "./FormatShapePane"; +import { ContentFittingDocumentView } from "./nodes/ContentFittingDocumentView"; +import { KeyValueBox } from "./nodes/KeyValueBox"; +import { PresBox } from "./nodes/PresBox"; +import { PropertiesButtons } from "./PropertiesButtons"; +import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector"; +import "./PropertiesView.scss"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; +const _global = (window /* browser */ || global /* node */) as any; + +interface PropertiesViewProps { + width: number; + height: number; + renderDepth: number; + ScreenToLocalTransform: () => Transform; + onDown: (event: any) => void; +} + +@observer +export class PropertiesView extends React.Component { + private _widthUndo?: UndoManager.Batch; + + @computed get MAX_EMBED_HEIGHT() { return 200; } + + @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; } + @computed get selectedDocumentView() { + if (SelectionManager.SelectedDocuments().length) { + return SelectionManager.SelectedDocuments()[0]; + } else if (PresBox.Instance && PresBox.Instance._selectedArray.length) { + return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); + } else { return undefined; } + } + @computed get isPres(): boolean { + if (this.selectedDoc?.type === DocumentType.PRES) return true; + return false; + } + @computed get dataDoc() { return this.selectedDoc?.[DataSym]; } + + @observable layoutFields: boolean = false; + + @observable openOptions: boolean = true; + @observable openSharing: boolean = true; + @observable openFields: boolean = true; + @observable openLayout: boolean = true; + @observable openContexts: boolean = true; + @observable openAppearance: boolean = true; + @observable openTransform: boolean = true; + // @observable selectedUser: string = ""; + // @observable addButtonPressed: boolean = false; + @observable layoutDocAcls: boolean = false; + + //Pres Trails booleans: + @observable openPresTransitions: boolean = false; + @observable openPresProgressivize: boolean = false; + @observable openAddSlide: boolean = false; + @observable openSlideOptions: boolean = false; + + @observable inOptions: boolean = false; + @observable _controlBtn: boolean = false; + @observable _lock: boolean = false; + + @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; } + + @action + rtfWidth = () => { + if (this.selectedDoc) { + return Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20); + } else { + return 0; + } + } + @action + rtfHeight = () => { + if (this.selectedDoc) { + return this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + } else { + return 0; + } + } + + @action + docWidth = () => { + if (this.selectedDoc) { + const layoutDoc = this.selectedDoc; + const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); + if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.width - 20)); + return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20; + } else { + return 0; + } + } + + @action + docHeight = () => { + if (this.selectedDoc && this.dataDoc) { + const layoutDoc = this.selectedDoc; + return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => { + const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); + if (aspect) return this.docWidth() * aspect; + return layoutDoc._fitWidth ? (!this.dataDoc._nativeHeight ? NumCast(this.props.height) : + Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, + NumCast(this.props.height)))) : + NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; + })())); + } else { + return 0; + } + } + + @computed get expandedField() { + if (this.dataDoc && this.selectedDoc) { + const ids: { [key: string]: string } = {}; + const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : + SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? Doc.Layout(dv.layoutDoc) : dv.dataDoc); + docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key))); + const rows: JSX.Element[] = []; + for (const key of Object.keys(ids).slice().sort()) { + const docvals = new Set(); + docs.forEach(doc => docvals.add(doc[key])); + const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key]; + if (key[0] === "#") { + rows.push(
+ {key} +   +
); + } else { + const contentElement = contents !== undefined ? Field.toString(contents as Field) : "null"} + SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }} + />; + rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + } + rows.push(
+ ""} + SetValue={this.setKeyValue} /> +
); + return rows; + } + } + + @computed get noviceFields() { + if (this.dataDoc) { + const ids: { [key: string]: string } = {}; + const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => dv.dataDoc); + docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key))); + const rows: JSX.Element[] = []; + const noviceReqFields = ["author", "creationDate"]; + const noviceLayoutFields = ["_curPage"]; + const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL") && key !== "UseCors")), + ...noviceReqFields, ...noviceLayoutFields]; + for (const key of noviceKeys.sort()) { + const docvals = new Set(); + docs.forEach(doc => docvals.add(doc[key])); + const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key]; + if (key[0] === "#") { + rows.push(
+ {key} +   +
); + } else if (contents !== undefined) { + const value = Field.toString(contents as Field); + if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) { + rows.push(
+ {key + ": "} +
{value}
+
); + } else { + const contentElement = contents !== undefined ? Field.toString(contents as Field) : "null"} + SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }} + />; + + rows.push(
+ {key + ":"} +   + {contentElement} +
); + } + } + } + rows.push(
+ ""} + SetValue={this.setKeyValue} /> +
); + return rows; + } + } + + @undoBatch + setKeyValue = (value: string) => { + const docs = SelectionManager.SelectedDocuments().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc); + docs.forEach(doc => { + if (value.indexOf(":") !== -1) { + const newVal = value[0].toUpperCase() + value.substring(1, value.length); + KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); + return true; + } else if (value[0] === "#") { + const newVal = value + `:'${value}'`; + KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); + return true; + } + }); + return false; + } + + @observable transform: Transform = Transform.Identity(); + getTransform = () => this.transform; + propertiesDocViewRef = (ref: HTMLDivElement) => { + const observer = new _global.ResizeObserver(action((entries: any) => { + const cliRect = ref.getBoundingClientRect(); + this.transform = new Transform(-cliRect.x, -cliRect.y, 1); + })); + ref && observer.observe(ref); + } + + @computed get contexts() { + return !this.selectedDoc ? (null) : CollectionDockingView.AddRightSplit(doc)} />; + } + + previewBackground = () => "lightgrey"; + @computed get layoutPreview() { + if (SelectionManager.SelectedDocuments().length > 1) { + return "-- multiple selected --"; + } + if (this.selectedDoc) { + const layoutDoc = Doc.Layout(this.selectedDoc); + const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight; + const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth; + return
+ false} + whenActiveChanged={emptyFunction} + addDocTab={returnFalse} + pinToPres={emptyFunction} + bringToFront={returnFalse} + ContentScaling={returnOne} + dontRegisterView={true} + dropAction={undefined} + /> +
; + } else { + return null; + } + } + + /** + * Handles the changing of a user's permissions from the permissions panel. + */ + @undoBatch + changePermissions = (e: any, user: string) => { + SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!); + } + + /** + * @returns the options for the permissions dropdown. + */ + getPermissionsSelect(user: string, permission: string) { + return ; + } + + /** + * @returns the notification icon. On clicking, it should notify someone of a document been shared with them. + */ + @computed get notifyIcon() { + return Notify with message
}> +
+ +
+ ; + } + + /** + * ... next to the owner that opens the main SharingManager interface on click. + */ + @computed get expansionIcon() { + return {"Show more permissions"}
}> +
{ + if (this.selectedDocumentView || this.selectedDoc) { + SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDocumentView ? this.selectedDocumentView : undefined, this.selectedDoc); + } + }}> + +
+ ; + } + + /** + * @returns a row of the permissions panel + */ + sharingItem(name: string, effectiveAcl: symbol, permission: string) { + return
this.selectedUser = this.selectedUser === name ? "" : name)} + > +
{name}
+ {/* {name !== "Me" ? this.notifyIcon : null} */} +
+ {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission} + {permission === "Owner" ? this.expansionIcon : null} +
+
; + } + + /** + * @returns the sharing and permissiosn panel. + */ + @computed get sharingTable() { + const AclMap = new Map([ + [AclPrivate, SharingPermissions.None], + [AclReadonly, SharingPermissions.View], + [AclAddonly, SharingPermissions.Add], + [AclEdit, SharingPermissions.Edit], + [AclAdmin, SharingPermissions.Admin] + ]); + + const target = this.layoutDocAcls ? this.selectedDoc! : this.selectedDoc![DataSym]; + + const effectiveAcl = GetEffectiveAcl(target); + const tableEntries = []; + + // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { + if (target[AclSym]) { + for (const [key, value] of Object.entries(target[AclSym])) { + const name = key.substring(4).replace("_", "."); + if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public"/* && sidebarUsersDisplayed![name] !== false*/) { + tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value as symbol)!)); + } + } + } + + // if (Doc.UserDoc().sidebarUsersDisplayed) { + // for (const [name, value] of Object.entries(sidebarUsersDisplayed!)) { + // if (value === true && !this.selectedDoc![`ACL-${name.substring(8).replace(".", "_")}`]) tableEntries.push(this.sharingItem(name.substring(8), effectiveAcl, SharingPermissions.None)); + // } + // } + // }) + + // shifts the current user, owner, public to the top of the doc. + tableEntries.unshift(this.sharingItem("Public", effectiveAcl, (AclMap.get(target[AclSym]?.["ACL-Public"]) || SharingPermissions.None))); + tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === target.author ? "Owner" : AclMap.get(effectiveAcl)!)); + if (Doc.CurrentUserEmail !== target.author) tableEntries.unshift(this.sharingItem(StrCast(target.author), effectiveAcl, "Owner")); + + return
+ {tableEntries} +
; + } + + @computed get fieldsCheckbox() { + return ; + } + + @action + toggleCheckbox = () => { + this.layoutFields = !this.layoutFields; + } + + @computed get editableTitle() { + const titles = new Set(); + SelectionManager.SelectedDocuments().forEach(dv => titles.add(StrCast(dv.rootDoc.title))); + const title = Array.from(titles.keys()).length > 1 ? "--multiple selected--" : StrCast(this.selectedDoc?.title); + return
title} + SetValue={this.setTitle} />
; + } + + @undoBatch + @action + setTitle = (value: string) => { + if (SelectionManager.SelectedDocuments().length > 1) { + SelectionManager.SelectedDocuments().map(dv => Doc.SetInPlace(dv.rootDoc, "title", value, true)); + return true; + } else if (this.dataDoc) { + if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, "title", value, true); + else KeyValueBox.SetField(this.dataDoc, "title", value, true); + return true; + } + return false; + } + + + @undoBatch + @action + rotate = (angle: number) => { + const _centerPoints: { X: number, Y: number }[] = []; + if (this.selectedDoc) { + const doc = this.selectedDoc; + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const xs = ink.map(p => p.X); + const ys = ink.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + _centerPoints.push({ X: left, Y: top }); + } + } + + var index = 0; + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + doc.rotation = Number(doc.rotation) + Number(angle); + const inks = Cast(doc.data, InkField)?.inkData; + if (inks) { + const newPoints: { X: number, Y: number }[] = []; + inks.forEach(ink => { + const newX = Math.cos(angle) * (ink.X - _centerPoints[index].X) - Math.sin(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].X; + const newY = Math.sin(angle) * (ink.X - _centerPoints[index].X) + Math.cos(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].Y; + newPoints.push({ X: newX, Y: newY }); + }); + doc.data = new InkField(newPoints); + const xs = newPoints.map(p => p.X); + const ys = newPoints.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + + doc._height = (bottom - top); + doc._width = (right - left); + } + index++; + } + } + } + + + + @computed + get controlPointsButton() { + return
+ {"Edit points"}
}> +
FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn)} style={{ backgroundColor: FormatShapePane.Instance._controlBtn ? "black" : "" }}> + +
+ + {FormatShapePane.Instance._lock ? "Unlock ratio" : "Lock ratio"}
}> +
FormatShapePane.Instance._lock = !FormatShapePane.Instance._lock)} > + +
+ + {"Rotate 90˚"}
}> +
this.rotate(Math.PI / 2))}> + +
+ + ; + } + + inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => { + return
+
{title}
+ { + setter(e.target.value); + }} + onKeyPress={e => { + e.stopPropagation(); + }} /> +
+
this.upDownButtons("up", key)))} > + +
+
this.upDownButtons("down", key)))} > + +
+
+
; + } + + inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { + return
+ {this.inputBox(key, value, setter, title1)} + {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)} +
; + } + + @action + upDownButtons = (dirs: string, field: string) => { + switch (field) { + case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; + // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; + case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break; + case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break; + case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; + case "wid": + const oldWidth = NumCast(this.selectedDoc?._width); + const oldHeight = NumCast(this.selectedDoc?._height); + const oldX = NumCast(this.selectedDoc?.x); + const oldY = NumCast(this.selectedDoc?.y); + this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10)); + FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height))); + const doc = this.selectedDoc; + if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var j = 0; j < ink.length; j++) { + // (new x — oldx) + (oldxpoint * newWidt)/oldWidth + const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth; + const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight; + newPoints.push({ X: newX, Y: newY }); + } + doc.data = new InkField(newPoints); + } + } + break; + case "hgt": + const oWidth = NumCast(this.selectedDoc?._width); + const oHeight = NumCast(this.selectedDoc?._height); + const oX = NumCast(this.selectedDoc?.x); + const oY = NumCast(this.selectedDoc?.y); + this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10)); + FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width))); + const docu = this.selectedDoc; + if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) { + const ink = Cast(docu.data, InkField)?.inkData; + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var j = 0; j < ink.length; j++) { + // (new x — oldx) + (oldxpoint * newWidt)/oldWidth + const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth; + const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight; + newPoints.push({ X: newX, Y: newY }); + } + docu.data = new InkField(newPoints); + } + } + break; + } + } + + getField(key: string) { + //if (this.selectedDoc) { + return Field.toString(this.selectedDoc?.[key] as Field); + // } else { + // return undefined as Opt; + // } + } + + @computed get shapeXps() { return this.getField("x"); } + @computed get shapeYps() { return this.getField("y"); } + @computed get shapeRot() { return this.getField("rotation"); } + @computed get shapeHgt() { return this.getField("_height"); } + @computed get shapeWid() { return this.getField("_width"); } + set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); } + set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); } + set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); } + set shapeWid(value) { + const oldWidth = NumCast(this.selectedDoc?._width); + this.selectedDoc && (this.selectedDoc._width = Number(value)); + FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); + } + set shapeHgt(value) { + const oldHeight = NumCast(this.selectedDoc?._height); + this.selectedDoc && (this.selectedDoc._height = Number(value)); + FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); + } + + @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; }, "H:", "wid", this.shapeWid, (val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; }, "W:"); } + @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeXps = val; } return true; }, "X:", "Yps", this.shapeYps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeYps = val; } return true; }, "Y:"); } + @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, "∠:", "rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, ""); } + + + @observable private _fillBtn = false; + @observable private _lineBtn = false; + + private _lastFill = "#D0021B"; + private _lastLine = "#D0021B"; + private _lastDash: any = "2"; + + @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } + @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } + set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); } + set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); } + + colorButton(value: string, type: string, setter: () => {}) { + // return
this.changeScrolling(false)} + // onPointerLeave={e => this.changeScrolling(true)}> + // + return
setter()))}> +
+ {value === "" || value === "transparent" ?

: ""} +
; + // + //
; + + } + + @undoBatch + @action + switchStk = (color: ColorState) => { + const val = String(color.hex); + this.colorStk = val; + return true; + } + @undoBatch + @action + switchFil = (color: ColorState) => { + const val = String(color.hex); + this.colorFil = val; + return true; + } + + colorPicker(setter: (color: string) => {}, type: string) { + return ; + } + + @computed get fillButton() { return this.colorButton(this.colorFil, "fill", () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } + @computed get lineButton() { return this.colorButton(this.colorStk, "line", () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } + + @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } + @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } + + @computed get strokeAndFill() { + return
+
+
+
Fill:
+
{this.fillButton}
+
+
+
Stroke:
+
{this.lineButton}
+
+
+ {this._fillBtn ? this.fillPicker : ""} + {this._lineBtn ? this.linePicker : ""} +
; + } + + @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; } + @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; } + @computed get unStrokd() { return this.selectedDoc?.color ? true : false; } + @computed get widthStk() { return this.getField("strokeWidth") || "1"; } + @computed get markHead() { return this.getField("strokeStartMarker") || ""; } + @computed get markTail() { return this.getField("strokeEndMarker") || ""; } + set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } + set dashdStk(value) { + value && (this._lastDash = value) && (this.unStrokd = false); + this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined); + } + set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); } + set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } + set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); } + set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); } + + + @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); } + + + regInput = (key: string, value: any, setter: (val: string) => {}) => { + return
+ setter(e.target.value)} /> +
+
this.upDownButtons("up", key)))} > + +
+
this.upDownButtons("down", key)))} > + +
+
+
; + } + + @computed get widthAndDash() { + return
+
+
+
Width:
+
{this.stkInput}
+
+ this.widthStk = e.target.value))} + onMouseDown={(e) => { this._widthUndo = UndoManager.StartBatch("width undo"); }} + onMouseUp={(e) => { this._widthUndo?.end(); this._widthUndo = undefined; }} + /> +
+ +
+
+
Arrow Head:
+ this.markHead = this.markHead ? "" : "arrow"))} /> +
+
+
Arrow End:
+ this.markTail = this.markTail ? "" : "arrow"))} /> +
+
+
+
Dashed Line:
+ +
+
; + } + + @undoBatch @action + changeDash = () => { + this.dashdStk = this.dashdStk === "2" ? "0" : "2"; + } + + @computed get appearanceEditor() { + return
+ {this.widthAndDash} + {this.strokeAndFill} +
; + } + + @computed get transformEditor() { + return
+ {this.controlPointsButton} + {this.hgtInput} + {this.XpsInput} + {this.rotInput} +
; + } + + /** + * Handles adding and removing members from the sharing panel + */ + // handleUserChange = (selectedUser: string, add: boolean) => { + // if (!Doc.UserDoc().sidebarUsersDisplayed) Doc.UserDoc().sidebarUsersDisplayed = new Doc; + // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { + // sidebarUsersDisplayed![`display-${selectedUser}`] = add; + // !add && runInAction(() => this.selectedUser = ""); + // }); + // } + + render() { + if (!this.selectedDoc && !this.isPres) { + return
+
+ No Document Selected +
+
; + + } else { + const novice = Doc.UserDoc().noviceMode; + + if (this.selectedDoc && !this.isPres) { + return
+
+ Properties + {/*
+ +
*/} +
+
+ {this.editableTitle} +
+
this.inOptions = true)} + onPointerLeave={action(() => this.inOptions = false)}> +
this.openOptions = !this.openOptions)} + style={{ backgroundColor: this.openOptions ? "black" : "" }}> + Options +
+ +
+
+ {!this.openOptions ? (null) : +
+ +
} +
+
+
this.openSharing = !this.openSharing)} + style={{ backgroundColor: this.openSharing ? "black" : "" }}> + Sharing {"&"} Permissions +
+ +
+
+ {!this.openSharing ? (null) : +
+
+ this.layoutDocAcls = !this.layoutDocAcls)} + checked={this.layoutDocAcls} + />; +
Layout
+
+ {this.sharingTable} + {/*
+ + + + {this.addButtonPressed ? + // : + : + null} +
*/} +
} +
+ + {!this.isInk ? (null) : +
+
this.openAppearance = !this.openAppearance)} + style={{ backgroundColor: this.openAppearance ? "black" : "" }}> + Appearance +
+ +
+
+ {!this.openAppearance ? (null) : +
+ {this.appearanceEditor} +
} +
} + + {this.isInk ?
+
this.openTransform = !this.openTransform)} + style={{ backgroundColor: this.openTransform ? "black" : "" }}> + Transform +
+ +
+
+ {this.openTransform ?
+ {this.transformEditor} +
: null} +
: null} + +
+
this.openFields = !this.openFields)} + style={{ backgroundColor: this.openFields ? "black" : "" }}> + Fields {"&"} Tags +
+ +
+
+ {!novice && this.openFields ?
+ {this.fieldsCheckbox} +
Layout
+
: null} + {!this.openFields ? (null) : +
+ {novice ? this.noviceFields : this.expandedField} +
} +
+
+
this.openContexts = !this.openContexts)} + style={{ backgroundColor: this.openContexts ? "black" : "" }}> + Contexts +
+ +
+
+ {this.openContexts ?
{this.contexts}
: null} +
+
+
this.openLayout = !this.openLayout)} + style={{ backgroundColor: this.openLayout ? "black" : "" }}> + Layout +
+ +
+
+ {this.openLayout ?
{this.layoutPreview}
: null} +
+
; + } + if (this.isPres) { + const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0; + return
+
+ Presentation +
+
+ {this.editableTitle} +
+ {PresBox.Instance?._selectedArray.length} selected +
+ {PresBox.Instance?.listOfSelected} +
+
+
+ {!selectedItem ? (null) :
+
{ this.openPresTransitions = !this.openPresTransitions; })} + style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}> +     Transitions +
+ +
+
+ {this.openPresTransitions ?
+ {PresBox.Instance.transitionDropdown} +
: null} +
} + {!selectedItem ? (null) :
+
{ this.openPresProgressivize = !this.openPresProgressivize; })} + style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}> +     Progressivize +
+ +
+
+ {this.openPresProgressivize ?
+ {PresBox.Instance.progressivizeDropdown} +
: null} +
} + {!selectedItem ? (null) :
+
{ this.openSlideOptions = !this.openSlideOptions; })} + style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}> +     {PresBox.Instance.stringType} options +
+ +
+
+ {this.openSlideOptions ?
+ {PresBox.Instance.optionsDropdown} +
: null} +
} +
+
{ this.openAddSlide = !this.openAddSlide; })} + style={{ backgroundColor: this.openAddSlide ? "black" : "" }}> +     Add new slide +
+ +
+
+ {this.openAddSlide ?
+ {PresBox.Instance.newDocumentDropdown} +
: null} +
+ {/*
+
{ this.openSharing = !this.openSharing; })} + style={{ backgroundColor: this.openSharing ? "black" : "" }}> + Sharing {"&"} Permissions +
+ +
+
+ {this.openSharing ?
+ {this.sharingTable} +
: null} +
*/} +
; + } + } + } +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 06cadcacf..8301d3df8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -31,7 +31,7 @@ import "./CollectionDockingView.scss"; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; -import { DockingViewButtonSelector } from './ParentDocumentSelector'; +import { CollectionDockingViewMenu } from './CollectionDockingViewMenu'; import React = require("react"); const _global = (window /* browser */ || global /* node */) as any; @@ -80,11 +80,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { @undoBatch public CloseFullScreen = () => { - const target = this._goldenLayout._maximisedItem; - if (target) { - target.remove(); - this.stateChanged(); - } + this._goldenLayout._maximisedItem?.toggleMaximise(); + this.stateChanged(); } @undoBatch @@ -453,7 +450,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); } tabCreated = (tab: any) => { - (tab.contentItem.element[0]?.firstChild?.firstChild as any)?.InitTab(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content) + tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content) } stackCreated = (stack: any) => { @@ -563,7 +560,7 @@ export class DockedFrameRenderer extends React.Component { (view) => { if (view) { ReactDOM.render( - [view]} Stack={stack} /> + [view]} Stack={stack} /> , gearSpan); tab._disposers.buttonDisposer?.(); diff --git a/src/client/views/collections/CollectionDockingViewMenu.scss b/src/client/views/collections/CollectionDockingViewMenu.scss new file mode 100644 index 000000000..4157f0f7e --- /dev/null +++ b/src/client/views/collections/CollectionDockingViewMenu.scss @@ -0,0 +1,34 @@ + +.dockingViewButtonSelector { + div { + overflow: visible !important; + } + + display: inline-block; + width:100%; + height:100%; +} +.dockingViewButtonSelector-flyout { + position: relative; + z-index: 9999; + background-color: #eeeeee; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + color: black; + + padding: 10px; + border-radius: 3px; + display: inline-block; + height: 100%; + width: 100%; + border-radius: 3px; + + hr { + height: 1px; + margin: 0px; + background-color: gray; + border-top: 0px; + border-bottom: 0px; + border-right: 0px; + border-left: 0px; + } +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingViewMenu.tsx b/src/client/views/collections/CollectionDockingViewMenu.tsx new file mode 100644 index 000000000..4fe97452c --- /dev/null +++ b/src/client/views/collections/CollectionDockingViewMenu.tsx @@ -0,0 +1,51 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; +import { action, computed, observable } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import { DocumentButtonBar } from "../DocumentButtonBar"; +import { DocumentView } from "../nodes/DocumentView"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; + +@observer +export class CollectionDockingViewMenu extends React.Component<{ views: () => DocumentView[], Stack: any }> { + customStylesheet(styles: any) { + return { + ...styles, + panel: { + ...styles.panel, + minWidth: "100px" + }, + }; + } + _ref = React.createRef(); + + @computed get flyout() { + return ( +
+ +
+ ); + } + + @observable _tooltipOpen: boolean = false; + render() { + return this._tooltipOpen = false)} title={<>
Tap for toolbar, drag to create alias in another pane
} placement="bottom"> + !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))} + onPointerDown={action(e => { + if (getComputedStyle(this._ref.current!).width !== "100%") { + e.stopPropagation(); e.preventDefault(); + } + this.props.views()[0]?.select(false); + this._tooltipOpen = false; + })} > + + + + +
; + } +} diff --git a/src/client/views/collections/ParentDocumentSelector.scss b/src/client/views/collections/ParentDocumentSelector.scss deleted file mode 100644 index bc9cf4848..000000000 --- a/src/client/views/collections/ParentDocumentSelector.scss +++ /dev/null @@ -1,63 +0,0 @@ -.parentDocumentSelector-linkFlyout { - div { - overflow: visible !important; - } - - .metadataEntry-outerDiv { - overflow: hidden !important; - pointer-events: all; - } -} - -.parentDocumentSelector-flyout { - position: relative; - z-index: 9999; - background-color: #eeeeee; - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - color: black; - - padding: 10px; - border-radius: 3px; - display: inline-block; - height: 100%; - width: 100%; - border-radius: 3px; - - hr { - height: 1px; - margin: 0px; - background-color: gray; - border-top: 0px; - border-bottom: 0px; - border-right: 0px; - border-left: 0px; - } -} - -.parentDocumentSelector-button { - pointer-events: all; - position: relative; - display: inline-block; - - svg { - // width:20px !important; - //height:20px; - } -} - -.parentDocumentSelector-metadata { - pointer-events: auto; - padding-right: 5px; - width: 25px; - display: inline-block; -} - -.buttonSelector { - div { - overflow: visible !important; - } - - display: inline-block; - width:100%; - height:100%; -} \ No newline at end of file diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx deleted file mode 100644 index f019ddfe6..000000000 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip } from "@material-ui/core"; -import { computed, IReactionDisposer, observable, reaction, runInAction, action } from "mobx"; -import { observer } from "mobx-react"; -import * as React from "react"; -import { Doc } from "../../../fields/Doc"; -import { Id } from "../../../fields/FieldSymbols"; -import { NumCast } from "../../../fields/Types"; -import { SearchUtil } from "../../util/SearchUtil"; -import { DocumentButtonBar } from "../DocumentButtonBar"; -import { DocumentView } from "../nodes/DocumentView"; -import { CollectionDockingView } from "./CollectionDockingView"; -import { CollectionViewType } from "./CollectionView"; -import './ParentDocumentSelector.scss'; -const higflyout = require("@hig/flyout"); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; - -type SelectorProps = { - Document: Doc, - Stack?: any, - hideTitle?: boolean, - addDocTab(doc: Doc, location: string): void -}; - -@observer -export class SelectorContextMenu extends React.Component { - @observable private _docs: { col: Doc, target: Doc }[] = []; - @observable private _otherDocs: { col: Doc, target: Doc }[] = []; - _reaction: IReactionDisposer | undefined; - - componentDidMount() { - this._reaction = reaction(() => this.props.Document, () => this.fetchDocuments(), { fireImmediately: true }); - } - componentWillUnmount() { - this._reaction?.(); - } - async fetchDocuments() { - const aliases = await SearchUtil.GetAliasesOfDocument(this.props.Document); - const containerProtoSets = await Promise.all(aliases.map(async alias => ((await SearchUtil.Search("", true, { fq: `data_l:"${alias[Id]}"` })).docs))); - const containerProtos = containerProtoSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); - const containerSets = await Promise.all(Array.from(containerProtos.keys()).map(async container => SearchUtil.GetAliasesOfDocument(container))); - const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()); - const doclayoutSets = await Promise.all(Array.from(containers.keys()).map(async (dp) => SearchUtil.GetAliasesOfDocument(dp))); - const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set()).keys()); - runInAction(() => { - this._docs = doclayouts.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).filter(doc => !Doc.IsSystem(doc)).map(doc => ({ col: doc, target: this.props.Document })); - this._otherDocs = []; - }); - } - - getOnClick({ col, target }: { col: Doc, target: Doc }) { - return () => { - col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; - if (col._viewType === CollectionViewType.Freeform) { - const newPanX = NumCast(target.x) + NumCast(target._width) / 2; - const newPanY = NumCast(target.y) + NumCast(target._height) / 2; - col._panX = newPanX; - col._panY = newPanY; - } - this.props.addDocTab(col, "inTab"); // bcz: dataDoc? - }; - } - - render() { - return
- {this.props.hideTitle ? (null) :

Contexts:

} - {this._docs.map(doc =>

{doc.col.title?.toString()}

)} - {this._otherDocs.length ?
: null} - {this._otherDocs.map(doc =>

{doc.col.title?.toString()}

)} -
; - } -} -@observer -export class DockingViewButtonSelector extends React.Component<{ views: () => DocumentView[], Stack: any }> { - customStylesheet(styles: any) { - return { - ...styles, - panel: { - ...styles.panel, - minWidth: "100px" - }, - }; - } - _ref = React.createRef(); - - @computed get flyout() { - return ( -
- -
- ); - } - - @observable _tooltipOpen: boolean = false; - render() { - return this._tooltipOpen = false)} title={<>
Tap for toolbar, drag to create alias in another pane
} placement="bottom"> - !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))} - onPointerDown={action(e => { - if (getComputedStyle(this._ref.current!).width !== "100%") { - e.stopPropagation(); e.preventDefault(); - } - this.props.views()[0]?.select(false); - this._tooltipOpen = false; - })} > - - - - -
; - } -} diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss b/src/client/views/collections/collectionFreeForm/FormatShapePane.scss deleted file mode 100644 index d49ab27fb..000000000 --- a/src/client/views/collections/collectionFreeForm/FormatShapePane.scss +++ /dev/null @@ -1,68 +0,0 @@ -.antimodeMenu-button { - width: 200px; - position: relative; - text-align: left; - - .color-previewI { - width: 100%; - height: 40%; - } - - .color-previewII { - width: 100%; - height: 100%; - } -} - -.antimenu-Buttonup { - position: absolute; - width: 20; - height: 10; - right: 0; - padding: 0; -} - -.formatShapePane-inputBtn { - width: inherit; - position: absolute; -} - -.btn-group-palette { - .sketch-picker { - background: #323232; - width: 160px !important; - height: 80% !important; - - .flexbox-fit { - background: #323232; - } - } -} - -.btn-group { - display: grid; - grid-template-columns: auto auto auto auto; - /* Make the buttons appear below each other */ -} - -.btn-group-palette { - display: block; - /* Make the buttons appear below each other */ -} - -.btn-draw { - display: inline; - /* Make the buttons appear below each other */ -} - -.btn2-group { - display: block; - background: #323232; - grid-template-columns: auto; - - /* Make the buttons appear below each other */ - .antimodeMenu-button { - background: #323232; - display: block; - } -} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx b/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx deleted file mode 100644 index e6ec274f0..000000000 --- a/src/client/views/collections/collectionFreeForm/FormatShapePane.tsx +++ /dev/null @@ -1,545 +0,0 @@ -import React = require("react"); -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, Field, Opt } from "../../../../fields/Doc"; -import { Document } from "../../../../fields/documentSchemas"; -import { InkField } from "../../../../fields/InkField"; -import { BoolCast, Cast, NumCast } from "../../../../fields/Types"; -import { DocumentType } from "../../../documents/DocumentTypes"; -import { SelectionManager } from "../../../util/SelectionManager"; -import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu"; -import "./FormatShapePane.scss"; -import { undoBatch } from "../../../util/UndoManager"; -import { ColorState, SketchPicker } from 'react-color'; - -@observer -export class FormatShapePane extends AntimodeMenu { - static Instance: FormatShapePane; - - private _lastFill = "#D0021B"; - private _lastLine = "#D0021B"; - private _lastDash = "2"; - private _mode = ["fill-drip", "ruler-combined"]; - - @observable private _subOpen = [false, false]; - @observable private _currMode = "fill-drip"; - @observable _lock = false; - @observable private _fillBtn = false; - @observable private _lineBtn = false; - @observable _controlBtn = false; - @observable private _controlPoints: { X: number, Y: number }[] = []; - @observable _currPoint = -1; - - getField(key: string) { - return this.selectedInk?.reduce((p, i) => - (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt); - } - - @computed get selectedInk() { - const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK); - return inks.length ? inks : undefined; - } - @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } - @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } - @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } - @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } - @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } - @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } - @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } - @computed get widthStk() { return this.getField("strokeWidth") || "1"; } - @computed get markHead() { return this.getField("strokeStartMarker") || ""; } - @computed get markTail() { return this.getField("strokeEndMarker") || ""; } - @computed get shapeHgt() { return this.getField("_height"); } - @computed get shapeWid() { return this.getField("_width"); } - @computed get shapeXps() { return this.getField("x"); } - @computed get shapeYps() { return this.getField("y"); } - @computed get shapeRot() { return this.getField("rotation"); } - set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } - set solidFil(value) { this.unFilled = !value; } - set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } - set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } - set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } - set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } - set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } - set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } - set dashdStk(value) { - value && (this._lastDash = value) && (this.unStrokd = false); - this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); - } - set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } - set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } - set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } - set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } - set shapeWid(value) { - this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldWidth = NumCast(i.rootDoc._width); - i.rootDoc._width = Number(value); - this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); - }); - } - set shapeHgt(value) { - this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldHeight = NumCast(i.rootDoc._height); - i.rootDoc._height = Number(value); - this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); - }); - } - - constructor(props: Readonly<{}>) { - super(props); - FormatShapePane.Instance = this; - this._canFade = false; - runInAction(() => this.Pinned = BoolCast(Doc.UserDoc()["menuFormatShape-pinned"])); - } - - @action - closePane = () => { - this.fadeOut(false); - this.Pinned = false; - } - - @action - upDownButtons = (dirs: string, field: string) => { - switch (field) { - case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; - // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; - case "Xps": this.selectedInk?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break; - case "Yps": this.selectedInk?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break; - case "stk": this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; - case "wid": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - //redraw points - const oldWidth = NumCast(i.rootDoc._width); - const oldHeight = NumCast(i.rootDoc._height); - const oldX = NumCast(i.rootDoc.x); - const oldY = NumCast(i.rootDoc.y); - i.rootDoc._width = oldWidth + (dirs === "up" ? 10 : - 10); - this._lock && (i.rootDoc._height = (i.rootDoc._width / oldWidth * NumCast(i.rootDoc._height))); - const doc = Document(i.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; - const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; - newPoints.push({ X: newX, Y: newY }); - }); - Doc.GetProto(doc).data = new InkField(newPoints); - } - } - }); - break; - case "hgt": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldWidth = NumCast(i.rootDoc._width); - const oldHeight = NumCast(i.rootDoc._height); - const oldX = NumCast(i.rootDoc.x); - const oldY = NumCast(i.rootDoc.y); i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10); - this._lock && (i.rootDoc._width = (i.rootDoc._height / oldHeight * NumCast(i.rootDoc._width))); - const doc = Document(i.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; - const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; - newPoints.push({ X: newX, Y: newY }); - }); - Doc.GetProto(doc).data = new InkField(newPoints); - } - } - }); - break; - } - } - - @undoBatch - @action - addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => { - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - var counter = 0; - for (var k = 0; k < index; k++) { - control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++); - } - //decide where to put the new coordinate - const spNum = Math.floor(counter / 2) * 4 + 2; - - for (var i = 0; i < spNum; i++) { - ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - for (var j = 0; j < 4; j++) { - newPoints.push({ X: x, Y: y }); - - } - for (var i = spNum; i < ink.length; i++) { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - this._currPoint = -1; - Doc.GetProto(doc).data = new InkField(newPoints); - } - } - } - })); - } - - @undoBatch - @action - deletePoints = () => { - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1 && this._currPoint !== -1) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink && ink.length > 4) { - const newPoints: { X: number, Y: number }[] = []; - const toRemove = Math.floor(((this._currPoint + 2) / 4)); - for (var i = 0; i < ink.length; i++) { - if (Math.floor((i + 2) / 4) !== toRemove) { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - } - this._currPoint = -1; - Doc.GetProto(doc).data = new InkField(newPoints); - if (newPoints.length === 4) { - const newerPoints: { X: number, Y: number }[] = []; - newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); - newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); - newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); - newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); - Doc.GetProto(doc).data = new InkField(newerPoints); - - } - } - } - } - })); - } - - @undoBatch - @action - rotate = (angle: number) => { - const _centerPoints: { X: number, Y: number }[] = []; - SelectionManager.SelectedDocuments().forEach(action(inkView => { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const xs = ink.map(p => p.X); - const ys = ink.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - const right = Math.max(...xs); - const bottom = Math.max(...ys); - _centerPoints.push({ X: left, Y: top }); - } - } - })); - - var index = 0; - SelectionManager.SelectedDocuments().forEach(action(inkView => { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - doc.rotation = Number(doc.rotation) + Number(angle); - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - - const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X; - const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y; - newPoints.push({ X: newX, Y: newY }); - }); - Doc.GetProto(doc).data = new InkField(newPoints); - const xs = newPoints.map(p => p.X); - const ys = newPoints.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - const right = Math.max(...xs); - const bottom = Math.max(...ys); - - doc._height = (bottom - top); - doc._width = (right - left); - } - index++; - } - })); - } - - @undoBatch - @action - control = (xDiff: number, yDiff: number, controlNum: number) => { - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - - const newPoints: { X: number, Y: number }[] = []; - const order = controlNum % 4; - for (var i = 0; i < ink.length; i++) { - if (controlNum === i || - (order === 0 && i === controlNum + 1) || - (order === 0 && controlNum !== 0 && i === controlNum - 2) || - (order === 0 && controlNum !== 0 && i === controlNum - 1) || - (order === 3 && i === controlNum - 1) || - (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) || - (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) - || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1)) - ) { - newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) }); - } - else { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - } - const oldx = doc.x; - const oldy = doc.y; - const xs = ink.map(p => p.X); - const ys = ink.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - Doc.GetProto(doc).data = new InkField(newPoints); - const xs2 = newPoints.map(p => p.X); - const ys2 = newPoints.map(p => p.Y); - const left2 = Math.min(...xs2); - const top2 = Math.min(...ys2); - const right2 = Math.max(...xs2); - const bottom2 = Math.max(...ys2); - doc._height = (bottom2 - top2); - doc._width = (right2 - left2); - //if points move out of bounds - - doc.x = oldx - (left - left2); - doc.y = oldy - (top - top2); - - } - } - } - })); - } - - @undoBatch - @action - switchStk = (color: ColorState) => { - const val = String(color.hex); - this.colorStk = val; - return true; - } - - @undoBatch - @action - switchFil = (color: ColorState) => { - const val = String(color.hex); - this.colorFil = val; - return true; - } - - - colorPicker(setter: (color: string) => {}, type: string) { - return
- -
; - } - inputBox = (key: string, value: any, setter: (val: string) => {}) => { - return <> - setter(e.target.value)))} - autoFocus /> - -
- - ; - } - - inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { - return <> - {title1} -

{title2}

- - setter(e.target.value)} - autoFocus /> - - - {title2 === "" ? "" : <> - setter2(e.target.value)} - autoFocus /> - -
- } - ; - } - - - colorButton(value: string, setter: () => {}) { - return <> - - ; - } - - controlPointsButton() { - return <> - - - -

- ; - } - - lockRatioButton() { - return <> - -

- ; - } - - rotate90Button() { - return <> - -

- ; - } - @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } - @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } - - @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } - @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } - - @computed get stkInput() { return this.inputBox("stk", this.widthStk, (val: string) => this.widthStk = val); } - @computed get dashInput() { return this.inputBox("dsh", this.widthStk, (val: string) => this.widthStk = val); } - - @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); } - @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); } - @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } - - @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); } - - @computed get controlPoints() { return this.controlPointsButton(); } - @computed get lockRatio() { return this.lockRatioButton(); } - @computed get rotate90() { return this.rotate90Button(); } - @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } - - - @computed get propertyGroupItems() { - const fillCheck =
= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}> - Fill: - {this.fillButton} -
- Stroke: - {this.lineButton} -
- - {this._fillBtn ? this.fillPicker : ""} - {this._lineBtn ? this.linePicker : ""} - {this._fillBtn || this._lineBtn ? "" :
} - {(this.solidStk || this.dashdStk) ? "Width" : ""} - {(this.solidStk || this.dashdStk) ? this.stkInput : ""} - {(this.solidStk || this.dashdStk) ? this.widthStk = e.target.value))} /> : (null)} -
- {(this.solidStk || this.dashdStk) ? <> -

Arrow Head

- this.markHead = this.markHead ? "" : "arrow"))} style={{ position: "absolute", right: 110, width: 20 }} /> -

Arrow End

- this.markTail = this.markTail ? "" : "arrow"))} style={{ position: "absolute", right: 0, width: 20 }} /> -
- : ""} - Dash: - this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} style={{ position: "absolute", right: 110, width: 20 }} /> -
; - - - - const sizeCheck = - -
= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}> - {this.controlPoints} - {this.hgtInput} - {this.XpsInput} - {this.rotInput} - -
; - - - const subMenus = this._currMode === "fill-drip" ? [`Appearance`, 'Transform'] : []; - const menuItems = this._currMode === "fill-drip" ? [fillCheck, sizeCheck] : []; - const indexOffset = 0; - - return
- {subMenus.map((subMenu, i) => -
- - {menuItems[i]} -
)} -
; - } - - @computed get closeBtn() { - return ; - } - - @computed get propertyGroupBtn() { - return
- {this._mode.map(mode => - )} -
; - } - - render() { - return this.getElementVert([this.closeBtn, - this.propertyGroupItems]); - } -} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss deleted file mode 100644 index 254afeb0a..000000000 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ /dev/null @@ -1,779 +0,0 @@ -.propertiesView { - - background-color: rgb(205, 205, 205); - height: 100%; - font-family: "Noto Sans"; - cursor: auto; - - overflow-x: hidden; - overflow-y: scroll; - - .propertiesView-title { - background-color: rgb(159, 159, 159); - text-align: center; - padding-top: 12px; - padding-bottom: 12px; - display: flex; - font-size: 18px; - font-weight: bold; - justify-content: center; - - .propertiesView-title-icon { - width: 20px; - height: 20px; - padding-left: 38px; - margin-top: -5px; - align-items: flex-end; - margin-left: auto; - margin-right: 10px; - - &:hover { - color: grey; - cursor: pointer; - } - - } - - } - - .propertiesView-name { - border-bottom: 1px solid black; - padding: 8.5px; - font-size: 12.5px; - - &:hover { - cursor: text; - } - } - - .propertiesView-settings { - border-bottom: 1px solid black; - //padding: 8.5px; - font-size: 12.5px; - font-weight: bold; - - .propertiesView-settings-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-settings-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-settings-content { - margin-left: 12px; - padding-bottom: 10px; - padding-top: 8px; - } - - } - - .propertiesView-sharing { - border-bottom: 1px solid black; - //padding: 8.5px; - - .propertiesView-sharing-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-sharing-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-sharing-content { - font-size: 10px; - padding: 10px; - margin-left: 5px; - - .propertiesView-acls-checkbox { - float: right; - height: 20px; - margin-top: -20px; - margin-right: -15; - - .propertiesView-acls-checkbox-text { - font-size: 7px; - margin-top: -10px; - margin-left: 6px; - } - } - - .change-buttons { - display: flex; - - button { - width: 5; - height: 5; - } - - input { - width: 100%; - } - } - } - } - - .propertiesView-appearance { - border-bottom: 1px solid black; - //padding: 8.5px; - - .propertiesView-appearance-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-appearance-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-appearance-content { - font-size: 10px; - padding: 10px; - margin-left: 5px; - } - } - - .propertiesView-transform { - border-bottom: 1px solid black; - //padding: 8.5px; - - .propertiesView-transform-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-transform-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-transform-content { - font-size: 10px; - padding: 10px; - margin-left: 5px; - } - } - - .notify-button { - padding: 2px; - width: 12px; - height: 12px; - background-color: black; - border-radius: 10px; - padding-left: 2px; - padding-right: 2px; - margin-top: 2px; - margin-left: 3px; - - .notify-button-icon { - width: 6px; - height: 6.5px; - margin-left: .5px; - } - - &:hover { - background-color: rgb(158, 158, 158); - cursor: pointer; - } - } - - .expansion-button-icon { - width: 11px; - height: 11px; - color: black; - margin-left: 27px; - - &:hover { - color: rgb(131, 131, 131); - cursor: pointer; - } - } - - .propertiesView-sharingTable { - - // whatever's commented out - add it back in when adding the buttons - - // border: 1.5px solid black; - border: 1px solid black; - padding: 5px; // remove when adding buttons - border-radius: 6px; // remove when adding buttons - margin-right: 10px; // remove when adding buttons - // width: 100%; - // display: inline-table; - background-color: #ececec; - max-height: 130px; - overflow-y: scroll; - width: 92%; - - .propertiesView-sharingTable-item { - - display: flex; - // padding: 5px; - padding: 3px; - align-items: center; - border-bottom: 0.5px solid grey; - - &:hover .propertiesView-sharingTable-item-name { - overflow-x: unset; - white-space: unset; - overflow-wrap: break-word; - } - - .propertiesView-sharingTable-item-name { - font-weight: bold; - width: 95px; - overflow-x: hidden; - display: inline-block; - text-overflow: ellipsis; - white-space: nowrap; - } - - .propertiesView-sharingTable-item-permission { - display: flex; - align-items: flex-end; - margin-left: auto; - - .permissions-select { - border: none; - background-color: inherit; - width: 75px; - //text-align: justify; // for Edge - //text-align-last: end; - - &:hover { - cursor: pointer; - } - } - } - - &:last-child { - border-bottom: none; - } - } - } - - .propertiesView-fields { - border-bottom: 1px solid black; - //padding: 8.5px; - - .propertiesView-fields-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-fields-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - - } - - .propertiesView-fields-checkbox { - float: right; - height: 20px; - margin-top: -9px; - - .propertiesView-fields-checkbox-text { - font-size: 7px; - margin-top: -10px; - margin-left: 6px; - } - } - - .propertiesView-fields-content { - font-size: 10px; - margin-left: 2px; - padding: 10px; - - &:hover { - cursor: pointer; - } - } - } - - .field { - display: flex; - font-size: 7px; - background-color: #e8e8e8; - padding-right: 3px; - border: 0.5px solid grey; - border-radius: 5px; - padding-left: 3px; - } - - .uneditable-field { - display: flex; - overflow-y: visible; - margin-bottom: 2px; - - &:hover { - cursor: auto; - } - } - .propertiesView-contexts { - - .propertiesView-contexts-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-contexts-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-contexts-content { - overflow: hidden; - padding: 10px; - } - - } - - .propertiesView-layout { - - .propertiesView-layout-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-layout-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-layout-content { - overflow: hidden; - padding: 10px; - } - - } - - .propertiesView-presTrails { - border-bottom: 1px solid black; - //padding: 8.5px; - - .propertiesView-presTrails-title { - font-weight: bold; - font-size: 12.5px; - padding: 4px; - display: flex; - color: white; - padding-left: 8px; - background-color: rgb(51, 51, 51); - - &:hover { - cursor: pointer; - } - - .propertiesView-presTrails-title-icon { - float: right; - justify-items: right; - align-items: flex-end; - margin-left: auto; - margin-right: 9px; - - &:hover { - cursor: pointer; - } - } - } - - .propertiesView-presTrails-content { - font-size: 10px; - padding: 10px; - margin-left: 5px; - } - } -} - -.inking-button { - - display: flex; - - .inking-button-points { - background-color: #333333; - padding: 7px; - border-radius: 7px; - margin-right: 32px; - width: 32; - height: 32; - padding-top: 9px; - margin-left: 18px; - - &:hover { - background: rgb(131, 131, 131); - transform: scale(1.05); - cursor: pointer; - } - } - - .inking-button-lock { - background-color: #333333; - padding: 7px; - border-radius: 7px; - margin-right: 32px; - width: 32; - height: 32; - padding-top: 9px; - padding-left: 10px; - - &:hover { - background: rgb(131, 131, 131); - transform: scale(1.05); - cursor: pointer; - } - } - - .inking-button-rotate { - background-color: #333333; - padding: 7px; - border-radius: 7px; - width: 32; - height: 32; - padding-top: 9px; - padding-left: 10px; - - &:hover { - background: rgb(131, 131, 131); - transform: scale(1.05); - cursor: pointer; - } - } -} - -.inputBox-duo { - display: flex; -} - -.inputBox { - - margin-top: 10px; - display: flex; - height: 19px; - margin-right: 15px; - - .inputBox-title { - font-size: 12px; - padding-right: 5px; - } - - .inputBox-input { - font-size: 10px; - width: 50px; - margin-right: 1px; - border-radius: 3px; - - &:hover { - cursor: pointer; - } - } - - .inputBox-button { - - .inputBox-button-up { - background-color: #333333; - height: 9px; - padding-left: 3px; - padding-right: 3px; - padding-top: 1px; - padding-bottom: 1px; - border-radius: 1.5px; - - &:hover { - background: rgb(131, 131, 131); - transform: scale(1.05); - cursor: pointer; - } - } - - .inputBox-button-down { - background-color: #333333; - height: 9px; - padding-left: 3px; - padding-right: 3px; - padding-top: 1px; - padding-bottom: 1px; - border-radius: 1.5px; - - &:hover { - background: rgb(131, 131, 131); - transform: scale(1.05); - cursor: pointer; - } - } - - } -} - -.color-palette { - width: 160px; - height: 360; -} - -.strokeAndFill { - display: flex; - margin-top: 10px; - - .fill { - margin-right: 40px; - display: flex; - padding-bottom: 7px; - margin-left: 35px; - - .fill-title { - font-size: 12px; - margin-right: 2px; - } - - .fill-button { - padding-top: 2px; - margin-top: -1px; - } - } - - .stroke { - display: flex; - - .stroke-title { - font-size: 12px; - } - - .stroke-button { - padding-top: 2px; - margin-left: 2px; - margin-top: -1px; - } - } -} - -.propertiesView-presSelected { - border-top: solid 1px darkgrey; - width: 100%; - padding-top: 5px; - font-family: Roboto; - font-weight: 500; - display: inline-flex; - - .propertiesView-selectedList { - border-left: solid 1px darkgrey; - margin-left: 10px; - padding-left: 5px; - - .selectedList-items { - font-size: 12; - font-weight: 300; - margin-top: 1; - } - } -} - -.widthAndDash { - - .width { - .width-top { - display: flex; - - .width-title { - font-size: 12; - margin-right: 20px; - margin-left: 35px; - text-align: center; - } - - .width-input { - margin-right: 30px; - margin-top: -10px; - } - } - - .width-range { - margin-right: 1px; - margin-bottom: 6; - } - } - - .arrows { - - display: flex; - margin-bottom: 3px; - margin-left: 4px; - - .arrows-head { - - display: flex; - margin-right: 35px; - - .arrows-head-title { - font-size: 10; - } - - .arrows-head-input { - margin-left: 6px; - margin-top: 2px; - } - } - - .arrows-tail { - display: flex; - - .arrows-tail-title { - font-size: 10; - } - - .arrows-tail-input { - margin-left: 6px; - margin-top: 2px; - } - } - } - - .dashed { - - display: flex; - margin-left: 64px; - margin-bottom: 6px; - - .dashed-title { - font-size: 10; - } - - .dashed-input { - margin-left: 6px; - margin-top: 2px; - } - } -} - -.editable-title { - border: none; - padding: 6px; - padding-bottom: 2px; - background: #eeeeee; - border-top: 1px solid; - border-left: 1px solid; - - &:hover { - border: 0.75px solid rgb(122, 28, 28); - } -} - - -.properties-flyout { - grid-column: 2/4; -} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx deleted file mode 100644 index 560f09931..000000000 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ /dev/null @@ -1,1090 +0,0 @@ -import React = require("react"); -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Checkbox, Tooltip } from "@material-ui/core"; -import { action, computed, observable } from "mobx"; -import { observer } from "mobx-react"; -import { ColorState, SketchPicker } from "react-color"; -import { AclAddonly, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, DataSym, Doc, Field, HeightSym, WidthSym } from "../../../../fields/Doc"; -import { Id } from "../../../../fields/FieldSymbols"; -import { InkField } from "../../../../fields/InkField"; -import { ComputedField } from "../../../../fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../../fields/Types"; -import { GetEffectiveAcl, SharingPermissions } from "../../../../fields/util"; -import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../../Utils"; -import { DocumentType } from "../../../documents/DocumentTypes"; -import { DocumentManager } from "../../../util/DocumentManager"; -import { SelectionManager } from "../../../util/SelectionManager"; -import { SharingManager } from "../../../util/SharingManager"; -import { Transform } from "../../../util/Transform"; -import { undoBatch, UndoManager } from "../../../util/UndoManager"; -import { EditableView } from "../../EditableView"; -import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView"; -import { KeyValueBox } from "../../nodes/KeyValueBox"; -import { PresBox } from "../../nodes/PresBox"; -import { PropertiesButtons } from "../../PropertiesButtons"; -import { CollectionDockingView } from "../CollectionDockingView"; -import { SelectorContextMenu } from "../ParentDocumentSelector"; -import { FormatShapePane } from "./FormatShapePane"; -import "./FormatShapePane.scss"; -import "./PropertiesView.scss"; -const higflyout = require("@hig/flyout"); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; -const _global = (window /* browser */ || global /* node */) as any; - -interface PropertiesViewProps { - width: number; - height: number; - renderDepth: number; - ScreenToLocalTransform: () => Transform; - onDown: (event: any) => void; -} - -@observer -export class PropertiesView extends React.Component { - private _widthUndo?: UndoManager.Batch; - - @computed get MAX_EMBED_HEIGHT() { return 200; } - - @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; } - @computed get selectedDocumentView() { - if (SelectionManager.SelectedDocuments().length) { - return SelectionManager.SelectedDocuments()[0]; - } else if (PresBox.Instance && PresBox.Instance._selectedArray.length) { - return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); - } else { return undefined; } - } - @computed get isPres(): boolean { - if (this.selectedDoc?.type === DocumentType.PRES) return true; - return false; - } - @computed get dataDoc() { return this.selectedDoc?.[DataSym]; } - - @observable layoutFields: boolean = false; - - @observable openOptions: boolean = true; - @observable openSharing: boolean = true; - @observable openFields: boolean = true; - @observable openLayout: boolean = true; - @observable openContexts: boolean = true; - @observable openAppearance: boolean = true; - @observable openTransform: boolean = true; - // @observable selectedUser: string = ""; - // @observable addButtonPressed: boolean = false; - @observable layoutDocAcls: boolean = false; - - //Pres Trails booleans: - @observable openPresTransitions: boolean = false; - @observable openPresProgressivize: boolean = false; - @observable openAddSlide: boolean = false; - @observable openSlideOptions: boolean = false; - - @observable inOptions: boolean = false; - @observable _controlBtn: boolean = false; - @observable _lock: boolean = false; - - @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; } - - @action - rtfWidth = () => { - if (this.selectedDoc) { - return Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20); - } else { - return 0; - } - } - @action - rtfHeight = () => { - if (this.selectedDoc) { - return this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; - } else { - return 0; - } - } - - @action - docWidth = () => { - if (this.selectedDoc) { - const layoutDoc = this.selectedDoc; - const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); - if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.width - 20)); - return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.width - 20) : this.props.width - 20; - } else { - return 0; - } - } - - @action - docHeight = () => { - if (this.selectedDoc && this.dataDoc) { - const layoutDoc = this.selectedDoc; - return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => { - const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); - if (aspect) return this.docWidth() * aspect; - return layoutDoc._fitWidth ? (!this.dataDoc._nativeHeight ? NumCast(this.props.height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, - NumCast(this.props.height)))) : - NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; - })())); - } else { - return 0; - } - } - - @computed get expandedField() { - if (this.dataDoc && this.selectedDoc) { - const ids: { [key: string]: string } = {}; - const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : - SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? Doc.Layout(dv.layoutDoc) : dv.dataDoc); - docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key))); - const rows: JSX.Element[] = []; - for (const key of Object.keys(ids).slice().sort()) { - const docvals = new Set(); - docs.forEach(doc => docvals.add(doc[key])); - const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key]; - if (key[0] === "#") { - rows.push(
- {key} -   -
); - } else { - const contentElement = contents !== undefined ? Field.toString(contents as Field) : "null"} - SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }} - />; - rows.push(
- {key + ":"} -   - {contentElement} -
); - } - } - rows.push(
- ""} - SetValue={this.setKeyValue} /> -
); - return rows; - } - } - - @computed get noviceFields() { - if (this.dataDoc) { - const ids: { [key: string]: string } = {}; - const docs = SelectionManager.SelectedDocuments().length < 2 ? [this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => dv.dataDoc); - docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key))); - const rows: JSX.Element[] = []; - const noviceReqFields = ["author", "creationDate"]; - const noviceLayoutFields = ["_curPage"]; - const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL") && key !== "UseCors")), - ...noviceReqFields, ...noviceLayoutFields]; - for (const key of noviceKeys.sort()) { - const docvals = new Set(); - docs.forEach(doc => docvals.add(doc[key])); - const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key]; - if (key[0] === "#") { - rows.push(
- {key} -   -
); - } else if (contents !== undefined) { - const value = Field.toString(contents as Field); - if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) { - rows.push(
- {key + ": "} -
{value}
-
); - } else { - const contentElement = contents !== undefined ? Field.toString(contents as Field) : "null"} - SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }} - />; - - rows.push(
- {key + ":"} -   - {contentElement} -
); - } - } - } - rows.push(
- ""} - SetValue={this.setKeyValue} /> -
); - return rows; - } - } - - @undoBatch - setKeyValue = (value: string) => { - const docs = SelectionManager.SelectedDocuments().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.SelectedDocuments().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc); - docs.forEach(doc => { - if (value.indexOf(":") !== -1) { - const newVal = value[0].toUpperCase() + value.substring(1, value.length); - KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); - return true; - } else if (value[0] === "#") { - const newVal = value + `:'${value}'`; - KeyValueBox.SetField(doc, newVal.substring(0, newVal.indexOf(":")), newVal.substring(newVal.indexOf(":") + 1, newVal.length), true); - return true; - } - }); - return false; - } - - @observable transform: Transform = Transform.Identity(); - getTransform = () => this.transform; - propertiesDocViewRef = (ref: HTMLDivElement) => { - const observer = new _global.ResizeObserver(action((entries: any) => { - const cliRect = ref.getBoundingClientRect(); - this.transform = new Transform(-cliRect.x, -cliRect.y, 1); - })); - ref && observer.observe(ref); - } - - @computed get contexts() { - return !this.selectedDoc ? (null) : CollectionDockingView.AddRightSplit(doc)} />; - } - - previewBackground = () => "lightgrey"; - @computed get layoutPreview() { - if (SelectionManager.SelectedDocuments().length > 1) { - return "-- multiple selected --"; - } - if (this.selectedDoc) { - const layoutDoc = Doc.Layout(this.selectedDoc); - const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight; - const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth; - return
- false} - whenActiveChanged={emptyFunction} - addDocTab={returnFalse} - pinToPres={emptyFunction} - bringToFront={returnFalse} - ContentScaling={returnOne} - dontRegisterView={true} - dropAction={undefined} - /> -
; - } else { - return null; - } - } - - /** - * Handles the changing of a user's permissions from the permissions panel. - */ - @undoBatch - changePermissions = (e: any, user: string) => { - SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, this.selectedDoc!); - } - - /** - * @returns the options for the permissions dropdown. - */ - getPermissionsSelect(user: string, permission: string) { - return ; - } - - /** - * @returns the notification icon. On clicking, it should notify someone of a document been shared with them. - */ - @computed get notifyIcon() { - return Notify with message
}> -
- -
- ; - } - - /** - * ... next to the owner that opens the main SharingManager interface on click. - */ - @computed get expansionIcon() { - return {"Show more permissions"}}> -
{ - if (this.selectedDocumentView || this.selectedDoc) { - SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDocumentView ? this.selectedDocumentView : undefined, this.selectedDoc); - } - }}> - -
-
; - } - - /** - * @returns a row of the permissions panel - */ - sharingItem(name: string, effectiveAcl: symbol, permission: string) { - return
this.selectedUser = this.selectedUser === name ? "" : name)} - > -
{name}
- {/* {name !== "Me" ? this.notifyIcon : null} */} -
- {effectiveAcl === AclAdmin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission} - {permission === "Owner" ? this.expansionIcon : null} -
-
; - } - - /** - * @returns the sharing and permissiosn panel. - */ - @computed get sharingTable() { - const AclMap = new Map([ - [AclPrivate, SharingPermissions.None], - [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], - [AclEdit, SharingPermissions.Edit], - [AclAdmin, SharingPermissions.Admin] - ]); - - const target = this.layoutDocAcls ? this.selectedDoc! : this.selectedDoc![DataSym]; - - const effectiveAcl = GetEffectiveAcl(target); - const tableEntries = []; - - // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { - if (target[AclSym]) { - for (const [key, value] of Object.entries(target[AclSym])) { - const name = key.substring(4).replace("_", "."); - if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public"/* && sidebarUsersDisplayed![name] !== false*/) { - tableEntries.push(this.sharingItem(name, effectiveAcl, AclMap.get(value as symbol)!)); - } - } - } - - // if (Doc.UserDoc().sidebarUsersDisplayed) { - // for (const [name, value] of Object.entries(sidebarUsersDisplayed!)) { - // if (value === true && !this.selectedDoc![`ACL-${name.substring(8).replace(".", "_")}`]) tableEntries.push(this.sharingItem(name.substring(8), effectiveAcl, SharingPermissions.None)); - // } - // } - // }) - - // shifts the current user, owner, public to the top of the doc. - tableEntries.unshift(this.sharingItem("Public", effectiveAcl, (AclMap.get(target[AclSym]?.["ACL-Public"]) || SharingPermissions.None))); - tableEntries.unshift(this.sharingItem("Me", effectiveAcl, Doc.CurrentUserEmail === target.author ? "Owner" : AclMap.get(effectiveAcl)!)); - if (Doc.CurrentUserEmail !== target.author) tableEntries.unshift(this.sharingItem(StrCast(target.author), effectiveAcl, "Owner")); - - return
- {tableEntries} -
; - } - - @computed get fieldsCheckbox() { - return ; - } - - @action - toggleCheckbox = () => { - this.layoutFields = !this.layoutFields; - } - - @computed get editableTitle() { - const titles = new Set(); - SelectionManager.SelectedDocuments().forEach(dv => titles.add(StrCast(dv.rootDoc.title))); - const title = Array.from(titles.keys()).length > 1 ? "--multiple selected--" : StrCast(this.selectedDoc?.title); - return
title} - SetValue={this.setTitle} />
; - } - - @undoBatch - @action - setTitle = (value: string) => { - if (SelectionManager.SelectedDocuments().length > 1) { - SelectionManager.SelectedDocuments().map(dv => Doc.SetInPlace(dv.rootDoc, "title", value, true)); - return true; - } else if (this.dataDoc) { - if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, "title", value, true); - else KeyValueBox.SetField(this.dataDoc, "title", value, true); - return true; - } - return false; - } - - - @undoBatch - @action - rotate = (angle: number) => { - const _centerPoints: { X: number, Y: number }[] = []; - if (this.selectedDoc) { - const doc = this.selectedDoc; - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const xs = ink.map(p => p.X); - const ys = ink.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - const right = Math.max(...xs); - const bottom = Math.max(...ys); - _centerPoints.push({ X: left, Y: top }); - } - } - - var index = 0; - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - doc.rotation = Number(doc.rotation) + Number(angle); - const inks = Cast(doc.data, InkField)?.inkData; - if (inks) { - const newPoints: { X: number, Y: number }[] = []; - inks.forEach(ink => { - const newX = Math.cos(angle) * (ink.X - _centerPoints[index].X) - Math.sin(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].X; - const newY = Math.sin(angle) * (ink.X - _centerPoints[index].X) + Math.cos(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].Y; - newPoints.push({ X: newX, Y: newY }); - }); - doc.data = new InkField(newPoints); - const xs = newPoints.map(p => p.X); - const ys = newPoints.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - const right = Math.max(...xs); - const bottom = Math.max(...ys); - - doc._height = (bottom - top); - doc._width = (right - left); - } - index++; - } - } - } - - - - @computed - get controlPointsButton() { - return
- {"Edit points"}
}> -
FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn)} style={{ backgroundColor: FormatShapePane.Instance._controlBtn ? "black" : "" }}> - -
- - {FormatShapePane.Instance._lock ? "Unlock ratio" : "Lock ratio"}}> -
FormatShapePane.Instance._lock = !FormatShapePane.Instance._lock)} > - -
-
- {"Rotate 90˚"}}> -
this.rotate(Math.PI / 2))}> - -
-
- ; - } - - inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => { - return
-
{title}
- { - setter(e.target.value); - }} - onKeyPress={e => { - e.stopPropagation(); - }} /> -
-
this.upDownButtons("up", key)))} > - -
-
this.upDownButtons("down", key)))} > - -
-
-
; - } - - inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { - return
- {this.inputBox(key, value, setter, title1)} - {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)} -
; - } - - @action - upDownButtons = (dirs: string, field: string) => { - switch (field) { - case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; - // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; - case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break; - case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break; - case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; - case "wid": - const oldWidth = NumCast(this.selectedDoc?._width); - const oldHeight = NumCast(this.selectedDoc?._height); - const oldX = NumCast(this.selectedDoc?.x); - const oldY = NumCast(this.selectedDoc?.y); - this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height))); - const doc = this.selectedDoc; - if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - for (var j = 0; j < ink.length; j++) { - // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth; - const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight; - newPoints.push({ X: newX, Y: newY }); - } - doc.data = new InkField(newPoints); - } - } - break; - case "hgt": - const oWidth = NumCast(this.selectedDoc?._width); - const oHeight = NumCast(this.selectedDoc?._height); - const oX = NumCast(this.selectedDoc?.x); - const oY = NumCast(this.selectedDoc?.y); - this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width))); - const docu = this.selectedDoc; - if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) { - const ink = Cast(docu.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - for (var j = 0; j < ink.length; j++) { - // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth; - const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight; - newPoints.push({ X: newX, Y: newY }); - } - docu.data = new InkField(newPoints); - } - } - break; - } - } - - getField(key: string) { - //if (this.selectedDoc) { - return Field.toString(this.selectedDoc?.[key] as Field); - // } else { - // return undefined as Opt; - // } - } - - @computed get shapeXps() { return this.getField("x"); } - @computed get shapeYps() { return this.getField("y"); } - @computed get shapeRot() { return this.getField("rotation"); } - @computed get shapeHgt() { return this.getField("_height"); } - @computed get shapeWid() { return this.getField("_width"); } - set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); } - set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); } - set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); } - set shapeWid(value) { - const oldWidth = NumCast(this.selectedDoc?._width); - this.selectedDoc && (this.selectedDoc._width = Number(value)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); - } - set shapeHgt(value) { - const oldHeight = NumCast(this.selectedDoc?._height); - this.selectedDoc && (this.selectedDoc._height = Number(value)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); - } - - @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; }, "H:", "wid", this.shapeWid, (val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; }, "W:"); } - @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeXps = val; } return true; }, "X:", "Yps", this.shapeYps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeYps = val; } return true; }, "Y:"); } - @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, "∠:", "rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, ""); } - - - @observable private _fillBtn = false; - @observable private _lineBtn = false; - - private _lastFill = "#D0021B"; - private _lastLine = "#D0021B"; - private _lastDash: any = "2"; - - @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } - @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } - set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); } - set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); } - - colorButton(value: string, type: string, setter: () => {}) { - // return
this.changeScrolling(false)} - // onPointerLeave={e => this.changeScrolling(true)}> - // - return
setter()))}> -
- {value === "" || value === "transparent" ?

: ""} -
; - // - //
; - - } - - @undoBatch - @action - switchStk = (color: ColorState) => { - const val = String(color.hex); - this.colorStk = val; - return true; - } - @undoBatch - @action - switchFil = (color: ColorState) => { - const val = String(color.hex); - this.colorFil = val; - return true; - } - - colorPicker(setter: (color: string) => {}, type: string) { - return ; - } - - @computed get fillButton() { return this.colorButton(this.colorFil, "fill", () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } - @computed get lineButton() { return this.colorButton(this.colorStk, "line", () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } - - @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } - @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } - - @computed get strokeAndFill() { - return
-
-
-
Fill:
-
{this.fillButton}
-
-
-
Stroke:
-
{this.lineButton}
-
-
- {this._fillBtn ? this.fillPicker : ""} - {this._lineBtn ? this.linePicker : ""} -
; - } - - @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; } - @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; } - @computed get unStrokd() { return this.selectedDoc?.color ? true : false; } - @computed get widthStk() { return this.getField("strokeWidth") || "1"; } - @computed get markHead() { return this.getField("strokeStartMarker") || ""; } - @computed get markTail() { return this.getField("strokeEndMarker") || ""; } - set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } - set dashdStk(value) { - value && (this._lastDash = value) && (this.unStrokd = false); - this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined); - } - set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); } - set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } - set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); } - set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); } - - - @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); } - - - regInput = (key: string, value: any, setter: (val: string) => {}) => { - return
- setter(e.target.value)} /> -
-
this.upDownButtons("up", key)))} > - -
-
this.upDownButtons("down", key)))} > - -
-
-
; - } - - @computed get widthAndDash() { - return
-
-
-
Width:
-
{this.stkInput}
-
- this.widthStk = e.target.value))} - onMouseDown={(e) => { this._widthUndo = UndoManager.StartBatch("width undo"); }} - onMouseUp={(e) => { this._widthUndo?.end(); this._widthUndo = undefined; }} - /> -
- -
-
-
Arrow Head:
- this.markHead = this.markHead ? "" : "arrow"))} /> -
-
-
Arrow End:
- this.markTail = this.markTail ? "" : "arrow"))} /> -
-
-
-
Dashed Line:
- -
-
; - } - - @undoBatch @action - changeDash = () => { - this.dashdStk = this.dashdStk === "2" ? "0" : "2"; - } - - @computed get appearanceEditor() { - return
- {this.widthAndDash} - {this.strokeAndFill} -
; - } - - @computed get transformEditor() { - return
- {this.controlPointsButton} - {this.hgtInput} - {this.XpsInput} - {this.rotInput} -
; - } - - /** - * Handles adding and removing members from the sharing panel - */ - // handleUserChange = (selectedUser: string, add: boolean) => { - // if (!Doc.UserDoc().sidebarUsersDisplayed) Doc.UserDoc().sidebarUsersDisplayed = new Doc; - // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { - // sidebarUsersDisplayed![`display-${selectedUser}`] = add; - // !add && runInAction(() => this.selectedUser = ""); - // }); - // } - - render() { - if (!this.selectedDoc && !this.isPres) { - return
-
- No Document Selected -
-
; - - } else { - const novice = Doc.UserDoc().noviceMode; - - if (this.selectedDoc && !this.isPres) { - return
-
- Properties - {/*
- -
*/} -
-
- {this.editableTitle} -
-
this.inOptions = true)} - onPointerLeave={action(() => this.inOptions = false)}> -
this.openOptions = !this.openOptions)} - style={{ backgroundColor: this.openOptions ? "black" : "" }}> - Options -
- -
-
- {!this.openOptions ? (null) : -
- -
} -
-
-
this.openSharing = !this.openSharing)} - style={{ backgroundColor: this.openSharing ? "black" : "" }}> - Sharing {"&"} Permissions -
- -
-
- {!this.openSharing ? (null) : -
-
- this.layoutDocAcls = !this.layoutDocAcls)} - checked={this.layoutDocAcls} - />; -
Layout
-
- {this.sharingTable} - {/*
- - - - {this.addButtonPressed ? - // : - : - null} -
*/} -
} -
- - {!this.isInk ? (null) : -
-
this.openAppearance = !this.openAppearance)} - style={{ backgroundColor: this.openAppearance ? "black" : "" }}> - Appearance -
- -
-
- {!this.openAppearance ? (null) : -
- {this.appearanceEditor} -
} -
} - - {this.isInk ?
-
this.openTransform = !this.openTransform)} - style={{ backgroundColor: this.openTransform ? "black" : "" }}> - Transform -
- -
-
- {this.openTransform ?
- {this.transformEditor} -
: null} -
: null} - -
-
this.openFields = !this.openFields)} - style={{ backgroundColor: this.openFields ? "black" : "" }}> - Fields {"&"} Tags -
- -
-
- {!novice && this.openFields ?
- {this.fieldsCheckbox} -
Layout
-
: null} - {!this.openFields ? (null) : -
- {novice ? this.noviceFields : this.expandedField} -
} -
-
-
this.openContexts = !this.openContexts)} - style={{ backgroundColor: this.openContexts ? "black" : "" }}> - Contexts -
- -
-
- {this.openContexts ?
{this.contexts}
: null} -
-
-
this.openLayout = !this.openLayout)} - style={{ backgroundColor: this.openLayout ? "black" : "" }}> - Layout -
- -
-
- {this.openLayout ?
{this.layoutPreview}
: null} -
-
; - } - if (this.isPres) { - const selectedItem: boolean = PresBox.Instance?._selectedArray.length > 0; - return
-
- Presentation -
-
- {this.editableTitle} -
- {PresBox.Instance?._selectedArray.length} selected -
- {PresBox.Instance?.listOfSelected} -
-
-
- {!selectedItem ? (null) :
-
{ this.openPresTransitions = !this.openPresTransitions; })} - style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}> -     Transitions -
- -
-
- {this.openPresTransitions ?
- {PresBox.Instance.transitionDropdown} -
: null} -
} - {!selectedItem ? (null) :
-
{ this.openPresProgressivize = !this.openPresProgressivize; })} - style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}> -     Progressivize -
- -
-
- {this.openPresProgressivize ?
- {PresBox.Instance.progressivizeDropdown} -
: null} -
} - {!selectedItem ? (null) :
-
{ this.openSlideOptions = !this.openSlideOptions; })} - style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}> -     {PresBox.Instance.stringType} options -
- -
-
- {this.openSlideOptions ?
- {PresBox.Instance.optionsDropdown} -
: null} -
} -
-
{ this.openAddSlide = !this.openAddSlide; })} - style={{ backgroundColor: this.openAddSlide ? "black" : "" }}> -     Add new slide -
- -
-
- {this.openAddSlide ?
- {PresBox.Instance.newDocumentDropdown} -
: null} -
- {/*
-
{ this.openSharing = !this.openSharing; })} - style={{ backgroundColor: this.openSharing ? "black" : "" }}> - Sharing {"&"} Permissions -
- -
-
- {this.openSharing ?
- {this.sharingTable} -
: null} -
*/} -
; - } - } - } -} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6e1357b41..2d03c5154 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -30,6 +30,7 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; +import { FormatShapePane } from '../FormatShapePane'; import { DocumentContentsView } from "./DocumentContentsView"; import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; @@ -38,7 +39,6 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { RadialMenu } from './RadialMenu'; import { TaskCompletionBox } from './TaskCompletedBox'; import React = require("react"); -import { FormatShapePane } from '../collections/collectionFreeForm/FormatShapePane'; export type DocFocusFunc = () => boolean; diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss index 42ef2958e..62ee9513c 100644 --- a/src/client/views/nodes/LinkAnchorBox.scss +++ b/src/client/views/nodes/LinkAnchorBox.scss @@ -22,6 +22,11 @@ padding-left: 2px; padding-top: 1px; } + .linkAnchorBox-button { + pointer-events: all; + position: relative; + display: inline-block; + } } .linkAnchorBox-cont-small { diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 1ef25c80d..8e5ee09ff 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -148,7 +148,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent {!this._editing && !this._forceOpen ? (null) : this._isOpen = true} onClose={action(() => this._isOpen = this._forceOpen = this._editing = false)}> - + } -- cgit v1.2.3-70-g09d2 From 4f3c8cfab12f1c5fff27faa0998a286d210148d2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 29 Aug 2020 16:39:17 -0400 Subject: major restructure of how addDocTabs works. now you can add/replace with options for left/right/top/bottom and specific panel names --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DictationManager.ts | 4 +- src/client/util/DocumentManager.ts | 4 +- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/GlobalKeyHandler.ts | 4 +- src/client/views/MainView.tsx | 8 +- src/client/views/PropertiesButtons.tsx | 4 +- src/client/views/PropertiesDocContextSelector.tsx | 2 +- src/client/views/PropertiesView.tsx | 2 +- src/client/views/animationtimeline/Keyframe.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 298 ++++++++------------- .../views/collections/CollectionSchemaCells.tsx | 2 +- .../CollectionSchemaMovableTableHOC.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 8 +- src/client/views/collections/SchemaTable.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- src/client/views/linking/LinkEditor.tsx | 20 +- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 16 +- src/client/views/nodes/FontIconBox.tsx | 2 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/nodes/LinkAnchorBox.tsx | 6 +- src/client/views/nodes/LinkDocPreview.tsx | 6 +- src/client/views/nodes/PresBox.tsx | 6 +- src/client/views/nodes/VideoBox.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +- .../formattedText/FormattedTextBoxComment.tsx | 16 +- .../formattedText/ProsemirrorExampleTransfer.ts | 2 +- .../views/nodes/formattedText/RichTextMenu.tsx | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/nodes/formattedText/nodes_rts.ts | 4 +- src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 2 +- src/fields/RichTextUtils.ts | 2 +- 39 files changed, 202 insertions(+), 268 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2ca1b95d7..262086735 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -847,7 +847,7 @@ export namespace Docs { { type: type, content: [ - ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, false, config.initialWidth)) + ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth)) ] } ] diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7937072da..20f8fe9c1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1063,7 +1063,7 @@ export class CurrentUserUtils { Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { const copy = Doc.copyDragFactory(dragFactory); if (copy) { - CollectionDockingView.AddRightSplit(copy); + CollectionDockingView.AddSplit(copy, "right"); const view = DocumentManager.Instance.getFirstDocumentView(copy); view && SelectionManager.SelectDoc(view, false); } diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index e66c15fcb..231e1fa8d 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -323,7 +323,7 @@ export namespace DictationManager { ["open fields", { action: (target: DocumentView) => { const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); - target.props.addDocTab(kvp, "onRight"); + target.props.addDocTab(kvp, "add:right"); } }], @@ -337,7 +337,7 @@ export namespace DictationManager { const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; proto.data = new RichTextField(proseMirrorState); proto.backgroundColor = "#eeffff"; - target.props.addDocTab(newBox, "onRight"); + target.props.addDocTab(newBox, "add:right"); } }] diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fb54fbefc..93c37dbd1 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -124,7 +124,7 @@ export class DocumentManager { } static addRightSplit = (doc: Doc, finished?: () => void) => { - CollectionDockingView.AddRightSplit(doc); + CollectionDockingView.AddSplit(doc, "right"); finished?.(); } public jumpToDocument = async ( @@ -231,7 +231,7 @@ export class DocumentManager { containerDoc._currentTimecode = targetTimecode; const targetContext = await target?.context as Doc; const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; - DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished); + DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "add:right"), finished), targetNavContext, linkDoc, undefined, doc, finished); } else { finished?.(); } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index dd223390a..764a2547c 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -156,7 +156,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } - CollectionDockingView.AddRightSplit(googleDoc); + CollectionDockingView.AddSplit(googleDoc, "right"); } else if (e.altKey) { e.preventDefault(); window.open(googleDocUrl); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6c64a3714..c7a24133c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -188,7 +188,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const alias = Doc.MakeAlias(selectedDocs[0].props.Document); alias.context = undefined; //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); - CollectionDockingView.AddRightSplit(alias); + CollectionDockingView.AddSplit(alias, "right"); } } SelectionManager.DeselectAll(); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 2794a9f4e..db3e3d0dd 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -210,7 +210,7 @@ export class KeyManager { return { stopPropagation: false, preventDefault: false }; } } - MainView.Instance.mainFreeform && CollectionDockingView.AddRightSplit(MainView.Instance.mainFreeform); + MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right"); break; case "arrowleft": if (document.activeElement) { @@ -218,7 +218,7 @@ export class KeyManager { return { stopPropagation: false, preventDefault: false }; } } - MainView.Instance.mainFreeform && CollectionDockingView.CloseRightSplit(MainView.Instance.mainFreeform); + MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform); break; case "backspace": if (document.activeElement) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6a960f4d5..7b0c6fa9f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -236,7 +236,7 @@ export class MainView extends React.Component { reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, initialized => initialized && received && DocServer.GetRefField(received).then(docField => { if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { - CollectionDockingView.AddRightSplit(docField); + CollectionDockingView.AddSplit(docField, "right"); } }), ); @@ -260,7 +260,7 @@ export class MainView extends React.Component { } const pres = Docs.Create.PresDocument(new List(), { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeStatus: "replaced", boxShadow: "0 0", system: true }); - CollectionDockingView.AddRightSplit(pres); + CollectionDockingView.AddSplit(pres, "right"); Doc.UserDoc().activePresentation = pres; const myPresentations = Doc.UserDoc().myPresentations as Doc; Doc.AddDocToList(myPresentations, "data", pres); @@ -382,9 +382,9 @@ export class MainView extends React.Component { @computed get topOffset() { return (CollectionMenu.Instance?.Pinned ? 35 : 0) + Number(SEARCH_PANEL_HEIGHT.replace("px", "")); } flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, where: string, libraryPath?: Doc[]): boolean => { - return where === "close" ? CollectionDockingView.CloseRightSplit(doc) : + return where === "close" ? CollectionDockingView.CloseSplit(doc) : doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : - CollectionDockingView.AddRightSplit(doc); + CollectionDockingView.AddSplit(doc, "right"); } sidebarScreenToLocal = () => new Transform(0, (CollectionMenu.Instance.Pinned ? -35 : 0) - Number(SEARCH_PANEL_HEIGHT.replace("px", "")), 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-58, 0); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index e95b28d6c..6ac6571d3 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -160,7 +160,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } - CollectionDockingView.AddRightSplit(googleDoc); + CollectionDockingView.AddRight(googleDoc, "right"); } else if (e.altKey) { e.preventDefault(); window.open(googleDocUrl); @@ -343,7 +343,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { dv.toggleFollowLink("inPlace", true, false); } else if (value === "linkOnRight") { dv.noOnClick(); - dv.toggleFollowLink("onRight", false, false); + dv.toggleFollowLink("add:right", false, false); } }); } diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index b382298f3..427748fe7 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -44,7 +44,7 @@ export class PropertiesDocContextSelector extends React.Component { } @computed get contexts() { - return !this.selectedDoc ? (null) : CollectionDockingView.AddRightSplit(doc)} />; + return !this.selectedDoc ? (null) : CollectionDockingView.AddSplit(doc, "right")} />; } previewBackground = () => "lightgrey"; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 4fb362ab1..e84022366 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -331,7 +331,7 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("button", "Show Data", action(() => { const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 }); - CollectionDockingView.AddRightSplit(kvp); + CollectionDockingView.AddSplit(kvp, "right"); })), TimelineMenu.Instance.addItem("button", "Delete", action(() => { (this.regiondata.keyframes as List).splice(this.keyframes.indexOf(kf), 1); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8301d3df8..62c865436 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,6 +1,6 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { clamp } from 'lodash'; +import { clamp, pull } from 'lodash'; import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; @@ -38,7 +38,7 @@ const _global = (window /* browser */ || global /* node */) as any; @observer export class CollectionDockingView extends CollectionSubView(doc => doc) { @observable public static Instance: CollectionDockingView; - public static makeDocumentConfig(document: Doc, isDisplayPanel?: boolean, width?: number) { + public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) { return { type: 'react-component', component: 'DocumentFrameRenderer', @@ -46,7 +46,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { width: width, props: { documentId: document[Id], - isDisplayPanel // flag for whether a tab should be considered a placeholder that has its contents replaced with new content + panelName // name of tab that can be used to close or replace its contents } }; } @@ -85,26 +85,20 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } @undoBatch - @action - public static CloseRightSplit(document: Opt): boolean { - const tryClose = (childItem: any) => { - if (childItem.config?.component === "DocumentFrameRenderer") { - if ((!document && childItem.config.props.isDisplayPanel) || (document && childItem.config.props.documentId === document[Id])) { - childItem.remove(); - return true; - } + public static CloseSplit(document: Opt, panelName?: string): boolean { + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document); + if (tab) { + const j = tab.header.parent.contentItems.indexOf(tab.contentItem); + if (j !== -1) { + tab.header.parent.contentItems[j].remove(); + return CollectionDockingView.Instance.layoutChanged(); } - return false; - }; - const retVal = !CollectionDockingView.Instance?._goldenLayout.root.contentItems[0].isRow ? false : - Array.from(CollectionDockingView.Instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => Array.from(child.contentItems).some(tryClose)); + } - retVal && document && CollectionDockingView.Instance.layoutChanged(); - return retVal; + return false; } @undoBatch - @action public static OpenFullScreen(doc: Doc, libraryPath?: Doc[]) { const instance = CollectionDockingView.Instance; if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { @@ -124,176 +118,94 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { return true; } - public static ReplaceRightSplit(document: Doc, libraryPath?: Doc[], addToSplit?: boolean): boolean { - if (!CollectionDockingView.Instance) return false; - const instance = CollectionDockingView.Instance; - const retVal = !instance._goldenLayout.root.contentItems[0].isRow ? false : - Array.from(instance._goldenLayout.root.contentItems[0].contentItems).some((child: any) => { - if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && - child.contentItems[0].config.isDisplayPanel) { - const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, true); - child.addChild(newItemStackConfig, undefined); - !addToSplit && child.contentItems[0].remove(); - return true; - } - return Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { - if (tab.config.props.isDisplayPanel) { - const newItemStackConfig = CollectionDockingView.makeDocumentConfig(document, true); - child.addChild(newItemStackConfig, undefined); - !addToSplit && child.contentItems[j].remove(); - return true; - } - return false; - }); - }); - retVal && instance.layoutChanged(); - return retVal; - } - - // - // Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split - // @undoBatch - @action - public static AddRightSplit(document: Doc, isDisplayPanel: Opt = undefined) { - if (!CollectionDockingView.Instance) return false; - - const ind = Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === document); - if (ind !== -1) { - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys())[ind]; - const activeContentItem = tab.header.parent.getActiveContentItem(); - if (tab.contentItem !== activeContentItem) { - tab.header.parent.setActiveContentItem(tab.contentItem); - } - tab.setActive(true); - } else { - if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document); - const instance = CollectionDockingView.Instance; - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, isDisplayPanel)] - }; - - const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); - - if (instance._goldenLayout.root.contentItems.length === 0) { - instance._goldenLayout.root.addChild(newContentItem); - } else if (instance._goldenLayout.root.contentItems[0].isRow) { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem); - } else { - const collayout = instance._goldenLayout.root.contentItems[0]; - const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); - collayout.parent.replaceChild(collayout, newRow); - - newRow.addChild(newContentItem, undefined, true); - newRow.addChild(collayout, 0, true); - - collayout.config.width = 50; - newContentItem.config.width = 50; - } - newContentItem.callDownwards('_$init'); - instance.layoutChanged(); + public static ReplaceTab(document: Doc, panelName: string, stack: any, addToSplit?: boolean): boolean { + const instance = CollectionDockingView.Instance; + if (!instance) return false; + const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName); + if (!panelName && stack) { + const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config); + const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout); + stack.addChild(newContentItem.contentItems[0], undefined); + stack.contentItems[activeContentItemIndex].remove(); + return CollectionDockingView.Instance.layoutChanged(); } - return true; + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => tab.contentItem.config.props.panelName === panelName); + if (tab) { + tab.header.parent.addChild(newConfig, undefined); + const j = tab.header.parent.contentItems.indexOf(tab.contentItem); + !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove(); + return CollectionDockingView.Instance.layoutChanged(); + } + return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } - // // Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side // - @action - public static AddSplit(document: Doc, pullSide: string, libraryPath?: Doc[]) { - if (!CollectionDockingView.Instance) return false; + @undoBatch + public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { const instance = CollectionDockingView.Instance; - const newItemStackConfig = { - type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(document, undefined)] - }; + if (!instance) return false; + const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); - const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); - - if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns - instance._goldenLayout.root.addChild(newContentItem); - } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row - if (pullSide === "left") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); - } else if (pullSide === "right") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem); - } else if (pullSide === "top" || pullSide === "bottom") { - // if not going in a row layout, must add already existing content into column - const rowlayout = instance._goldenLayout.root.contentItems[0]; - const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); - rowlayout.parent.replaceChild(rowlayout, newColumn); - if (pullSide === "top") { - newColumn.addChild(rowlayout, undefined, true); - newColumn.addChild(newContentItem, 0, true); - } else if (pullSide === "bottom") { - newColumn.addChild(newContentItem, undefined, true); - newColumn.addChild(rowlayout, 0, true); + if (!pullSide && stack) { + stack.addChild(docContentConfig, undefined); + } else { + const newItemStackConfig = { type: 'stack', content: [docContentConfig] }; + const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); + if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns + instance._goldenLayout.root.addChild(newContentItem); + } else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row + switch (pullSide) { + default: + case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break; + case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break; + case "top": + case "bottom": + // if not going in a row layout, must add already existing content into column + const rowlayout = instance._goldenLayout.root.contentItems[0]; + const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); + rowlayout.parent.replaceChild(rowlayout, newColumn); + if (pullSide === "top") { + newColumn.addChild(rowlayout, undefined, true); + newColumn.addChild(newContentItem, 0, true); + } else if (pullSide === "bottom") { + newColumn.addChild(newContentItem, undefined, true); + newColumn.addChild(rowlayout, 0, true); + } + + rowlayout.config.height = 50; + newContentItem.config.height = 50; } - - rowlayout.config.height = 50; - newContentItem.config.height = 50; - } - } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column - if (pullSide === "top") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); - } else if (pullSide === "bottom") { - instance._goldenLayout.root.contentItems[0].addChild(newContentItem); - } else if (pullSide === "left" || pullSide === "right") { - // if not going in a row layout, must add already existing content into column - const collayout = instance._goldenLayout.root.contentItems[0]; - const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); - collayout.parent.replaceChild(collayout, newRow); - - if (pullSide === "left") { - newRow.addChild(collayout, undefined, true); - newRow.addChild(newContentItem, 0, true); - } else if (pullSide === "right") { - newRow.addChild(newContentItem, undefined, true); - newRow.addChild(collayout, 0, true); + } else if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column + switch (pullSide) { + case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break; + case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break; + case "left": + case "right": + default: + // if not going in a row layout, must add already existing content into column + const collayout = instance._goldenLayout.root.contentItems[0]; + const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); + collayout.parent.replaceChild(collayout, newRow); + + if (pullSide === "left") { + newRow.addChild(collayout, undefined, true); + newRow.addChild(newContentItem, 0, true); + } else { + newRow.addChild(newContentItem, undefined, true); + newRow.addChild(collayout, 0, true); + } + + collayout.config.width = 50; + newContentItem.config.width = 50; } - - collayout.config.width = 50; - newContentItem.config.width = 50; } + newContentItem.callDownwards('_$init'); } - newContentItem.callDownwards('_$init'); - instance.layoutChanged(); - return true; - } - - // - // Creates a vertical split on the right side of the docking view, and then adds the Document to that split - // - @undoBatch - public static UseRightSplit(document: Doc, libraryPath?: Doc[], shiftKey?: boolean) { - if (shiftKey || !CollectionDockingView.ReplaceRightSplit(document, libraryPath, shiftKey)) { - return CollectionDockingView.AddRightSplit(document, true); - } - return false; - } - - @undoBatch - public static AddTab(stack: any, document: Doc, libraryPath?: Doc[]) { - const instance = CollectionDockingView.Instance; - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, undefined); - if (stack === undefined) { - stack = instance._goldenLayout.root; - while (!stack.isStack) { - if (stack.contentItems.length) { - stack = stack.contentItems[0]; - } else { - stack.addChild({ type: 'stack', content: [docContentConfig] }); - stack = undefined; - break; - } - } - } - stack?.addChild(docContentConfig, undefined); - instance.layoutChanged(); - return true; + return instance.layoutChanged(); } @undoBatch @@ -303,6 +215,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { this._goldenLayout.emit('stateChanged'); this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); this.stateChanged(); + return true; } async setupGoldenLayout() { @@ -450,7 +363,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); } tabCreated = (tab: any) => { - tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content) + tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous abs (ie, when dragging a tab around a new tab is created for the old content) } stackCreated = (stack: any) => { @@ -458,9 +371,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { if (e.target === stack.header.element[0] && e.button === 2) { const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], { + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - })); + }), "", stack); } }); @@ -477,9 +390,9 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size const emptyPane = CurrentUserUtils.EmptyPane; emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - CollectionDockingView.AddTab(stack, Docs.Create.FreeformDocument([], { + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` - })); + }), "", stack); })); } @@ -601,7 +514,7 @@ export class DockedFrameRenderer extends React.Component { Doc.AddDocToList(curPres, "data", pinDoc); if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; if (!DocumentManager.Instance.getDocumentView(curPres)) { - CollectionDockingView.AddRightSplit(curPres); + CollectionDockingView.AddSplit(curPres, "right"); } DocumentManager.Instance.jumpToDocument(doc, false, undefined, Cast(doc.context, Doc, null)); } @@ -688,17 +601,26 @@ export class DockedFrameRenderer extends React.Component { get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; } + // adds a tab to the layout based on the locaiton parameter which can be: + // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab, + // add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right + // replace[:{left,right,top,bottom,}] - e.g., "replace" will replace the current stack contents, + // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, + // "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right + // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); if (doc._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - switch (location) { - case "onRight": return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? - CollectionDockingView.CloseRightSplit(doc) : CollectionDockingView.AddRightSplit(doc); - case "close": return CollectionDockingView.CloseRightSplit(doc); - case "replace": return CollectionDockingView.UseRightSplit(doc); + const locationFields = location.split(":"); + const locationParams = locationFields.length > 1 ? locationFields[1] : ""; + switch (locationFields[0]) { + case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); + case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); case "inPlace": - default: return CollectionDockingView.AddTab(this.stack, doc); + case "add": + default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? + CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack); } } @@ -830,6 +752,6 @@ export class DockedFrameRenderer extends React.Component {
); } } -Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); }, +Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddSplit(doc, "right"); }, "opens up the inputted document on the right side of the screen", "(doc: any)"); -Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); }); +Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 18ca29aaa..0d3a951cf 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -370,7 +370,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell { })} /> -
this._doc && this.props.addDocTab(this._doc, "onRight")} className="collectionSchemaView-cellContents-docButton"> +
this._doc && this.props.addDocTab(this._doc, "add:right")} className="collectionSchemaView-cellContents-docButton">
; diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index a3475b481..383a9312f 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -246,7 +246,7 @@ export class MovableRow extends React.Component {
this.props.removeDoc(this.props.rowInfo.original))}>
-
this.props.addDocTab(this.props.rowInfo.original, "onRight")}>
+
this.props.addDocTab(this.props.rowInfo.original, "add:right")}>
{children} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index fffbe65a3..80a6afba7 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -117,7 +117,7 @@ class TreeView extends React.Component { Doc.ComputeContentBounds(DocListCast(this.props.document[this.fieldKey])); } - @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight"); + @undoBatch openRight = () => this.props.addDocTab(this.doc, "add:right"); @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { return this.doc !== target && this.props.removeDoc?.(doc) === true && addDoc(doc); } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 1da0e601a..64ad3abbf 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -305,7 +305,7 @@ export class CollectionView extends Touchable { const newRendition = Doc.MakeAlias(this.props.Document); newRendition._viewType = vtype; - this.props.addDocTab(newRendition, "onRight"); + this.props.addDocTab(newRendition, "add:right"); return newRendition; }, false); @@ -313,10 +313,10 @@ export class CollectionView extends Touchable this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }) : null; if (this.props.Document.childLayout instanceof Doc) { - optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "onRight"), icon: "project-diagram" }); + optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.props.Document.childLayout as Doc, "add:right"), icon: "project-diagram" }); } if (this.props.Document.childClickedOpenTemplateView instanceof Doc) { - optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "onRight"), icon: "project-diagram" }); + optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.props.Document.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); } !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.props.Document.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.props.Document.isInPlaceContainer = !this.props.Document.isInPlaceContainer, icon: "project-diagram" }); @@ -331,7 +331,7 @@ export class CollectionView extends Touchable { const alias = Doc.MakeAlias(this.props.Document); DocUtils.makeCustomViewClicked(alias, undefined, func.key); - this.props.addDocTab(alias, "onRight"); + this.props.addDocTab(alias, "add:right"); } })); DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick => diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 2fe00a245..1fb7aa04a 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -545,7 +545,7 @@ export class SchemaTable extends React.Component { } onOpenClick = () => { - this._showDoc && this.props.addDocTab(this._showDoc, "onRight"); + this._showDoc && this.props.addDocTab(this._showDoc, "add:right"); } getPreviewTransform = (): Transform => { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 759d142ea..1aa30fc02 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -76,7 +76,7 @@ export class MarqueeView extends React.Component this.props.addDocTab( - Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "onRight")); + Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 200, x, y, _nativeHeight: 962, _nativeWidth: 850, isAnnotating: false, title: "bing", UseCors: true }), "add:right")); cm.displayMenu(this._downX, this._downY); e.stopPropagation(); @@ -400,7 +400,7 @@ export class MarqueeView extends React.Component { Default
this.changeFollowBehavior("onRight")}> - Always open in a new pane + onPointerDown={() => this.changeFollowBehavior("add:left")}> + Always open in new left pane
this.changeFollowBehavior("replace")}> + onPointerDown={() => this.changeFollowBehavior("add:right")}> + Always open in new right pane +
+
this.changeFollowBehavior("replace:right")}> Always replace right tab
+
this.changeFollowBehavior("replace:left")}> + Always replace left tab +
this.changeFollowBehavior("fullScreen")}> Always open full screen
this.changeFollowBehavior("inTab")}> + onPointerDown={() => this.changeFollowBehavior("add")}> Always open in a new tab
+
this.changeFollowBehavior("replace")}> + Replace Tab +
{this.props.linkDoc.linksToAnnotation ?
this.changeFollowBehavior("openExternal")}> diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index a77122456..05b6b584d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -167,7 +167,7 @@ export class LinkMenuItem extends React.Component { }); } } else { - DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "onRight"), false); + DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.sourceDoc, doc => this.props.addDocTab(doc, "add:right"), false); } linkDoc.linksToAnnotation && Hypothesis.scrollToAnnotation(StrCast(this.props.linkDoc.annotationId), this.props.destinationDoc); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2d03c5154..48a1688a6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -180,10 +180,10 @@ export class DocumentView extends DocComponent(Docu const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1]; RadialMenu.Instance.openMenu(pt.pageX - 15, pt.pageY - 15); - // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "map-pin", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 }); const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 }); - // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "onRight"), icon: "trash", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 }); RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 }); @@ -323,7 +323,7 @@ export class DocumentView extends DocComponent(Docu fullScreenDoc = Doc.MakeAlias(this.props.Document); fullScreenDoc.layoutKey = "layout_fullScreen"; } - this.props.addDocTab(fullScreenDoc, "inTab"); + this.props.addDocTab(fullScreenDoc, "add"); }, "double tap"); SelectionManager.DeselectAll(); } @@ -351,7 +351,7 @@ export class DocumentView extends DocComponent(Docu this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 500); } else clickFunc(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself - this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "onRight"); + this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right"); } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { this.allLinks.length && this.followLinkClick(e.altKey, e.ctrlKey, e.shiftKey); } else { @@ -748,7 +748,7 @@ export class DocumentView extends DocComponent(Docu const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); const appearance = cm.findByDescription("UI Controls..."); const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : []; - templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); + templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" }); //DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" }); !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" }); @@ -766,7 +766,7 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" }); onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" }); - !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("onRight", false, false), icon: "link" }); + !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" }); onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" }); onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" }); onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" }); @@ -810,8 +810,8 @@ export class DocumentView extends DocComponent(Docu const help = cm.findByDescription("Help..."); const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : []; - !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); - helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "onRight"), icon: "keyboard" }); + !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); + helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index bc5abb0a4..87142babd 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -39,7 +39,7 @@ export class FontIconBox extends DocComponent( showTemplate = (): void => { const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); - dragFactory && this.props.addDocTab(dragFactory, "onRight"); + dragFactory && this.props.addDocTab(dragFactory, "add:right"); } dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index b732f5f83..c5ff42a1a 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -230,7 +230,7 @@ export class KeyValueBox extends React.Component { openItems.push({ description: "Default Perspective", event: () => { this.props.addDocTab(this.props.Document, "close"); - this.props.addDocTab(this.fieldDocToLayout, "onRight"); + this.props.addDocTab(this.fieldDocToLayout, "add:right"); }, icon: "image" }); !open && cm.addItem({ description: "Change Perspective...", subitems: openItems, icon: "external-link-alt" }); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 6dd85b7f5..74d10d087 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component { if (value instanceof Doc) { e.stopPropagation(); e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "onRight"), icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); ContextMenu.Instance.displayMenu(e.clientX, e.clientY); } } diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 8e5ee09ff..10b6aa02e 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -73,7 +73,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { - DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, e.altKey ? "inTab" : "onRight")), false); + DocumentManager.Instance.FollowLink(this.rootDoc, anchorContainerDoc, document => this.props.addDocTab(document, StrCast(this.layoutDoc.linkOpenLocation, "add:right")), false); this._editing = false; }), 300 - (Date.now() - this._lastTap)); } @@ -87,14 +87,14 @@ export class LinkAnchorBox extends ViewBoxBaseComponent { - this.props.addDocTab(this.rootDoc, "onRight"); + this.props.addDocTab(this.rootDoc, "add:right"); } openLinkTargetOnRight = (e: React.MouseEvent) => { const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null)); alias.isLinkButton = undefined; alias._isBackground = undefined; alias.layoutKey = "layout"; - this.props.addDocTab(alias, "onRight"); + this.props.addDocTab(alias, "add:right"); } @action openLinkEditor = action((e: React.MouseEvent) => { diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index f154c5f56..e174a95af 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -40,7 +40,7 @@ export class LinkDocPreview extends React.Component { async followDefault() { DocumentLinksButton.EditLink = undefined; LinkDocPreview.LinkInfo = undefined; - this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "onRight"), false) : null; + this._targetDoc ? DocumentManager.Instance.FollowLink(this.props.linkDoc, this._targetDoc, doc => this.props.addDocTab(doc, "add:right"), false) : null; } componentDidUpdate() { this.updatePreview(); } @@ -69,9 +69,9 @@ export class LinkDocPreview extends React.Component { pointerDown = (e: React.PointerEvent) => { if (this.props.linkDoc && this.props.linkSrc) { DocumentManager.Instance.FollowLink(this.props.linkDoc, this.props.linkSrc, - (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation)); } else if (this.props.href) { - this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "onRight"); + this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { title: this.props.href, _width: 200, _height: 400, UseCors: true }), "add:right"); } } width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f60c521ad..a03c45a53 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -238,7 +238,7 @@ export class PresBox extends ViewBoxBaseComponent // If openDocument is selected then it should open the document for the user if (activeItem.openDocument) { - this.props.addDocTab(activeItem, "replace"); + this.props.addDocTab(activeItem, "replace:right"); } else //docToJump stayed same meaning, it was not in the group or was the last element in the group if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') { @@ -443,7 +443,7 @@ export class PresBox extends ViewBoxBaseComponent if (this.layoutDoc.inOverlay) { this.layoutDoc.presStatus = 'edit'; Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc); - CollectionDockingView.AddRightSplit(this.rootDoc); + CollectionDockingView.AddSplit(this.rootDoc, "right"); this.layoutDoc.inOverlay = false; } else if (this.layoutDoc.context && docView) { this.layoutDoc.presStatus = 'manual'; @@ -1115,7 +1115,7 @@ export class PresBox extends ViewBoxBaseComponent DockedFrameRenderer.PinDoc(doc, false); this.gotoDocument(this.childDocs.length, this.itemIndex); } else { - this.props.addDocTab(doc, "onRight"); + this.props.addDocTab(doc, "add:right"); } } } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 578061344..d50a10bdd 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -82,7 +82,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { e.stopPropagation(); const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]); - collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight"); + collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right"); } @@ -191,7 +191,7 @@ export class DashFieldViewInternal extends React.Component c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, "#f1efeb")); list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb")); alias._pivotField = this._fieldKey.startsWith("#") ? "#" : this._fieldKey; - this.props.tbox.props.addDocTab(alias, "onRight"); + this.props.tbox.props.addDocTab(alias, "add:right"); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 356e36b76..c49e38f72 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -222,7 +222,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key); const allLinks = [{ href: Utils.prepend("/doc/" + id), title: value, targetId: id }]; - const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "onRight", title: value }); + const link = this._editorView.state.schema.marks.linkAnchor.create({ allLinks, location: "add:right", title: value }); const mval = this._editorView.state.schema.marks.metadataVal.create(); const offset = (tx.selection.to === range!.end - 1 ? -1 : 0); tx = tx.addMark(textEndSelection - value.length + offset, textEndSelection, link).addMark(textEndSelection - value.length + offset, textEndSelection, mval); @@ -465,7 +465,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const linkDoc = data.linkDocument!; const anchor1Title = linkDoc.anchor1 instanceof Doc ? StrCast(linkDoc.anchor1.title) : "-untitled-"; const anchor1Id = linkDoc.anchor1 instanceof Doc ? linkDoc.anchor1[Id] : ""; - this.makeLinkToSelection(linkDoc[Id], anchor1Title, "onRight", anchor1Id); + this.makeLinkToSelection(linkDoc[Id], anchor1Title, "add:right", anchor1Id); } getNodeEndpoints(context: Node, node: Node): { from: number, to: number } | null { @@ -811,7 +811,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (linkDoc) { const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-"; const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : ""; - this.makeLinkToSelection(linkDoc[Id], anchor2Title, "onRight", anchor2Id); + this.makeLinkToSelection(linkDoc[Id], anchor2Title, "add:right", anchor2Id); } }, { fireImmediately: true } @@ -1096,7 +1096,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; - const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "onRight", title, docref: true }); + const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); } @@ -1243,10 +1243,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp FormattedTextBoxComment.Hide(); if (FormattedTextBoxComment.linkDoc) { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + this.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right"); } else { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, this.props.Document, - (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + (doc: Doc, followLinkLocation: string) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation)); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 55d225adc..f41f03620 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -103,7 +103,7 @@ export class FormattedTextBoxComment { this.deleteLink(); } else if (FormattedTextBoxComment._followRef && FormattedTextBoxComment._followRef.contains(e.target as any)) { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right"); } else { const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(FormattedTextBoxComment.linkDoc.anchor2, Doc) : (Cast(FormattedTextBoxComment.linkDoc.anchor1, Doc)) @@ -112,28 +112,28 @@ export class FormattedTextBoxComment { if (FormattedTextBoxComment.linkDoc.follow) { if (FormattedTextBoxComment.linkDoc.follow === "Default") { - DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false); + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false); } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in right tab") { - if (target) { textBox.props.addDocTab(target, "onRight"); } + if (target) { textBox.props.addDocTab(target, "add:right"); } } else if (FormattedTextBoxComment.linkDoc.follow === "Always open in new tab") { - if (target) { textBox.props.addDocTab(target, "inTab"); } + if (target) { textBox.props.addDocTab(target, "add"); } } } else { - DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "onRight"), false); + DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, doc => textBox.props.addDocTab(doc, "add:right"), false); } } } else { if (FormattedTextBoxComment.linkDoc.type !== DocumentType.LINK) { - textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "inTab" : "onRight"); + textBox.props.addDocTab(FormattedTextBoxComment.linkDoc, e.ctrlKey ? "add" : "add:right"); } else { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, - (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation)); + (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation)); } } } } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) { - textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight"); + textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "add:right"); } keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation( FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index a65eea50f..c6bacc1a8 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -104,7 +104,7 @@ export function buildKeymap>(schema: S, props: any, mapKey //Command to create a new Tab with a PDF of all the command shortcuts bind("Mod-/", (state: EditorState, dispatch: (tx: Transaction) => void) => { const newDoc = Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _fitWidth: true, _width: 300, _height: 300 }); - props.addDocTab(newDoc, "onRight"); + props.addDocTab(newDoc, "add:right"); }); //Commands to modify BlockType diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 399f72608..307238ea1 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -810,7 +810,7 @@ export class RichTextMenu extends AntimodeMenu {

Linked to:

- +
; @@ -857,7 +857,7 @@ export class RichTextMenu extends AntimodeMenu { // TODO: should check for valid URL @undoBatch makeLinkToURL = (target: string, lcoation: string) => { - ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRight", "", target); + ((this.view as any)?.TextView as FormattedTextBox).makeLinkToSelection("", target, "onRadd:rightight", "", target); } @undoBatch diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 7e632a0ee..3fadfe842 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -279,7 +279,7 @@ export class RichTextRules { DocUtils.Publish(target, docid, returnFalse, returnFalse); DocUtils.MakeLink({ doc: this.Document }, { doc: target }, "portal to"); }); - const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "onRight", title: docid, targetId: docid }); + const link = state.schema.marks.linkAnchor.create({ href: Utils.prepend("/doc/" + docid), location: "add:right", title: docid, targetId: docid }); return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link); } return state.tr; diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index 1616500f6..64f7d27e5 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -148,7 +148,7 @@ export const nodes: { [index: string]: NodeSpec } = { alt: { default: null }, title: { default: null }, float: { default: "left" }, - location: { default: "onRight" }, + location: { default: "add:right" }, docid: { default: "" } }, group: "inline", @@ -177,7 +177,7 @@ export const nodes: { [index: string]: NodeSpec } = { height: { default: 100 }, title: { default: null }, float: { default: "right" }, - location: { default: "onRight" }, + location: { default: "add:right" }, hidden: { default: false }, fieldKey: { default: "" }, docid: { default: "" }, diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 98638ecc2..a33068a18 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -99,7 +99,7 @@ class RegionAnnotation extends React.Component { else if (e.button === 0) { const annoGroup = await Cast(this.props.document.group, Doc); if (annoGroup) { - DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "inTab" : followLinkLocation), false, undefined); + DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined); e.stopPropagation(); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ed33ac822..d89e66ec5 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -604,7 +604,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { -- cgit v1.2.3-70-g09d2 From 442572a66aaa6b50d8d01d46d79b7d6cf2f247a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 29 Aug 2020 17:42:48 -0400 Subject: fixed maximize docdecoration to toggle target w/o making an alias. use ctrlKey to make an alias. removed dashboardBrush stuff. cleaned up DocDecoartions css slightly --- src/client/util/CurrentUserUtils.ts | 9 ++- src/client/views/DocumentDecorations.scss | 84 ++++++++++++---------- src/client/views/DocumentDecorations.tsx | 82 +++++++++------------ src/client/views/MainView.tsx | 17 +++-- .../views/collections/CollectionDockingView.tsx | 13 ++-- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../CollectionFreeFormLayoutEngines.tsx | 3 +- .../collectionGrid/CollectionGridView.scss | 11 --- src/client/views/nodes/DocumentView.tsx | 7 +- .../views/nodes/formattedText/DashDocView.tsx | 5 +- .../views/nodes/formattedText/RichTextSchema.tsx | 5 +- src/client/views/search/SearchBox.tsx | 2 +- 13 files changed, 117 insertions(+), 125 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 20f8fe9c1..2ca395164 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1020,7 +1020,7 @@ export class CurrentUserUtils { } public static snapshotDashboard = (userDoc: Doc) => { - const copy = CollectionDockingView.Copy(Cast(userDoc.activeDashboard, Doc, null)); + const copy = CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); CurrentUserUtils.openDashboard(userDoc, copy); } @@ -1031,7 +1031,11 @@ export class CurrentUserUtils { const dashboards = Cast(userDoc.myDashboards, Doc) as Doc; const dashboardCount = DocListCast(dashboards.data).length + 1; const emptyPane = Cast(userDoc.emptyPane, Doc, null); - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + try { + emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + } catch (e) { + console.log(e) + } const freeformOptions: DocumentOptions = { x: 0, y: 400, @@ -1054,6 +1058,7 @@ export class CurrentUserUtils { CurrentUserUtils.openDashboard(userDoc, dashboardDoc); } + public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index b5c32ca1d..8291d7212 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -16,21 +16,11 @@ $linkGap : 3px; grid-template-columns: 8px 16px 1fr 8px 8px; pointer-events: none; - #documentDecorations-centerCont { + .documentDecorations-centerCont { grid-column: 3; background: none; } - .documentDecorations-resizer { - pointer-events: auto; - background: $alt-accent; - opacity: 0.1; - } - - .documentDecorations-resizer:hover { - opacity: 1; - } - .documentDecorations-selector { pointer-events: auto; height: 15px; @@ -49,27 +39,43 @@ $linkGap : 3px; grid-row: 4; } - #documentDecorations-topLeftResizer, - #documentDecorations-leftResizer, - #documentDecorations-bottomLeftResizer { + .documentDecorations-topLeftResizer, + .documentDecorations-topRightResizer, + .documentDecorations-bottomLeftResizer, + .documentDecorations-bottomRightResizer, + .documentDecorations-leftResizer, + .documentDecorations-topResizer, + .documentDecorations-bottomResizer, + .documentDecorations-rightResizer { + pointer-events: auto; + background: $alt-accent; + opacity: 0.1; + &:hover { + opacity: 1; + } + } + + .documentDecorations-topLeftResizer, + .documentDecorations-leftResizer, + .documentDecorations-bottomLeftResizer { grid-column: 1 } - #documentDecorations-topResizer, - #documentDecorations-bottomResizer { + .documentDecorations-topResizer, + .documentDecorations-bottomResizer { grid-column-start: 2; grid-column-end: 5; } - #documentDecorations-bottomRightResizer, - #documentDecorations-topRightResizer, - #documentDecorations-rightResizer { + .documentDecorations-bottomRightResizer, + .documentDecorations-topRightResizer, + .documentDecorations-rightResizer { grid-column-start: 5; grid-column-end: 7; } - #documentDecorations-rotation, - #documentDecorations-borderRadius { + .documentDecorations-rotation, + .documentDecorations-borderRadius { grid-column: 5; grid-row: 4; border-radius: 100%; @@ -87,69 +93,69 @@ $linkGap : 3px; position: absolute; } } - #documentDecorations-rotation { + .documentDecorations-rotation { background: transparent; right: -15; } - #documentDecorations-topLeftResizer, - #documentDecorations-bottomRightResizer { + .documentDecorations-topLeftResizer, + .documentDecorations-bottomRightResizer { cursor: nwse-resize; background: unset; opacity: 1; } - #documentDecorations-topLeftResizer { + .documentDecorations-topLeftResizer { border-left: 2px solid; border-top: solid 2px; } - #documentDecorations-bottomRightResizer { + .documentDecorations-bottomRightResizer { border-right: 2px solid; border-bottom: solid 2px; } - #documentDecorations-topLeftResizer:hover, - #documentDecorations-bottomRightResizer:hover { + .documentDecorations-topLeftResizer:hover, + .documentDecorations-bottomRightResizer:hover { opacity: 1; } - #documentDecorations-bottomRightResizer { + .documentDecorations-bottomRightResizer { grid-row: 4; } - #documentDecorations-topRightResizer, - #documentDecorations-bottomLeftResizer { + .documentDecorations-topRightResizer, + .documentDecorations-bottomLeftResizer { cursor: nesw-resize; background: unset; opacity: 1; } - #documentDecorations-topRightResizer { + .documentDecorations-topRightResizer { border-right: 2px solid; border-top: 2px solid; } - #documentDecorations-bottomLeftResizer { + .documentDecorations-bottomLeftResizer { border-left: 2px solid; border-bottom: 2px solid; } - #documentDecorations-topRightResizer:hover, - #documentDecorations-bottomLeftResizer:hover { + .documentDecorations-topRightResizer:hover, + .documentDecorations-bottomLeftResizer:hover { cursor: nesw-resize; background: dimGray; opacity: 1; } - #documentDecorations-topResizer, - #documentDecorations-bottomResizer { + .documentDecorations-topResizer, + .documentDecorations-bottomResizer { cursor: ns-resize; } - #documentDecorations-leftResizer, - #documentDecorations-rightResizer { + .documentDecorations-leftResizer, + .documentDecorations-rightResizer { cursor: ew-resize; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c7a24133c..6db5186ba 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -24,6 +24,7 @@ import { FormatShapePane } from './FormatShapePane'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import e = require('express'); +import { CurrentUserUtils } from '../util/CurrentUserUtils'; @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { @@ -177,7 +178,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } @action onMaximizeDown = (e: React.PointerEvent): void => { - setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); + if (e.ctrlKey) { + const selectedDocs = SelectionManager.SelectedDocuments(); + const alias = Doc.MakeAlias(selectedDocs[0].props.Document); + alias.context = undefined; + //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); + CollectionDockingView.AddSplit(alias, "right"); + e.stopPropagation(); + e.preventDefault(); + } else { + setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); + } } @undoBatch @action @@ -185,10 +196,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (e.button === 0) { const selectedDocs = SelectionManager.SelectedDocuments(); if (selectedDocs.length) { - const alias = Doc.MakeAlias(selectedDocs[0].props.Document); - alias.context = undefined; - //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); - CollectionDockingView.AddSplit(alias, "right"); + CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); } } SelectionManager.DeselectAll(); @@ -333,7 +341,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { }); if (e.button === 0) { - this._resizeHdlId = e.currentTarget.id; + this._resizeHdlId = e.currentTarget.className; const bounds = e.currentTarget.getBoundingClientRect(); this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX; this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY; @@ -575,7 +583,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return ; } render() { - const darkScheme = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimgray" : undefined; + const darkScheme = CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimgray" : undefined; const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { @@ -587,35 +595,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> }); const minimal = bounds.r - bounds.x < 100 ? true : false; const maximizeIcon = minimal ? ( -
Show context menu
} placement="top"> + Show context menu
} placement="top">
) : canDelete ? ( -
Close
} placement="top"> + Close} placement="top">
{/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/}
) : (null); const titleArea = this._edtingTitle ? + this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : <> - this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> - {minimal ? (null) :
{ - // if (!seldoc.props.Document["title-custom"]) { - // seldoc.props.Document["title-custom"] = true; - // StrCast(Doc.GetProto(seldoc.props.Document).title).startsWith("-") && (Doc.GetProto(seldoc.props.Document).title = StrCast(seldoc.props.Document.title).substring(1)); - // this._accumulatedTitle = StrCast(seldoc.props.Document.title); - // } - // DocUtils.Publish(seldoc.props.Document, this._accumulatedTitle, seldoc.props.addDocument, seldoc.props.removeDocument); - // })} - > - {/* */} -
} - : - <> - {minimal ? (null) :
Show context menu
} placement="top">
+ {minimal ? (null) : Show context menu
} placement="top">
}
@@ -656,37 +650,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> {maximizeIcon} {titleArea} {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : -
{`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}
} placement="top"> + {`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}
} placement="top">
} -
Open In a New Pane
} placement="top">
+ Open In a New Pane
} placement="top">
{ e.preventDefault(); e.stopPropagation(); }} onPointerDown={this.onMaximizeDown}> {SelectionManager.SelectedDocuments().length === 1 ? : "..."}
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
-
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
-
e.preventDefault()}>
+
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> +
+
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> +
e.preventDefault()} /> {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : -
tap to select containing document
} placement="top"> -
tap to select containing document
} placement="top"> +
e.preventDefault()}>
} -
e.preventDefault()}>{useRotation && "⟲"}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7b0c6fa9f..83d9a3498 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -13,7 +13,7 @@ import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { listSpec } from '../../fields/Schema'; -import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, FieldValue, StrCast, PromiseValue } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; @@ -75,10 +75,10 @@ export class MainView extends React.Component { @observable private _panelHeight: number = 0; @observable private _flyoutTranslate: boolean = false; @observable public flyoutWidth: number = 0; - private get darkScheme() { return BoolCast(Cast(this.userDoc?.activeDashboard, Doc, null)?.darkScheme); } + private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeDashboard, Doc)) : CurrentUserUtils.GuestDashboard; } + @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } @computed public get mainFreeform(): Opt { return (docs => (docs && docs.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } @computed public get searchDoc() { return Cast(this.userDoc.mySearchPanelDoc, Doc) as Doc; } @@ -226,8 +226,7 @@ export class MainView extends React.Component { // Load the user's active dashboard, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; if (received && !this.userDoc) { - reaction( - () => CurrentUserUtils.GuestTarget, + reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true } ); @@ -241,11 +240,11 @@ export class MainView extends React.Component { }), ); } - const doc = this.userDoc && await Cast(this.userDoc.activeDashboard, Doc); - if (doc) { - CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + const activeDash = PromiseValue(this.userDoc.activeDashboard); + if (activeDash) { + activeDash.then(dash => dash instanceof Doc && CurrentUserUtils.openDashboard(this.userDoc, dash)); } else { - CurrentUserUtils.createNewDashboard(Doc.UserDoc()); + CurrentUserUtils.createNewDashboard(this.userDoc); } } } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 62c865436..93e77e0a2 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -140,6 +140,13 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } + + @undoBatch + public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) { + return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? + CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName); + } + // // Creates a split on any side of the docking view based on the passed input pullSide and then adds the Document to the requested side // @@ -262,8 +269,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { config => { if (!this._goldenLayout || this._ignoreStateChange !== config) { this.setupGoldenLayout(); - DocListCast(CurrentUserUtils.MyDashboards.data).map(d => d.dashboardBrush = false); - this.props.Document.dashboardBrush = true; } this._ignoreStateChange = ""; }); @@ -274,7 +279,6 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { componentWillUnmount: () => void = () => { try { - this.props.Document.dashboardBrush = false; this._goldenLayout.unbind('stackCreated', this.stackCreated); this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); } catch (e) { } @@ -619,8 +623,7 @@ export class DockedFrameRenderer extends React.Component { case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); case "inPlace": case "add": - default: return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? - CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, locationParams, this.stack); + default: return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 80a6afba7..84fd4cbe8 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -483,7 +483,7 @@ class TreeView extends React.Component { style={{ fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined, textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined, - outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined, + outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined, pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined }} > {view} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 64ad3abbf..81403de46 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -583,7 +583,7 @@ export class CollectionView extends Touchable {this.showIsTagged()} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 646ae94fb..bc2cb2d20 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -10,6 +10,7 @@ import { Id, ToString } from "../../../../fields/FieldSymbols"; import { ObjectField } from "../../../../fields/ObjectField"; import { RefField } from "../../../../fields/RefField"; import { listSpec } from "../../../../fields/Schema"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; export interface ViewDefBounds { type: string; @@ -359,7 +360,7 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } - const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; + const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss index 4d8473be9..60ec02f47 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.scss +++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss @@ -134,17 +134,6 @@ } -// .documentDecorations-container .documentDecorations-resizer { -// pointer-events: none; -// } - -// #documentDecorations-bottomRightResizer, -// #documentDecorations-bottomLeftResizer, -// #documentDecorations-topRightResizer, -// #documentDecorations-topLeftResizer { -// visibility: collapse; -// } - /* Chrome, Safari, Edge, Opera */ input::-webkit-outer-spin-button, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 48a1688a6..f4eb71145 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -39,6 +39,7 @@ import { LinkDescriptionPopup } from './LinkDescriptionPopup'; import { RadialMenu } from './RadialMenu'; import { TaskCompletionBox } from './TaskCompletedBox'; import React = require("react"); +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; export type DocFocusFunc = () => boolean; @@ -588,7 +589,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action deleteClicked = (): void => { - if (Doc.UserDoc().activeDashboard === this.props.Document) { + if (CurrentUserUtils.ActiveDashboard === this.props.Document) { alert("Can't delete the active dashboard"); } else { const selected = SelectionManager.SelectedDocuments().slice(); @@ -625,7 +626,7 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { - if (this.props.Document === Doc.UserDoc().activeDashboard) { + if (this.props.Document === CurrentUserUtils.ActiveDashboard) { alert("linking to document tabs not yet supported. Drop link on document content."); return; } @@ -1041,7 +1042,7 @@ export class DocumentView extends DocComponent(Docu const fullDegree = Doc.isBrushedHighlightedDegree(this.props.Document); const borderRounding = this.layoutDoc.borderRounding; const localScale = fullDegree; - const highlightColors = Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? + const highlightColors = CurrentUserUtils.ActiveDashboard?.darkScheme ? ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] : ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"]; const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"]; diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 08bab8d12..3b77735a7 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -12,6 +12,7 @@ import { DocumentView } from "../DocumentView"; import { FormattedTextBox } from "./FormattedTextBox"; import { Transform } from "../../../util/Transform"; import React = require("react"); +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; interface IDashDocView { node: any; @@ -175,7 +176,7 @@ export class DashDocView extends React.Component { const outerStyle = { position: "relative" as "relative", textIndent: "0", - border: "1px solid " + StrCast(this._textBox.Document.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")), + border: "1px solid " + StrCast(this._textBox.Document.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")), width: this.props.node.props.width, height: this.props.node.props.height, display: this.props.node.props.hidden ? "none" : "inline-block", @@ -202,7 +203,7 @@ export class DashDocView extends React.Component { ({ dim, color }) => { spanStyle.width = outerStyle.width = Math.max(20, dim[0]) + "px"; spanStyle.height = outerStyle.height = Math.max(20, dim[1]) + "px"; - outerStyle.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")); + outerStyle.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); }, { fireImmediately: true }); if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") { diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx index b76e520b7..f0bacb735 100644 --- a/src/client/views/nodes/formattedText/RichTextSchema.tsx +++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx @@ -13,6 +13,7 @@ import { Transform } from "../../../util/Transform"; import { DocumentView } from "../DocumentView"; import { FormattedTextBox } from "./FormattedTextBox"; import React = require("react"); +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; export class DashDocView { @@ -43,7 +44,7 @@ export class DashDocView { this._outer = document.createElement("span"); this._outer.style.position = "relative"; this._outer.style.textIndent = "0"; - this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")); + this._outer.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); this._outer.style.width = node.attrs.width; this._outer.style.height = node.attrs.height; this._outer.style.display = node.attrs.hidden ? "none" : "inline-block"; @@ -126,7 +127,7 @@ export class DashDocView { this._reactionDisposer = reaction(() => ({ dim: [finalLayout[WidthSym](), finalLayout[HeightSym]()], color: finalLayout.color }), ({ dim, color }) => { this._dashSpan.style.width = this._outer.style.width = Math.max(20, dim[0]) + "px"; this._dashSpan.style.height = this._outer.style.height = Math.max(20, dim[1]) + "px"; - this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (Cast(Doc.UserDoc().activeDashboard, Doc, null).darkScheme ? "dimGray" : "lightGray")); + this._outer.style.border = "1px solid " + StrCast(finalLayout.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); }, { fireImmediately: true }); const doReactRender = (finalLayout: Doc, resolvedDataDoc: Doc) => { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index d7a8ed404..3926208ae 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -507,7 +507,7 @@ export class SearchBox extends ViewBoxBaseComponent
CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}> -- cgit v1.2.3-70-g09d2 From 561a8d0bd130ea5c1fd68a4dd2344f0333d388cc Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 30 Aug 2020 13:10:54 -0400 Subject: fixed importBox upload to give feedback, share code. fixed stacking autoHeight to happen immediately. fixed images to not need to request image information. changed doc decorations to change image native width. changed alert() when dropping on impermissibale doc. fixed allow dropping over images,etc. changed default image nativeSize to 900 --- src/client/documents/Documents.ts | 23 +++++---- src/client/util/CurrentUserUtils.ts | 1 - src/client/views/DocumentDecorations.tsx | 6 +-- src/client/views/MainView.tsx | 56 +++++----------------- .../views/collections/CollectionStackingView.tsx | 9 ++++ src/client/views/collections/CollectionSubView.tsx | 3 +- src/client/views/collections/TabDocView.tsx | 4 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/globalCssVariables.scss | 2 + src/client/views/globalCssVariables.scss.d.ts | 1 + src/client/views/nodes/DocumentView.tsx | 7 ++- src/client/views/nodes/ImageBox.tsx | 49 +++++++++++-------- src/mobile/ImageUpload.tsx | 8 ++-- src/server/ApiManagers/UploadManager.ts | 2 +- 14 files changed, 84 insertions(+), 89 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 262086735..9a24a8052 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,5 +1,5 @@ -import { runInAction, action } from "mobx"; -import { extname, basename } from "path"; +import { runInAction } from "mobx"; +import { basename, extname } from "path"; import { DateField } from "../../fields/DateField"; import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc"; import { HtmlField } from "../../fields/HtmlField"; @@ -12,21 +12,22 @@ import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { AudioField, ImageField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; import { MessageStore } from "../../server/Message"; +import { Upload } from "../../server/SharedMediaTypes"; import { OmitKeys, Utils } from "../../Utils"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { DocServer } from "../DocServer"; +import { Networking } from "../Network"; import { DocumentManager } from "../util/DocumentManager"; import { dropActionType } from "../util/DragManager"; import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox"; import { LinkManager } from "../util/LinkManager"; import { Scripting } from "../util/Scripting"; -import { UndoManager, undoBatch } from "../util/UndoManager"; -import { DocumentType } from "./DocumentTypes"; -import { SearchBox } from "../views/search/SearchBox"; +import { undoBatch, UndoManager } from "../util/UndoManager"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { ContextMenu } from "../views/ContextMenu"; import { ContextMenuProps } from "../views/ContextMenuItem"; +import { DFLT_IMAGE_NATIVE_DIM } from "../views/globalCssVariables.scss"; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke"; import { AudioBox } from "../views/nodes/AudioBox"; import { ColorBox } from "../views/nodes/ColorBox"; @@ -46,11 +47,12 @@ import { SliderBox } from "../views/nodes/SliderBox"; import { VideoBox } from "../views/nodes/VideoBox"; import { WebBox } from "../views/nodes/WebBox"; import { PresElementBox } from "../views/presentationview/PresElementBox"; +import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; -import { Networking } from "../Network"; -import { Upload } from "../../server/SharedMediaTypes"; +import { DocumentType } from "./DocumentTypes"; const path = require('path'); +const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); export interface DocumentOptions { system?: boolean; _autoHeight?: boolean; @@ -146,6 +148,7 @@ export interface DocumentOptions { borderRounding?: string; boxShadow?: string; dontRegisterChildViews?: boolean; + dontRegisterView?: boolean; lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document. see PresBox. "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form "onChildDoubleClick-rawScript"?: string; // onChildDoubleClick script in raw text form @@ -1210,8 +1213,10 @@ export namespace DocUtils { proto.text = result.rawText; proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); if (Upload.isImageInformation(result)) { - proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400; - proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight); + const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); + proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation; + proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim; + proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); proto.contentSize = result.contentSize; } generatedDocuments.push(doc); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 260552879..b9dd18a36 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -864,7 +864,6 @@ export class CurrentUserUtils { } } - static setupClickEditorTemplates(doc: Doc) { if (doc["clickFuncs-child"] === undefined) { // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6db5186ba..de87b8aa5 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -475,10 +475,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> doc[DataSym][fieldKey + "-nativeHeight"] = doc._nativeHeight = nheight = doc._height || 0; } const anno = Cast(doc.annotationOn, Doc, null); - if (e.ctrlKey && anno) { + if (e.ctrlKey && (anno || doc.type === DocumentType.IMG)) { dW !== 0 && runInAction(() => { - const dataDoc = anno[DataSym]; - const annoFieldKey = Doc.LayoutFieldKey(anno); + const dataDoc = (anno ?? doc)[DataSym]; + const annoFieldKey = Doc.LayoutFieldKey(anno ?? doc); const nw = NumCast(dataDoc[annoFieldKey + "-nativeWidth"]); const nh = NumCast(dataDoc[annoFieldKey + "-nativeHeight"]); dataDoc[annoFieldKey + "-nativeWidth"] = nw + (dW > 0 ? 10 : -10); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 07ba7e805..338d9544e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -9,7 +9,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import Measure from 'react-measure'; import * as rp from 'request-promise'; -import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; +import { Doc, DocListCast, Field, Opt, DocListCastAsync } from '../../fields/Doc'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { listSpec } from '../../fields/Schema'; @@ -18,7 +18,7 @@ import { TraceMobx } from '../../fields/util'; import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; -import { Docs } from '../documents/Documents'; +import { Docs, DocUtils } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { Networking } from '../Network'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; @@ -63,6 +63,8 @@ import { PDFMenu } from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { SearchBox } from './search/SearchBox'; +import { CollectionSubView, CollectionSubViewLoader } from './collections/CollectionSubView'; +import ReactLoading from 'react-loading'; @observer export class MainView extends React.Component { @@ -847,8 +849,6 @@ export class MainView extends React.Component { } importDocument = () => { - const sidebar = Cast(Doc.UserDoc().myImportDocs, Doc, null); - const sidebarDocView = DocumentManager.Instance.getDocumentView(sidebar); const input = document.createElement("input"); input.type = "file"; input.multiple = true; @@ -864,55 +864,21 @@ export class MainView extends React.Component { const json = await response.json(); if (json !== "error") { const doc = await DocServer.GetRefField(json); - if (doc instanceof Doc && sidebarDocView) { - sidebarDocView.props.addDocument?.(doc); + if (doc instanceof Doc) { setTimeout(() => { SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { docs.docs.forEach(d => LinkManager.Instance.addLink(d)); }); }, 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } } } else if (input.files && input.files.length !== 0) { - const files = input.files || []; - Array.from(files).forEach(async file => { - const res = await Networking.UploadFilesToServer(file); - res.map(async ({ result }) => { - const name = file.name; - if (result instanceof Error) { - return; - } - const path = Utils.prepend(result.accessPaths.agnostic.client); - let doc: Doc; - // Case 1: File is a video - if (file.type.includes("video")) { - doc = Docs.Create.VideoDocument(path, { _height: 100, title: name }); - // Case 2: File is a PDF document - } else if (file.type === "application/pdf") { - doc = Docs.Create.PdfDocument(path, { _height: 100, _fitWidth: true, title: name }); - // Case 3: File is an image - } else if (file.type.includes("image")) { - doc = Docs.Create.ImageDocument(path, { _height: 100, title: name }); - // Case 4: File is an audio document - } else { - doc = Docs.Create.AudioDocument(path, { title: name }); - } - const res = await rp.get(Utils.prepend("/getUserDocumentId")); - if (!res) { - throw new Error("No user id returned"); - } - const field = await DocServer.GetRefField(res); - let pending: Opt; - if (field instanceof Doc) { - pending = sidebar; - } - if (pending) { - const data = await Cast(pending.data, listSpec(Doc)); - if (data) data.push(doc); - else pending.data = new List([doc]); - } - }); + const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); + const disposer = OverlayView.Instance.addElement(, { x: 300, y: 200 }); + + DocListCastAsync(importDocs.data).then(async list => { + list?.push(... await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {})); + disposer(); }); } else { console.log("No file selected"); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 5386d617c..04c464b73 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -38,6 +38,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); _pivotFieldDisposer?: IReactionDisposer; + _autoHeightDisposer?: IReactionDisposer; _docXfs: any[] = []; _columnStart: number = 0; @observable _heightMap = new Map(); @@ -148,10 +149,12 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) () => this.pivotField, () => this.layoutDoc._columnHeaders = new List() ); + this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight, this.forceAutoHeight); } componentWillUnmount() { super.componentWillUnmount(); this._pivotFieldDisposer?.(); + this._autoHeightDisposer?.(); } @action @@ -384,6 +387,11 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) return this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + offsety); } + forceAutoHeight = () => { + const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; + Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); + } + sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => { const key = this.pivotField; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; @@ -450,6 +458,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) const subItems: ContextMenuProps[] = []; subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" }); subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); + subItems.push({ description: "Clear All", event: () => this.dataDoc.data = new List([]), icon: "times" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" }); } } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index cb3f486bb..7820e2fa3 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -228,7 +228,8 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document); added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse); } else added = res; - e.stopPropagation(); + added && e.stopPropagation(); + return added; } else { ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData }); added = this.addDocument(docDragData.droppedDocuments); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 5acf825f1..fb3de3b68 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -217,8 +217,8 @@ export class TabDocView extends React.Component { } ScreenToLocalTransform = () => { - if (this._mainCont && this._mainCont.children) { - const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0].firstChild as HTMLElement); + if (this._mainCont?.children) { + const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0]?.firstChild as HTMLElement); const scale = Utils.GetScreenTransform(this._mainCont).scale; return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.contentScaling() / scale); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1aa30fc02..f928e3fb8 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -783,7 +783,7 @@ export class MarqueeView extends React.Component e.preventDefault()} onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> {this._visible ? this.marqueeDiv : null} diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss index 7eb07ff55..70a7946ba 100644 --- a/src/client/views/globalCssVariables.scss +++ b/src/client/views/globalCssVariables.scss @@ -36,6 +36,7 @@ $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should $COLLECTION_BORDER_WIDTH: 1; $MINIMIZED_ICON_SIZE:25; $MAX_ROW_HEIGHT: 44px; +$DFLT_IMAGE_NATIVE_DIM: 900px; :export { contextMenuZindex: $contextMenu-zindex; @@ -45,4 +46,5 @@ $MAX_ROW_HEIGHT: 44px; SEARCH_THUMBNAIL_SIZE: $search-thumnail-size; ANTIMODEMENU_HEIGHT: $antimodemenu-height; SEARCH_PANEL_HEIGHT: $searchpanel-height; + DFLT_IMAGE_NATIVE_DIM: $DFLT_IMAGE_NATIVE_DIM; } \ No newline at end of file diff --git a/src/client/views/globalCssVariables.scss.d.ts b/src/client/views/globalCssVariables.scss.d.ts index fd1f60f13..531b38d68 100644 --- a/src/client/views/globalCssVariables.scss.d.ts +++ b/src/client/views/globalCssVariables.scss.d.ts @@ -7,6 +7,7 @@ interface IGlobalScss { SEARCH_THUMBNAIL_SIZE: string; ANTIMODEMENU_HEIGHT: string; SEARCH_PANEL_HEIGHT: string; + DFLT_IMAGE_NATIVE_DIM: string; } declare const globalCssVariables: IGlobalScss; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f4eb71145..8c8562267 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -198,7 +198,7 @@ export class DocumentView extends DocComponent(Docu this._mainCont.current && (this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(this._mainCont.current, this.onTouchStart.bind(this))); // this._mainCont.current && (this.holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this))); - if (!this.props.dontRegisterView) { + if (!BoolCast(this.rootDoc.dontRegisterView, this.props.dontRegisterView)) { DocumentManager.Instance.DocumentViews.push(this); } } @@ -627,7 +627,10 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action drop = async (e: Event, de: DragManager.DropEvent) => { if (this.props.Document === CurrentUserUtils.ActiveDashboard) { - alert("linking to document tabs not yet supported. Drop link on document content."); + if ((e.target as any)?.closest?.("*.lm_content")) { + alert("You can't perform this move most likely because you don't have permission to modify the destination."); + } + else alert("linking to document tabs not yet supported. Drop link on document content."); return; } const makeLink = action((linkDoc: Doc) => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6cce5fb87..688bac725 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, observable, runInAction, reaction, IReactionDisposer } from 'mobx'; import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; @@ -26,7 +26,6 @@ import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); -const requestImageSize = require('../../util/request-image-size'); const path = require('path'); const { Howl } = require('howler'); @@ -63,6 +62,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; + private _pathDisposer?: IReactionDisposer; @observable private _audioState = 0; @observable static _showControls: boolean; @observable uploadIcon = uploadIcons.idle; @@ -72,6 +72,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent this.paths.length && this.resize(this.paths[0]), + () => true, + { fireImmediately: true }); + } + componentWillUnmount() { + this._pathDisposer?.(); + } + @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { @@ -242,29 +251,29 @@ export class ImageBox extends ViewBoxAnnotatableComponent { const basePath = imgPath.replace(/_[oms]./, ""); + const curPath = this.dataDoc[this.fieldKey + "-path"]; const cachedNativeSize = { - width: basePath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]) : 0, - height: basePath === this.dataDoc[this.fieldKey + "-path"] ? NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]) : 0, + width: basePath === curPath || !curPath ? NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]) : 0, + height: basePath === curPath || !curPath ? NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]) : 0, }; const docAspect = this.layoutDoc[HeightSym]() / this.layoutDoc[WidthSym](); const cachedAspect = cachedNativeSize.height / cachedNativeSize.width; if (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(NumCast(this.layoutDoc._width) / NumCast(this.layoutDoc._height) - cachedNativeSize.width / cachedNativeSize.height) > 0.05) { if (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) { - requestImageSize(imgPath).then(action((inquiredSize: any) => { - const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180; - const rotatedNativeSize = { width: inquiredSize.width, height: inquiredSize.height }; - if (inquiredSize.orientation === 6 || rotation === 90 || rotation === 270) { - rotatedNativeSize.width = inquiredSize.height; - rotatedNativeSize.height = inquiredSize.width; - } - const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width; - if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) { - this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect; - this.dataDoc[this.fieldKey + "-nativeWidth"] = this.layoutDoc._nativeWidth = this.layoutDoc._width; - this.dataDoc[this.fieldKey + "-nativeHeight"] = this.layoutDoc._nativeHeight = this.layoutDoc._height; - this.dataDoc[this.fieldKey + "-path"] = basePath; - } - })).catch(console.log); + const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]) % 180; + const orientation = NumCast(this.dataDoc[this.fieldKey + "-nativeOrientation"]); + if (orientation === 6 || rotation === 90 || rotation === 270) { + this.layoutDoc._nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]); + this.layoutDoc._nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]); + } else { + this.layoutDoc._nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]); + this.layoutDoc._nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]); + } + const rotatedAspect = NumCast(this.layoutDoc._nativeHeight) / NumCast(this.layoutDoc._nativeWidth); + if (this.layoutDoc[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) { + this.layoutDoc._height = this.layoutDoc[WidthSym]() * rotatedAspect; + this.dataDoc[this.fieldKey + "-path"] = basePath; + } } else if (Math.abs(1 - docAspect / cachedAspect) > 0.1) { this.layoutDoc._width = this.layoutDoc[WidthSym]() || cachedNativeSize.width; this.layoutDoc._height = this.layoutDoc[WidthSym]() * cachedAspect; @@ -385,8 +394,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent(); +const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @observer export class Uploader extends React.Component { @@ -52,13 +54,13 @@ export class Uploader extends React.Component { let doc = null; // Case 1: File is a video if (file.type === "video/mp4") { - doc = Docs.Create.VideoDocument(path, { _nativeWidth: 400, _width: 400, title: name }); + doc = Docs.Create.VideoDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name }); // Case 2: File is a PDF document } else if (file.type === "application/pdf") { - doc = Docs.Create.PdfDocument(path, { _nativeWidth: 400, _width: 400, _fitWidth: true, title: name }); + doc = Docs.Create.PdfDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, _fitWidth: true, title: name }); // Case 3: File is another document type (most likely Image) } else { - doc = Docs.Create.ImageDocument(path, { _nativeWidth: 400, _width: 400, title: name }); + doc = Docs.Create.ImageDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name }); } this.setOpacity(4, "1"); // Slab 4 const res = await rp.get(Utils.prepend("/getUserDocumentId")); diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index e088cd2c4..76f5afe16 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -55,7 +55,7 @@ export default class UploadManager extends ApiManager { const results: Upload.FileResponse[] = []; for (const key in files) { const result = await DashUploadUtils.upload(files[key]); - result && results.push(result); + result && !(result.result instanceof Error) && results.push(result); } _success(res, results); resolve(); -- cgit v1.2.3-70-g09d2 From 28b904a0a1d8a5d90f128b4487aba047b69bfd70 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 31 Aug 2020 14:36:46 -0400 Subject: moved the non-rendering contents of FormatShapePane into InkStrokeProperties. --- src/client/views/DocumentDecorations.tsx | 6 +- src/client/views/FormatShapePane.scss | 68 ---- src/client/views/FormatShapePane.tsx | 545 ------------------------------- src/client/views/GlobalKeyHandler.ts | 4 +- src/client/views/InkStrokeProperties.ts | 268 +++++++++++++++ src/client/views/InkingStroke.tsx | 32 +- src/client/views/MainView.tsx | 6 +- src/client/views/PropertiesView.tsx | 21 +- src/client/views/nodes/DocumentView.tsx | 4 +- src/mobile/MobileInterface.tsx | 2 + 10 files changed, 309 insertions(+), 647 deletions(-) delete mode 100644 src/client/views/FormatShapePane.scss delete mode 100644 src/client/views/FormatShapePane.tsx create mode 100644 src/client/views/InkStrokeProperties.ts (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index de87b8aa5..6951cb592 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -20,11 +20,11 @@ import { undoBatch, UndoManager } from "../util/UndoManager"; import { CollectionDockingView } from './collections/CollectionDockingView'; import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; -import { FormatShapePane } from './FormatShapePane'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import e = require('express'); import { CurrentUserUtils } from '../util/CurrentUserUtils'; +import { InkStrokeProperties } from './InkStrokeProperties'; @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { @@ -332,7 +332,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const doc = Document(element.rootDoc); if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height) { this._inkDocs.push({ x: doc.x, y: doc.y, width: doc._width, height: doc._height }); - if (FormatShapePane.Instance._lock) { + if (InkStrokeProperties.Instance?._lock) { doc._nativeHeight = doc._height; doc._nativeWidth = doc._width; } @@ -365,7 +365,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> var fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0; SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { const doc = Document(element.rootDoc); - if (doc.type === DocumentType.INK && doc._width && doc._height && FormatShapePane.Instance._lock) { + if (doc.type === DocumentType.INK && doc._width && doc._height && InkStrokeProperties.Instance?._lock) { fixedAspect = NumCast(doc._nativeWidth) / NumCast(doc._nativeHeight); } })); diff --git a/src/client/views/FormatShapePane.scss b/src/client/views/FormatShapePane.scss deleted file mode 100644 index d49ab27fb..000000000 --- a/src/client/views/FormatShapePane.scss +++ /dev/null @@ -1,68 +0,0 @@ -.antimodeMenu-button { - width: 200px; - position: relative; - text-align: left; - - .color-previewI { - width: 100%; - height: 40%; - } - - .color-previewII { - width: 100%; - height: 100%; - } -} - -.antimenu-Buttonup { - position: absolute; - width: 20; - height: 10; - right: 0; - padding: 0; -} - -.formatShapePane-inputBtn { - width: inherit; - position: absolute; -} - -.btn-group-palette { - .sketch-picker { - background: #323232; - width: 160px !important; - height: 80% !important; - - .flexbox-fit { - background: #323232; - } - } -} - -.btn-group { - display: grid; - grid-template-columns: auto auto auto auto; - /* Make the buttons appear below each other */ -} - -.btn-group-palette { - display: block; - /* Make the buttons appear below each other */ -} - -.btn-draw { - display: inline; - /* Make the buttons appear below each other */ -} - -.btn2-group { - display: block; - background: #323232; - grid-template-columns: auto; - - /* Make the buttons appear below each other */ - .antimodeMenu-button { - background: #323232; - display: block; - } -} \ No newline at end of file diff --git a/src/client/views/FormatShapePane.tsx b/src/client/views/FormatShapePane.tsx deleted file mode 100644 index 293a06dc0..000000000 --- a/src/client/views/FormatShapePane.tsx +++ /dev/null @@ -1,545 +0,0 @@ -import React = require("react"); -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { ColorState, SketchPicker } from 'react-color'; -import { Doc, Field, Opt } from "../../fields/Doc"; -import { Document } from "../../fields/documentSchemas"; -import { InkField } from "../../fields/InkField"; -import { BoolCast, Cast, NumCast } from "../../fields/Types"; -import { DocumentType } from "../documents/DocumentTypes"; -import { SelectionManager } from "../util/SelectionManager"; -import { undoBatch } from "../util/UndoManager"; -import { AntimodeMenu, AntimodeMenuProps } from "./AntimodeMenu"; -import "./FormatShapePane.scss"; - -@observer -export class FormatShapePane extends AntimodeMenu { - static Instance: FormatShapePane; - - private _lastFill = "#D0021B"; - private _lastLine = "#D0021B"; - private _lastDash = "2"; - private _mode = ["fill-drip", "ruler-combined"]; - - @observable private _subOpen = [false, false]; - @observable private _currMode = "fill-drip"; - @observable _lock = false; - @observable private _fillBtn = false; - @observable private _lineBtn = false; - @observable _controlBtn = false; - @observable private _controlPoints: { X: number, Y: number }[] = []; - @observable _currPoint = -1; - - getField(key: string) { - return this.selectedInk?.reduce((p, i) => - (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt); - } - - @computed get selectedInk() { - const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK); - return inks.length ? inks : undefined; - } - @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } - @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } - @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } - @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } - @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } - @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } - @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } - @computed get widthStk() { return this.getField("strokeWidth") || "1"; } - @computed get markHead() { return this.getField("strokeStartMarker") || ""; } - @computed get markTail() { return this.getField("strokeEndMarker") || ""; } - @computed get shapeHgt() { return this.getField("_height"); } - @computed get shapeWid() { return this.getField("_width"); } - @computed get shapeXps() { return this.getField("x"); } - @computed get shapeYps() { return this.getField("y"); } - @computed get shapeRot() { return this.getField("rotation"); } - set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } - set solidFil(value) { this.unFilled = !value; } - set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } - set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } - set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } - set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } - set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } - set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } - set dashdStk(value) { - value && (this._lastDash = value) && (this.unStrokd = false); - this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); - } - set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } - set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } - set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } - set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } - set shapeWid(value) { - this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldWidth = NumCast(i.rootDoc._width); - i.rootDoc._width = Number(value); - this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); - }); - } - set shapeHgt(value) { - this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldHeight = NumCast(i.rootDoc._height); - i.rootDoc._height = Number(value); - this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); - }); - } - - constructor(props: Readonly<{}>) { - super(props); - FormatShapePane.Instance = this; - this._canFade = false; - runInAction(() => this.Pinned = BoolCast(Doc.UserDoc()["menuFormatShape-pinned"])); - } - - @action - closePane = () => { - this.fadeOut(false); - this.Pinned = false; - } - - @action - upDownButtons = (dirs: string, field: string) => { - switch (field) { - case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break; - // case "rot": this.selectedInk?.forEach(i => i.rootDoc.rotation = NumCast(i.rootDoc.rotation) + (dirs === "up" ? 0.1 : -0.1)); break; - case "Xps": this.selectedInk?.forEach(i => i.rootDoc.x = NumCast(i.rootDoc.x) + (dirs === "up" ? 10 : -10)); break; - case "Yps": this.selectedInk?.forEach(i => i.rootDoc.y = NumCast(i.rootDoc.y) + (dirs === "up" ? 10 : -10)); break; - case "stk": this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = NumCast(i.rootDoc.strokeWidth) + (dirs === "up" ? .1 : -.1)); break; - case "wid": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - //redraw points - const oldWidth = NumCast(i.rootDoc._width); - const oldHeight = NumCast(i.rootDoc._height); - const oldX = NumCast(i.rootDoc.x); - const oldY = NumCast(i.rootDoc.y); - i.rootDoc._width = oldWidth + (dirs === "up" ? 10 : - 10); - this._lock && (i.rootDoc._height = (i.rootDoc._width / oldWidth * NumCast(i.rootDoc._height))); - const doc = Document(i.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; - const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; - newPoints.push({ X: newX, Y: newY }); - }); - Doc.GetProto(doc).data = new InkField(newPoints); - } - } - }); - break; - case "hgt": this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { - const oldWidth = NumCast(i.rootDoc._width); - const oldHeight = NumCast(i.rootDoc._height); - const oldX = NumCast(i.rootDoc.x); - const oldY = NumCast(i.rootDoc.y); i.rootDoc._height = oldHeight + (dirs === "up" ? 10 : - 10); - this._lock && (i.rootDoc._width = (i.rootDoc._height / oldHeight * NumCast(i.rootDoc._width))); - const doc = Document(i.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - // (new x — oldx) + (oldxpoint * newWidt)/oldWidth - const newX = ((doc.x || 0) - oldX) + (i.X * (doc._width || 0)) / oldWidth; - const newY = ((doc.y || 0) - oldY) + (i.Y * (doc._height || 0)) / oldHeight; - newPoints.push({ X: newX, Y: newY }); - }); - Doc.GetProto(doc).data = new InkField(newPoints); - } - } - }); - break; - } - } - - @undoBatch - @action - addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => { - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const newPoints: { X: number, Y: number }[] = []; - var counter = 0; - for (var k = 0; k < index; k++) { - control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++); - } - //decide where to put the new coordinate - const spNum = Math.floor(counter / 2) * 4 + 2; - - for (var i = 0; i < spNum; i++) { - ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - for (var j = 0; j < 4; j++) { - newPoints.push({ X: x, Y: y }); - - } - for (var i = spNum; i < ink.length; i++) { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - this._currPoint = -1; - Doc.GetProto(doc).data = new InkField(newPoints); - } - } - } - })); - } - - @undoBatch - @action - deletePoints = () => { - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1 && this._currPoint !== -1) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink && ink.length > 4) { - const newPoints: { X: number, Y: number }[] = []; - const toRemove = Math.floor(((this._currPoint + 2) / 4)); - for (var i = 0; i < ink.length; i++) { - if (Math.floor((i + 2) / 4) !== toRemove) { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - } - this._currPoint = -1; - Doc.GetProto(doc).data = new InkField(newPoints); - if (newPoints.length === 4) { - const newerPoints: { X: number, Y: number }[] = []; - newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); - newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); - newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); - newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); - Doc.GetProto(doc).data = new InkField(newerPoints); - - } - } - } - } - })); - } - - @undoBatch - @action - rotate = (angle: number) => { - const _centerPoints: { X: number, Y: number }[] = []; - SelectionManager.SelectedDocuments().forEach(action(inkView => { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - const xs = ink.map(p => p.X); - const ys = ink.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - const right = Math.max(...xs); - const bottom = Math.max(...ys); - _centerPoints.push({ X: left, Y: top }); - } - } - })); - - var index = 0; - SelectionManager.SelectedDocuments().forEach(action(inkView => { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - doc.rotation = Number(doc.rotation) + Number(angle); - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - - const newPoints: { X: number, Y: number }[] = []; - ink.forEach(i => { - const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X; - const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y; - newPoints.push({ X: newX, Y: newY }); - }); - Doc.GetProto(doc).data = new InkField(newPoints); - const xs = newPoints.map(p => p.X); - const ys = newPoints.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - const right = Math.max(...xs); - const bottom = Math.max(...ys); - - doc._height = (bottom - top); - doc._width = (right - left); - } - index++; - } - })); - } - - @undoBatch - @action - control = (xDiff: number, yDiff: number, controlNum: number) => { - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1) { - const doc = Document(inkView.rootDoc); - if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { - const ink = Cast(doc.data, InkField)?.inkData; - if (ink) { - - const newPoints: { X: number, Y: number }[] = []; - const order = controlNum % 4; - for (var i = 0; i < ink.length; i++) { - if (controlNum === i || - (order === 0 && i === controlNum + 1) || - (order === 0 && controlNum !== 0 && i === controlNum - 2) || - (order === 0 && controlNum !== 0 && i === controlNum - 1) || - (order === 3 && i === controlNum - 1) || - (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) || - (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) - || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1)) - ) { - newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) }); - } - else { - newPoints.push({ X: ink[i].X, Y: ink[i].Y }); - } - } - const oldx = doc.x; - const oldy = doc.y; - const xs = ink.map(p => p.X); - const ys = ink.map(p => p.Y); - const left = Math.min(...xs); - const top = Math.min(...ys); - Doc.GetProto(doc).data = new InkField(newPoints); - const xs2 = newPoints.map(p => p.X); - const ys2 = newPoints.map(p => p.Y); - const left2 = Math.min(...xs2); - const top2 = Math.min(...ys2); - const right2 = Math.max(...xs2); - const bottom2 = Math.max(...ys2); - doc._height = (bottom2 - top2); - doc._width = (right2 - left2); - //if points move out of bounds - - doc.x = oldx - (left - left2); - doc.y = oldy - (top - top2); - - } - } - } - })); - } - - @undoBatch - @action - switchStk = (color: ColorState) => { - const val = String(color.hex); - this.colorStk = val; - return true; - } - - @undoBatch - @action - switchFil = (color: ColorState) => { - const val = String(color.hex); - this.colorFil = val; - return true; - } - - - colorPicker(setter: (color: string) => {}, type: string) { - return
- -
; - } - inputBox = (key: string, value: any, setter: (val: string) => {}) => { - return <> - setter(e.target.value)))} - autoFocus /> - -
- - ; - } - - inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => { - return <> - {title1} -

{title2}

- - setter(e.target.value)} - autoFocus /> - - - {title2 === "" ? "" : <> - setter2(e.target.value)} - autoFocus /> - -
- } - ; - } - - - colorButton(value: string, setter: () => {}) { - return <> - - ; - } - - controlPointsButton() { - return <> - - - -

- ; - } - - lockRatioButton() { - return <> - -

- ; - } - - rotate90Button() { - return <> - -

- ; - } - @computed get fillButton() { return this.colorButton(this.colorFil, () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); } - @computed get lineButton() { return this.colorButton(this.colorStk, () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); } - - @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); } - @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); } - - @computed get stkInput() { return this.inputBox("stk", this.widthStk, (val: string) => this.widthStk = val); } - @computed get dashInput() { return this.inputBox("dsh", this.widthStk, (val: string) => this.widthStk = val); } - - @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => this.shapeHgt = val, "H:", "wid", this.shapeWid, (val: string) => this.shapeWid = val, "W:"); } - @computed get widInput() { return this.inputBox("wid", this.shapeWid, (val: string) => this.shapeWid = val); } - @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; return true; }, "∠:", "rot", this.shapeRot, (val: string) => this.shapeRot = val, ""); } - - @computed get YpsInput() { return this.inputBox("Yps", this.shapeYps, (val: string) => this.shapeYps = val); } - - @computed get controlPoints() { return this.controlPointsButton(); } - @computed get lockRatio() { return this.lockRatioButton(); } - @computed get rotate90() { return this.rotate90Button(); } - @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => this.shapeXps = val, "X:", "Yps", this.shapeYps, (val: string) => this.shapeYps = val, "Y:"); } - - - @computed get propertyGroupItems() { - const fillCheck =
= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}> - Fill: - {this.fillButton} -
- Stroke: - {this.lineButton} -
- - {this._fillBtn ? this.fillPicker : ""} - {this._lineBtn ? this.linePicker : ""} - {this._fillBtn || this._lineBtn ? "" :
} - {(this.solidStk || this.dashdStk) ? "Width" : ""} - {(this.solidStk || this.dashdStk) ? this.stkInput : ""} - {(this.solidStk || this.dashdStk) ? this.widthStk = e.target.value))} /> : (null)} -
- {(this.solidStk || this.dashdStk) ? <> -

Arrow Head

- this.markHead = this.markHead ? "" : "arrow"))} style={{ position: "absolute", right: 110, width: 20 }} /> -

Arrow End

- this.markTail = this.markTail ? "" : "arrow"))} style={{ position: "absolute", right: 0, width: 20 }} /> -
- : ""} - Dash: - this.dashdStk = this.dashdStk === "2" ? "0" : "2"))} style={{ position: "absolute", right: 110, width: 20 }} /> -
; - - - - const sizeCheck = - -
= 1) ? "" : "none", width: "inherit", backgroundColor: "#323232", color: "white", }}> - {this.controlPoints} - {this.hgtInput} - {this.XpsInput} - {this.rotInput} - -
; - - - const subMenus = this._currMode === "fill-drip" ? [`Appearance`, 'Transform'] : []; - const menuItems = this._currMode === "fill-drip" ? [fillCheck, sizeCheck] : []; - const indexOffset = 0; - - return
- {subMenus.map((subMenu, i) => -
- - {menuItems[i]} -
)} -
; - } - - @computed get closeBtn() { - return ; - } - - @computed get propertyGroupBtn() { - return
- {this._mode.map(mode => - )} -
; - } - - render() { - return this.getElementVert([this.closeBtn, - this.propertyGroupItems]); - } -} \ No newline at end of file diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index a79973aee..076be3ad6 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -19,7 +19,7 @@ import { CollectionDockingView } from "./collections/CollectionDockingView"; import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu"; import { ContextMenu } from "./ContextMenu"; import { DocumentDecorations } from "./DocumentDecorations"; -import { FormatShapePane } from "./FormatShapePane"; +import { InkStrokeProperties } from "./InkStrokeProperties"; import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import { DocumentView } from "./nodes/DocumentView"; @@ -86,7 +86,7 @@ export class KeyManager { case "escape": DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; - FormatShapePane.Instance._controlBtn = false; + InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false); Doc.SetSelectedTool(InkTool.None); var doDeselect = true; diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts new file mode 100644 index 000000000..ad5c70fb1 --- /dev/null +++ b/src/client/views/InkStrokeProperties.ts @@ -0,0 +1,268 @@ +import { action, computed, observable } from "mobx"; +import { ColorState } from 'react-color'; +import { Doc, Field, Opt } from "../../fields/Doc"; +import { Document } from "../../fields/documentSchemas"; +import { InkField } from "../../fields/InkField"; +import { Cast, NumCast } from "../../fields/Types"; +import { DocumentType } from "../documents/DocumentTypes"; +import { SelectionManager } from "../util/SelectionManager"; +import { undoBatch } from "../util/UndoManager"; + +export class InkStrokeProperties { + static Instance: InkStrokeProperties | undefined; + + private _lastFill = "#D0021B"; + private _lastLine = "#D0021B"; + private _lastDash = "2"; + + @observable _lock = false; + @observable _controlBtn = false; + @observable _currPoint = -1; + + getField(key: string) { + return this.selectedInk?.reduce((p, i) => + (p === undefined || (p && p === i.rootDoc[key])) && i.rootDoc[key] !== "0" ? Field.toString(i.rootDoc[key] as Field) : "", undefined as Opt); + } + + @computed get selectedInk() { + const inks = SelectionManager.SelectedDocuments().filter(i => Document(i.rootDoc).type === DocumentType.INK); + return inks.length ? inks : undefined; + } + @computed get unFilled() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.fillColor ? true : false, true) || false; } + @computed get unStrokd() { return this.selectedInk?.reduce((p, i) => p && !i.rootDoc.color ? true : false, true) || false; } + @computed get solidFil() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.fillColor ? true : false, true) || false; } + @computed get solidStk() { return this.selectedInk?.reduce((p, i) => p && i.rootDoc.color && (!i.rootDoc.strokeDash || i.rootDoc.strokeDash === "0") ? true : false, true) || false; } + @computed get dashdStk() { return !this.unStrokd && this.getField("strokeDash") || ""; } + @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; } + @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; } + @computed get widthStk() { return this.getField("strokeWidth") || "1"; } + @computed get markHead() { return this.getField("strokeStartMarker") || ""; } + @computed get markTail() { return this.getField("strokeEndMarker") || ""; } + @computed get shapeHgt() { return this.getField("_height"); } + @computed get shapeWid() { return this.getField("_width"); } + @computed get shapeXps() { return this.getField("x"); } + @computed get shapeYps() { return this.getField("y"); } + @computed get shapeRot() { return this.getField("rotation"); } + set unFilled(value) { this.colorFil = value ? "" : this._lastFill; } + set solidFil(value) { this.unFilled = !value; } + set colorFil(value) { value && (this._lastFill = value); this.selectedInk?.forEach(i => i.rootDoc.fillColor = value ? value : undefined); } + set colorStk(value) { value && (this._lastLine = value); this.selectedInk?.forEach(i => i.rootDoc.color = value ? value : undefined); } + set markHead(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeStartMarker = value); } + set markTail(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeEndMarker = value); } + set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; } + set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; } + set dashdStk(value) { + value && (this._lastDash = value) && (this.unStrokd = false); + this.selectedInk?.forEach(i => i.rootDoc.strokeDash = value ? this._lastDash : undefined); + } + set shapeXps(value) { this.selectedInk?.forEach(i => i.rootDoc.x = Number(value)); } + set shapeYps(value) { this.selectedInk?.forEach(i => i.rootDoc.y = Number(value)); } + set shapeRot(value) { this.selectedInk?.forEach(i => i.rootDoc.rotation = Number(value)); } + set widthStk(value) { this.selectedInk?.forEach(i => i.rootDoc.strokeWidth = Number(value)); } + set shapeWid(value) { + this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { + const oldWidth = NumCast(i.rootDoc._width); + i.rootDoc._width = Number(value); + this._lock && (i.rootDoc._height = (i.rootDoc._width * NumCast(i.rootDoc._height)) / oldWidth); + }); + } + set shapeHgt(value) { + this.selectedInk?.filter(i => i.rootDoc._width && i.rootDoc._height).forEach(i => { + const oldHeight = NumCast(i.rootDoc._height); + i.rootDoc._height = Number(value); + this._lock && (i.rootDoc._width = (i.rootDoc._height * NumCast(i.rootDoc._width)) / oldHeight); + }); + } + + constructor() { + InkStrokeProperties.Instance = this; + } + + @undoBatch + @action + addPoints = (x: number, y: number, pts: { X: number, Y: number }[], index: number, control: { X: number, Y: number }[]) => { + this.selectedInk?.forEach(action(inkView => { + if (this.selectedInk?.length === 1) { + const doc = Document(inkView.rootDoc); + if (doc.type === DocumentType.INK) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + var counter = 0; + for (var k = 0; k < index; k++) { + control.forEach(pt => (pts[k].X === pt.X && pts[k].Y === pt.Y) && counter++); + } + //decide where to put the new coordinate + const spNum = Math.floor(counter / 2) * 4 + 2; + + for (var i = 0; i < spNum; i++) { + ink[i] && newPoints.push({ X: ink[i].X, Y: ink[i].Y }); + } + for (var j = 0; j < 4; j++) { + newPoints.push({ X: x, Y: y }); + + } + for (var i = spNum; i < ink.length; i++) { + newPoints.push({ X: ink[i].X, Y: ink[i].Y }); + } + this._currPoint = -1; + Doc.GetProto(doc).data = new InkField(newPoints); + } + } + } + })); + } + + @undoBatch + @action + deletePoints = () => { + this.selectedInk?.forEach(action(inkView => { + if (this.selectedInk?.length === 1 && this._currPoint !== -1) { + const doc = Document(inkView.rootDoc); + if (doc.type === DocumentType.INK) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink && ink.length > 4) { + const newPoints: { X: number, Y: number }[] = []; + const toRemove = Math.floor(((this._currPoint + 2) / 4)); + for (var i = 0; i < ink.length; i++) { + if (Math.floor((i + 2) / 4) !== toRemove) { + newPoints.push({ X: ink[i].X, Y: ink[i].Y }); + } + } + this._currPoint = -1; + Doc.GetProto(doc).data = new InkField(newPoints); + if (newPoints.length === 4) { + const newerPoints: { X: number, Y: number }[] = []; + newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); + newerPoints.push({ X: newPoints[0].X, Y: newPoints[0].Y }); + newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); + newerPoints.push({ X: newPoints[3].X, Y: newPoints[3].Y }); + Doc.GetProto(doc).data = new InkField(newerPoints); + + } + } + } + } + })); + } + + @undoBatch + @action + rotate = (angle: number) => { + const _centerPoints: { X: number, Y: number }[] = []; + SelectionManager.SelectedDocuments().forEach(action(inkView => { + const doc = Document(inkView.rootDoc); + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const xs = ink.map(p => p.X); + const ys = ink.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + _centerPoints.push({ X: left, Y: top }); + } + } + })); + + var index = 0; + SelectionManager.SelectedDocuments().forEach(action(inkView => { + const doc = Document(inkView.rootDoc); + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + doc.rotation = Number(doc.rotation) + Number(angle); + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + + const newPoints: { X: number, Y: number }[] = []; + ink.forEach(i => { + const newX = Math.cos(angle) * (i.X - _centerPoints[index].X) - Math.sin(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].X; + const newY = Math.sin(angle) * (i.X - _centerPoints[index].X) + Math.cos(angle) * (i.Y - _centerPoints[index].Y) + _centerPoints[index].Y; + newPoints.push({ X: newX, Y: newY }); + }); + Doc.GetProto(doc).data = new InkField(newPoints); + const xs = newPoints.map(p => p.X); + const ys = newPoints.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + + doc._height = (bottom - top); + doc._width = (right - left); + } + index++; + } + })); + } + + @undoBatch + @action + control = (xDiff: number, yDiff: number, controlNum: number) => { + this.selectedInk?.forEach(action(inkView => { + if (this.selectedInk?.length === 1) { + const doc = Document(inkView.rootDoc); + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + + const newPoints: { X: number, Y: number }[] = []; + const order = controlNum % 4; + for (var i = 0; i < ink.length; i++) { + if (controlNum === i || + (order === 0 && i === controlNum + 1) || + (order === 0 && controlNum !== 0 && i === controlNum - 2) || + (order === 0 && controlNum !== 0 && i === controlNum - 1) || + (order === 3 && i === controlNum - 1) || + (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 1) || + (order === 3 && controlNum !== ink.length - 1 && i === controlNum + 2) + || ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlNum === 0 || controlNum === ink.length - 1)) + ) { + newPoints.push({ X: ink[i].X - (xDiff * inkView.props.ScreenToLocalTransform().Scale), Y: ink[i].Y - (yDiff * inkView.props.ScreenToLocalTransform().Scale) }); + } + else { + newPoints.push({ X: ink[i].X, Y: ink[i].Y }); + } + } + const oldx = doc.x; + const oldy = doc.y; + const xs = ink.map(p => p.X); + const ys = ink.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + Doc.GetProto(doc).data = new InkField(newPoints); + const xs2 = newPoints.map(p => p.X); + const ys2 = newPoints.map(p => p.Y); + const left2 = Math.min(...xs2); + const top2 = Math.min(...ys2); + const right2 = Math.max(...xs2); + const bottom2 = Math.max(...ys2); + doc._height = (bottom2 - top2); + doc._width = (right2 - left2); + //if points move out of bounds + + doc.x = oldx - (left - left2); + doc.y = oldy - (top - top2); + + } + } + } + })); + } + + @undoBatch + @action + switchStk = (color: ColorState) => { + const val = String(color.hex); + this.colorStk = val; + return true; + } + + @undoBatch + @action + switchFil = (color: ColorState) => { + const val = String(color.hex); + this.colorFil = val; + return true; + } +} \ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 02628527f..1a1a3d75c 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -13,10 +13,10 @@ import { Scripting } from "../util/Scripting"; import { UndoManager } from "../util/UndoManager"; import { ContextMenu } from "./ContextMenu"; import { ViewBoxBaseComponent } from "./DocComponent"; -import { FormatShapePane } from "./FormatShapePane"; import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); +import { InkStrokeProperties } from "./InkStrokeProperties"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -56,15 +56,17 @@ export class InkingStroke extends ViewBoxBaseComponent { - FormatShapePane.Instance._currPoint = i; + if (!InkStrokeProperties.Instance) return; + InkStrokeProperties.Instance._currPoint = i; document.addEventListener("keydown", this.delPts, true); } @action onControlMove = (e: PointerEvent, down: number[]): boolean => { + if (!InkStrokeProperties.Instance) return false; const xDiff = this._prevX - e.clientX; const yDiff = this._prevY - e.clientY; - FormatShapePane.Instance.control(xDiff, yDiff, this._controlNum); + InkStrokeProperties.Instance.control(xDiff, yDiff, this._controlNum); this._prevX = e.clientX; this._prevY = e.clientY; return false; @@ -79,8 +81,8 @@ export class InkingStroke extends ViewBoxBaseComponent { - if (e instanceof KeyboardEvent ? e.key === "-" : true) { - FormatShapePane.Instance.deletePoints(); + if (InkStrokeProperties.Instance && (e instanceof KeyboardEvent ? e.key === "-" : true)) { + InkStrokeProperties.Instance.deletePoints(); } } @@ -88,6 +90,8 @@ export class InkingStroke extends ViewBoxBaseComponent { FormatShapePane.Instance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} pointerEvents="all" cursor="all-scroll" + onPointerDown={(e) => { formatInstance.addPoints(pts.X, pts.Y, apoints, i, controlPoints); }} pointerEvents="all" cursor="all-scroll" /> ); @@ -170,16 +174,16 @@ export class InkingStroke extends ViewBoxBaseComponent this.onControlDown(e, pts.I)} pointerEvents="all" cursor="all-scroll" display={(pts.dot1 === FormatShapePane.Instance._currPoint || pts.dot2 === FormatShapePane.Instance._currPoint) ? "inherit" : "none"} /> + onPointerDown={(e) => this.onControlDown(e, pts.I)} pointerEvents="all" cursor="all-scroll" display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> ); const handleLines = handleLine.map((pts, i) => + display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> + display={(pts.dot1 === formatInstance._currPoint || pts.dot2 === formatInstance._currPoint) ? "inherit" : "none"} /> ); @@ -198,7 +202,7 @@ export class InkingStroke extends ViewBoxBaseComponent FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn), icon: "paint-brush" }); + cm.addItem({ description: "Edit Points", event: action(() => formatInstance._controlBtn = !formatInstance._controlBtn), icon: "paint-brush" }); //cm.addItem({ description: "Format Shape...", event: this.formatShape, icon: "paint-brush" }); } }} @@ -206,10 +210,10 @@ export class InkingStroke extends ViewBoxBaseComponent {hpoints} {points} - {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? addpoints : ""} - {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? controls : ""} - {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? handles : ""} - {FormatShapePane.Instance._controlBtn && this.props.isSelected() ? handleLines : ""} + {formatInstance._controlBtn && this.props.isSelected() ? addpoints : ""} + {formatInstance._controlBtn && this.props.isSelected() ? controls : ""} + {formatInstance._controlBtn && this.props.isSelected() ? handles : ""} + {formatInstance._controlBtn && this.props.isSelected() ? handleLines : ""} ); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bf9408b57..96a596f15 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -35,9 +35,9 @@ import { CollectionViewType } from './collections/CollectionView'; import { ContextMenu } from './ContextMenu'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; -import { FormatShapePane } from "./FormatShapePane"; +import { InkStrokeProperties } from './InkStrokeProperties'; import { GestureOverlay } from './GestureOverlay'; -import { SEARCH_PANEL_HEIGHT, MENU_PANEL_WIDTH } from './globalCssVariables.scss'; +import { MENU_PANEL_WIDTH, SEARCH_PANEL_HEIGHT } from './globalCssVariables.scss'; import { KeyManager } from './GlobalKeyHandler'; import { LinkMenu } from './linking/LinkMenu'; import "./MainView.scss"; @@ -107,6 +107,7 @@ export class MainView extends React.Component { constructor(props: Readonly<{}>) { super(props); + new InkStrokeProperties(); MainView.Instance = this; CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any; @@ -593,7 +594,6 @@ export class MainView extends React.Component { {this.search} - {LinkDescriptionPopup.descriptionPopup ? : null} {DocumentLinksButton.EditLink ? : (null)} {LinkDocPreview.LinkInfo ? { @computed get controlPointsButton() { - return
+ const formatInstance = InkStrokeProperties.Instance; + return !formatInstance ? (null) :
{"Edit points"}
}> -
FormatShapePane.Instance._controlBtn = !FormatShapePane.Instance._controlBtn)} style={{ backgroundColor: FormatShapePane.Instance._controlBtn ? "black" : "" }}> +
formatInstance._controlBtn = !formatInstance._controlBtn)} style={{ backgroundColor: formatInstance._controlBtn ? "black" : "" }}>
- {FormatShapePane.Instance._lock ? "Unlock ratio" : "Lock ratio"}
}> -
FormatShapePane.Instance._lock = !FormatShapePane.Instance._lock)} > - + {formatInstance._lock ? "Unlock ratio" : "Lock ratio"}
}> +
formatInstance._lock = !formatInstance._lock)} > +
{"Rotate 90˚"}
}> @@ -594,7 +595,7 @@ export class PropertiesView extends React.Component { const oldX = NumCast(this.selectedDoc?.x); const oldY = NumCast(this.selectedDoc?.y); this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height))); + InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height))); const doc = this.selectedDoc; if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) { const ink = Cast(doc.data, InkField)?.inkData; @@ -616,7 +617,7 @@ export class PropertiesView extends React.Component { const oX = NumCast(this.selectedDoc?.x); const oY = NumCast(this.selectedDoc?.y); this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width))); + InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width))); const docu = this.selectedDoc; if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) { const ink = Cast(docu.data, InkField)?.inkData; @@ -654,12 +655,12 @@ export class PropertiesView extends React.Component { set shapeWid(value) { const oldWidth = NumCast(this.selectedDoc?._width); this.selectedDoc && (this.selectedDoc._width = Number(value)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); + InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) * NumCast(this.selectedDoc?._height)) / oldWidth); } set shapeHgt(value) { const oldHeight = NumCast(this.selectedDoc?._height); this.selectedDoc && (this.selectedDoc._height = Number(value)); - FormatShapePane.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); + InkStrokeProperties.Instance?._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight); } @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; }, "H:", "wid", this.shapeWid, (val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; }, "W:"); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8c8562267..23142d95b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -30,7 +30,7 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; -import { FormatShapePane } from '../FormatShapePane'; +import { InkStrokeProperties } from '../InkStrokeProperties'; import { DocumentContentsView } from "./DocumentContentsView"; import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; @@ -316,7 +316,7 @@ export class DocumentView extends DocComponent(Docu func(); } else if (!Doc.IsSystem(this.props.Document)) { if (this.props.Document.type === DocumentType.INK) { - FormatShapePane.Instance._controlBtn = true; + InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true); } else { UndoManager.RunInBatch(() => { let fullScreenDoc = this.props.Document; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index b8633b06f..3eae2841d 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -38,6 +38,7 @@ import { Uploader } from "./ImageUpload"; import "./AudioUpload.scss"; import "./ImageUpload.scss"; import "./MobileInterface.scss"; +import { InkStrokeProperties } from '../client/views/InkStrokeProperties'; library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, @@ -68,6 +69,7 @@ export class MobileInterface extends React.Component { constructor(props: Readonly<{}>) { super(props); + new InkStrokeProperties(); this._library = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web MobileInterface.Instance = this; } -- cgit v1.2.3-70-g09d2 From 3f3ba063bb6e3bde20dafc8a9e6d7fe9254e22fe Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 2 Sep 2020 15:30:45 -0400 Subject: fixed behavior of menuButtons to toggle contents. added start of a filter panel. --- src/client/documents/DocumentTypes.ts | 7 +- src/client/documents/Documents.ts | 8 + src/client/util/CurrentUserUtils.ts | 17 ++ src/client/views/DocumentDecorations.tsx | 32 ++-- src/client/views/MainView.tsx | 9 +- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FilterBox.scss | 44 ++++++ src/client/views/nodes/FilterBox.tsx | 197 ++++++++++++++++++++++++ src/client/views/search/SearchBox.tsx | 2 +- src/fields/Doc.ts | 9 +- 12 files changed, 304 insertions(+), 28 deletions(-) create mode 100644 src/client/views/nodes/FilterBox.scss create mode 100644 src/client/views/nodes/FilterBox.tsx (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 1bef6fa08..37a148e55 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -13,7 +13,8 @@ export enum DocumentType { INK = "inks", // ink stroke SCREENSHOT = "screenshot", // view of a desktop application FONTICON = "fonticonbox", // font icon - SEARCH = "search", // search query + FILTER = "filter", + SEARCH = "search", // search query LABEL = "label", // simple text label BUTTON = "button", // onClick button WEBCAM = "webcam", // webcam @@ -32,10 +33,10 @@ export enum DocumentType { YOUTUBE = "youtube", // youtube directory (view of you tube search results) DOCHOLDER = "docholder", // nested document (view of a document) SEARCHITEM = "searchitem", - COMPARISON = "comparison", // before/after view with slider (view of 2 images) + COMPARISON = "comparison", // before/after view with slider (view of 2 images) GROUP = "group", // group of users LINKDB = "linkdb", // database of links ??? why do we have this - SCRIPTDB = "scriptdb", // database of scripts + SCRIPTDB = "scriptdb", // database of scripts GROUPDB = "groupdb" // database of groups } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index feb84843d..fbcecbec6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -50,6 +50,7 @@ import { PresElementBox } from "../views/presentationview/PresElementBox"; import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; +import { FilterBox } from "../views/nodes/FilterBox"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @@ -246,6 +247,10 @@ export namespace Docs { layout: { view: SearchBox, dataField: defaultDataKey }, options: { _width: 400 } }], + [DocumentType.FILTER, { + layout: { view: FilterBox, dataField: defaultDataKey }, + options: { _width: 400 } + }], [DocumentType.COLOR, { layout: { view: ColorBox, dataField: defaultDataKey }, options: { _nativeWidth: 220, _nativeHeight: 300 } @@ -821,6 +826,9 @@ export namespace Docs { export function FontIconDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { hideLinkButton: true, ...(options || {}) }); } + export function FilterDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.FILTER), undefined, { ...(options || {}) }); + } export function PresElementBoxDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 734050b05..164c3ab67 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -516,6 +516,7 @@ export class CurrentUserUtils { { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' }, + { title: "Filter", target: Cast(doc.myFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' }, { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' }, { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, @@ -775,6 +776,21 @@ export class CurrentUserUtils { (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Clear All"]); } } + static setupFilterDocs(doc: Doc) { + // setup Recently Closed library item + doc.myFilter === undefined; + if (doc.myFilter === undefined) { + doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({ + title: "FilterDoc", _height: 500, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true + })); + const clearAll = ScriptField.MakeScript(`self.data = new List([])`); + (doc.myFilter as any as Doc).contextMenuScripts = new List([clearAll!]); + (doc.myFilter as any as Doc).contextMenuLabels = new List(["Clear All"]); + } + } static setupUserDoc(doc: Doc) { @@ -806,6 +822,7 @@ export class CurrentUserUtils { CurrentUserUtils.setupDashboards(doc); CurrentUserUtils.setupPresentations(doc); CurrentUserUtils.setupRecentlyClosedDocs(doc); + CurrentUserUtils.setupFilterDocs(doc); CurrentUserUtils.setupUserDoc(doc); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6951cb592..7781d6069 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from '@material-ui/core'; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { AclAdmin, AclEdit, DataSym, Doc, Field } from "../../fields/Doc"; +import { AclAdmin, AclEdit, DataSym, Doc, Field, WidthSym, HeightSym } from "../../fields/Doc"; import { Document } from '../../fields/documentSchemas'; import { HtmlField } from '../../fields/HtmlField'; import { InkField } from "../../fields/InkField"; @@ -11,7 +11,7 @@ import { ScriptField } from '../../fields/ScriptField'; import { Cast, NumCast } from "../../fields/Types"; import { GetEffectiveAcl } from '../../fields/util'; import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from "../../Utils"; -import { DocUtils } from "../documents/Documents"; +import { DocUtils, Docs } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; @@ -178,17 +178,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } @action onMaximizeDown = (e: React.PointerEvent): void => { - if (e.ctrlKey) { - const selectedDocs = SelectionManager.SelectedDocuments(); - const alias = Doc.MakeAlias(selectedDocs[0].props.Document); - alias.context = undefined; - //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); - CollectionDockingView.AddSplit(alias, "right"); - e.stopPropagation(); - e.preventDefault(); - } else { - setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); - } + setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); } @undoBatch @action @@ -196,7 +186,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (e.button === 0) { const selectedDocs = SelectionManager.SelectedDocuments(); if (selectedDocs.length) { - CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); + if (e.ctrlKey) { + const alias = Doc.MakeAlias(selectedDocs[0].props.Document); + alias.context = undefined; + //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); + CollectionDockingView.AddSplit(alias, "right"); + } else if (e.shiftKey) { + const alias = Doc.MakeAlias(selectedDocs[0].props.Document); + alias.context = undefined; + alias.x = -alias[WidthSym]() / 2; + alias.y = -alias[HeightSym]() / 2; + //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right"); + } else { + CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); + } } } SelectionManager.DeselectAll(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7c7c5b72b..e6e80d9fb 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -372,9 +372,9 @@ export class MainView extends React.Component { @action selectMenu = (button: Doc) => { const title = StrCast(Doc.GetProto(button).title); - const closed = !this._flyoutWidth; + const willOpen = !this._flyoutWidth || this._panelContent !== title; this.closeFlyout(); - if (this._panelContent !== title || !this._flyoutWidth) { + if (willOpen) { switch (this._panelContent = title) { case "Settings": SettingsManager.Instance.open(); @@ -384,7 +384,7 @@ export class MainView extends React.Component { SearchBox.Instance.enter(undefined); break; default: - closed && this.expandFlyout(button); + this.expandFlyout(button); } } return true; @@ -434,6 +434,7 @@ export class MainView extends React.Component { this._lastButton && (this._lastButton.color = "white"); this._lastButton && (this._lastButton._backgroundColor = ""); this._panelContent = "none"; + this._sidebarContent.proto = undefined; this._flyoutWidth = 0; }); @@ -520,7 +521,7 @@ export class MainView extends React.Component { ; } - select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; + select = (ctrlPressed: boolean) => { }; @computed get search() { TraceMobx(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 3c230537c..5cdf337a9 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -191,7 +191,7 @@ export class TabDocView extends React.Component { if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) { this._isActive = this.props.glContainer.tab.isActive; this._isActive && setTimeout(() => this.view && SelectionManager.SelectDoc(this.view, false), 0); - (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged(); + (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged(); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 5d5a1f7f3..1b2070c0f 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -25,6 +25,7 @@ import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { PresBox } from "./PresBox"; import { SearchBox } from "../search/SearchBox"; +import { FilterBox } from "./FilterBox"; import { ColorBox } from "./ColorBox"; import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo"; import { LinkAnchorBox } from "./LinkAnchorBox"; @@ -191,7 +192,7 @@ export class DocumentContentsView extends React.Component(Docu let stopPropagate = true; let preventDefault = true; !this.props.Document._isBackground && this.props.bringToFront(this.props.Document); - if (this._doubleTap && this.props.renderDepth && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click + if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss new file mode 100644 index 000000000..c5d6e2505 --- /dev/null +++ b/src/client/views/nodes/FilterBox.scss @@ -0,0 +1,44 @@ + + + .collectionTimeView-treeView { + display: flex; + flex-direction: column; + width: 200px; + height: 100%; + position: absolute; + right: 0; + top: 0; + border-left: solid 1px; + z-index: 1; + + .collectionTimeView-addfacet { + display: inline-block; + width: 200px; + height: 30px; + background: darkGray; + text-align: left; + + .collectionTimeView-button { + align-items: center; + display: flex; + width: 100%; + height: 100%; + + .collectionTimeView-span { + margin: auto; + } + } + + >div, + >div>div { + width: 100%; + height: 100%; + } + } + + .collectionTimeView-tree { + display: inline-block; + width: 100%; + height: calc(100% - 30px); + } + } \ No newline at end of file diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx new file mode 100644 index 000000000..7bffc9184 --- /dev/null +++ b/src/client/views/nodes/FilterBox.tsx @@ -0,0 +1,197 @@ +import React = require("react"); +import { action, computed } from "mobx"; +import { observer } from "mobx-react"; +import { ColorState, SketchPicker } from 'react-color'; +import { Doc, Opt, DocListCast, Field, DataSym } from "../../../fields/Doc"; +import { Utils, returnEmptyFilter, returnEmptyDoclist, returnZero, emptyPath, returnFalse, emptyFunction, returnOne } from "../../../Utils"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { InkTool } from "../../../fields/InkField"; +import { makeInterface, listSpec } from "../../../fields/Schema"; +import { StrCast, Cast } from "../../../fields/Types"; +import { SelectionManager } from "../../util/SelectionManager"; +import { undoBatch } from "../../util/UndoManager"; +import { ViewBoxBaseComponent } from "../DocComponent"; +import "./ColorBox.scss"; +import { FieldView, FieldViewProps } from './FieldView'; +import { DocumentType } from "../../documents/DocumentTypes"; +import { CollectionTreeView } from "../collections/CollectionTreeView"; +import { ScriptField, ComputedField } from "../../../fields/ScriptField"; +import { Docs } from "../../documents/Documents"; +import { RichTextField } from "../../../fields/RichTextField"; +import { List } from "../../../fields/List"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import './FilterBox.scss'; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; + +type FilterBoxDocument = makeInterface<[typeof documentSchema]>; +const FilterBoxDocument = makeInterface(documentSchema); + +@observer +export class FilterBox extends ViewBoxBaseComponent(FilterBoxDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); } + + @computed get _allFacets() { + return ["author", "creationDate", "type", "text", "context"]; + // const noviceReqFields = ["author", "creationDate", "type", "text", "context"]; + // const noviceLayoutFields: string[] = [];//["_curPage"]; + // const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; + + // const facets = new Set([...noviceReqFields, ...noviceLayoutFields]); + // this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); + // Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key))); + + // return Array.from(facets).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort(); + } + /** + * Responds to clicking the check box in the flyout menu + */ + facetClick = (facetHeader: string) => { + const targetDoc = CollectionDockingView.Instance.props.Document; + const found = DocListCast(this.dataDoc[this.props.fieldKey]).findIndex(doc => doc.title === facetHeader); + if (found !== -1) { + (this.dataDoc[this.props.fieldKey] as List).splice(found, 1); + const docFilter = Cast(targetDoc._docFilters, listSpec("string")); + if (docFilter) { + let index: number; + while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { + docFilter.splice(index, 3); + } + } + const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string")); + if (docRangeFilters) { + let index: number; + while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { + docRangeFilters.splice(index, 3); + } + } + } else { + const allCollectionDocs = DocListCast((targetDoc.data as any)[0].data); + var rtfields = 0; + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => { + const field = child[facetHeader] as Field; + const fieldStr = Field.toString(field); + if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++; + return set.add(fieldStr); + }, new Set())); + + let nonNumbers = 0; + let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; + facetValues.map(val => { + const num = Number(val); + if (Number.isNaN(num)) { + nonNumbers++; + } else { + minVal = Math.min(num, minVal); + maxVal = Math.max(num, maxVal); + } + }); + let newFacet: Opt; + if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) { + newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + newFacet._textBoxPadding = 4; + const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`; + newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" }); + } else if (nonNumbers / facetValues.length < .1) { + newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true }); + const newFacetField = Doc.LayoutFieldKey(newFacet); + const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05); + const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05); + newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0]; + newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1]; + Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal; + Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal; + const scriptText = `setDocFilterRange(this?.target, "${facetHeader}", range)`; + newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); + } else { + newFacet = new Doc(); + newFacet.sytem = true; + newFacet.title = facetHeader; + newFacet.treeViewOpen = true; + newFacet.type = DocumentType.COL; + const capturedVariables = { layoutDoc: targetDoc, dataDoc: (targetDoc.data as any)[0][DataSym] }; + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables); + } + newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet); + } + } + filterBackground = () => "rgba(105, 105, 105, 0.432)"; + get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves) + @computed get scriptField() { + const scriptText = "setDocFilter(this?.target, heading, this.title, checked)"; + const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); + return script ? () => script : undefined; + } + + @computed get filterView() { + const facetCollection = this.props.Document.proto as Doc; + const flyout = ( +
e.stopPropagation()}> + {this._allFacets.map(facet => )} +
+ ); + + return this.props.dontRegisterView ? (null) :
+
e.stopPropagation()}> + +
+ + Facet Filters +
+
+
+
+ +
+
; + } + render() { + return this.filterView; + } +} \ No newline at end of file diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 6c5278892..3907d2d5c 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -45,7 +45,7 @@ export class SearchBox extends ViewBoxBaseComponent = undefined; private _disposers: { [name: string]: IReactionDisposer } = {}; - private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; + private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; private docsforfilter: Doc[] | undefined = []; private realTotalResults: number = 0; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bc31c1a21..08d949b5e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,6 +23,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { LinkManager } from "../client/util/LinkManager"; import JSZip = require("jszip"); import { saveAs } from "file-saver"; +import { CollectionDockingView } from "../client/views/collections/CollectionDockingView"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -1048,7 +1049,8 @@ export namespace Doc { doc.layoutKey = deiconify || "layout"; } export function setDocFilterRange(target: Doc, key: string, range?: number[]) { - const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []); + const container = target ?? CollectionDockingView.Instance.props.Document; + const docRangeFilters = Cast(container._docRangeFilters, listSpec("string"), []); for (let i = 0; i < docRangeFilters.length; i += 3) { if (docRangeFilters[i] === key) { docRangeFilters.splice(i, 3); @@ -1059,14 +1061,15 @@ export namespace Doc { docRangeFilters.push(key); docRangeFilters.push(range[0].toString()); docRangeFilters.push(range[1].toString()); - target._docRangeFilters = new List(docRangeFilters); + container._docRangeFilters = new List(docRangeFilters); } } // filters document in a container collection: // all documents with the specified value for the specified key are included/excluded // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) { + export function setDocFilter(target: Opt, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) { + const container = target ?? CollectionDockingView.Instance.props.Document; const docFilters = Cast(container._docFilters, listSpec("string"), []); runInAction(() => { for (let i = 0; i < docFilters.length; i += 3) { -- cgit v1.2.3-70-g09d2 From ac54c0c466c796c462af399016389a79bc6a4e92 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 3 Sep 2020 00:29:45 -0400 Subject: doc decorations removes menu and iconify icon when too small now. --- src/client/views/DocumentDecorations.tsx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7781d6069..81c1676b0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -598,16 +598,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return collectionAcl === AclAdmin || collectionAcl === AclEdit; }); const minimal = bounds.r - bounds.x < 100 ? true : false; - const maximizeIcon = minimal ? ( - Show context menu
} placement="top"> -
- -
) : canDelete ? ( - Close
} placement="top"> -
- {/* Currently, this is set to be enabled if there is no ink selected. It might be interesting to think about minimizing ink if it's useful? -syip2*/} - -
) : (null); + const closeIcon = canDelete ? ( + Close
} placement="top"> +
+ +
) : (null); const titleArea = this._edtingTitle ? {minimal ? (null) : Show context menu
} placement="top">
} -
+
{`${this.selectionTitle}`}
; @@ -651,9 +646,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> - {maximizeIcon} + {closeIcon} {titleArea} - {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : + {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK || minimal ? (null) : {`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`}
} placement="top">
-- cgit v1.2.3-70-g09d2