From a5a4cc47944766a29e8b6dc2364c35de52c1400d Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Sep 2021 02:40:40 -0400 Subject: fixed top-down resizing of web pages and pdfs. turned off Cors option for novices. --- src/client/views/DocumentDecorations.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6f9697703..3d6f157b6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -280,6 +280,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P case "documentDecorations-topResizer": dY = -1; dH = -move[1]; + dragBottom = true; break; case "documentDecorations-bottomLeftResizer": dX = -1; @@ -387,10 +388,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { Doc.GetProto(doc).data = new InkField(inkPts.map(ipt => // (new x — oldx) + newWidth * (oldxpoint /oldWidth) - ({ - X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width, - Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height - }))); + ({ + X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width, + Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height + }))); Doc.SetNativeWidth(doc, undefined); Doc.SetNativeHeight(doc, undefined); }); -- cgit v1.2.3-70-g09d2 From 240f2c9bda4a9a32d84d1de93820f6fbc8eef458 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Sep 2021 12:24:34 -0400 Subject: fixed equations/ink to work properly when not fitwidth in lightbox. don't show titles for ink or equations using fields now. --- src/client/documents/Documents.ts | 8 +++++--- src/client/views/DocumentDecorations.tsx | 20 ++++++++++-------- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/EquationBox.scss | 3 +++ src/client/views/nodes/EquationBox.tsx | 7 ++++++- .../views/nodes/formattedText/FormattedTextBox.tsx | 24 ++++++++++------------ 6 files changed, 37 insertions(+), 27 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e8185400e..fafbc4a7d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -430,7 +430,7 @@ export namespace Docs { }], [DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: ComputedField.MakeFunction("links(self)") as any } + options: { links: ComputedField.MakeFunction("links(self)") as any, hideResizeHandles: true, hideDecorationTitle: true } }], [DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, @@ -463,9 +463,9 @@ export namespace Docs { layout: { view: CollectionView, dataField: defaultDataKey }, options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true } }], - [DocumentType.INK, { + [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method layout: { view: InkingStroke, dataField: defaultDataKey }, - options: { _fontFamily: "cursive", backgroundColor: "transparent", links: ComputedField.MakeFunction("links(self)") as any } + options: { links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SCREENSHOT, { layout: { view: ScreenshotBox, dataField: defaultDataKey }, @@ -716,6 +716,8 @@ export namespace Docs { I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString("data"); I.color = color; + I.hideDecorationTitle = true; // don't show title when selected + I.hideOpenButton = true; // don't show open full screen button when selected I.fillColor = fillColor; I.strokeWidth = strokeWidth; I.strokeBezier = strokeBezier; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3d6f157b6..d785d5419 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -388,10 +388,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] })) .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => { Doc.GetProto(doc).data = new InkField(inkPts.map(ipt => // (new x — oldx) + newWidth * (oldxpoint /oldWidth) - ({ - X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width, - Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height - }))); + ({ + X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width, + Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height + }))); Doc.SetNativeWidth(doc, undefined); Doc.SetNativeHeight(doc, undefined); }); @@ -421,7 +421,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P 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 canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup); + const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles; + const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle; + const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton); const canDelete = SelectionManager.Views().some(docView => { const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit; return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) && @@ -473,12 +475,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> {!canDelete ?
: topBtn("close", "times", undefined, this.onCloseClick, "Close")} - {seldoc.props.hideDecorationTitle || seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea} - {seldoc.props.hideResizeHandles || seldoc.props.Document.type === DocumentType.EQUATION ? (null) : + {hideTitle ? (null) : titleArea}{!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")} + + {hideResizers ? (null) : <> - {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : + {SelectionManager.Views().length !== 1 || hideTitle ? (null) : topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)} - {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
e.preventDefault()} />
e.preventDefault()} />
e.preventDefault()} /> diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d2b4b0348..60ceac007 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1168,7 +1168,7 @@ export class DocumentView extends React.Component { } const xf = (this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling)).inverse(); const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)]; - if (this.docView.props.LayoutTemplateString?.includes("LinkAnchorBox")) { + if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { const docuBox = this.docView.ContentDiv.getElementsByClassName("linkAnchorBox-cont"); if (docuBox.length) return docuBox[0].getBoundingClientRect(); } diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss index e69de29bb..6c9d53d10 100644 --- a/src/client/views/nodes/EquationBox.scss +++ b/src/client/views/nodes/EquationBox.scss @@ -0,0 +1,3 @@ +.equationBox-cont { + transform-origin: top left; +} \ No newline at end of file diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index 11ef6562f..f1f802c13 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -84,8 +84,13 @@ export class EquationBox extends ViewBoxBaseComponent !e.ctrlKey && e.stopPropagation()} + const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); + return (
!e.ctrlKey && e.stopPropagation()} style={{ + transform: `scale(${scale})`, + width: `${100 / scale}%`, + height: `${100 / scale}%`, pointerEvents: !this.props.isSelected() ? "none" : undefined, }} onKeyDown={e => e.stopPropagation()} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 3e5c4456c..ebd509669 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1469,19 +1469,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } tryUpdateScrollHeight = () => { - if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) { - const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); - const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; - if (children) { - const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; - if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { - setScrollHeight(); - } else { - setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... - } + const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); + const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; + if (children) { + const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { + setScrollHeight(); + } else { + setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } } } -- cgit v1.2.3-70-g09d2 From eb50e46332c2f0b48c7cb165d55245aab317ef08 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 21 Sep 2021 19:43:32 -0400 Subject: fixed up some darkScheme css, enabled comic mode for developers, enabled opening up fields on dashboards in myDocuments --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 30 ++++++++++------ src/client/util/SelectionManager.ts | 14 ++------ src/client/util/SettingsManager.tsx | 19 +++++----- src/client/views/ContextMenu.scss | 1 - src/client/views/DocumentDecorations.scss | 28 +++++++++++---- src/client/views/DocumentDecorations.tsx | 42 +++++++++++++--------- src/client/views/MainView.scss | 12 +++---- src/client/views/MainView.tsx | 14 ++++---- src/client/views/StyleProvider.tsx | 5 +-- .../views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 9 +++-- .../CollectionFreeFormLayoutEngines.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++- .../collectionSchema/CollectionSchemaView.tsx | 7 ++-- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/button/FontIconBox.tsx | 2 -- .../views/nodes/formattedText/DashDocView.tsx | 3 +- src/mobile/MobileInterface.tsx | 4 +-- 19 files changed, 118 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 fafbc4a7d..206f65bfd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -862,7 +862,7 @@ export namespace Docs { export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true, isFolder: true }); const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true, isFolder: true }); - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } export function DirectoryImportDocument(options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d5dc9e2be..568a9ddbd 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -20,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { TreeView } from "../views/collections/TreeView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; @@ -38,7 +39,6 @@ import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; -import { TreeView } from "../views/collections/TreeView"; interface Button { title?: string; @@ -816,16 +816,19 @@ export class CurrentUserUtils { _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true, explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." })); - // const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); - // const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - // const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); + const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`); + const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); + const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); - (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, shareDashboard!, removeDashboard!]); - (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); - (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "user-friends", "times"]); - // (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); - // (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); + const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()'); + // (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, shareDashboard!, removeDashboard!]); + // (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); + // (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "user-friends", "times"]); + (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); + (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); + (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "chalkboard", "tv", "camera", "users", "times"]); + (doc.myDashboards as any as Doc).childContextMenuFilters = new List([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]); } return doc.myDashboards as any as Doc; } @@ -1302,7 +1305,6 @@ export class CurrentUserUtils { }, { fireImmediately: true }); // Document properties on load doc.system = true; - doc.darkScheme = ColorScheme.Dark; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; doc._raiseWhenDragged = true; @@ -1321,7 +1323,6 @@ export class CurrentUserUtils { doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); - doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // @@ -1358,6 +1359,10 @@ export class CurrentUserUtils { // }); setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); + if (doc.activeDashboard instanceof Doc) { + // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) + doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; + } return doc; } @@ -1637,4 +1642,7 @@ Scripting.addGlobal(function makeTopLevelFolder() { const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder); +}); +Scripting.addGlobal(function toggleComicMode() { + Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); \ No newline at end of file diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index bac13373c..e507ec3bf 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -2,7 +2,6 @@ import { action, observable, ObservableMap } from "mobx"; import { computedFn } from "mobx-utils"; import { Doc, Opt } from "../../fields/Doc"; import { DocumentType } from "../documents/DocumentTypes"; -import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView"; import { CollectionViewType } from "../views/collections/CollectionView"; import { DocumentView } from "../views/nodes/DocumentView"; @@ -13,12 +12,10 @@ export namespace SelectionManager { @observable IsDragging: boolean = false; SelectedViews: ObservableMap = new ObservableMap(); @observable SelectedSchemaDocument: Doc | undefined; - @observable SelectedSchemaCollection: CollectionSchemaView | undefined; @action - SelectSchemaView(collectionView: Opt, doc: Opt) { + SelectSchemaViewDoc(doc: Opt) { manager.SelectedSchemaDocument = doc; - manager.SelectedSchemaCollection = collectionView; } @action SelectView(docView: DocumentView, ctrlPressed: boolean): void { @@ -33,7 +30,6 @@ export namespace SelectionManager { } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) { Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false)); manager.SelectedSchemaDocument = undefined; - manager.SelectedSchemaCollection = undefined; manager.SelectedViews.clear(); manager.SelectedViews.set(docView, docView.rootDoc); } @@ -47,7 +43,6 @@ export namespace SelectionManager { } @action DeselectAll(): void { - manager.SelectedSchemaCollection = undefined; manager.SelectedSchemaDocument = undefined; Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenChildContentsActiveChanged(false)); manager.SelectedViews.clear(); @@ -62,8 +57,8 @@ export namespace SelectionManager { export function SelectView(docView: DocumentView, ctrlPressed: boolean): void { manager.SelectView(docView, ctrlPressed); } - export function SelectSchemaView(colSchema: Opt, document: Opt): void { - manager.SelectSchemaView(colSchema, document); + export function SelectSchemaViewDoc(document: Opt): void { + manager.SelectSchemaViewDoc(document); } const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed @@ -96,9 +91,6 @@ export namespace SelectionManager { export function SelectedSchemaDoc(): Doc | undefined { return manager.SelectedSchemaDocument; } - export function SelectedSchemaCollection(): CollectionSchemaView | undefined { - return manager.SelectedSchemaCollection; - } export function Docs(): Doc[] { return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking); } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index bd91db779..6a26dfdc7 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -19,9 +19,9 @@ export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; export enum ColorScheme { - Dark = "Dark", - Light = "Light", - System = "Match System" + Dark = "-Dark", + Light = "-Light", + System = "-MatchSystem" } @observer @@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> { @observable activeTab = "Accounts"; @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; } - @computed get colorScheme() { return Doc.UserDoc().colorScheme; } + @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; } constructor(props: {}) { super(props); @@ -81,16 +81,16 @@ export class SettingsManager extends React.Component<{}> { const scheme: ColorScheme = (e.currentTarget as any).value; switch (scheme) { case ColorScheme.Light: - Doc.UserDoc().colorScheme = ColorScheme.Light; + CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" }); break; case ColorScheme.Dark: - Doc.UserDoc().colorScheme = ColorScheme.Dark; + CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark; addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" }); break; case ColorScheme.System: default: window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { - Doc.UserDoc().colorScheme = e.matches ? ColorScheme.Dark : ColorScheme.Light; + CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) }); break; } @@ -119,6 +119,7 @@ export class SettingsManager extends React.Component<{}> {
; const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System]; + const schemeMap = ["Light", "Dark", "Match system"]; return
@@ -132,8 +133,8 @@ export class SettingsManager extends React.Component<{}> {
Color Scheme
- + {colorSchemes.map((scheme, i) => )}
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 47ae0424b..ea24dbf6d 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -8,7 +8,6 @@ flex-direction: column; background: whitesmoke; border-radius: 3px; - border: solid $light-gray 1px; } // .contextMenu-item:first-child { diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index a9f50f81b..d8ad47ecb 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -2,10 +2,14 @@ $linkGap: 3px; +.documentDecorations-Dark, .documentDecorations { position: absolute; z-index: 2000; } +.documentDecorations-Dark { + background: dimgray; +} .documentDecorations-container { z-index: $docDecorations-zindex; position: absolute; @@ -50,12 +54,17 @@ $linkGap: 3px; pointer-events: auto; background: $medium-gray; opacity: 0.1; - &:hover { opacity: 1; } } + .documentDecorations-resizer-Dark + { + background: $light-gray; + opacity: 0.2; + } + .documentDecorations-topLeftResizer, .documentDecorations-leftResizer, .documentDecorations-bottomLeftResizer { @@ -221,6 +230,7 @@ $linkGap: 3px; cursor: ns-resize; } + .documentDecorations-title-Dark, .documentDecorations-title { opacity: 1; grid-column-start: 2; @@ -233,14 +243,22 @@ $linkGap: 3px; height: 22px; position: absolute; - .documentDecorations-titleSpan { + .documentDecorations-titleSpan, + .documentDecorations-titleSpan-Dark { width: 100%; border-radius: 8px; - background: #ffffffcf; + background: #ffffffa0; position: absolute; display: inline-block; cursor: move; } + .documentDecorations-titleSpan-Dark { + background: hsla(0, 0%, 0%, 0.412); + } + } + .documentDecorations-title-Dark { + color: white; + background: black; } .documentDecorations-contextMenu { @@ -439,10 +457,6 @@ $linkGap: 3px; } } -.documentDecorations-darkScheme { - background: dimgray; -} - #template-list { position: absolute; top: 25px; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d785d5419..e9a54d6a5 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -8,7 +8,7 @@ import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSy import { Document } from '../../fields/documentSchemas'; import { InkField } from "../../fields/InkField"; import { ScriptField } from '../../fields/ScriptField'; -import { Cast, NumCast } from "../../fields/Types"; +import { Cast, NumCast, StrCast } from "../../fields/Types"; import { GetEffectiveAcl } from '../../fields/util'; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils"; import { Docs } from "../documents/Documents"; @@ -27,6 +27,8 @@ import { LightboxView } from './LightboxView'; import { DocumentView } from "./nodes/DocumentView"; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import React = require("react"); +import { dark } from '@material-ui/core/styles/createPalette'; +import { color } from 'd3-color'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> { @@ -241,7 +243,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P InkStrokeProperties.Instance?._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK) .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc)); - if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles + const resizeHdl = this._resizeHdlId.split(" ")[0]; + if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles const project = (p: number[], a: number[], b: number[]) => { const atob = [b[0] - a[0], b[1] - a[1]]; const atop = [p[0] - a[0], p[1] - a[1]]; @@ -264,7 +267,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P this._snapY = thisPt.y; let dragBottom = false, dragRight = false, dragBotRight = false; let dX = 0, dY = 0, dW = 0, dH = 0; - switch (this._resizeHdlId) { + switch (this._resizeHdlId.split(" ")[0]) { case "": break; case "documentDecorations-topLeftResizer": dX = -1; @@ -437,11 +440,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
); + const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); const titleArea = this._edtingTitle ? - this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : + this.titleBlur()} + onChange={action(e => this._accumulatedTitle = e.target.value)} + onKeyPress={this.titleEntered} /> :
- {`${this.selectionTitle}`} + {`${this.selectionTitle}`}
; let inMainMenuPanel = false; @@ -457,8 +466,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight)); const useRotation = seldoc.rootDoc.type === DocumentType.INK; + const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : ""; - return (
+ return (
{SelectionManager.Views().length !== 1 || hideTitle ? (null) : topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)} -
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) : topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")} diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 4f871f5ec..7fa841002 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -41,7 +41,7 @@ } .mainView-container, -.mainView-container-dark { +.mainView-container-Dark { width: 100%; height: 100%; position: absolute; @@ -65,7 +65,7 @@ } } -.mainView-container-dark { +.mainView-container-Dark { color: $light-gray; .lm_goldenlayout { @@ -91,7 +91,7 @@ .contextMenu-cont, .contextMenu-item { - background: $medium-gray; + background: $dark-gray; } .contextMenu-item:hover { @@ -144,7 +144,7 @@ } } -.mainView-innerContent, .mainView-innerContent-dark { +.mainView-innerContent, .mainView-innerContent-Dark { display: contents; flex-direction: row; position: relative; @@ -175,7 +175,7 @@ .mainView-libraryHandle { background-color: $light-gray; } -.mainView-innerContent-dark +.mainView-innerContent-Dark { .propertiesView { background-color: #252525; @@ -198,7 +198,7 @@ background: #353535; } } -.mainView-container-dark { +.mainView-container-Dark { .contextMenu-cont { background: $medium-gray; color: $white; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d854f118f..c99ba447c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -26,7 +26,7 @@ import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; import { Scripting } from '../util/Scripting'; import { SelectionManager } from '../util/SelectionManager'; -import { SettingsManager } from '../util/SettingsManager'; +import { SettingsManager, ColorScheme } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; @@ -88,7 +88,7 @@ export class MainView extends React.Component { @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; } @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } + @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); } @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } @computed public get mainFreeform(): Opt { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } @@ -430,10 +430,10 @@ export class MainView extends React.Component { const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined; return <> {this.leftMenuPanel} -
+
{this.flyout}
- +
@@ -441,7 +441,7 @@ export class MainView extends React.Component { {this.dockingContent}
- +
{this.propertiesWidth() < 10 ? (null) : } @@ -459,7 +459,7 @@ export class MainView extends React.Component { this._dashUIHeight = r.getBoundingClientRect().height; })).observe(r); }} style={{ - color: this.darkScheme ? "rgb(205,205,205)" : "black", + color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black", height: `calc(100% - ${this.topOfDashUI}px)`, width: "100%", }} > @@ -601,7 +601,7 @@ export class MainView extends React.Component { } render() { - return (
((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} ref={r => { r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3c88a4830..1eb7a222e 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -22,6 +22,7 @@ import "./StyleProvider.scss"; import React = require("react"); import Color = require('color'); import { lightOrDark } from '../../Utils'; +import { ColorScheme } from '../util/SettingsManager'; export enum StyleLayers { Background = "background" @@ -49,7 +50,7 @@ export enum StyleProp { FontSize = "fontSize", // size of text font } -function darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } +function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; } function toggleBackground(doc: Doc) { UndoManager.RunInBatch(() => runInAction(() => { @@ -143,7 +144,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? Doc.UserDoc().activeCollectionNestedBackground : - Doc.UserDoc().activeCollectionBackground))); + Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE)))); break; //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 6a22acae8..3ea190a98 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -353,7 +353,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action setFocused = (doc: Doc) => this._focusedTable = doc; @action setPreviewDoc = (doc: Opt) => { - SelectionManager.SelectSchemaView(this, doc); + SelectionManager.SelectSchemaViewDoc(doc); this._previewDoc = doc; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 97de097e0..a3da0e0e4 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -481,13 +481,16 @@ export class TreeView extends React.Component { } @computed get validExpandViewTypes() { - if (this.doc.viewType === CollectionViewType.Docking) return [this.fieldKey]; + if (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) { + return [this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : "layout"]; + } const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; const links = () => DocListCast(this.doc.links).length ? "links" : ""; - const data = () => this.childDocs && !this.props.treeView.dashboardMode ? this.fieldKey : ""; + const data = () => this.childDocs ? this.fieldKey : ""; const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; - return [data(), "layout", ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); + const layout = this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; + return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); } @action expandNextviewType = () => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 37444a9dc..b3c57d33a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -7,6 +7,7 @@ import { Cast, NumCast, StrCast } from "../../../../fields/Types"; import { aggregateBounds } from "../../../../Utils"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import React = require("react"); +import { ColorScheme } from "../../../util/SettingsManager"; export interface ViewDefBounds { type: string; @@ -361,7 +362,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: CurrentUserUtils.ActiveDashboard?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; + const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "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/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 94cf1c5a6..0b12f6c21 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -51,6 +51,7 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import Color = require("color"); +import { ColorScheme } from "../../../util/SettingsManager"; export const panZoomSchema = createSchema({ _panX: "number", @@ -1419,6 +1420,7 @@ export class CollectionFreeFormView extends CollectionSubView { const ctx = el?.getContext('2d'); @@ -1429,7 +1431,7 @@ export class CollectionFreeFormView extends CollectionSubView 50 ? [3, 3] : [1, 5]); ctx.clearRect(0, 0, w, h); if (ctx) { - ctx.strokeStyle = "rgba(0, 0, 0, 0.5)"; + ctx.strokeStyle = strokeStyle; ctx.beginPath(); for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) { ctx.moveTo(x, Cy - h); diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index dfe99ffc8..12493ecc1 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -337,8 +337,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { {this.renderTypes(this._col)} {this.renderColors(this._col)}
- +
; } @@ -353,7 +354,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action setFocused = (doc: Doc) => this._focusedTable = doc; @action setPreviewDoc = (doc: Opt) => { - SelectionManager.SelectSchemaView(this, doc); + SelectionManager.SelectSchemaViewDoc(doc); this._previewDoc = doc; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3e15ed661..5d0b91b91 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -25,7 +25,6 @@ import { InteractionUtils } from '../../util/InteractionUtils'; import { LinkManager } from '../../util/LinkManager'; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from "../../util/SelectionManager"; -import { ColorScheme } from "../../util/SettingsManager"; import { SharingManager } from '../../util/SharingManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from "../../util/Transform"; @@ -50,6 +49,7 @@ import { ScriptingBox } from "./ScriptingBox"; import { PresBox } from './trails/PresBox'; import React = require("react"); import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { ColorScheme } from "../../util/SettingsManager"; const { Howl } = require('howler'); interface Window { @@ -1061,9 +1061,7 @@ export class DocumentViewInternal extends DocComponent(Fon render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index e519de1c5..149836e93 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -12,6 +12,7 @@ import { FormattedTextBox } from "./FormattedTextBox"; import React = require("react"); import * as ReactDOM from 'react-dom'; import { observer } from "mobx-react"; +import { ColorScheme } from "../../../util/SettingsManager"; export class DashDocView { _fieldWrapper: HTMLSpanElement; // container for label and value @@ -20,7 +21,7 @@ export class DashDocView { this._fieldWrapper = document.createElement("span"); this._fieldWrapper.style.position = "relative"; this._fieldWrapper.style.textIndent = "0"; - this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); + this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "lightGray")); this._fieldWrapper.style.width = node.attrs.width; this._fieldWrapper.style.height = node.attrs.height; this._fieldWrapper.style.display = node.attrs.hidden ? "none" : "inline-block"; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 404e828ea..652804126 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -17,7 +17,7 @@ import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents'; import { DocumentType } from "../client/documents/DocumentTypes"; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; import { Scripting } from '../client/util/Scripting'; -import { SettingsManager } from '../client/util/SettingsManager'; +import { SettingsManager, ColorScheme } from '../client/util/SettingsManager'; import { Transform } from '../client/util/Transform'; import { UndoManager } from "../client/util/UndoManager"; import { TabDocView } from '../client/views/collections/TabDocView'; @@ -403,7 +403,7 @@ export class MobileInterface extends React.Component { const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); - const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); + const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`); dashboardDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, cloneDashboard!]); -- cgit v1.2.3-70-g09d2 From dab30019adba202ba2266acf046ad3c41f90c6e2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Sep 2021 14:06:51 -0400 Subject: videoBox fitsWidth properly in lightbox. documentdecorations less hacky for deciding when to allow nativewidth/height changes by adding props to document type templates. fixed height of properties pane & updated CSS for leftflyout & properties pane. marquees no longer oveflow image/video boxes. --- src/client/documents/Documents.ts | 12 +++++++++--- src/client/views/DocumentDecorations.tsx | 29 +++++++++++------------------ src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/MainView.scss | 9 ++++----- src/client/views/MainView.tsx | 12 ++++++------ src/client/views/nodes/ImageBox.scss | 1 + src/client/views/nodes/ImageBox.tsx | 4 ++-- src/client/views/nodes/VideoBox.scss | 15 ++++++++++++++- src/client/views/nodes/VideoBox.tsx | 22 +++++++++++++++------- src/client/views/nodes/WebBox.tsx | 8 ++++---- 10 files changed, 67 insertions(+), 47 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8ac647b99..403620bdd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -352,7 +352,7 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.RTF, { layout: { view: FormattedTextBox, dataField: "text" }, - options: { _height: 150, _xMargin: 10, _yMargin: 10, links: ComputedField.MakeFunction("links(self)") as any } + options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, @@ -372,7 +372,7 @@ export namespace Docs { }], [DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any } + options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, @@ -392,7 +392,7 @@ export namespace Docs { }], [DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any } + options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: defaultDataKey }, @@ -506,6 +506,12 @@ export namespace Docs { const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); // fetch the actual prototype documents from the server const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds); + Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB + Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true; + Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true; + Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB + Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true; + Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true; // update this object to include any default values: DocumentOptions for all prototypes prototypeIds.map(id => { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e9a54d6a5..bd61c9f60 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -319,12 +319,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P let height = (doc._height || (nheight / nwidth * width)); height = !height || isNaN(height) ? 20 : height; const scale = docView.props.ScreenToLocalTransform().Scale; - const canModifyNativeDim = e.ctrlKey || doc.allowReflow; + const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable; if (nwidth && nheight) { if (nwidth / nheight !== width / height && !dragBottom) { height = nheight / nwidth * width; } - if (canModifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction + if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth; else dW = dH * nwidth / nheight; } @@ -334,34 +334,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); const fixedAspect = (nwidth && nheight); - if (canModifyNativeDim && [DocumentType.IMG, DocumentType.SCREENSHOT, DocumentType.VID].includes(doc.type as DocumentType)) { - dW !== 0 && runInAction(() => { - const dataDoc = doc[DataSym]; - const nw = Doc.NativeWidth(dataDoc); - const nh = Doc.NativeHeight(dataDoc); - Doc.SetNativeWidth(dataDoc, nw + (dW > 0 ? 10 : -10)); - Doc.SetNativeHeight(dataDoc, nh + (dW > 0 ? 10 : -10) * nh / nw); - }); - } - else if (fixedAspect) { - if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !canModifyNativeDim)) || dragRight) { - if (dragRight && canModifyNativeDim) { + if (fixedAspect) { + if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) { + if (dragRight && modifyNativeDim) { doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc); } else { if (!doc._fitWidth) doc._height = nheight / nwidth * actualdW; - else if (!canModifyNativeDim || dragBotRight) doc._height = actualdH; + else if (!modifyNativeDim || dragBotRight) doc._height = actualdH; } doc._width = actualdW; } else { - if (dragBottom && (canModifyNativeDim || docView.layoutDoc._fitWidth)) { // frozen web pages and others that fitWidth can't grow horizontally to match a vertical resize so the only choice is to change the nativeheight even if the ctrl key isn't used + if (dragBottom && (modifyNativeDim || + (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc); doc._autoHeight = false; } else { if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH; - else if (!canModifyNativeDim || dragBotRight) doc._width = actualdW; + else if (!modifyNativeDim || dragBotRight) doc._width = actualdW; } - doc._height = actualdH; + if (!modifyNativeDim) doc._height = Math.min(nheight / nwidth * NumCast(doc._width), actualdH); + else doc._height = actualdH; } } else { dH && (doc._height = actualdH); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 574d28b8f..364bf05e2 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -141,7 +141,7 @@ export class KeyManager { case "delete": case "backspace": if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") { - const selected = SelectionManager.Views().filter(dv => !dv.topMost);; + const selected = SelectionManager.Views().filter(dv => !dv.topMost); UndoManager.RunInBatch(() => { SelectionManager.DeselectAll(); selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document)); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 7fa841002..c3cdb7dde 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -109,9 +109,9 @@ .properties-container { height: 100%; - position: relative; - left: 100%; - top: calc(-100% - 36px); + position: absolute; + right: 0; + top: 0; z-index: 3000; } @@ -298,9 +298,8 @@ width: var(--flyoutHandleWidth); height: 55px; top: 50%; - left: -10px; border-radius: 8px; - position: relative; + position: absolute; z-index: 41; // lm_maximised has a z-index of 40 and this needs to be above that touch-action: none; cursor: grab; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7edcd6217..3f7df705f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -426,23 +426,23 @@ export class MainView extends React.Component { } @computed get mainInnerContent() { - const width = this.propertiesWidth() + this._leftMenuFlyoutWidth + this.leftMenuWidth(); - const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined; + const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); + const width = this.propertiesWidth() + leftMenuFlyoutWidth; return <> {this.leftMenuPanel}
{this.flyout} -
+
-
+
{this.dockingContent} -
+
-
+
{this.propertiesWidth() < 10 ? (null) : }
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 9142a68ed..4238f6d29 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -13,6 +13,7 @@ width: 100%; pointer-events: none; mix-blend-mode: multiply; // bcz: makes text fuzzy! + overflow: hidden; } .imageBox-fader { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index b927e98db..89f70985c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -354,15 +354,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent ; + return
; } marqueeDown = (e: React.PointerEvent) => { @@ -568,10 +568,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent [this.youtubeVideoId ? this.youtubeContent : this.content]; scaling = () => this.props.scaling?.() || 1; - panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100; - panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100; + panelWidth = (): number => this.fitWidth ? this.props.PanelWidth() : (Doc.NativeAspect(this.rootDoc) || 1) * this.panelHeight(); + panelHeight = (): number => this.fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.heightPercent / 100 * this.props.PanelHeight(); screenToLocalTransform = () => { const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent); @@ -585,10 +586,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent { e.stopPropagation(); e.preventDefault(); }}>
-
+
- {this.uIButtons} {this.annotationLayer} - {this.renderTimeline} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : } + {this.renderTimeline} + {this.uIButtons}
); } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 8e6586735..37d716993 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -430,9 +430,9 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); funcs.push({ - description: (!this.layoutDoc.allowReflow ? "Allow" : "Prevent") + " Reflow", event: () => { - const nw = !this.layoutDoc.allowReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); - this.layoutDoc.allowReflow = !nw; + description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => { + const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); + this.layoutDoc.forceReflow = !nw; if (nw) { Doc.SetInPlace(this.layoutDoc, this.fieldKey + "-nativeWidth", nw, true); } @@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent + style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}> {this.urlContent}
; } -- cgit v1.2.3-70-g09d2 From aed57a2d6435007676409aeba562fc11d0c4a44d Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 29 Sep 2021 01:38:15 -0400 Subject: a number of undo/redo fixes for ink (snapping to tangent, add points, dragging tangents). also tried to make storage of undo events more efficient when dragging ink controls (avoid saving hundreds of copies of the InkField) --- src/client/util/UndoManager.ts | 19 +++++++++++++++++++ src/client/views/DocumentDecorations.tsx | 5 ++++- src/client/views/InkControlPtHandles.tsx | 27 ++++++++++++++++----------- src/client/views/InkStrokeProperties.ts | 19 +++++++++++-------- src/client/views/InkTangentHandles.tsx | 24 ++++++++++++------------ src/client/views/InkingStroke.tsx | 8 ++++---- src/fields/util.ts | 13 ++++++------- 7 files changed, 72 insertions(+), 43 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 05fb9f378..536d90371 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -70,6 +70,7 @@ export namespace UndoManager { export interface UndoEvent { undo: () => void; redo: () => void; + prop: string; } type UndoBatch = UndoEvent[]; @@ -104,6 +105,23 @@ export namespace UndoManager { export function GetOpenBatches(): Without[] { return openBatches; } + export function FilterBatches(fieldTypes: string[]) { + var fieldCounts: { [key: string]: number } = {}; + const lastStack = UndoManager.undoStack.lastElement(); + if (lastStack) { + lastStack.forEach(ev => fieldTypes.includes(ev.prop) && (fieldCounts[ev.prop] = (fieldCounts[ev.prop] || 0) + 1)); + var fieldCount2: { [key: string]: number } = {}; + runInAction(() => + UndoManager.undoStack[UndoManager.undoStack.length - 1] = lastStack.filter(ev => { + if (fieldTypes.includes(ev.prop)) { + fieldCount2[ev.prop] = (fieldCount2[ev.prop] || 0) + 1; + if (fieldCount2[ev.prop] === 1 || fieldCount2[ev.prop] === fieldCounts[ev.prop]) return true; + return false; + } + return true; + })); + } + } export function TraceOpenBatches() { console.log(`Open batches:\n\t${openBatches.map(batch => batch.batchName).join("\n\t")}\n`); } @@ -140,6 +158,7 @@ export namespace UndoManager { batchCounter--; // console.log("End " + batchCounter); if (batchCounter === 0 && currentBatch?.length) { + // console.log("------ended----") if (!cancel) { undoStack.push(currentBatch); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bd61c9f60..bd9c3509b 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -202,7 +202,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P InkStrokeProperties.Instance?.rotateInk(2 * movement.X / angle * (Math.PI / 180)); return false; }, - () => this._rotateUndo?.end(), + () => { + this._rotateUndo?.end(); + UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); + }, emptyFunction); this._prevY = e.clientY; this._inkCenterPts = SelectionManager.Views() diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index eb0eebcdf..898c3bf26 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -24,7 +24,6 @@ export interface InkControlProps { export class InkControlPtHandles extends React.Component { @observable private _overControl = -1; - @observable private _overAddPoint = -1; /** * Handles the movement of a selected control point when the user clicks and drags. * @param controlIndex The index of the currently selected control point. @@ -32,8 +31,7 @@ export class InkControlPtHandles extends React.Component { @action onControlDown = (e: React.PointerEvent, controlIndex: number): void => { if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance.moveControl(0, 0, 1); - const controlUndo = UndoManager.StartBatch("DocDecs set radius"); + var controlUndo: UndoManager.Batch | undefined; const screenScale = this.props.ScreenToLocalTransform().Scale; const order = controlIndex % 4; const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this.props.inkCtrlPoints.length) % this.props.inkCtrlPoints.length; @@ -41,17 +39,26 @@ export class InkControlPtHandles extends React.Component { const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { + if (!controlUndo) controlUndo = UndoManager.StartBatch("drag ink ctrl pt"); InkStrokeProperties.Instance?.moveControl(-delta[0] * screenScale, -delta[1] * screenScale, controlIndex); return false; }, - () => controlUndo?.end(), + () => { + controlUndo?.end(); + UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); + }, action((e: PointerEvent, doubleTap: boolean | undefined) => { const equivIndex = controlIndex === 0 ? this.props.inkCtrlPoints.length - 1 : controlIndex === this.props.inkCtrlPoints.length - 1 ? 0 : controlIndex; - if (doubleTap && brokenIndices?.includes(equivIndex)) { - InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB); - } - if (doubleTap && brokenIndices?.includes(controlIndex)) { - InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + if (doubleTap) { + if (brokenIndices?.includes(equivIndex)) { + if (!controlUndo) controlUndo = UndoManager.StartBatch("make smooth"); + InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB); + } + if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { + if (!controlUndo) controlUndo = UndoManager.StartBatch("make smooth"); + InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + } + controlUndo?.end(); } })); } @@ -62,8 +69,6 @@ export class InkControlPtHandles extends React.Component { */ @action onEnterControl = (i: number) => { this._overControl = i; }; @action onLeaveControl = () => { this._overControl = -1; }; - @action onEnterAddPoint = (i: number) => { this._overAddPoint = i; }; - @action onLeaveAddPoint = () => { this._overAddPoint = -1; }; /** * Deletes the currently selected point. diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 6a503cc91..3770eb7c1 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -214,14 +214,16 @@ export class InkStrokeProperties { */ snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { this.applyFunction((doc: Doc, ink: InkData) => { - const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); - if (brokenIndices) { - doc.brokenInkIndices = new List(brokenIndices.filter(brokenIndex => brokenIndex !== controlIndex)); + const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"), []); + const ind = brokenIndices.findIndex(value => value === controlIndex); + if (ind !== -1) { + brokenIndices.splice(ind, 1); const [controlPoint, handleA, handleB] = [ink[controlIndex], ink[handleIndexA], ink[handleIndexB]]; const oppositeHandleA = this.rotatePoint(handleA, controlPoint, Math.PI); const angleDifference = this.angleChange(handleB, oppositeHandleA, controlPoint); - ink[handleIndexB] = this.rotatePoint(handleB, controlPoint, angleDifference); - return ink; + const inkCopy = ink.slice(); + inkCopy[handleIndexB] = this.rotatePoint(handleB, controlPoint, angleDifference); + return inkCopy; } }); } @@ -277,13 +279,14 @@ export class InkStrokeProperties { const oppositeHandlePoint = ink[oppositeHandleIndex]; const controlPoint = ink[controlIndex]; const newHandlePoint = { X: ink[handleIndex].X - deltaX / xScale, Y: ink[handleIndex].Y - deltaY / yScale }; - ink[handleIndex] = newHandlePoint; + const inkCopy = ink.slice(); + inkCopy[handleIndex] = newHandlePoint; const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")); // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle). if ((!brokenIndices || !brokenIndices?.includes(controlIndex)) && (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) { const angle = this.angleChange(oldHandlePoint, newHandlePoint, controlPoint); - ink[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle); + inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle); } - return ink; + return inkCopy; }) } \ No newline at end of file diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx index dbe9ca027..759e48134 100644 --- a/src/client/views/InkTangentHandles.tsx +++ b/src/client/views/InkTangentHandles.tsx @@ -27,18 +27,21 @@ export class InkTangentHandles extends React.Component { */ onHandleDown = (e: React.PointerEvent, handleIndex: number): void => { if (InkStrokeProperties.Instance) { - InkStrokeProperties.Instance.moveControl(0, 0, 1); - const controlUndo = UndoManager.StartBatch("DocDecs set radius"); + var controlUndo: UndoManager.Batch | undefined; const screenScale = this.props.ScreenToLocalTransform().Scale; const order = handleIndex % 4; const oppositeHandleRawIndex = order === 1 ? handleIndex - 3 : handleIndex + 3; const oppositeHandleIndex = (oppositeHandleRawIndex < 0 ? this.props.screenCtrlPoints.length + oppositeHandleRawIndex : oppositeHandleRawIndex) % this.props.screenCtrlPoints.length; const controlIndex = (order === 1 ? handleIndex - 1 : handleIndex + 2) % this.props.screenCtrlPoints.length; setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { + if (!controlUndo) controlUndo = UndoManager.StartBatch("DocDecs move tangent"); if (e.altKey) this.onBreakTangent(controlIndex); InkStrokeProperties.Instance?.moveHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleIndex, oppositeHandleIndex, controlIndex); return false; - }, () => controlUndo?.end(), emptyFunction + }, () => { + controlUndo?.end(); + UndoManager.FilterBatches(["data", "x", "y", "width", "height"]); + }, emptyFunction ); } } @@ -50,15 +53,12 @@ export class InkTangentHandles extends React.Component { */ @action onBreakTangent = (controlIndex: number) => { - const doc = this.props.inkDoc; - if (doc) { - const closed = this.props.screenCtrlPoints.lastElement().X === this.props.screenCtrlPoints[0].X && this.props.screenCtrlPoints.lastElement().Y === this.props.screenCtrlPoints[0].Y; - const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number")) || new List; - if (!brokenIndices?.includes(controlIndex) && - ((controlIndex > 0 && controlIndex < this.props.screenCtrlPoints.length - 1) || closed)) { - brokenIndices.push(controlIndex); - doc.brokenInkIndices = brokenIndices; - } + const closed = this.props.screenCtrlPoints.lastElement().X === this.props.screenCtrlPoints[0].X && this.props.screenCtrlPoints.lastElement().Y === this.props.screenCtrlPoints[0].Y; + var brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number")); + if (!brokenIndices?.includes(controlIndex) && + ((controlIndex > 0 && controlIndex < this.props.screenCtrlPoints.length - 1) || closed)) { + if (brokenIndices) brokenIndices.push(controlIndex); + else this.props.inkDoc.brokenInkIndices = new List([controlIndex]); } } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index b921014a3..867677005 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -77,8 +77,8 @@ export class InkingStroke extends ViewBoxBaseComponent { + onPointerMove = (e: React.PointerEvent) => { const { inkData, inkScaleX, inkScaleY, inkStrokeWidth, inkTop, inkLeft } = this.inkScaledData(); const screenPts = inkData.map(point => this.props.ScreenToLocalTransform().inverse().transformPoint( (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, @@ -212,7 +212,7 @@ export class InkingStroke extends ViewBoxBaseComponent this._nearestScrPt = undefined)} - onPointerMove={this.props.isSelected() ? this.onPointerOver : undefined} + onPointerMove={this.props.isSelected() ? this.onPointerMove : undefined} onPointerDown={this.onPointerDown} onClick={e => this._handledClick && e.stopPropagation()} onContextMenu={() => { diff --git a/src/fields/util.ts b/src/fields/util.ts index 3590c2dea..99dfc04d9 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -98,13 +98,12 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } - !receiver[Initializing] && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && UndoManager.AddEvent({ - redo: () => receiver[prop] = value, - undo: () => { - // console.log("Undo: " + prop + " = " + curValue); // bcz: uncomment to log undo - receiver[prop] = curValue; - } - }); + !receiver[Initializing] && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) && + UndoManager.AddEvent({ + redo: () => receiver[prop] = value, + undo: () => receiver[prop] = curValue, + prop: prop?.toString() + }); return true; } return false; -- cgit v1.2.3-70-g09d2 From 93bc6d3fbfa35c7f47b7000e0ea36d9ab81d7eba Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 14 Oct 2021 15:33:41 -0400 Subject: fixed document resizing. --- src/client/views/DocumentDecorations.tsx | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index bd9c3509b..17a81149c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -318,8 +318,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P const doc = Document(docView.rootDoc); const nwidth = docView.nativeWidth; const nheight = docView.nativeHeight; - const width = (doc._width || 0); - let height = (doc._height || (nheight / nwidth * width)); + const docheight = doc._height || 0; + const docwidth = doc._width || 0; + const width = docwidth; + let height = (docheight || (nheight / nwidth * width)); height = !height || isNaN(height) ? 20 : height; const scale = docView.props.ScreenToLocalTransform().Scale; const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable; @@ -332,17 +334,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P else dW = dH * nwidth / nheight; } } - const actualdW = Math.max(width + (dW * scale), 20); - const actualdH = Math.max(height + (dH * scale), 20); - doc.x = (doc.x || 0) + dX * (actualdW - width); - doc.y = (doc.y || 0) + dY * (actualdH - height); + let actualdW = Math.max(width + (dW * scale), 20); + let actualdH = Math.max(height + (dH * scale), 20); const fixedAspect = (nwidth && nheight); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc); } else { - if (!doc._fitWidth) doc._height = nheight / nwidth * actualdW; + if (!doc._fitWidth) { + actualdH = nheight / nwidth * actualdW; + doc._height = actualdH; + } else if (!modifyNativeDim || dragBotRight) doc._height = actualdH; } doc._width = actualdW; @@ -353,10 +356,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc); doc._autoHeight = false; } else { - if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH; + if (!doc._fitWidth) { + actualdW = nwidth / nheight * actualdH; + doc._width = actualdW; + } else if (!modifyNativeDim || dragBotRight) doc._width = actualdW; } - if (!modifyNativeDim) doc._height = Math.min(nheight / nwidth * NumCast(doc._width), actualdH); + if (!modifyNativeDim) { + actualdH = Math.min(nheight / nwidth * NumCast(doc._width), actualdH); + doc._height = actualdH; + } else doc._height = actualdH; } } else { @@ -364,6 +373,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P dW && (doc._width = actualdW); dH && (doc._autoHeight = false); } + doc.x = (doc.x || 0) + dX * (actualdW - docwidth); + doc.y = (doc.y || 0) + dY * (actualdH - docheight); doc._lastModified = new DateField(); } const val = this._dragHeights.get(docView.layoutDoc); -- cgit v1.2.3-70-g09d2 From 3e0a9ff2c708891a15a681e5af549caf0b18ff60 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 26 Oct 2021 12:06:36 -0400 Subject: when document is in lightbox view and a link is followed to its sidebar, we no longer reopen the document in its context. fixed selecting annotations on web/pdf. don't change document height when shown in a linkPreview. webBox fixes for pages with scripts. fixed range bounds when clicking on web text several fixes to web pages on server and client. client webbox allows clicks on divs with onclick instead of doing selection also hacky fix so that google search url doesn't keep expanding by removing 'q=' regions also added prevent/allow script menu item server grabs all html and hides id="google.." which are ads. also rewrites hrefs starting with http to route through corsProxy also removes target=_blank tags to prevent pages from opening outside of dash. also cleaned up routes and comments also when not logged in, references to anything in dash domain route to /home --- package-lock.json | 5 + package.json | 1 + src/Utils.ts | 2 +- src/client/util/LinkManager.ts | 6 +- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/InkControlPtHandles.tsx | 16 +-- src/client/views/InkStrokeProperties.ts | 72 +++++++------ src/client/views/InkTangentHandles.tsx | 6 +- src/client/views/InkingStroke.tsx | 9 +- src/client/views/collections/CollectionSubView.tsx | 5 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/WebBox.tsx | 77 +++++++++++--- src/server/server_Initialization.ts | 115 ++++++++++++--------- 14 files changed, 206 insertions(+), 122 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/package-lock.json b/package-lock.json index c1dd8506f..3aff3a549 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9264,6 +9264,11 @@ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "optional": true }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", diff --git a/package.json b/package.json index 5d10c0d54..99c818062 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,7 @@ "libxmljs": "^0.19.7", "lodash": "^4.17.15", "material-ui": "^0.20.2", + "memorystream": "^0.3.1", "mobile-detect": "^1.4.4", "mobx": "^5.15.7", "mobx-react": "^5.4.4", diff --git a/src/Utils.ts b/src/Utils.ts index 53182cc9c..bfb29fe8d 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -599,7 +599,7 @@ export function getWordAtPoint(elem: any, x: number, y: number): string | undefi range.selectNodeContents(elem); var currentPos = 0; const endPos = range.endOffset; - while (currentPos + 1 < endPos) { + while (currentPos + 1 <= endPos) { range.setStart(elem, currentPos); range.setEnd(elem, currentPos + 1); const rangeRect = range.getBoundingClientRect(); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 90a8f2737..62b13e2c6 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -234,8 +234,10 @@ export class LinkManager { setTimeout(LightboxView.Next); finished?.(); } else { - const containerDoc = Cast(target.annotationOn, Doc, null) || target; - const targetContext = Cast(containerDoc?.context, Doc, null); + const containerAnnoDoc = Cast(target.annotationOn, Doc, null); + const containerDoc = containerAnnoDoc || target; + const containerDocContext = Cast(containerDoc?.context, Doc, null); + const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext; const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, finished); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 17a81149c..5b44a0552 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -195,11 +195,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P @action onRotateDown = (e: React.PointerEvent): void => { this._rotateUndo = UndoManager.StartBatch("rotatedown"); + const pt = { x: (this.Bounds.x + this.Bounds.r) / 2, y: (this.Bounds.y + this.Bounds.b) / 2 }; setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { const movement = { X: delta[0], Y: e.clientY - down[1] }; const angle = Math.max(1, Math.abs(movement.Y / 10)); - InkStrokeProperties.Instance?.rotateInk(2 * movement.X / angle * (Math.PI / 180)); + const selectedInk = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); + InkStrokeProperties.Instance?.rotateInk(selectedInk, 2 * movement.X / angle * (Math.PI / 180), pt); return false; }, () => { diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index 0644488b3..73de4a3e0 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -3,18 +3,20 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../fields/Doc"; import { ControlPoint, InkData, PointData } from "../../fields/InkField"; +import { List } from "../../fields/List"; import { listSpec } from "../../fields/Schema"; import { Cast } from "../../fields/Types"; import { setupMoveUpEvents } from "../../Utils"; import { Transform } from "../util/Transform"; import { UndoManager } from "../util/UndoManager"; import { Colors } from "./global/globalEnums"; -import { InkStrokeProperties } from "./InkStrokeProperties"; -import { List } from "../../fields/List"; import { InkingStroke } from "./InkingStroke"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { DocumentView } from "./nodes/DocumentView"; export interface InkControlProps { inkDoc: Doc; + inkView: DocumentView; inkCtrlPoints: InkData; screenCtrlPoints: InkData; screenSpaceLineWidth: number; @@ -51,12 +53,12 @@ export class InkControlPtHandles extends React.Component { setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("drag ink ctrl pt"); - InkStrokeProperties.Instance?.moveControlPtHandle(delta[0] * screenScale, delta[1] * screenScale, controlIndex); + InkStrokeProperties.Instance?.moveControlPtHandle(this.props.inkView, delta[0] * screenScale, delta[1] * screenScale, controlIndex); return false; }), action(() => { if (this.controlUndo) { - InkStrokeProperties.Instance?.snapControl(this.props.inkDoc, controlIndex); + InkStrokeProperties.Instance?.snapControl(this.props.inkView, controlIndex); } this.controlUndo?.end(); this.controlUndo = undefined; @@ -71,11 +73,11 @@ export class InkControlPtHandles extends React.Component { } else { if (brokenIndices?.includes(equivIndex)) { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance?.snapHandleTangent(equivIndex, handleIndexA, handleIndexB); + InkStrokeProperties.Instance?.snapHandleTangent(this.props.inkView, equivIndex, handleIndexA, handleIndexB); } if (equivIndex !== controlIndex && brokenIndices?.includes(controlIndex)) { if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("make smooth"); - InkStrokeProperties.Instance?.snapHandleTangent(controlIndex, handleIndexA, handleIndexB); + InkStrokeProperties.Instance?.snapHandleTangent(this.props.inkView, controlIndex, handleIndexA, handleIndexB); } } this.controlUndo?.end(); @@ -98,7 +100,7 @@ export class InkControlPtHandles extends React.Component { @action onDelete = (e: KeyboardEvent) => { if (["-", "Backspace", "Delete"].includes(e.key)) { - InkStrokeProperties.Instance?.deletePoints(); + InkStrokeProperties.Instance?.deletePoints(this.props.inkView); e.stopPropagation(); } } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index ee30caa3d..33e25bbbb 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -1,16 +1,15 @@ import { Bezier } from "bezier-js"; -import { action, computed, observable, reaction } from "mobx"; -import { Doc } from "../../fields/Doc"; -import { Document } from "../../fields/documentSchemas"; +import { action, observable, reaction } from "mobx"; +import { Doc, Opt } from "../../fields/Doc"; import { InkData, InkField, InkTool, PointData } from "../../fields/InkField"; import { List } from "../../fields/List"; import { listSpec } from "../../fields/Schema"; import { Cast, NumCast } from "../../fields/Types"; import { DocumentType } from "../documents/DocumentTypes"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; import { InkingStroke } from "./InkingStroke"; +import { DocumentView } from "./nodes/DocumentView"; export class InkStrokeProperties { static Instance: InkStrokeProperties | undefined; @@ -25,21 +24,16 @@ export class InkStrokeProperties { reaction(() => CurrentUserUtils.SelectedTool, tool => (tool !== InkTool.None) && (this._controlButton = false)); } - @computed get selectedInk() { - const inks = SelectionManager.Views().filter(i => Document(i.rootDoc).type === DocumentType.INK); - return inks.length ? inks : undefined; - } - /** * Helper function that enables other functions to be applied to a particular ink instance. * @param func The inputted function. * @param requireCurrPoint Indicates whether the current selected point is needed. */ - applyFunction = (func: (doc: Doc, ink: InkData, ptsXscale: number, ptsYscale: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { + applyFunction = (strokes: Opt, func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => { var appliedFunc = false; - this.selectedInk?.forEach(action(inkView => { - if (this.selectedInk?.length === 1 && (!requireCurrPoint || this._currentPoint !== -1)) { - const doc = Document(inkView.rootDoc); + (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(action(inkView => { + if (!requireCurrPoint || this._currentPoint !== -1) { + const doc = inkView.rootDoc; if (doc.type === DocumentType.INK && doc.width && doc.height) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { @@ -47,7 +41,7 @@ export class InkStrokeProperties { const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); const ptsXscale = ((NumCast(doc._width) - NumCast(doc.strokeWidth)) / ((oldXrange.max - oldXrange.min) || 1)) || 1; const ptsYscale = ((NumCast(doc._height) - NumCast(doc.strokeWidth)) / ((oldYrange.max - oldYrange.min) || 1)) || 1; - const newPoints = func(doc, ink, ptsXscale, ptsYscale); + const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth)); if (newPoints) { const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X)); const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y)); @@ -73,8 +67,9 @@ export class InkStrokeProperties { */ @undoBatch @action - addPoints = (t: number, i: number, controls: { X: number, Y: number }[]) => { - this.applyFunction((doc: Doc, ink: InkData) => { + addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number, Y: number }[]) => { + this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.rootDoc; const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]]; const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t); const splicepts = [...newsegs.left.points, ...newsegs.right.points]; @@ -143,9 +138,10 @@ export class InkStrokeProperties { */ @undoBatch @action - deletePoints = () => this.applyFunction((doc: Doc, ink: InkData) => { + deletePoints = (inkView: DocumentView) => this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.rootDoc; const newPoints: { X: number, Y: number }[] = []; - const toRemove = Math.floor(((this._currentPoint + 2) / 4)); + const toRemove = Math.floor((this._currentPoint + 2) / 4); const last = this._currentPoint === ink.length - 1; for (let i = 0; i < ink.length; i++) { if (Math.floor((i + 2) / 4) !== toRemove && (toRemove !== 0 || i > 3)) { @@ -164,17 +160,22 @@ export class InkStrokeProperties { */ @undoBatch @action - rotateInk = (angle: number) => { - this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { - const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X)); - const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y)); - const centerPoint = { X: (oldXrange.min + oldXrange.max) / 2, Y: (oldYrange.min + oldYrange.max) / 2 }; + rotateInk = (inkStrokes: DocumentView[], angle: number, scrpt: { x: number, y: number }) => { + this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => { + const oldXrangeMin = Math.min(...ink.map(p => p.X)); + const oldYrangeMin = Math.min(...ink.map(p => p.Y)); + const docViewCenterPt = view.screenToLocalTransform().transformPoint(scrpt.x, scrpt.y); + const inkCenterPt = { + X: (docViewCenterPt[0] - inkStrokeWidth / 2) / xScale + oldXrangeMin, + Y: (docViewCenterPt[1] - inkStrokeWidth / 2) / yScale + oldYrangeMin + }; const newPoints = ink.map(i => { - const pt = { X: i.X - centerPoint.X, Y: i.Y - centerPoint.Y }; + const pt = { X: i.X - inkCenterPt.X, Y: i.Y - inkCenterPt.Y }; const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * yScale / xScale; const newY = Math.sin(angle) * pt.X * xScale / yScale + Math.cos(angle) * pt.Y; - return { X: newX + centerPoint.X, Y: newY + centerPoint.Y }; + return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y }; }); + const doc = view.rootDoc; doc.rotation = NumCast(doc.rotation) + angle; return newPoints; }); @@ -185,8 +186,8 @@ export class InkStrokeProperties { */ @undoBatch @action - moveControlPtHandle = (deltaX: number, deltaY: number, controlIndex: number) => - this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { + moveControlPtHandle = (inkView: DocumentView, deltaX: number, deltaY: number, controlIndex: number) => + this.applyFunction(inkView, (view: DocumentView, ink: InkData, xScale: number, yScale: number) => { const order = controlIndex % 4; const closed = InkingStroke.IsClosed(ink); @@ -235,7 +236,8 @@ export class InkStrokeProperties { /** * Handles the movement/scaling of a control point. */ - snapControl = (inkDoc: Doc, controlIndex: number) => { + snapControl = (inkView: DocumentView, controlIndex: number) => { + const inkDoc = inkView.rootDoc; const ink = Cast(inkDoc.data, InkField)?.inkData; if (ink) { const closed = InkingStroke.IsClosed(ink); @@ -257,8 +259,8 @@ export class InkStrokeProperties { const near = Math.sqrt((nearestPt.X - refPt.X) * (nearestPt.X - refPt.X) * ptsXscale * ptsXscale + (nearestPt.Y - refPt.Y) * (nearestPt.Y - refPt.Y) * ptsYscale * ptsYscale); - if (near / (this.selectedInk?.lastElement().props.ScreenToLocalTransform().Scale || 1) < 10) { - return this.moveControlPtHandle((nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex); + if (near / (inkView.props.ScreenToLocalTransform().Scale || 1) < 10) { + return this.moveControlPtHandle(inkView, (nearestPt.X - ink[controlIndex].X) * ptsXscale, (nearestPt.Y - ink[controlIndex].Y) * ptsYscale, controlIndex); } } return false; @@ -269,8 +271,9 @@ export class InkStrokeProperties { * @param handleIndexA The handle point that retains its current position. * @param handleIndexB The handle point that is rotated to be 180 degrees from its opposite. */ - snapHandleTangent = (controlIndex: number, handleIndexA: number, handleIndexB: number) => { - this.applyFunction((doc: Doc, ink: InkData) => { + snapHandleTangent = (inkView: DocumentView, controlIndex: number, handleIndexA: number, handleIndexB: number) => { + this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + const doc = view.rootDoc; const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"), []); const ind = brokenIndices.findIndex(value => value === controlIndex); if (ind !== -1) { @@ -330,8 +333,9 @@ export class InkStrokeProperties { */ @undoBatch @action - moveTangentHandle = (deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => - this.applyFunction((doc: Doc, ink: InkData, xScale: number, yScale: number) => { + moveTangentHandle = (inkView: DocumentView, deltaX: number, deltaY: number, handleIndex: number, oppositeHandleIndex: number, controlIndex: number) => + this.applyFunction(inkView, (view: DocumentView, ink: InkData, xScale: number, yScale: number) => { + const doc = view.rootDoc; const closed = InkingStroke.IsClosed(ink); const oldHandlePoint = ink[handleIndex]; const oppositeHandlePoint = ink[oppositeHandleIndex]; diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx index df5bebf31..f88a20448 100644 --- a/src/client/views/InkTangentHandles.tsx +++ b/src/client/views/InkTangentHandles.tsx @@ -10,11 +10,13 @@ import { emptyFunction, setupMoveUpEvents } from "../../Utils"; import { Transform } from "../util/Transform"; import { UndoManager } from "../util/UndoManager"; import { Colors } from "./global/globalEnums"; -import { InkStrokeProperties } from "./InkStrokeProperties"; import { InkingStroke } from "./InkingStroke"; +import { InkStrokeProperties } from "./InkStrokeProperties"; +import { DocumentView } from "./nodes/DocumentView"; export interface InkHandlesProps { inkDoc: Doc; + inkView: DocumentView; screenCtrlPoints: InkData; screenSpaceLineWidth: number; ScreenToLocalTransform: () => Transform; @@ -37,7 +39,7 @@ export class InkTangentHandles extends React.Component { setupMoveUpEvents(this, e, (e: PointerEvent, down: number[], delta: number[]) => { if (!controlUndo) controlUndo = UndoManager.StartBatch("DocDecs move tangent"); if (e.altKey) this.onBreakTangent(controlIndex); - InkStrokeProperties.Instance?.moveTangentHandle(-delta[0] * screenScale, -delta[1] * screenScale, handleIndex, oppositeHandleIndex, controlIndex); + InkStrokeProperties.Instance?.moveTangentHandle(this.props.inkView, -delta[0] * screenScale, -delta[1] * screenScale, handleIndex, oppositeHandleIndex, controlIndex); return false; }, () => { controlUndo?.end(); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 752db1413..d312331d0 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -1,17 +1,17 @@ import React = require("react"); -import { Bezier } from "bezier-js"; import { action, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../fields/Doc"; import { documentSchema } from "../../fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../fields/InkField"; import { makeInterface } from "../../fields/Schema"; -import { Cast, NumCast, StrCast, BoolCast } from "../../fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InteractionUtils } from "../util/InteractionUtils"; +import { SnappingManager } from "../util/SnappingManager"; import { ContextMenu } from "./ContextMenu"; import { ViewBoxBaseComponent } from "./DocComponent"; import { Colors } from "./global/globalEnums"; @@ -20,7 +20,6 @@ import "./InkStroke.scss"; import { InkStrokeProperties } from "./InkStrokeProperties"; import { InkTangentHandles } from "./InkTangentHandles"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; -import { SnappingManager } from "../util/SnappingManager"; import Color = require("color"); type InkDocument = makeInterface<[typeof documentSchema]>; @@ -83,7 +82,7 @@ export class InkingStroke extends ViewBoxBaseComponent (schemaCtor: (doc: Doc) => T, moreProps?: // } } if (uriList) { - console.log("Web URI = ", uriList); // const existingWebDoc = await Hypothesis.findWebDoc(uriList); // if (existingWebDoc) { // const alias = Doc.MakeAlias(existingWebDoc); @@ -390,7 +389,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: // addDocument(alias); // } else { - console.log("Adding ..."); const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig) ...options, title: uriList.split("#annotations:")[0], @@ -399,8 +397,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: _nativeWidth: 850, useCors: true }); - console.log(" ... " + newDoc.title); - console.log(" ... " + addDocument(newDoc) + " " + newDoc.title); + addDocument(newDoc); } return; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7dcd63b80..febccbfcc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -75,7 +75,7 @@ export type collectionFreeformViewProps = { scaleField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; - dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are trnasparent or not. + dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not. // However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents. }; @@ -1472,7 +1472,7 @@ export class CollectionFreeFormView extends CollectionSubView -
+
{this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)} this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); contentScaling = () => this.ContentScale; onClickFunc = () => this.onClickHandler; - setHeight = (height: number) => this.layoutDoc._height = height; + setHeight = (height: number) => { + if (this.props.renderDepth !== -1) { + this.layoutDoc._height = height; + } + } setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); isContentActive = (outsideReaction?: boolean) => { return CurrentUserUtils.SelectedTool !== InkTool.None || diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 37d716993..9956cc36b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -58,8 +58,10 @@ export class WebBox extends ViewBoxAnnotatableComponent(); private _searchRef = React.createRef(); private _searchString = ""; + @observable private _webUrl = ""; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render. + @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe. so thisforces the ifrmae to be rebuilt when allowScripts is toggled @observable private _searching: boolean = false; - @observable _showSidebar = false; + @observable private _showSidebar = false; @observable private _scrollTimer: any; @observable private _overlayAnnoInfo: Opt; @observable private _marqueeing: number[] | undefined; @@ -79,6 +81,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._webUrl = this._url); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it. } @action @@ -239,7 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. } else { this._iframeClick = this._iframe ?? undefined; @@ -269,9 +272,24 @@ export class WebBox extends ViewBoxAnnotatableComponent { const iframe = this._iframe; - this._iframe?.contentDocument?.addEventListener("pointerup", this.iframeUp); + let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString()); + if (requrlraw !== this._url.toString()) { + if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) { + const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g); + const newsearch = matches?.lastElement()!; + if (matches) { + requrlraw = requrlraw.substring(0, requrlraw.indexOf(newsearch)); + for (let i = 1; i < Array.from(matches)?.length; i++) { + requrlraw = requrlraw.replace(matches[i], ""); + } + } + requrlraw = requrlraw.replace(/q=[^&]*/, newsearch.substring(1)).replace("search&", "search?").replace("?gbv=1", ""); + } + this.submitURL(requrlraw, undefined, true); + } if (iframe?.contentDocument) { - iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown); + iframe.contentDocument.addEventListener("pointerup", this.iframeUp); + iframe.contentDocument.addEventListener("pointerdown", this.iframeDown); this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight); setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); const initialScroll = this._initialScroll; @@ -281,13 +299,15 @@ export class WebBox extends ViewBoxAnnotatableComponent { + iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => { let href = ""; - for (let ele = e.target; ele; ele = ele.parentElement) { + for (let ele = e.target as any; ele; ele = ele.parentElement) { href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href; } - if (href && this.webField?.origin) { - this.submitURL(href.replace(Utils.prepend(""), this.webField?.origin)); + const origin = this.webField?.origin; + if (href && origin) { + e.stopPropagation(); + setTimeout(() => this.submitURL(href.replace(Utils.prepend(""), origin))); if (this._outerRef.current) { this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop); this._outerRef.current.scrollLeft = 0; @@ -338,8 +358,15 @@ export class WebBox extends ViewBoxAnnotatableComponent([...history, this._url]); this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; + } return true; } return false; @@ -350,10 +377,16 @@ export class WebBox extends ViewBoxAnnotatableComponent([this._url]); else this.dataDoc[this.fieldKey + "-future"] = new List([...future, this._url]); this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); - console.log(this._urlHash); + if (this._webUrl === this._url) { + this._webUrl = curUrl; + setTimeout(action(() => this._webUrl = this._url)); + } else { + this._webUrl = this._url; + } return true; } return false; @@ -362,9 +395,8 @@ export class WebBox extends ViewBoxAnnotatableComponent { return Math.abs(s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0)); } - @action - submitURL = (newUrl?: string, preview?: boolean) => { + submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => { if (!newUrl) return; if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl; try { @@ -376,7 +408,10 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); + funcs.push({ + description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => { + this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts; + if (this._iframe) { + runInAction(() => this._hackHide = true); + setTimeout(action(() => this._hackHide = false)); + } + }, icon: "snowflake" + }); funcs.push({ description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => { const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); @@ -468,18 +512,19 @@ export class WebBox extends ViewBoxAnnotatableComponent; } else if (field instanceof WebField) { - const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._url) : this._url; + const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl; view =