From e2c8ca0bf9e9f1c27d333b0b51ae3dd9d6cba05f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 5 May 2020 13:33:26 -0400 Subject: fixed some interactions with WebDocuments --- src/client/views/DocumentButtonBar.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/client/views/DocumentButtonBar.tsx') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 3624cdb6d..7ba47deb5 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -22,6 +22,7 @@ import React = require("react"); import { DragManager } from '../util/DragManager'; import { MetadataEntryMenu } from './MetadataEntryMenu'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; +import { Docs } from '../documents/Documents'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -167,10 +168,14 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) :
e.altKey && runInAction(() => this.openHover = true)} + onPointerEnter={e => (e.altKey || e.shiftKey) && runInAction(() => this.openHover = true)} onPointerLeave={action(() => this.openHover = false)} onClick={e => { - if (e.altKey) { + if (e.shiftKey) { + e.preventDefault(); + CollectionDockingView.AddRightSplit(Docs.Create.WebDocument(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`, + { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false })); + } else if (e.altKey) { e.preventDefault(); window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); } else { -- cgit v1.2.3-70-g09d2 From 269ea1e31959cd279cea91276fb80e9d19116878 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 5 May 2020 13:46:54 -0700 Subject: slight ux improvements for google docs utility button --- src/client/views/DocumentButtonBar.tsx | 46 +++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'src/client/views/DocumentButtonBar.tsx') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 7ba47deb5..8fc4cd4d4 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -1,5 +1,5 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faArrowAltCircleDown, faPhotoVideo, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { faArrowAltCircleDown, faPhotoVideo, faArrowAltCircleUp, faArrowAltCircleRight, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; @@ -32,6 +32,7 @@ library.add(faTag); library.add(faTimes); library.add(faArrowAltCircleDown); library.add(faArrowAltCircleUp); +library.add(faArrowAltCircleRight); library.add(faStopCircle); library.add(faCheckCircle); library.add(faCloudUploadAlt); @@ -42,6 +43,12 @@ library.add(faPhotoVideo); const cloud: IconProp = "cloud-upload-alt"; const fetch: IconProp = "sync-alt"; +enum UtilityButtonState { + Default, + OpenRight, + OpenExternally +} + @observer export class DocumentButtonBar extends React.Component<{ views: (DocumentView | undefined)[], stack?: any }, {}> { private _linkButton = React.createRef(); @@ -58,7 +65,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | @observable public isAnimatingFetch = false; @observable public isAnimatingPulse = false; - @observable private openHover = false; + @observable private openHover: UtilityButtonState = UtilityButtonState.Default; @observable public static Instance: DocumentButtonBar; public static hasPushedHack = false; @@ -166,18 +173,32 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | const dataDoc = targetDoc && Doc.GetProto(targetDoc); const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none"; return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) :
{ + switch (this.openHover) { + default: + case UtilityButtonState.Default: return `${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`; + case UtilityButtonState.OpenRight: return "Open in Right Split"; + case UtilityButtonState.OpenExternally: return "Open in new Browser Tab"; + } + })()} style={{ backgroundColor: this.pullColor }} - onPointerEnter={e => (e.altKey || e.shiftKey) && runInAction(() => this.openHover = true)} - onPointerLeave={action(() => this.openHover = false)} + onPointerEnter={action(e => { + if (e.altKey) { + this.openHover = UtilityButtonState.OpenExternally; + } else if (e.shiftKey) { + this.openHover = UtilityButtonState.OpenRight; + } + })} + onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)} onClick={e => { + const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`; if (e.shiftKey) { e.preventDefault(); - CollectionDockingView.AddRightSplit(Docs.Create.WebDocument(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`, - { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false })); + const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false }; + CollectionDockingView.AddRightSplit(Docs.Create.WebDocument(googleDocUrl, options)); } else if (e.altKey) { e.preventDefault(); - window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); + window.open(googleDocUrl); } else { this.clearPullColor(); DocumentButtonBar.hasPulledHack = false; @@ -187,7 +208,14 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | }}> { + switch (this.openHover) { + default: + case UtilityButtonState.Default: return dataDoc.unchanged === false ? (this.pullIcon as any) : fetch; + case UtilityButtonState.OpenRight: return "arrow-alt-circle-right"; + case UtilityButtonState.OpenExternally: return "share"; + } + })()} />
; } -- cgit v1.2.3-70-g09d2 From a67dc240024ccb329f2509c70556a7b78ca2c4ab Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 5 May 2020 20:22:49 -0400 Subject: fixed text selection in docHolder boxes by changing formattedTextBox not to stopPropagation on pointerDowns. added a googleDoc field on documents to open the same Dash-googleDoc doc each time. --- src/client/views/DocumentButtonBar.tsx | 13 +- src/client/views/nodes/DocHolderBox.scss | 1 + src/client/views/nodes/DocHolderBox.tsx | 145 +++++++++++---------- src/client/views/nodes/DocumentView.tsx | 2 + src/client/views/nodes/FieldView.tsx | 1 + .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- 6 files changed, 89 insertions(+), 77 deletions(-) (limited to 'src/client/views/DocumentButtonBar.tsx') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 7ba47deb5..db566ee48 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -5,7 +5,7 @@ import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { RichTextField } from '../../new_fields/RichTextField'; -import { NumCast, StrCast } from "../../new_fields/Types"; +import { NumCast, StrCast, Cast } from "../../new_fields/Types"; import { emptyFunction, setupMoveUpEvents } from "../../Utils"; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; import { UndoManager } from "../util/UndoManager"; @@ -170,11 +170,16 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | style={{ backgroundColor: this.pullColor }} onPointerEnter={e => (e.altKey || e.shiftKey) && runInAction(() => this.openHover = true)} onPointerLeave={action(() => this.openHover = false)} - onClick={e => { + onClick={async (e: React.MouseEvent) => { if (e.shiftKey) { e.preventDefault(); - CollectionDockingView.AddRightSplit(Docs.Create.WebDocument(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`, - { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false })); + let googleDoc = await Cast(dataDoc.googleDoc, Doc); + if (!googleDoc) { + googleDoc = Docs.Create.WebDocument(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`, + { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false }); + dataDoc.googleDoc = googleDoc; + } + CollectionDockingView.AddRightSplit(googleDoc); } else if (e.altKey) { e.preventDefault(); window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); diff --git a/src/client/views/nodes/DocHolderBox.scss b/src/client/views/nodes/DocHolderBox.scss index 3a27c16c1..4949d16a3 100644 --- a/src/client/views/nodes/DocHolderBox.scss +++ b/src/client/views/nodes/DocHolderBox.scss @@ -8,6 +8,7 @@ margin: auto; color: white; position: absolute; + padding: 3px; } .contentFittingDocumentView { position: absolute; diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx index 9787877cc..17b2daabc 100644 --- a/src/client/views/nodes/DocHolderBox.tsx +++ b/src/client/views/nodes/DocHolderBox.tsx @@ -3,7 +3,7 @@ import { action, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, Field } from "../../../new_fields/Doc"; import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface } from "../../../new_fields/Schema"; +import { makeInterface, listSpec } from "../../../new_fields/Schema"; import { ComputedField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { TraceMobx } from "../../../new_fields/util"; @@ -32,7 +32,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent(); _curSelection = -1; componentDidMount() { - this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => { + this._prevSelectionDisposer = reaction(() => this.dataDoc[this.fieldKey], (data) => { if (data instanceof Doc && !this.isSelectionLocked()) { this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1); this._selections.push(data); @@ -53,13 +53,13 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { - this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey]; + this.dataDoc[this.fieldKey] = this.dataDoc[this.fieldKey]; } showSelection = () => { - this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); + this.dataDoc[this.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); } isSelectionLocked = () => { - const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey); + const kvpstring = Field.toKeyValueString(this.dataDoc, this.fieldKey); return !kvpstring || kvpstring.includes("DOC"); } toggleLockSelection = () => { @@ -69,13 +69,13 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { this.lockSelection(); if (this._curSelection > 0) { - this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection]; + this.dataDoc[this.fieldKey] = this._selections[--this._curSelection]; return true; } } nextSelection = () => { if (this._curSelection < this._selections.length - 1 && this._selections.length) { - this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection]; + this.dataDoc[this.fieldKey] = this._selections[++this._curSelection]; return true; } } @@ -89,8 +89,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { let hitWidget: boolean | undefined = false; if (this._contRef.current!.getBoundingClientRect().top + this.yPad > e.clientY) hitWidget = (() => { this.props.select(false); return true; })(); @@ -107,67 +107,70 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent this.props.PanelWidth() - 2 * this.xPad; pheight = () => this.props.PanelHeight() - 2 * this.yPad; getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad); - isActive = () => this.active() || !this.props.renderDepth; - layoutTemplateDoc = () => Cast(this.props.Document.childLayoutTemplate, Doc, null); + isActive = (outsideReaction: boolean) => this.active(outsideReaction) || this.props.renderDepth <= 1; + layoutTemplateDoc = () => Cast(this.layoutDoc.childLayoutTemplate, Doc, null); get renderContents() { - const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null); + const containedDoc = Cast(this.dataDoc[this.fieldKey], Doc, null); const layoutTemplate = StrCast(this.layoutDoc.childLayoutString); - const contents = !(containedDoc instanceof Doc) ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ? - : - ; + const contents = !(containedDoc instanceof Doc) || + Cast(containedDoc[Doc.LayoutFieldKey(containedDoc)], listSpec(Doc), null)?.includes(this.rootDoc) + ? (null) : this.layoutDoc.childLayoutString || this.layoutTemplateDoc() ? + : + ; return contents; } render() { @@ -183,7 +186,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent {this.renderContents}
+ style={{ marginTop: - this.yPad, background: "black" }}>
; @@ -195,13 +198,13 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { this._dropDisposer?.(); - ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document)); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.rootDoc)); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5a2ad8c88..2d5d7d231 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -63,6 +63,7 @@ export interface DocumentViewProps { LayoutTemplate?: () => Opt; LibraryPath: Doc[]; fitToBox?: boolean; + ignoreAutoHeight?: boolean; contextMenuItems?: () => { script: ScriptField, label: string }[]; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected onClick?: ScriptField; @@ -1009,6 +1010,7 @@ export class DocumentView extends DocComponent(Docu renderDepth={this.props.renderDepth} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight} + ignoreAutoHeight={this.props.ignoreAutoHeight} focus={this.props.focus} parentActive={this.props.parentActive} whenActiveChanged={this.props.whenActiveChanged} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 016d2a1ae..7db634e74 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -43,6 +43,7 @@ export interface FieldViewProps { whenActiveChanged: (isActive: boolean) => void; dontRegisterView?: boolean; focus: (doc: Doc) => void; + ignoreAutoHeight?: boolean; PanelWidth: () => number; PanelHeight: () => number; NativeHeight: () => number; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ce096f81b..5dc22d6f6 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -985,7 +985,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } if (e.button === 0 && this.active(true) && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // don't stop propagation if clicking in the sidebar - e.stopPropagation(); + //e.stopPropagation(); } } if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { @@ -1217,7 +1217,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @action tryUpdateHeight(limitHeight?: number) { let scrollHeight = this._ref.current?.scrollHeight; - if (this.layoutDoc._autoHeight && scrollHeight && + if (this.layoutDoc._autoHeight && !this.props.ignoreAutoHeight && scrollHeight && getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation if (limitHeight && scrollHeight > limitHeight) { scrollHeight = limitHeight; -- cgit v1.2.3-70-g09d2 From 51a1f88b4e975946eb5ac8cef75a122e197cd872 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 7 May 2020 01:35:11 -0400 Subject: added batch requesting of list items. fixed some performance issues with tree views. --- src/client/DocServer.ts | 64 +++++++++++------- src/client/util/Scripting.ts | 2 +- src/client/views/DocumentButtonBar.tsx | 32 ++++----- src/client/views/DocumentDecorations.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 8 +-- .../views/collections/CollectionStackingView.tsx | 10 ++- .../views/collections/CollectionTreeView.scss | 1 - .../views/collections/CollectionTreeView.tsx | 18 +++-- .../views/collections/ParentDocumentSelector.tsx | 4 +- src/new_fields/List.ts | 76 +++++++++++++++------- src/new_fields/Proxy.ts | 18 ++++- .../authentication/models/current_user_utils.ts | 13 ++-- src/server/database.ts | 2 +- 13 files changed, 161 insertions(+), 89 deletions(-) (limited to 'src/client/views/DocumentButtonBar.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index 0c9d5f75c..bf9fd232c 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -7,6 +7,7 @@ import { RefField } from '../new_fields/RefField'; import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; import GestureOverlay from './views/GestureOverlay'; import MobileInkOverlay from '../mobile/MobileInkOverlay'; +import { runInAction } from 'mobx'; /** * This class encapsulates the transfer and cross-client synchronization of @@ -109,6 +110,7 @@ export namespace DocServer { GUID = identifier; _socket = OpenSocket(`${protocol}//${hostname}:${port}`); + _GetCachedRefField = _GetCachedRefFieldImpl; _GetRefField = _GetRefFieldImpl; _GetRefFields = _GetRefFieldsImpl; _CreateField = _CreateFieldImpl; @@ -243,12 +245,22 @@ export namespace DocServer { return Promise.resolve(cached); } }; + const _GetCachedRefFieldImpl = (id: string): Opt => { + const cached = _cache[id]; + if (cached !== undefined && !(cached instanceof Promise)) { + return cached; + } + } let _GetRefField: (id: string) => Promise> = errorFunc; + let _GetCachedRefField: (id: string) => Opt = errorFunc; export function GetRefField(id: string): Promise> { return _GetRefField(id); } + export function GetCachedRefField(id: string): Opt { + return _GetCachedRefField(id); + } export async function getYoutubeChannels() { const apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels }); @@ -308,32 +320,34 @@ export namespace DocServer { const deserializeFields = getSerializedFields.then(async fields => { const fieldMap: { [id: string]: RefField } = {}; const proms: Promise[] = []; - for (const field of fields) { - if (field !== undefined && field !== null) { - // deserialize - const prom = SerializationHelper.Deserialize(field).then(deserialized => { - fieldMap[field.id] = deserialized; - - //overwrite or delete any promises (that we inserted as flags - // to indicate that the field was in the process of being fetched). Now everything - // should be an actual value within or entirely absent from the cache. - if (deserialized !== undefined) { - _cache[field.id] = deserialized; - } else { - delete _cache[field.id]; - } - return deserialized; - }); - // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) - // we set the value at the field's id to a promise that will resolve to the field. - // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). - // The mapping in the .then call ensures that when other callers await these promises, they'll - // get the resolved field - _cache[field.id] = prom; - // adds to a list of promises that will be awaited asynchronously - proms.push(prom); + runInAction(() => { + for (const field of fields) { + if (field !== undefined && field !== null) { + // deserialize + const prom = SerializationHelper.Deserialize(field).then(deserialized => { + fieldMap[field.id] = deserialized; + + //overwrite or delete any promises (that we inserted as flags + // to indicate that the field was in the process of being fetched). Now everything + // should be an actual value within or entirely absent from the cache. + if (deserialized !== undefined) { + _cache[field.id] = deserialized; + } else { + delete _cache[field.id]; + } + return deserialized; + }); + // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) + // we set the value at the field's id to a promise that will resolve to the field. + // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). + // The mapping in the .then call ensures that when other callers await these promises, they'll + // get the resolved field + _cache[field.id] = prom; + // adds to a list of promises that will be awaited asynchronously + proms.push(prom); + } } - } + }); await Promise.all(proms); return fieldMap; }); diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 12628273b..8b7b9c9c7 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -136,7 +136,7 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an if (batch) { batch.end(); } - onError && onError(error); + onError?.(error); return { success: false, error, result: errorVal }; } }; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index cf57094b2..d58eb5875 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -50,11 +50,9 @@ enum UtilityButtonState { } @observer -export class DocumentButtonBar extends React.Component<{ views: (DocumentView | undefined)[], stack?: any }, {}> { +export class DocumentButtonBar extends React.Component<{ views: () => (DocumentView | undefined)[], stack?: any }, {}> { private _linkButton = React.createRef(); private _dragRef = React.createRef(); - private _downX = 0; - private _downY = 0; private _pullAnimating = false; private _pushAnimating = false; private _pullColorAnimating = false; @@ -71,7 +69,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | public static hasPushedHack = false; public static hasPulledHack = false; - constructor(props: { views: (DocumentView | undefined)[] }) { + constructor(props: { views: () => (DocumentView | undefined)[] }) { super(props); runInAction(() => DocumentButtonBar.Instance = this); } @@ -113,7 +111,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | this._pullColorAnimating = false; }); - get view0() { return this.props.views?.[0]; } + get view0() { return this.props.views()?.[0]; } @action onLinkButtonMoved = (e: PointerEvent) => { @@ -265,7 +263,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | const view0 = this.view0; return !view0 ? (null) :
this.props.views.filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction /> /* tfs: @bcz This might need to be the data document? */}> + content={ this.props.views().filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction /> /* tfs: @bcz This might need to be the data document? */}>
e.stopPropagation()} > {}
@@ -289,7 +287,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | } onAliasButtonMoved = () => { if (this._dragRef.current) { - const dragDocView = this.props.views[0]!; + const dragDocView = this.view0!; const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]); const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); dragData.embedDoc = true; @@ -308,16 +306,18 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | get templateButton() { const view0 = this.view0; const templates: Map = new Map(); + const views = this.props.views(); Array.from(Object.values(Templates.TemplateList)).map(template => - templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean))); - return !view0 ? (null) :
- this._aliasDown = true)} onClose={action(() => this._aliasDown = false)} - content={!this._aliasDown ? (null) : v).map(v => v as DocumentView)} templates={templates} />}> -
- {} -
-
-
; + templates.set(template, views.reduce((checked, doc) => checked || doc?.props.Document["_show" + template.Name] ? true : false, false as boolean))); + return !view0 ? (null) : +
+ this._aliasDown = true)} onClose={action(() => this._aliasDown = false)} + content={!this._aliasDown ? (null) : v).map(v => v as DocumentView)} templates={templates} />}> +
+ {} +
+
+
; } render() { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b89806656..9b6105811 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -499,7 +499,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
- +
); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6cd5d1b1b..6f5989052 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -517,14 +517,14 @@ export class CollectionDockingView extends React.Component ((view: Opt) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)), (views) => { - !rendered && ReactDOM.render( - + ReactDOM.render( + views} Stack={stack} /> , gearSpan); - rendered = true; + tab.buttonDisposer?.(); }); tab.reactComponents = [gearSpan]; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4cc2e1a5f..c199988c0 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -57,6 +57,14 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) } @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; } + constructor(props: any) { + super(props); + + if (this.sectionHeaders === undefined) { + this.props.Document.sectionHeaders = new List(); + } + } + children(docs: Doc[], columns?: number) { TraceMobx(); this._docXfs.length = 0; @@ -85,7 +93,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) setTimeout(() => this.props.Document.sectionHeaders = new List(), 0); return new Map(); } - const sectionHeaders: SchemaHeaderField[] = Array.from(this.sectionHeaders); + const sectionHeaders = Array.from(this.sectionHeaders); const fields = new Map(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); let changed = false; this.filteredChildren.map(d => { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index a00bb6bfb..2aac81146 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -81,7 +81,6 @@ position: relative; text-overflow: ellipsis; white-space: pre-wrap; - overflow: hidden; min-width: 10px; // width:100%;//width: max-content; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 297c11e35..4fb54cc07 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -34,6 +34,7 @@ import "./CollectionTreeView.scss"; import { CollectionViewType } from './CollectionView'; import React = require("react"); import { makeTemplate } from '../../util/DropConverter'; +import { TraceMobx } from '../../../new_fields/util'; export interface TreeViewProps { @@ -100,7 +101,7 @@ class TreeView extends React.Component { get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, "fields"); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; } - @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; } + @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.props.document.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; } @computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.templateDataDoc ? this.templateDataDoc : this.props.document; } @@ -326,6 +327,7 @@ class TreeView extends React.Component { rtfHeight = () => this.rtfWidth() < Doc.Layout(this.props.document)?.[WidthSym]() ? Math.min(Doc.Layout(this.props.document)?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; @computed get renderContent() { + TraceMobx(); const expandKey = this.treeViewExpandedView === this.fieldKey ? this.fieldKey : this.treeViewExpandedView === "links" ? "links" : undefined; if (expandKey !== undefined) { const remDoc = (doc: Doc) => this.remove(doc, expandKey); @@ -419,13 +421,15 @@ class TreeView extends React.Component { return [{ script: focusScript!, label: "Focus" }]; } _docRef = React.createRef(); + _editTitleScript: ScriptField | undefined; /** * Renders the EditableView title element for placement into the tree. */ @computed get renderTitle() { + TraceMobx(); const onItemDown = SetupDrag(this._tref, () => this.dataDoc, this.move, this.props.dropAction, this.props.treeViewId[Id], true); - const editTitle = ScriptField.MakeFunction("setInPlace(this, 'editTitle', true)"); + //!this._editTitleScript && (this._editTitleScript = ScriptField.MakeFunction("setInPlace(this, 'editTitle', true)")); const headerElements = ( <> @@ -449,7 +453,6 @@ class TreeView extends React.Component { return <>
{ ref={this._docRef} Document={this.props.document} DataDoc={undefined} - LibraryPath={this.props.libraryPath || []} + LibraryPath={this.props.libraryPath || emptyPath} addDocument={undefined} addDocTab={this.props.addDocTab} rootSelected={returnTrue} pinToPres={emptyFunction} - onClick={this.props.onChildClick || editTitle} + onClick={this.props.onChildClick || this._editTitleScript} dropAction={this.props.dropAction} moveDocument={this.move} removeDocument={this.removeDoc} @@ -478,7 +481,7 @@ class TreeView extends React.Component { NativeWidth={returnZero} contextMenuItems={this.contextMenuItems} renderDepth={1} - focus={emptyFunction} + focus={returnTrue} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} @@ -493,8 +496,9 @@ class TreeView extends React.Component { } render() { + TraceMobx(); const sorting = this.props.document[`${this.fieldKey}-sortAscending`]; - setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0); + //setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0); return
  • { diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 10c6ead1a..6e4f801c0 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -94,7 +94,7 @@ export class ParentDocSelector extends React.Component { } @observer -export class DockingViewButtonSelector extends React.Component<{ views: DocumentView[], Stack: any }> { +export class DockingViewButtonSelector extends React.Component<{ views: () => DocumentView[], Stack: any }> { customStylesheet(styles: any) { return { ...styles, @@ -120,7 +120,7 @@ export class DockingViewButtonSelector extends React.Component<{ views: Document if (getComputedStyle(this._ref.current!).width !== "100%") { e.stopPropagation(); e.preventDefault(); } - this.props.views[0]?.select(false); + this.props.views()[0]?.select(false); }} className="buttonSelector"> diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts index a43f11e82..fdabea365 100644 --- a/src/new_fields/List.ts +++ b/src/new_fields/List.ts @@ -2,12 +2,13 @@ import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/ import { Field } from "./Doc"; import { setter, getter, deleteProperty, updateFunction } from "./util"; import { serializable, alias, list } from "serializr"; -import { observable, action } from "mobx"; +import { observable, action, runInAction } from "mobx"; import { ObjectField } from "./ObjectField"; import { RefField } from "./RefField"; import { ProxyField } from "./Proxy"; -import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy } from "./FieldSymbols"; +import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols"; import { Scripting } from "../client/util/Scripting"; +import { DocServer } from "../client/DocServer"; const listHandlers: any = { /// Mutator methods @@ -54,11 +55,13 @@ const listHandlers: any = { return res; }, sort(cmpFunc: any) { + this[Self].__realFields(); // coerce retrieving entire array const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined); this[Update](); return res; }, splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) { + this[Self].__realFields(); // coerce retrieving entire array items = items.map(toObjectField); const list = this[Self]; for (let i = 0; i < items.length; i++) { @@ -94,102 +97,102 @@ const listHandlers: any = { }, /// Accessor methods concat: action(function (this: any, ...items: any[]) { + this[Self].__realFields(); return this[Self].__fields.map(toRealField).concat(...items); }), includes(valueToFind: any, fromIndex: number) { - const fields = this[Self].__fields; if (valueToFind instanceof RefField) { - return fields.map(toRealField).includes(valueToFind, fromIndex); + return this[Self].__realFields().includes(valueToFind, fromIndex); } else { - return fields.includes(valueToFind, fromIndex); + return this[Self].__fields.includes(valueToFind, fromIndex); } }, indexOf(valueToFind: any, fromIndex: number) { - const fields = this[Self].__fields; if (valueToFind instanceof RefField) { - return fields.map(toRealField).indexOf(valueToFind, fromIndex); + return this[Self].__realFields().indexOf(valueToFind, fromIndex); } else { - return fields.indexOf(valueToFind, fromIndex); + return this[Self].__fields.indexOf(valueToFind, fromIndex); } }, join(separator: any) { + this[Self].__realFields(); return this[Self].__fields.map(toRealField).join(separator); }, lastIndexOf(valueToFind: any, fromIndex: number) { - const fields = this[Self].__fields; if (valueToFind instanceof RefField) { - return fields.map(toRealField).lastIndexOf(valueToFind, fromIndex); + return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex); } else { - return fields.lastIndexOf(valueToFind, fromIndex); + return this[Self].__fields.lastIndexOf(valueToFind, fromIndex); } }, slice(begin: number, end: number) { + this[Self].__realFields(); return this[Self].__fields.slice(begin, end).map(toRealField); }, /// Iteration methods entries() { - return this[Self].__fields.map(toRealField).entries(); + return this[Self].__realFields().entries(); }, every(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).every(callback, thisArg); + return this[Self].__realFields().every(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, filter(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).filter(callback, thisArg); + return this[Self].__realFields().filter(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, find(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).find(callback, thisArg); + return this[Self].__realFields().find(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, findIndex(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).findIndex(callback, thisArg); + return this[Self].__realFields().findIndex(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, forEach(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).forEach(callback, thisArg); + return this[Self].__realFields().forEach(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, map(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).map(callback, thisArg); + return this[Self].__realFields().map(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, reduce(callback: any, initialValue: any) { - return this[Self].__fields.map(toRealField).reduce(callback, initialValue); + return this[Self].__realFields().reduce(callback, initialValue); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); }, reduceRight(callback: any, initialValue: any) { - return this[Self].__fields.map(toRealField).reduceRight(callback, initialValue); + return this[Self].__realFields().reduceRight(callback, initialValue); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); }, some(callback: any, thisArg: any) { - return this[Self].__fields.map(toRealField).some(callback, thisArg); + return this[Self].__realFields().some(callback, thisArg); // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. // If we don't want to support the array parameter, we should use this version instead // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); }, values() { - return this[Self].__fields.map(toRealField).values(); + return this[Self].__realFields().values(); }, [Symbol.iterator]() { - return this[Self].__fields.map(toRealField).values(); + return this[Self].__realFields().values(); } }; @@ -254,6 +257,31 @@ class ListImpl extends ObjectField { [key: number]: T | (T extends RefField ? Promise : never); + // this requests all ProxyFields at the same time to avoid the overhead + // of separate network requests and separate updates to the React dom. + private __realFields() { + const waiting = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue()); + const promised = waiting.map(f => f instanceof ProxyField ? f.promisedValue() : ""); + // if we find any ProxyFields that don't have a current value, then + // start the server request for all of them + if (promised.length) { + const promise = DocServer.GetRefFields(promised); + // as soon as we get the fields from the server, set all the list values in one + // action to generate one React dom update. + promise.then(fields => runInAction(() => { + waiting.map((w, i) => w instanceof ProxyField && w.setValue(fields[promised[i]])); + })); + // we also have to mark all lists items with this promise so that any calls to them + // will await the batch request. + // This counts on the handler for 'promise' in the call above being invoked before the + // handler for 'promise' in the lines below. + waiting.map((w, i) => { + w instanceof ProxyField && w.setPromise(promise.then(fields => fields[promised[i]])); + }); + } + return this.__fields.map(toRealField); + } + @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize }))) private get __fields() { return this.___fields; @@ -283,7 +311,7 @@ class ListImpl extends ObjectField { // console.log(diff); const update = this[OnUpdate]; // update && update(diff); - update && update(); + update?.(); } private [Self] = this; diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts index d50c0f14e..555faaad0 100644 --- a/src/new_fields/Proxy.ts +++ b/src/new_fields/Proxy.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { FieldWaiting } from "./Doc"; import { primitive, serializable } from "serializr"; -import { observable, action } from "mobx"; +import { observable, action, runInAction } from "mobx"; import { DocServer } from "../client/DocServer"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; @@ -61,6 +61,11 @@ export class ProxyField extends ObjectField { return undefined; } if (!this.promise) { + const cached = DocServer.GetCachedRefField(this.fieldId); + if (cached !== undefined) { + runInAction(() => this.cache = cached as any); + return cached as any; + } this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => { this.promise = undefined; this.cache = field; @@ -70,6 +75,17 @@ export class ProxyField extends ObjectField { } return this.promise as any; } + promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; } + setPromise(promise: any) { + this.promise = promise; + } + @action + setValue(field: any) { + this.promise = undefined; + this.cache = field; + if (field === undefined) this.failed = true; + return field; + } } export namespace ProxyField { diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index e7b448358..6e06fe931 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -21,8 +21,7 @@ import { FormattedTextBox } from "../../../client/views/nodes/formattedText/Form import { MainView } from "../../../client/views/MainView"; import { DocumentType } from "../../../client/documents/DocumentTypes"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { faBoxOpen } from "@fortawesome/free-solid-svg-icons"; -import { CollectionMulticolumnView, DimUnit } from "../../../client/views/collections/collectionMulticolumn/CollectionMulticolumnView"; +import { DimUnit } from "../../../client/views/collections/collectionMulticolumn/CollectionMulticolumnView"; export class CurrentUserUtils { private static curr_id: string; @@ -282,8 +281,12 @@ export class CurrentUserUtils { doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc], { title: "icon templates", _height: 75 })); } else { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); - DocListCastAsync(templateIconsDoc).then(list => templateIconsDoc.data = new List([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc])); + const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; + DocListCastAsync(templateIconsDoc.data).then(async curIcons => { + await Promise.all(curIcons!); + requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); + }); } return doc["template-icons"] as Doc; } @@ -501,7 +504,7 @@ export class CurrentUserUtils { static setupDocumentCollection(doc: Doc) { if (doc.myDocuments === undefined) { doc.myDocuments = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: true, lockedPosition: true, + title: "DOCUMENTS", _height: 42, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, lockedPosition: true, })); } return doc.myDocuments as Doc; diff --git a/src/server/database.ts b/src/server/database.ts index cfd707825..580f7f919 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -291,7 +291,7 @@ export namespace Database { } } - export const Instance: IDatabase = getDatabase(); + export const Instance = getDatabase(); export namespace Auxiliary { -- cgit v1.2.3-70-g09d2 From c1f49a0974982b803c565b6ac3c8931be7c8972f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 8 May 2020 16:57:30 -0400 Subject: added " " for switching between pan/zoom & marquee. --- src/client/views/DocumentButtonBar.tsx | 11 +---------- src/client/views/GlobalKeyHandler.ts | 5 +++++ src/client/views/PreviewCursor.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 23 ++++++++++++---------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 14 ++++++------- 6 files changed, 27 insertions(+), 30 deletions(-) (limited to 'src/client/views/DocumentButtonBar.tsx') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index d58eb5875..48685302a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -229,16 +229,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV return !targetDoc ? (null) :
    { - if (isPinned) { - DockedFrameRenderer.UnpinDoc(targetDoc); - } - else { - targetDoc.sourceContext = this.view0?.props.ContainingCollectionDoc; // bcz: !! Shouldn't need this ... use search to lookup contexts dynamically - DockedFrameRenderer.PinDoc(targetDoc); - } - }}> + onClick={e => DockedFrameRenderer.PinDoc(targetDoc, isPinned)}>
    ; diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 6cca4d69f..e6dd014c0 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -13,6 +13,8 @@ import { InkingControl } from "./InkingControl"; import { InkTool } from "../../new_fields/InkField"; import { DocumentView } from "./nodes/DocumentView"; import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager"; +import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView"; +import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; @@ -65,6 +67,9 @@ export default class KeyManager { private unmodified = action((keyname: string, e: KeyboardEvent) => { switch (keyname) { + case " ": + MarqueeView.DragState = !MarqueeView.DragState; + break; case "escape": const main = MainView.Instance; InkingControl.Instance.switchTool(InkTool.None); diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index df30c1215..80c7f3d25 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -81,7 +81,7 @@ export class PreviewCursor extends React.Component<{}> { if (e.key !== "Escape" && e.key !== "Backspace" && e.key !== "Delete" && e.key !== "CapsLock" && e.key !== "Alt" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Control" && e.key !== "Insert" && e.key !== "Home" && e.key !== "End" && e.key !== "PageUp" && e.key !== "PageDown" && - e.key !== "NumLock" && + e.key !== "NumLock" && e.key !== " " && (e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys !e.key.startsWith("Arrow") && !e.defaultPrevented) { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 3c33d4481..c1edc0fe7 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -683,16 +683,19 @@ export class DockedFrameRenderer extends React.Component { **/ @undoBatch @action - public static PinDoc(doc: Doc) { - //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; - if (curPres) { - const pinDoc = Doc.MakeAlias(doc); - pinDoc.presentationTargetDoc = doc; - pinDoc.presZoomButton = true; - Doc.AddDocToList(curPres, "data", pinDoc); - if (!DocumentManager.Instance.getDocumentView(curPres)) { - CollectionDockingView.AddRightSplit(curPres); + public static PinDoc(doc: Doc, unpin = false) { + if (unpin) DockedFrameRenderer.UnpinDoc(doc); + else { + //add this new doc to props.Document + const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + if (curPres) { + const pinDoc = Doc.MakeAlias(doc); + pinDoc.presentationTargetDoc = doc; + pinDoc.presZoomButton = true; + Doc.AddDocToList(curPres, "data", pinDoc); + if (!DocumentManager.Instance.getDocumentView(curPres)) { + CollectionDockingView.AddRightSplit(curPres); + } } } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 17c1dd44a..5d3d8eb4f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -563,7 +563,7 @@ export class CollectionFreeFormView extends CollectionSubView Transform; @@ -39,7 +37,7 @@ interface MarqueeViewProps { @observer export class MarqueeView extends React.Component { - private _mainCont = React.createRef(); + @observable public static DragState = true; @observable _lastX: number = 0; @observable _lastY: number = 0; @observable _downX: number = 0; @@ -168,9 +166,9 @@ export class MarqueeView extends React.Component { this._downX = this._lastX = e.clientX; this._downY = this._lastY = e.clientY; - if (e.button === 2 || (e.button === 0 && e.altKey)) { + if (e.button === 2 || (e.button === 0 && (e.altKey || !MarqueeView.DragState))) { this.setPreviewCursor(e.clientX, e.clientY, true); - if (e.altKey) { + if (e.altKey || !MarqueeView.DragState) { //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors. e.preventDefault(); } @@ -195,7 +193,7 @@ export class MarqueeView extends React.Component e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> {this._visible ? this.marqueeDiv : null} {this.props.children} -- cgit v1.2.3-70-g09d2 From a3839d4b5a2180e2e80fa79f6eb4459e2976f380 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 11 May 2020 14:59:54 -0400 Subject: changed Cors behavior for WebBox(On for everything except GoogldDocs). Update Freeze to work for ctrl-resizing text, and showing full screen. reorged context menus. --- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.scss | 1 + src/client/views/DocumentDecorations.tsx | 1 + src/client/views/MainView.tsx | 1 + src/client/views/PreviewCursor.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 12 ++++---- .../views/collections/CollectionStackingView.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 27 +++++++++--------- src/client/views/nodes/DocumentView.tsx | 25 ++++++---------- src/client/views/nodes/ImageBox.tsx | 33 +++++++++++++--------- src/client/views/nodes/WebBox.tsx | 8 ++---- .../views/nodes/formattedText/FormattedTextBox.tsx | 18 +++++++++++- .../formattedText/FormattedTextBoxComment.tsx | 2 +- src/new_fields/Doc.ts | 26 +++++++++++------ .../authentication/models/current_user_utils.ts | 2 +- 15 files changed, 94 insertions(+), 67 deletions(-) (limited to 'src/client/views/DocumentButtonBar.tsx') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 48685302a..10d9ec401 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -194,7 +194,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV e.preventDefault(); let googleDoc = await Cast(dataDoc.googleDoc, Doc); if (!googleDoc) { - const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false }; + const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, isAnnotating: false, UseCors: false }; googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 4f34eb9e3..15eb537da 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -72,6 +72,7 @@ $linkGap : 3px; grid-column-start: 5; grid-column-end: 7; border-radius: 100%; + background: dimgray; .borderRadiusTooltip { width: 10px; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 1054822c7..9669c21a9 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -301,6 +301,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { + if (e.ctrlKey && !element.props.Document._nativeHeight) element.toggleNativeDimensions(); if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { const doc = Document(element.rootDoc); let nwidth = doc._nativeWidth || 0; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cbaae63bc..560dd5d11 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -249,6 +249,7 @@ export class MainView extends React.Component { _width: this._panelWidth * .7, _height: this._panelHeight, title: "Collection " + workspaceCount, + _LODdisable: true }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const mainDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 14b5b3fce..2ddca369a 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -46,7 +46,7 @@ export class PreviewCursor extends React.Component<{}> { else if (re.test(e.clipboardData.getData("text/plain"))) { const url = e.clipboardData.getData("text/plain"); undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, { - title: url, _width: 500, _height: 300, + title: url, _width: 500, _height: 300, UseCors: true, // nativeWidth: 300, nativeHeight: 472.5, x: newPoint[0], y: newPoint[1] })))(); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 296b2453b..50e3b73eb 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -755,8 +755,10 @@ export class DockedFrameRenderer extends React.Component { } get layoutDoc() { return this._document && Doc.Layout(this._document); } - panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : this._panelWidth; - panelHeight = () => this._panelHeight; + nativeAspect = () => this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0; + panelWidth = () => this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : + (this.nativeAspect() && this.nativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.nativeAspect() : this._panelWidth); + panelHeight = () => this.nativeAspect() && this.nativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.nativeAspect() : this._panelHeight; nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0; nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0; @@ -794,7 +796,7 @@ export class DockedFrameRenderer extends React.Component { return Transform.Identity(); } 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; } + get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}%` : undefined; } addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); @@ -832,8 +834,8 @@ export class DockedFrameRenderer extends React.Component { ContentScaling={this.contentScaling} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight} - NativeHeight={returnZero} - NativeWidth={returnZero} + NativeHeight={this.nativeHeight} + NativeWidth={this.nativeWidth} ScreenToLocalTransform={this.ScreenToLocalTransform} renderDepth={0} parentActive={returnTrue} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index c199988c0..979e9e3f8 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -432,6 +432,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) if (!e.isPropagationStopped()) { const subItems: ContextMenuProps[] = []; subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" }); + subItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" }); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d24ba6bb0..129b245b8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -113,8 +113,8 @@ export class CollectionFreeFormView extends CollectionSubView !this.nativeWidth && !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling : 0; // shift so pan position is at center of window for non-overlay collections - private centeringShiftY = () => !this.nativeHeight && !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling : 0;// shift so pan position is at center of window for non-overlay collections + private centeringShiftX = () => !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / this.contentScaling : 0; // shift so pan position is at center of window for non-overlay collections + private centeringShiftY = () => !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / this.contentScaling : 0;// shift so pan position is at center of window for non-overlay collections private getTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1).translate(-this.centeringShiftX(), -this.centeringShiftY()).transform(this.getLocalTransform()); private getTransformOverlay = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth + 1, -this.borderWidth + 1); private getContainerTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth); @@ -340,12 +340,12 @@ export class CollectionFreeFormView extends CollectionSubView { + Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight()); + } private thumbIdentifier?: number; @@ -1138,6 +1138,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); + optionItems.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" }); optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index aca0433bb..6c9ade83f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -648,18 +648,8 @@ export class DocumentView extends DocComponent(Docu @undoBatch @action - public static unfreezeNativeDimensions(layoutDoc: Doc) { - layoutDoc._nativeWidth = undefined; - layoutDoc._nativeHeight = undefined; - } - toggleNativeDimensions = () => { - if (this.Document._nativeWidth || this.Document._nativeHeight) { - DocumentView.unfreezeNativeDimensions(this.layoutDoc); - } - else { - Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight()); - } + Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.PanelWidth(), this.props.PanelHeight()); } @undoBatch @@ -735,10 +725,6 @@ export class DocumentView extends DocComponent(Docu let options = cm.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; - optionItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); - optionItems.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); - optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); - optionItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); if (!options) { options = { description: "Options...", subitems: optionItems, icon: "compass" }; cm.addItem(options); @@ -757,6 +743,7 @@ export class DocumentView extends DocComponent(Docu onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => Doc.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "edit" }); !existingOnClick && cm.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); + const funcs: ContextMenuProps[] = []; if (this.Document.onDragStart) { funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.Document.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) }); @@ -768,7 +755,9 @@ export class DocumentView extends DocComponent(Docu const more = cm.findByDescription("More..."); const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : []; moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); - moreItems.push({ description: !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" }); + moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); + moreItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); + moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); if (!ClientUtils.RELEASE) { // let copies: ContextMenuProps[] = []; @@ -794,6 +783,7 @@ export class DocumentView extends DocComponent(Docu // a.download = `DocExport-${this.props.Document[Id]}.zip`; // a.click(); }); + const recommender_subitems: ContextMenuProps[] = []; recommender_subitems.push({ @@ -826,6 +816,9 @@ export class DocumentView extends DocComponent(Docu moreItems.push({ description: "Publish", event: () => DocUtils.Publish(this.props.Document, this.Document.title || "", this.props.addDocument, this.props.removeDocument), icon: "file" }); moreItems.push({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" }); !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" }); + + cm.moveAfter(cm.findByDescription("More...")!, cm.findByDescription("OnClick...")!); + runInAction(() => { if (!ClientUtils.RELEASE) { const setWriteMode = (mode: DocServer.WriteMode) => { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 4153c184e..d85373e98 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -157,21 +157,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(field.url.href), icon: "expand-arrows-alt" }); funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" }); - funcs.push({ - description: "Reset Native Dimensions", event: action(async () => { - const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]); - const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]); - if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) { - this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH; - this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight(); - } else { - this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth(); - this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW; - } - }), icon: "expand-arrows-alt" - }); + // funcs.push({ + // description: "Reset Native Dimensions", event: action(async () => { + // const curNW = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]); + // const curNH = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]); + // if (this.props.PanelWidth() / this.props.PanelHeight() > curNW / curNH) { + // this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelHeight() * curNW / curNH; + // this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelHeight(); + // } else { + // this.dataDoc[this.fieldKey + "-nativeWidth"] = this.props.PanelWidth(); + // this.dataDoc[this.fieldKey + "-nativeHeight"] = this.props.PanelWidth() * curNH / curNW; + // } + // }), icon: "expand-arrows-alt" + // }); const existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers..."); const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; @@ -181,6 +180,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(field.url.href), icon: "expand-arrows-alt" }); + !existingMore && ContextMenu.Instance.addItem({ description: "More...", subitems: mores, icon: "hand-point-right" }); } } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 8239d6fdf..384a6e8a5 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -129,14 +129,12 @@ export class WebBox extends ViewBoxAnnotatableComponent { + toggleAnnotationMode = () => { if (!this.layoutDoc.isAnnotating) { - //DocumentView.unfreezeNativeDimensions(this.layoutDoc); this.layoutDoc.lockedTransform = false; this.layoutDoc.isAnnotating = true; } else { - //Doc.freezeNativeDimensions(this.layoutDoc, this.props.PanelWidth(), this.props.PanelHeight()); this.layoutDoc.lockedTransform = true; this.layoutDoc.isAnnotating = false; } @@ -158,10 +156,10 @@ export class WebBox extends ViewBoxAnnotatableComponent
    -
    +
    -
    +
    { + Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight()); + } public static get DefaultLayout(): Doc | string | undefined { return Cast(Doc.UserDoc().defaultTextLayout, Doc, null) || StrCast(Doc.UserDoc().defaultTextLayout, null); @@ -438,6 +443,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc); }, icon: "eye" }); + //funcs.push({ description: `${this.Document._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" }); + funcs.push({ description: !this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze", event: this.toggleNativeDimensions, icon: "snowflake" }); funcs.push({ description: "Toggle Single Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Sidebar", event: () => this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Dictation Icon", event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" }); @@ -649,10 +656,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } ); - this._disposers.height = reaction( + this._disposers.autoHeight = reaction( () => [this.layoutDoc[WidthSym](), this.layoutDoc._autoHeight], () => this.tryUpdateHeight() ); + this._disposers.height = reaction( + () => this.layoutDoc[HeightSym](), + height => height <= 20 && (this.layoutDoc._autoHeight = true) + ); this.setupEditor(this.config, this.props.fieldKey); @@ -1206,6 +1217,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp render() { TraceMobx(); const style: { [key: string]: any } = {}; + const scale = this.props.ContentScaling() * NumCast(this.layoutDoc.scale, 1); const divKeys = ["width", "height", "background"]; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.rootDoc, this: this.layoutDoc }).result as string || ""; @@ -1258,6 +1270,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
    diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 9ad5aafb8..a33717855 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -91,7 +91,7 @@ export class FormattedTextBoxComment { (doc: Doc, followLinkLocation: string) => textBox.props.addDocTab(doc, e.ctrlKey ? "inTab" : 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 }), "onRight"); + textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400, UseCors: true }), "onRight"); } keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation( FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index e0627fb37..a1e1e11b1 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -20,7 +20,6 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { Docs, DocumentOptions } from "../client/documents/Documents"; import { PdfField, VideoField, AudioField, ImageField } from "./URLField"; import { LinkManager } from "../client/util/LinkManager"; -import { string32 } from "../../deploy/assets/pdf.worker"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -936,19 +935,28 @@ export namespace Doc { } } } - - export function freezeNativeDimensions(layoutDoc: Doc, width: number, height: number): void { - layoutDoc._autoHeight = false; - if (!layoutDoc._nativeWidth) { - layoutDoc._nativeWidth = NumCast(layoutDoc._width, width); - layoutDoc._nativeHeight = NumCast(layoutDoc._height, height); - } - } export function assignDocToField(doc: Doc, field: string, id: string) { DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout)); return id; } + export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) { + runInAction(() => { + if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) { + layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale; + layoutDoc._nativeWidth = undefined; + layoutDoc._nativeHeight = undefined; + } + else { + layoutDoc._autoHeight = false; + if (!layoutDoc._nativeWidth) { + layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth); + layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight); + } + } + }); + } + export function isDocPinned(doc: Doc) { //add this new doc to props.Document const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index bebf77a8c..b6ff10bab 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -315,7 +315,7 @@ export class CurrentUserUtils { { _width: 250, _height: 250, title: "container" }); } if (doc.emptyWebpage === undefined) { - doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600 }); + doc.emptyWebpage = Docs.Create.WebDocument("", { title: "New Webpage", _width: 600, UseCors: true }); } return [ { title: "Drag a collection", label: "Col", icon: "folder", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory, true)', dragFactory: doc.emptyCollection as Doc }, -- cgit v1.2.3-70-g09d2