From 36eed6cc3a3387a1f7755a848aea4e19e2645e14 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Thu, 9 Jan 2020 19:55:52 -0500 Subject: some stuff that ive played around with today. mobile interface, five fingers, other stuff --- .../authentication/models/current_user_utils.ts | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 220c37e2b..71dd34e68 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -28,6 +28,7 @@ export class CurrentUserUtils { @observable public static GuestTarget: Doc | undefined; @observable public static GuestWorkspace: Doc | undefined; + @observable public static GuestMobile: Doc | undefined; // a default set of note types .. not being used yet... static setupNoteTypes(doc: Doc) { @@ -55,8 +56,10 @@ export class CurrentUserUtils { { title: "clickable button", icon: "bolt", ignoreClick: true, drag: 'Docs.Create.ButtonDocument({ width: 150, height: 50, title: "Button" })' }, { title: "presentation", icon: "tv", ignoreClick: true, drag: 'Doc.UserDoc().curPresentation = Docs.Create.PresDocument(new List(), { width: 200, height: 500, title: "a presentation trail" })' }, { title: "import folder", icon: "cloud-upload-alt", ignoreClick: true, drag: 'Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 })' }, + { title: "mobile view", icon: "phone", ignoreClick: true, drag: 'Doc.UserDoc().activeMobile' }, { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use stamp", icon: "stamp", click: 'activateStamp(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this)', backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc }, { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, @@ -90,6 +93,29 @@ export class CurrentUserUtils { } } + static setupMobileButtons(doc: Doc, buttons?: string[]) { + const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ + { title: "record", icon: "microphone", ignoreClick: true, click: "FILL" }, + { title: "use pen", icon: "pen-nib", click: 'activatePen(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use highlighter", icon: "highlighter", click: 'activateBrush(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this,20,this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use eraser", icon: "eraser", click: 'activateEraser(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "pink", activePen: doc }, + { title: "use scrubber", icon: "eraser", click: 'activateScrubber(this.activePen.pen = sameDocs(this.activePen.pen, this) ? undefined : this);', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "green", activePen: doc }, + { title: "use drag", icon: "mouse-pointer", click: 'deactivateInk();this.activePen.pen = this;', ischecked: `sameDocs(this.activePen.pen, this)`, backgroundColor: "white", activePen: doc }, + ]; + return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => Docs.Create.FontIconDocument({ + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.click ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, + ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, + })); + } + + static setupMobileDoc(userDoc: Doc) { + return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), { + columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 + }); + } + // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. when clicked, this panel will be displayed in the target container (ie, sidebarContainer) static setupToolsPanel(sidebarContainer: Doc, doc: Doc) { // setup a masonry view of all he creators -- cgit v1.2.3-70-g09d2 From d86b4db095379d473820271868e8f7cd5830d502 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 13 Jan 2020 15:25:07 -0500 Subject: palette events are now on gestureOverlay, but cffv and dv is still capturing one and two finger events, which makes it impossible for the gestureoverlay to capture 5 fingers spread across different targets --- src/client/documents/Documents.ts | 3 + src/client/util/InteractionUtils.ts | 10 ++- src/client/views/GestureOverlay.tsx | 64 ++++++++++++++- src/client/views/InkingControl.tsx | 5 +- src/client/views/Palette.scss | 10 ++- src/client/views/Palette.tsx | 44 +++++++++- src/client/views/Touchable.tsx | 21 +++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 94 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 24 +++++- src/new_fields/documentSchemas.ts | 2 + .../authentication/models/current_user_utils.ts | 23 ++++++ 11 files changed, 224 insertions(+), 76 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1853be529..64abd4f57 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -103,6 +103,8 @@ export interface DocumentOptions { ischecked?: ScriptField; // returns whether a font icon box is checked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; + onPointerDown?: ScriptField; + onPointerUp?: ScriptField; dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; @@ -116,6 +118,7 @@ export interface DocumentOptions { color?: string; limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; + pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown } class EmptyBox { diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index 2e4e8c7ca..76b43da3c 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -8,12 +8,14 @@ export namespace InteractionUtils { const REACT_POINTER_PEN_BUTTON = 0; const ERASER_BUTTON = 5; - export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map): React.Touch[] { + export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); for (let i = 0; i < e.targetTouches.length; i++) { - const pt = e.targetTouches.item(i); + const pt: any = e.targetTouches.item(i); if (pt && prevPoints.has(pt.identifier)) { - myTouches.push(pt); + if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + myTouches.push(pt); + } } } return myTouches; @@ -23,7 +25,7 @@ export namespace InteractionUtils { switch (type) { // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2 case PENTYPE: - return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? POINTER_PEN_BUTTON : REACT_POINTER_PEN_BUTTON); + return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); default: diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 848927912..830c06f1f 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; import "./GestureOverlay.scss" -import { computed, observable, action } from "mobx"; +import { computed, observable, action, runInAction } from "mobx"; import { CreatePolyline } from "./InkingStroke"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; @@ -12,14 +12,22 @@ import { Doc } from "../../new_fields/Doc"; import { LinkManager } from "../util/LinkManager"; import { DocUtils } from "../documents/Documents"; import { undoBatch } from "../util/UndoManager"; +import { Scripting } from "../util/Scripting"; +import { FieldValue, Cast } from "../../new_fields/Types"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import Palette from "./Palette"; @observer export default class GestureOverlay extends Touchable { static Instance: GestureOverlay; @observable private _points: { X: number, Y: number }[] = []; + @observable private _palette?: JSX.Element; + @observable public Color: string = "rgb(244, 67, 54)"; + @observable public Width: number = 5; private _d1: Doc | undefined; + private thumbIdentifier?: number; constructor(props: Readonly<{}>) { super(props); @@ -27,6 +35,47 @@ export default class GestureOverlay extends Touchable { GestureOverlay.Instance = this; } + @action + handleHandDown = (e: React.TouchEvent) => { + const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + this.thumbIdentifier = thumb?.identifier; + const others = fingers.filter(f => f !== thumb); + const minX = Math.min(...others.map(f => f.clientX)); + const minY = Math.min(...others.map(f => f.clientY)); + // const t = this.getTransform().transformPoint(minX, minY); + // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); + + const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); + if (thumbDoc) { + this._palette = ; + } + + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchmove", this.handleHandMove); + document.addEventListener("touchmove", this.handleHandMove); + document.removeEventListener("touchend", this.handleHandUp); + document.addEventListener("touchend", this.handleHandUp); + } + + @action + handleHandMove = (e: TouchEvent) => { + for (let i = 0; i < e.changedTouches.length; i++) { + const pt = e.changedTouches.item(i); + if (pt?.identifier === this.thumbIdentifier) { + } + } + } + + @action + handleHandUp = (e: TouchEvent) => { + this.onTouchEnd(e); + if (this.prevPoints.size < 3) { + this._palette = undefined; + document.removeEventListener("touchend", this.handleHandUp); + } + } + @action onPointerDown = (e: React.PointerEvent) => { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { @@ -82,6 +131,8 @@ export default class GestureOverlay extends Touchable { return actionPerformed; } + + @action onPointerUp = (e: PointerEvent) => { if (this._points.length > 1) { @@ -157,16 +208,21 @@ export default class GestureOverlay extends Touchable { return ( - {CreatePolyline(this._points, B.left, B.top)} + {CreatePolyline(this._points, B.left, B.top, this.Color, this.Width)} ); } render() { return ( -
+
{this.props.children} + {this._palette} {this.currentStroke}
); } -} \ No newline at end of file +} + +Scripting.addGlobal("GestureOverlay", GestureOverlay); +Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); }); +Scripting.addGlobal(function resetPen() { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); \ No newline at end of file diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 243123352..be07a9024 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -10,12 +10,13 @@ import { Scripting } from "../util/Scripting"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch, UndoManager } from "../util/UndoManager"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import GestureOverlay from "./GestureOverlay"; export class InkingControl { @observable static Instance: InkingControl; @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(CurrentUserUtils.UserDocument.inkTool)) ?? InkTool.None; } - @computed private get _selectedColor(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; } - @computed private get _selectedWidth(): string { return FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; } + @computed private get _selectedColor(): string { return GestureOverlay.Instance.Color ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkColor)) ?? "rgb(244, 67, 54)"; } + @computed private get _selectedWidth(): string { return GestureOverlay.Instance.Width?.toString() ?? FieldValue(StrCast(CurrentUserUtils.UserDocument.inkWidth)) ?? "5"; } @observable public _open: boolean = false; constructor() { diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss index 60004c81f..2626774cb 100644 --- a/src/client/views/Palette.scss +++ b/src/client/views/Palette.scss @@ -4,15 +4,17 @@ height: 300px; touch-action: pan-x; overflow: scroll; + position: absolute; .palette-thumbContent { width: 100%; height: 100%; + } - .palette-button { - width: 100px; - height: 100px; - } + .palette-button { + width: 100px; + height: 100px; + background: blue; } } } \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 390b7e2c2..3649cccfe 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -1,22 +1,60 @@ import * as React from "react"; import "./Palette.scss"; import { PointData } from "../../new_fields/InkField"; +import { Doc } from "../../new_fields/Doc"; +import { Docs } from "../documents/Documents"; +import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; +import { List } from "../../new_fields/List"; +import { DocumentView } from "./nodes/DocumentView"; +import { emptyPath, returnFalse, emptyFunction, returnOne, returnEmptyString, returnTrue } from "../../Utils"; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { Transform } from "../util/Transform"; +import { computed, action } from "mobx"; +import { FieldValue, Cast } from "../../new_fields/Types"; +import { observer } from "mobx-react"; +import { DocumentContentsView } from "./nodes/DocumentContentsView"; +import { CollectionStackingView } from "./collections/CollectionStackingView"; +import { CollectionView } from "./collections/CollectionView"; export interface PaletteProps { x: number; y: number; thumb: number[]; + thumbDoc: Doc; } +@observer export default class Palette extends React.Component { render() { return (
-
console.log("hi")}>1
-
2
-
3
+ window.screen.width} + PanelHeight={() => window.screen.height} + renderDepth={0} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne}> +
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 3eb66ff72..24ea801a0 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -21,15 +21,26 @@ export abstract class Touchable extends React.Component { */ @action protected onTouchStart = (e: React.TouchEvent): void => { + const actualPts: React.Touch[] = []; for (let i = 0; i < e.targetTouches.length; i++) { const pt: any = e.targetTouches.item(i); + actualPts.push(pt); // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) // and this seems to be the only way of differentiating pen and touch on touch events - if (pt.radiusX > 0.5 && pt.radiusY > 0.5) { + if (pt.radiusX > 1 && pt.radiusY > 1) { this.prevPoints.set(pt.identifier, pt); } } + const ptsToDelete: number[] = []; + this.prevPoints.forEach(pt => { + if (!actualPts.includes(pt)) { + ptsToDelete.push(pt.identifier); + } + }); + + ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); + if (this.prevPoints.size) { switch (this.prevPoints.size) { case 1: @@ -57,13 +68,12 @@ export abstract class Touchable extends React.Component { */ @action protected onTouch = (e: TouchEvent): void => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); // if we're not actually moving a lot, don't consider it as dragging yet if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; this._touchDrag = true; if (this.holdTimer) { - console.log("clear"); clearTimeout(this.holdTimer); this.holdTimer = undefined; } @@ -99,7 +109,6 @@ export abstract class Touchable extends React.Component { } } if (this.holdTimer) { - console.log("clear"); clearTimeout(this.holdTimer); this.holdTimer = undefined; } @@ -152,7 +161,7 @@ export abstract class Touchable extends React.Component { } handleHandDown = (e: React.TouchEvent) => { - e.stopPropagation(); - e.preventDefault(); + // e.stopPropagation(); + // e.preventDefault(); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d7cccc036..84945c6e6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../../new_fields/FieldSymbols"; import { InkTool, InkField, InkData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; import { ScriptField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types"; import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; import { aggregateBounds, emptyFunction, intersectRect, returnOne, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; @@ -468,7 +468,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { handle1PointerMove = (e: TouchEvent) => { // panning a workspace if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const pt = myTouches[0]; if (pt) { if (InkingControl.Instance.selectedTool === InkTool.None) { @@ -490,7 +490,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { handle2PointersMove = (e: TouchEvent) => { // pinch zooming if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; @@ -849,51 +849,47 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } private thumbIdentifier?: number; - private hand?: React.Touch[]; - @action - handleHandDown = (e: React.TouchEvent) => { - const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); - this.hand = fingers; - const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); - this.thumbIdentifier = thumb?.identifier; - const others = fingers.filter(f => f !== thumb); - const minX = Math.min(...others.map(f => f.clientX)); - const minY = Math.min(...others.map(f => f.clientY)); - const t = this.getTransform().transformPoint(minX, minY); - const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); - this._palette = ; - - document.removeEventListener("touchmove", this.onTouch); - document.removeEventListener("touchmove", this.handleHandMove); - document.addEventListener("touchmove", this.handleHandMove); - document.removeEventListener("touchend", this.handleHandUp); - document.addEventListener("touchend", this.handleHandUp); - } - - @action - handleHandMove = (e: TouchEvent) => { - for (let i = 0; i < e.changedTouches.length; i++) { - const pt = e.changedTouches.item(i); - if (pt?.identifier === this.thumbIdentifier) { - } - } - } - - @action - handleHandUp = (e: TouchEvent) => { - console.log(e.changedTouches.length); - this.onTouchEnd(e); - if (this.prevPoints.size < 3) { - if (this.hand) { - for (const h of this.hand) { - this.prevPoints.has(h.identifier) && this.prevPoints.delete(h.identifier); - } - } - this._palette = undefined; - document.removeEventListener("touchend", this.handleHandUp); - } - } + // @action + // handleHandDown = (e: React.TouchEvent) => { + // const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + // const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + // this.thumbIdentifier = thumb?.identifier; + // const others = fingers.filter(f => f !== thumb); + // const minX = Math.min(...others.map(f => f.clientX)); + // const minY = Math.min(...others.map(f => f.clientY)); + // const t = this.getTransform().transformPoint(minX, minY); + // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); + + // const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); + // if (thumbDoc) { + // this._palette = ; + // } + + // document.removeEventListener("touchmove", this.onTouch); + // document.removeEventListener("touchmove", this.handleHandMove); + // document.addEventListener("touchmove", this.handleHandMove); + // document.removeEventListener("touchend", this.handleHandUp); + // document.addEventListener("touchend", this.handleHandUp); + // } + + // @action + // handleHandMove = (e: TouchEvent) => { + // for (let i = 0; i < e.changedTouches.length; i++) { + // const pt = e.changedTouches.item(i); + // if (pt?.identifier === this.thumbIdentifier) { + // } + // } + // } + + // @action + // handleHandUp = (e: TouchEvent) => { + // this.onTouchEnd(e); + // if (this.prevPoints.size < 3) { + // this._palette = undefined; + // document.removeEventListener("touchend", this.handleHandUp); + // } + // } onContextMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; @@ -958,12 +954,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ]; } - @observable private _palette?: JSX.Element; + // @observable private _palette?: JSX.Element; children = () => { const eles: JSX.Element[] = []; this.extensionDoc && (eles.push(...this.childViews())); - this._palette && (eles.push(this._palette)); + // this._palette && (eles.push(this._palette)); // this.currentStroke && (eles.push(this.currentStroke)); eles.push(); return eles; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 415d0e2ef..bdec94eb3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -59,6 +59,8 @@ export interface DocumentViewProps { LibraryPath: Doc[]; fitToBox?: boolean; onClick?: ScriptField; + onPointerDown?: ScriptField; + onPointerUp?: ScriptField; dragDivName?: string; addDocument?: (doc: Doc) => boolean; removeDocument?: (doc: Doc) => boolean; @@ -105,6 +107,8 @@ export class DocumentView extends DocComponent(Docu @computed get nativeWidth() { return this.layoutDoc.nativeWidth || 0; } @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } + @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; } + @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; } @action componentDidMount() { @@ -180,7 +184,7 @@ export class DocumentView extends DocComponent(Docu } } - onClick = async (e: React.MouseEvent) => { + onClick = async (e: React.MouseEvent | React.PointerEvent) => { if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { e.stopPropagation(); @@ -240,8 +244,9 @@ export class DocumentView extends DocComponent(Docu } handle1PointerDown = (e: React.TouchEvent) => { + if (this.Document.onPointerDown) return; if (!e.nativeEvent.cancelBubble) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; this._downX = touch.clientX; this._downY = touch.clientY; this._hitTemplateDrag = false; @@ -265,7 +270,7 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("touchmove", this.onTouch); } else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints)[0]; + const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { document.removeEventListener("touchmove", this.onTouch); @@ -293,7 +298,7 @@ export class DocumentView extends DocComponent(Docu @action handle2PointersMove = (e: TouchEvent) => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints); + const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; const oldPoint1 = this.prevPoints.get(pt1.identifier); @@ -363,6 +368,12 @@ export class DocumentView extends DocComponent(Docu } onPointerDown = (e: React.PointerEvent): void => { + if (this.onPointerDownHandler && this.onPointerDownHandler.script) { + this.onPointerDownHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + return; + } // console.log(e.button) // console.log(e.nativeEvent) // continue if the event hasn't been canceled AND we are using a moues or this is has an onClick or onDragStart function (meaning it is a button document) @@ -412,6 +423,11 @@ export class DocumentView extends DocComponent(Docu } onPointerUp = (e: PointerEvent): void => { + if (this.onPointerUpHandler && this.onPointerUpHandler.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + this.onPointerUpHandler.script.run({ this: this.Document.isTemplateField && this.props.DataDoc ? this.props.DataDoc : this.props.Document }, console.log); + document.removeEventListener("pointerup", this.onPointerUp); + return; + } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 21e69fbed..1cba3cba9 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -20,6 +20,8 @@ export const documentSchema = createSchema({ dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy") removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 71dd34e68..2ade6f102 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -110,6 +110,29 @@ export class CurrentUserUtils { })); } + static setupThumbButtons(doc: Doc) { + const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ + { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + ]; + return docProtoData.map(data => Docs.Create.FontIconDocument({ + nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, + onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, + ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true, + backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, + })); + } + + static setupThumbDoc(userDoc: Doc) { + if (!userDoc.thumbDoc) { + return Docs.Create.MasonryDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 + }); + } + return userDoc.thumbDoc; + } + static setupMobileDoc(userDoc: Doc) { return userDoc.activeMoble ?? Docs.Create.MasonryDocument(CurrentUserUtils.setupMobileButtons(userDoc), { columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 -- cgit v1.2.3-70-g09d2 From 19be14b5807b117f477b57b9ecc7b96247bf4fef Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Wed, 15 Jan 2020 13:30:08 -0500 Subject: the quick brown fox jumped over the lazy dog --- src/client/util/InteractionUtils.ts | 13 ++-- src/client/views/CollectionLinearView.tsx | 14 +++- src/client/views/GestureOverlay.tsx | 50 ++++++------- src/client/views/Palette.tsx | 4 ++ src/client/views/Touchable.tsx | 35 ++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 41 +++++------ src/client/views/nodes/DocumentView.tsx | 82 +++++++++++----------- .../authentication/models/current_user_utils.ts | 2 +- 8 files changed, 126 insertions(+), 115 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index 63ee2fb92..da42bdc93 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -11,7 +11,10 @@ export namespace InteractionUtils { export class MultiTouchEvent { constructor( readonly fingers: number, - readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, + // readonly points: T extends React.TouchEvent ? React.TouchList : TouchList, + readonly targetTouches: T extends React.TouchEvent ? React.Touch[] : Touch[], + readonly touches: T extends React.TouchEvent ? React.Touch[] : Touch[], + readonly changedTouches: T extends React.TouchEvent ? React.Touch[] : Touch[], readonly touchEvent: T extends React.TouchEvent ? React.TouchEvent : TouchEvent ) { } } @@ -43,13 +46,11 @@ export namespace InteractionUtils { }; } - export function GetMyTargetTouches(e: TouchEvent | React.TouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { + export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); - for (let i = 0; i < e.touches.length; i++) { - const pt: any = e.touches.item(i); + for (const pt of mte.touches) { if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { - for (let j = 0; j < e.targetTouches.length; j++) { - const tPt = e.targetTouches.item(j); + for (const tPt of mte.targetTouches) { if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { if (pt && prevPoints.has(pt.identifier)) { myTouches.push(pt); diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 38f2decdd..2262a3c0c 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -1,4 +1,4 @@ -import { action, IReactionDisposer, observable, reaction } from 'mobx'; +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc'; @@ -21,12 +21,15 @@ const LinearDocument = makeInterface(documentSchema); @observer export class CollectionLinearView extends CollectionSubView(LinearDocument) { @observable public addMenuToggle = React.createRef(); + @observable private _selectedIndex = -1; private _dropDisposer?: DragManager.DragDropDisposer; private _widthDisposer?: IReactionDisposer; + private _selectedDisposer?: IReactionDisposer; componentWillUnmount() { this._dropDisposer && this._dropDisposer(); this._widthDisposer && this._widthDisposer(); + this._selectedDisposer && this._selectedDisposer(); } componentDidMount() { @@ -35,6 +38,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { () => this.props.Document.width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10), { fireImmediately: true } ); + + this._selectedDisposer = reaction( + () => NumCast(this.props.Document.selectedIndex), + (i) => runInAction(() => this._selectedIndex = i), + { fireImmediately: true } + ); } protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); @@ -61,11 +70,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
- {this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => { + {this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { const nested = pair.layout.viewType === CollectionViewType.Linear; const dref = React.createRef(); const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; + const isSelected = this._selectedIndex === ind; return
{ for (let i = 0; i < e.targetTouches.length; i++) { const pt = e.targetTouches.item(i); - if (pt && !hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - ntt.push(pt); + if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + ntt.splice(ntt.indexOf(pt)); } } for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); - if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nct.push(pt); + if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + nct.splice(nct.indexOf(pt)); } } - for (let i = 0; i < e.changedTouches.length; i++) { + for (let i = 0; i < e.touches.length; i++) { const pt = e.touches.item(i); - if (pt && !hand.some((finger: React.Touch) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nt.push(pt); + if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { + nt.splice(ntt.indexOf(pt)); } } }); @@ -93,7 +93,7 @@ export default class GestureOverlay extends Touchable { ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); if (this.prevPoints.size && this.prevPoints.size < 5) { - const nts: any = this.getNewTouches(te); + const nts = this.getNewTouches(te); const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); target?.dispatchEvent( new CustomEvent>("dashOnTouchStart", @@ -101,12 +101,10 @@ export default class GestureOverlay extends Touchable { bubbles: true, detail: { fingers: this.prevPoints.size, - points: te.targetTouches, - touchEvent: new React.TouchEvent("start", { - targetTouches: nts.ntt, - touches: nts.nt, - changedTouches: nts.nct - }); + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct, + touchEvent: te } } ) @@ -131,25 +129,26 @@ export default class GestureOverlay extends Touchable { bubbles: true, detail: { fingers: this.prevPoints.size, - points: e.touches, - touchEvent: new TouchEvent("move", { - targetTouches: nts.ntt, - changedTouches: nts.nct, - touches: nts.nt, - }) + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct, + touchEvent: e } }) ); } onReactTouchEnd = (e: TouchEvent) => { + const nts: any = this.getNewTouches(e); document.dispatchEvent( new CustomEvent>("dashOnTouchEnd", { bubbles: true, detail: { fingers: this.prevPoints.size, - points: e.touches, + targetTouches: nts.ntt, + touches: nts.nt, + changedTouches: nts.nct, touchEvent: e } }) @@ -171,7 +170,7 @@ export default class GestureOverlay extends Touchable { } handleHandDown = (e: React.TouchEvent) => { - const fingers = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const fingers = Array.from(e.touches); const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); this.thumbIdentifier = thumb?.identifier; fingers.forEach((f) => this.prevPoints.delete(f.identifier)); @@ -201,6 +200,7 @@ export default class GestureOverlay extends Touchable { for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); if (pt?.identifier === this.thumbIdentifier) { + console.log("thumby movey") } } } diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 3649cccfe..aca1811a4 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -15,6 +15,9 @@ import { observer } from "mobx-react"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; import { CollectionStackingView } from "./collections/CollectionStackingView"; import { CollectionView } from "./collections/CollectionView"; +import { CollectionSubView, SubCollectionViewProps } from "./collections/CollectionSubView"; +import { makeInterface } from "../../new_fields/Schema"; +import { documentSchema } from "../../new_fields/documentSchemas"; export interface PaletteProps { x: number; @@ -25,6 +28,7 @@ export interface PaletteProps { @observer export default class Palette extends React.Component { + render() { return (
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index a71015b05..7800c4019 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -29,15 +29,13 @@ export abstract class Touchable extends React.Component { const actualPts: React.Touch[] = []; const te = me.touchEvent; // loop through all touches on screen - for (let i = 0; i < te.touches.length; i++) { - const pt: any = te.touches.item(i); + for (const pt of me.touches) { actualPts.push(pt); if (this.prevPoints.has(pt.identifier)) { this.prevPoints.set(pt.identifier, pt); } // only add the ones that are targeted on "this" element, but with the identifier that the screen touch gives - for (let j = 0; j < te.changedTouches.length; j++) { - const tPt = te.changedTouches.item(j); + for (const tPt of me.changedTouches) { if (pt.clientX === tPt.clientX && pt.clientY === tPt.clientY) { // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) // and this seems to be the only way of differentiating pen and touch on touch events @@ -58,22 +56,21 @@ export abstract class Touchable extends React.Component { // console.log(ptsToDelete.length); ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); - // console.log(this.prevPoints.size); if (this.prevPoints.size) { switch (this.prevPoints.size) { case 1: - this.handle1PointerDown(te); + this.handle1PointerDown(te, me); te.persist(); // if (this.holdTimer) { // clearTimeout(this.holdTimer) // this.holdTimer = undefined; // } - this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(te), HOLD_DURATION); + this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(te, me), HOLD_DURATION); // e.stopPropagation(); // console.log(this.holdTimer); break; case 2: - this.handle2PointersDown(te); + this.handle2PointersDown(te, me); // e.stopPropagation(); break; // case 5: @@ -89,7 +86,7 @@ export abstract class Touchable extends React.Component { @action protected onTouch = (e: Event, me: InteractionUtils.MultiTouchEvent): void => { const te = me.touchEvent; - const myTouches = InteractionUtils.GetMyTargetTouches(te, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); // if we're not actually moving a lot, don't consider it as dragging yet if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; @@ -102,15 +99,14 @@ export abstract class Touchable extends React.Component { // console.log(myTouches.length); switch (myTouches.length) { case 1: - this.handle1PointerMove(te); + this.handle1PointerMove(te, me); break; case 2: - this.handle2PointersMove(te); + this.handle2PointersMove(te, me); break; } - for (let i = 0; i < te.touches.length; i++) { - const pt = te.touches.item(i); + for (const pt of me.touches) { if (pt) { if (this.prevPoints.has(pt.identifier)) { this.prevPoints.set(pt.identifier, pt); @@ -124,8 +120,7 @@ export abstract class Touchable extends React.Component { // console.log(InteractionUtils.GetMyTargetTouches(e, this.prevPoints).length + " up"); // remove all the touches associated with the event const te = me.touchEvent; - for (let i = 0; i < te.changedTouches.length; i++) { - const pt = te.changedTouches.item(i); + for (const pt of me.changedTouches) { if (pt) { if (this.prevPoints.has(pt.identifier)) { this.prevPoints.delete(pt.identifier); @@ -155,31 +150,31 @@ export abstract class Touchable extends React.Component { this.removeEndListeners(); } - handle1PointerMove = (e: TouchEvent): any => { + handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { e.stopPropagation(); e.preventDefault(); } - handle2PointersMove = (e: TouchEvent): any => { + handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { e.stopPropagation(); e.preventDefault(); } - handle1PointerDown = (e: React.TouchEvent): any => { + handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); } - handle2PointersDown = (e: React.TouchEvent): any => { + handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); } - handle1PointerHoldStart = (e: React.TouchEvent): any => { + handle1PointerHoldStart = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { e.stopPropagation(); e.preventDefault(); this.removeMoveListeners(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3894e9d63..aaa585b55 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,7 +44,7 @@ import { TraceMobx } from "../../../../new_fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { LinkManager } from "../../../util/LinkManager"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; -import Palette from "../../Palette"; +import CollectionPaletteVIew from "../../Palette"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -276,7 +276,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerDown = (e: React.PointerEvent): void => { - if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { return; } this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; @@ -325,10 +325,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @action - handle1PointerDown = (e: React.TouchEvent) => { + handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble) { // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); - const pt = e.changedTouches.item(0); + const pt = me.changedTouches[0]; if (pt) { this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false; if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { @@ -466,10 +466,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - handle1PointerMove = (e: TouchEvent) => { + handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { // panning a workspace if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt = myTouches[0]; if (pt) { if (InkingControl.Instance.selectedTool === InkTool.None) { @@ -488,10 +488,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - handle2PointersMove = (e: TouchEvent) => { + handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { // pinch zooming if (!e.cancelBubble) { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; @@ -534,24 +534,25 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @action - handle2PointersDown = (e: React.TouchEvent) => { + handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble && this.props.active(true)) { // const pt1: React.Touch | null = e.targetTouches.item(0); // const pt2: React.Touch | null = e.targetTouches.item(1); // // if (!pt1 || !pt2) return; - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; - - const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2; - const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; - this._lastX = centerX; - this._lastY = centerY; - this.removeMoveListeners(); - this.addMoveListeners(); - this.removeEndListeners(); - this.addEndListeners(); - e.stopPropagation(); + if (pt1 && pt2) { + const centerX = Math.min(pt1.clientX, pt2.clientX) + Math.abs(pt2.clientX - pt1.clientX) / 2; + const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2; + this._lastX = centerX; + this._lastY = centerY; + this.removeMoveListeners(); + this.addMoveListeners(); + this.removeEndListeners(); + this.addEndListeners(); + e.stopPropagation(); + } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b33bebe7d..0b6a284d6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -134,46 +134,46 @@ export class DocumentView extends DocComponent(Docu // document.addEventListener("touchend", this.handle1PointerHoldEnd); // } - handle1PointerHoldMove = (e: TouchEvent): void => { - const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; - if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) { - this.handleRelease(); - } - document.removeEventListener("touchmove", this.handle1PointerHoldMove); - document.addEventListener("touchmove", this.handle1PointerHoldMove); - document.removeEventListener("touchend", this.handle1PointerHoldEnd); - document.addEventListener("touchend", this.handle1PointerHoldEnd); - } + // handle1PointerHoldMove = (e: TouchEvent): void => { + // const pt = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; + // if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) { + // this.handleRelease(); + // } + // document.removeEventListener("touchmove", this.handle1PointerHoldMove); + // document.addEventListener("touchmove", this.handle1PointerHoldMove); + // document.removeEventListener("touchend", this.handle1PointerHoldEnd); + // document.addEventListener("touchend", this.handle1PointerHoldEnd); + // } - handleRelease() { - RadialMenu.Instance.closeMenu(); - document.removeEventListener("touchmove", this.handle1PointerHoldMove); - document.removeEventListener("touchend", this.handle1PointerHoldEnd); - } + // handleRelease() { + // RadialMenu.Instance.closeMenu(); + // document.removeEventListener("touchmove", this.handle1PointerHoldMove); + // document.removeEventListener("touchend", this.handle1PointerHoldEnd); + // } - handle1PointerHoldEnd = (e: TouchEvent): void => { - RadialMenu.Instance.closeMenu(); - document.removeEventListener("touchmove", this.handle1PointerHoldMove); - document.removeEventListener("touchend", this.handle1PointerHoldEnd); - } + // handle1PointerHoldEnd = (e: TouchEvent): void => { + // RadialMenu.Instance.closeMenu(); + // document.removeEventListener("touchmove", this.handle1PointerHoldMove); + // document.removeEventListener("touchend", this.handle1PointerHoldEnd); + // } - @action - onRadialMenu = (e: React.TouchEvent): void => { - const pt = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; + // @action + // onRadialMenu = (e: React.TouchEvent): void => { + // const pt = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; - RadialMenu.Instance.openMenu(); + // RadialMenu.Instance.openMenu(); - RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Delete this document", event: () => this.props.ContainingCollectionView?.removeDocument(this.props.Document), icon: "trash", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, undefined, "onRight"), icon: "folder", selected: -1 }); - RadialMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Delete this document", event: () => this.props.ContainingCollectionView?.removeDocument(this.props.Document), icon: "trash", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, undefined, "onRight"), icon: "folder", selected: -1 }); + // RadialMenu.Instance.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 }); - RadialMenu.Instance.displayMenu(pt.pageX - 15, pt.pageY - 15); - if (!SelectionManager.IsSelected(this, true)) { - SelectionManager.SelectDoc(this, false); - } - e.stopPropagation(); - } + // RadialMenu.Instance.displayMenu(pt.pageX - 15, pt.pageY - 15); + // if (!SelectionManager.IsSelected(this, true)) { + // SelectionManager.SelectDoc(this, false); + // } + // e.stopPropagation(); + // } @action componentDidMount() { @@ -313,10 +313,10 @@ export class DocumentView extends DocComponent(Docu } } - handle1PointerDown = (e: React.TouchEvent) => { + handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (this.Document.onPointerDown) return; if (!e.nativeEvent.cancelBubble) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; + const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; this._downX = touch.clientX; this._downY = touch.clientY; this._hitTemplateDrag = false; @@ -334,13 +334,13 @@ export class DocumentView extends DocComponent(Docu } } - handle1PointerMove = (e: TouchEvent) => { + handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if ((e as any).formattedHandled) { e.stopPropagation; return; } if (e.cancelBubble && this.active) { this.removeMoveListeners(); } else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { - const touch = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true)[0]; + const touch = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true)[0]; if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { this.cleanUpInteractions(); @@ -353,7 +353,7 @@ export class DocumentView extends DocComponent(Docu } } - handle2PointersDown = (e: React.TouchEvent) => { + handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble && !this.isSelected()) { e.stopPropagation(); e.preventDefault(); @@ -366,8 +366,8 @@ export class DocumentView extends DocComponent(Docu } @action - handle2PointersMove = (e: TouchEvent) => { - const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent) => { + const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt1 = myTouches[0]; const pt2 = myTouches[1]; const oldPoint1 = this.prevPoints.get(pt1.identifier); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 2ade6f102..a850b0f9f 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -126,7 +126,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { - return Docs.Create.MasonryDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + return Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 }); } -- cgit v1.2.3-70-g09d2 From 4bf88786cc8a7e617bca4eb57f7639d69be06dcd Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 15 Jan 2020 17:42:41 -0500 Subject: wow this almost drove me crazy but swipey swipes on five fingers --- package-lock.json | 6 +-- src/client/views/CollectionLinearView.tsx | 6 ++- src/client/views/GestureOverlay.tsx | 45 ++++++++++++++++++---- src/client/views/Palette.scss | 19 ++++----- src/client/views/Palette.tsx | 20 ++++++++-- .../authentication/models/current_user_utils.ts | 4 +- 6 files changed, 75 insertions(+), 25 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/package-lock.json b/package-lock.json index da2648d9a..4a5de1c66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2645,7 +2645,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -15527,7 +15527,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -17768,7 +17768,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 2262a3c0c..0a3096833 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, HeightSym, WidthSym } from '../../new_fields/Doc'; import { makeInterface } from '../../new_fields/Schema'; -import { BoolCast, NumCast, StrCast } from '../../new_fields/Types'; +import { BoolCast, NumCast, StrCast, Cast } from '../../new_fields/Types'; import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from '../../Utils'; import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; @@ -13,6 +13,7 @@ import { CollectionSubView } from './collections/CollectionSubView'; import { DocumentView } from './nodes/DocumentView'; import { documentSchema } from '../../new_fields/documentSchemas'; import { Id } from '../../new_fields/FieldSymbols'; +import { ScriptField } from '../../new_fields/ScriptField'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -76,6 +77,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; const isSelected = this._selectedIndex === ind; + if (isSelected) { + Cast(pair.layout.proto?.onPointerDown, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); + } return
{ - const fingers = Array.from(e.touches); + const fingers = new Array(); + for (let i = 0; i < e.touches.length; i++) { + const pt: any = e.touches.item(i); + if (pt.radiusX > 1 && pt.radiusY > 1) { + for (let j = 0; j < e.targetTouches.length; j++) { + const tPt = e.targetTouches.item(j); + if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { + if (pt && this.prevPoints.has(pt.identifier)) { + fingers.push(pt); + } + } + } + } + } const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + if (thumb.identifier === this.thumbIdentifier) { + this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; + return; + } this.thumbIdentifier = thumb?.identifier; fingers.forEach((f) => this.prevPoints.delete(f.identifier)); this._hands.push(fingers); @@ -184,6 +205,9 @@ export default class GestureOverlay extends Touchable { const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); if (thumbDoc) { runInAction(() => { + this._thumbDoc = thumbDoc; + this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; this._palette = ; }); } @@ -199,17 +223,24 @@ export default class GestureOverlay extends Touchable { handleHandMove = (e: TouchEvent) => { for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); - if (pt?.identifier === this.thumbIdentifier) { - console.log("thumby movey") + if (pt && pt.identifier === this.thumbIdentifier && this._thumbX && this._thumbDoc) { + if (Math.abs(pt.clientX - this._thumbX) > 20) { + this._thumbDoc.selectedIndex = Math.max(0, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX)); + this._thumbX = pt.clientX; + } } } } @action handleHandUp = (e: TouchEvent) => { - // this.onTouchEnd(e); - this._palette = undefined; - document.removeEventListener("touchend", this.handleHandUp); + if (e.touches.length < 3) { + // this.onTouchEnd(e); + this._palette = undefined; + this.thumbIdentifier = undefined; + this._thumbDoc = undefined; + document.removeEventListener("touchend", this.handleHandUp); + } } @action diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss index 2626774cb..4513de2b0 100644 --- a/src/client/views/Palette.scss +++ b/src/client/views/Palette.scss @@ -1,20 +1,21 @@ .palette-container { .palette-thumb { - width: 300px; - height: 300px; touch-action: pan-x; overflow: scroll; position: absolute; + width: 90px; + height: 70px; .palette-thumbContent { - width: 100%; - height: 100%; - } + transition: transform .3s; + + .collectionView { + overflow: visible; - .palette-button { - width: 100px; - height: 100px; - background: blue; + .collectionLinearView-outer { + overflow: visible; + } + } } } } \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index aca1811a4..811c24f53 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -9,8 +9,8 @@ import { DocumentView } from "./nodes/DocumentView"; import { emptyPath, returnFalse, emptyFunction, returnOne, returnEmptyString, returnTrue } from "../../Utils"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import { Transform } from "../util/Transform"; -import { computed, action } from "mobx"; -import { FieldValue, Cast } from "../../new_fields/Types"; +import { computed, action, IReactionDisposer, reaction, observable } from "mobx"; +import { FieldValue, Cast, NumCast } from "../../new_fields/Types"; import { observer } from "mobx-react"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; import { CollectionStackingView } from "./collections/CollectionStackingView"; @@ -28,12 +28,26 @@ export interface PaletteProps { @observer export default class Palette extends React.Component { + private _selectedDisposer?: IReactionDisposer; + @observable private _selectedIndex: number = 0; + + componentDidMount = () => { + this._selectedDisposer = reaction( + () => NumCast(this.props.thumbDoc.selectedIndex), + (i) => this._selectedIndex = i, + { fireImmediately: true } + ); + } + + componentWillUnmount = () => { + this._selectedDisposer && this._selectedDisposer(); + } render() { return (
-
+
Docs.Create.FontIconDocument({ - nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, + nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true, @@ -127,7 +127,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { return Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { - width: 300, columnWidth: 100, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5 + width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true }); } return userDoc.thumbDoc; -- cgit v1.2.3-70-g09d2 From 4b0a056f1afaf20dea4be64c7b238748d99ad12e Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Fri, 17 Jan 2020 15:36:22 -0500 Subject: palette now doesn't interfere with touch --- src/client/documents/Documents.ts | 1 + src/client/util/InteractionUtils.ts | 2 +- src/client/views/GestureOverlay.tsx | 82 +++++++++++++++++----- src/client/views/MainView.tsx | 5 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../authentication/models/current_user_utils.ts | 1 + 6 files changed, 70 insertions(+), 23 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 64abd4f57..c49642de0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -119,6 +119,7 @@ export interface DocumentOptions { limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown + isExpanded?: boolean; // is linear view expanded } class EmptyBox { diff --git a/src/client/util/InteractionUtils.ts b/src/client/util/InteractionUtils.ts index da42bdc93..3e6d27242 100644 --- a/src/client/util/InteractionUtils.ts +++ b/src/client/util/InteractionUtils.ts @@ -49,7 +49,7 @@ export namespace InteractionUtils { export function GetMyTargetTouches(mte: InteractionUtils.MultiTouchEvent, prevPoints: Map, ignorePen: boolean): React.Touch[] { const myTouches = new Array(); for (const pt of mte.touches) { - if (ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { + if (!ignorePen || (pt.radiusX > 1 && pt.radiusY > 1)) { for (const tPt of mte.targetTouches) { if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { if (pt && prevPoints.has(pt.identifier)) { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 96c1513ff..854dff0e2 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -16,7 +16,8 @@ import { Scripting } from "../util/Scripting"; import { FieldValue, Cast, NumCast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; -import { Utils } from "../../Utils"; +import { Utils, emptyPath, emptyFunction } from "../../Utils"; +import { DocumentView } from "./nodes/DocumentView"; @observer export default class GestureOverlay extends Touchable { @@ -24,15 +25,15 @@ export default class GestureOverlay extends Touchable { @observable private _points: { X: number, Y: number }[] = []; @observable private _palette?: JSX.Element; + @observable private _elements: JSX.Element[]; @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; private _d1: Doc | undefined; private _thumbDoc: Doc | undefined; private _thumbX?: number; - private _thumbY?: number; private thumbIdentifier?: number; - private _hands: (React.Touch[])[] = []; + private _hands: Map = new Map(); protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @@ -49,22 +50,22 @@ export default class GestureOverlay extends Touchable { this._hands.forEach((hand) => { for (let i = 0; i < e.targetTouches.length; i++) { const pt = e.targetTouches.item(i); - if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - ntt.splice(ntt.indexOf(pt)); + if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { + ntt.splice(ntt.indexOf(pt), 1); } } for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); - if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nct.splice(nct.indexOf(pt)); + if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { + nct.splice(nct.indexOf(pt), 1); } } for (let i = 0; i < e.touches.length; i++) { const pt = e.touches.item(i); - if (pt && hand.some((finger) => finger.screenX === pt?.screenX && finger.screenY === pt.screenY)) { - nt.splice(ntt.indexOf(pt)); + if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) { + nt.splice(nt.indexOf(pt), 1); } } }); @@ -94,9 +95,10 @@ export default class GestureOverlay extends Touchable { }); ptsToDelete.forEach(pt => this.prevPoints.delete(pt)); + const nts = this.getNewTouches(te); + console.log(nts.nt.length); - if (this.prevPoints.size && this.prevPoints.size < 5) { - const nts = this.getNewTouches(te); + if (nts.nt.length < 5) { const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY); target?.dispatchEvent( new CustomEvent>("dashOnTouchStart", @@ -190,12 +192,12 @@ export default class GestureOverlay extends Touchable { const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); if (thumb.identifier === this.thumbIdentifier) { this._thumbX = thumb.clientX; - this._thumbY = thumb.clientY; + this._hands.set(thumb.identifier, fingers); return; } this.thumbIdentifier = thumb?.identifier; - fingers.forEach((f) => this.prevPoints.delete(f.identifier)); - this._hands.push(fingers); + // fingers.forEach((f) => this.prevPoints.delete(f.identifier)); + this._hands.set(thumb.identifier, fingers); const others = fingers.filter(f => f !== thumb); const minX = Math.min(...others.map(f => f.clientX)); const minY = Math.min(...others.map(f => f.clientY)); @@ -207,7 +209,6 @@ export default class GestureOverlay extends Touchable { runInAction(() => { this._thumbDoc = thumbDoc; this._thumbX = thumb.clientX; - this._thumbY = thumb.clientY; this._palette = ; }); } @@ -221,6 +222,29 @@ export default class GestureOverlay extends Touchable { @action handleHandMove = (e: TouchEvent) => { + const fingers = new Array(); + for (let i = 0; i < e.touches.length; i++) { + const pt: any = e.touches.item(i); + if (pt.radiusX > 1 && pt.radiusY > 1) { + for (let j = 0; j < e.targetTouches.length; j++) { + const tPt = e.targetTouches.item(j); + if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) { + if (pt && this.prevPoints.has(pt.identifier)) { + this._hands.forEach(hand => hand.some(f => { + if (f.identifier === pt.identifier) { + fingers.push(pt); + } + })); + } + } + } + } + } + const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + if (thumb?.identifier === this.thumbIdentifier) { + this._hands.set(thumb.identifier, fingers); + } + for (let i = 0; i < e.changedTouches.length; i++) { const pt = e.changedTouches.item(i); if (pt && pt.identifier === this.thumbIdentifier && this._thumbX && this._thumbDoc) { @@ -236,6 +260,7 @@ export default class GestureOverlay extends Touchable { handleHandUp = (e: TouchEvent) => { if (e.touches.length < 3) { // this.onTouchEnd(e); + if (this.thumbIdentifier) this._hands.delete(this.thumbIdentifier); this._palette = undefined; this.thumbIdentifier = undefined; this._thumbDoc = undefined; @@ -380,16 +405,35 @@ export default class GestureOverlay extends Touchable { ); } + @computed get elements() { + return [ + this.props.children, + this._elements, + this._palette, + this.currentStroke + ] + } + + @action + openFloatingDoc = (doc: Doc) => { + // this._elements.push( + // + // ) + } + render() { return (
- {this.props.children} - {this._palette} - {this.currentStroke} + {this.elements}
); } } Scripting.addGlobal("GestureOverlay", GestureOverlay); Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); }); -Scripting.addGlobal(function resetPen() { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); \ No newline at end of file +Scripting.addGlobal(function resetPen() { runInAction(() => { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; })); }); \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 93fb0a07c..b8b956270 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faChevronRight, faClone, faCloudUploadAlt, faCommentAlt, faCut, faEllipsisV, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, - faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp + faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp, faClipboard } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -137,6 +137,7 @@ export class MainView extends React.Component { library.add(faEllipsisV); library.add(faMusic); library.add(faPhone); + library.add(faClipboard); library.add(faStamp); this.initEventListeners(); this.initAuthenticationRouters(); @@ -518,7 +519,7 @@ export class MainView extends React.Component { - + diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index aaa585b55..b302bb1dc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -327,7 +327,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent) => { if (!e.nativeEvent.cancelBubble) { - // const myTouches = InteractionUtils.GetMyTargetTouches(e, this.prevPoints, true); + // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt = me.changedTouches[0]; if (pt) { this._hitCluster = this.props.Document.useCluster ? this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY)) !== -1 : false; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 8b8b1e029..eb48b28f2 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -114,6 +114,7 @@ export class CurrentUserUtils { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + // { title: "notepad", icon: "clipboard", pointerUp: "closeFloatingDoc(this.clipboard)", pointerDown: 'openFloatingDoc(this.clipboard)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, -- cgit v1.2.3-70-g09d2 From a61dfaab422886733a727eebe80b619230d59684 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 20 Jan 2020 13:40:57 -0500 Subject: working palette --- src/client/documents/Documents.ts | 1 + src/client/views/GestureOverlay.scss | 6 ++ src/client/views/GestureOverlay.tsx | 87 +++++++++++++++++----- .../views/collections/CollectionLinearView.tsx | 24 ++++-- .../authentication/models/current_user_utils.ts | 7 +- 5 files changed, 99 insertions(+), 26 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3617630f3..2aa848f5c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -109,6 +109,7 @@ export interface DocumentOptions { onPointerUp?: ScriptField; dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop + clipboard?: Doc; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; gridGap?: number; // gap between items in masonry view xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index 31601efd4..f9a52d976 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -5,4 +5,10 @@ top: 0; left: 0; touch-action: none; +} + +.clipboardDoc-cont { + position: absolute; + width: 300px; + height: 300px; } \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index ab26687d7..7fb8e7797 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { Touchable } from "./Touchable"; import { observer } from "mobx-react"; import "./GestureOverlay.scss"; -import { computed, observable, action, runInAction } from "mobx"; +import { computed, observable, action, runInAction, IReactionDisposer, reaction } from "mobx"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { InkingControl } from "./InkingControl"; @@ -12,11 +12,13 @@ import { LinkManager } from "../util/LinkManager"; import { DocUtils } from "../documents/Documents"; import { undoBatch } from "../util/UndoManager"; import { Scripting } from "../util/Scripting"; -import { FieldValue, Cast, NumCast } from "../../new_fields/Types"; +import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; -import { Utils, emptyPath, emptyFunction } from "../../Utils"; +import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from "../../Utils"; import { DocumentView } from "./nodes/DocumentView"; +import { Transform } from "../util/Transform"; +import { DocumentContentsView } from "./nodes/DocumentContentsView"; @observer export default class GestureOverlay extends Touchable { @@ -24,13 +26,16 @@ export default class GestureOverlay extends Touchable { @observable private _points: { X: number, Y: number }[] = []; @observable private _palette?: JSX.Element; - @observable private _elements: JSX.Element[]; + @observable private _clipboardDoc?: JSX.Element; @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; + @observable public SavedColor?: string; + @observable public SavedWidth?: number; private _d1: Doc | undefined; private _thumbDoc: Doc | undefined; - private _thumbX?: number; + @observable private _thumbX?: number; + @observable private _thumbY?: number; private thumbIdentifier?: number; private _hands: Map = new Map(); @@ -191,6 +196,7 @@ export default class GestureOverlay extends Touchable { const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); if (thumb.identifier === this.thumbIdentifier) { this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; this._hands.set(thumb.identifier, fingers); return; } @@ -208,6 +214,7 @@ export default class GestureOverlay extends Touchable { runInAction(() => { this._thumbDoc = thumbDoc; this._thumbX = thumb.clientX; + this._thumbY = thumb.clientY; this._palette = ; }); } @@ -407,32 +414,76 @@ export default class GestureOverlay extends Touchable { @computed get elements() { return [ this.props.children, - this._elements, + // this._clipboardDoc, this._palette, this.currentStroke - ] + ]; } @action - openFloatingDoc = (doc: Doc) => { - // this._elements.push( - // - // ) + public openFloatingDoc = (doc: Doc) => { + // const t = new Transform(-(this._clipboardDoc ? (this._thumbX ?? 0) : -350), -(this._thumbY ?? 0) + 350, 1); + // let t = + this._clipboardDoc = + new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + 350, 1)} + ContentScaling={returnOne} + PanelWidth={() => 300} + PanelHeight={() => 300} + renderDepth={0} + backgroundColor={returnEmptyString} + focus={emptyFunction} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} + />; + } + + @action + public closeFloatingDoc = () => { + this._clipboardDoc = undefined; } render() { return (
{this.elements} +
+ {this._clipboardDoc} +
); } } Scripting.addGlobal("GestureOverlay", GestureOverlay); -Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.Color = color; GestureOverlay.Instance.Width = width; }); }); -Scripting.addGlobal(function resetPen() { runInAction(() => { runInAction(() => { GestureOverlay.Instance.Color = "rgb(244, 67, 54)"; GestureOverlay.Instance.Width = 5; }); }); }); \ No newline at end of file +Scripting.addGlobal(function setPen(width: any, color: any) { + runInAction(() => { + GestureOverlay.Instance.SavedColor = GestureOverlay.Instance.Color; + GestureOverlay.Instance.Color = color; + GestureOverlay.Instance.SavedWidth = GestureOverlay.Instance.Width; + GestureOverlay.Instance.Width = width; + }); +}); +Scripting.addGlobal(function resetPen() { + runInAction(() => { + GestureOverlay.Instance.Color = GestureOverlay.Instance.SavedColor ?? "rgb(244, 67, 54)"; + GestureOverlay.Instance.Width = GestureOverlay.Instance.SavedWidth ?? 5; + }); +}); \ No newline at end of file diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index e91c2a01d..8b0638aa1 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -31,6 +31,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { this._dropDisposer && this._dropDisposer(); this._widthDisposer && this._widthDisposer(); this._selectedDisposer && this._selectedDisposer(); + this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { + Cast(pair.layout.proto?.onPointerUp, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); + }); } componentDidMount() { @@ -42,7 +45,22 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { this._selectedDisposer = reaction( () => NumCast(this.props.Document.selectedIndex), - (i) => runInAction(() => this._selectedIndex = i), + (i) => runInAction(() => { + this._selectedIndex = i; + let selected: any = undefined; + this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { + const isSelected = this._selectedIndex === ind; + if (isSelected) { + selected = pair; + } + else { + Cast(pair.layout.proto?.onPointerUp, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); + } + }); + if (selected && selected.layout) { + Cast(selected.layout.proto?.onPointerDown, ScriptField)?.script.run({ this: selected.layout.proto }, console.log); + } + }), { fireImmediately: true } ); } @@ -76,10 +94,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const dref = React.createRef(); const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; - const isSelected = this._selectedIndex === ind; - if (isSelected) { - Cast(pair.layout.proto?.onPointerDown, ScriptField)?.script.run({ this: pair.layout.proto }, console.log); - } return
Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, + clipboard: data.clipboard, onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, ischecked: data.ischecked ? ComputedField.MakeFunction(data.ischecked) : undefined, activePen: data.activePen, pointerHack: true, backgroundColor: data.backgroundColor, removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, @@ -127,7 +128,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { - return Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + userDoc.thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true }); } -- cgit v1.2.3-70-g09d2 From dabb4a9c66083b88eba7bfb07a2614634e124b10 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 20 Jan 2020 16:33:49 -0500 Subject: resizable palette --- src/client/documents/Documents.ts | 4 +- src/client/views/GestureOverlay.scss | 6 ++ src/client/views/GestureOverlay.tsx | 68 +++++++++++++++------- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionLinearView.tsx | 2 +- src/client/views/nodes/ButtonBox.tsx | 5 +- src/new_fields/documentSchemas.ts | 2 + .../authentication/models/current_user_utils.ts | 16 +++-- 8 files changed, 75 insertions(+), 30 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7e7b10ae8..821185518 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -80,7 +80,7 @@ export interface DocumentOptions { isTemplateDoc?: boolean; templates?: List; viewType?: number; - backgroundColor?: string; + backgroundColor?: string | ScriptField; ignoreClick?: boolean; lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed @@ -127,6 +127,8 @@ export interface DocumentOptions { // [key: string]: Opt; pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown isExpanded?: boolean; // is linear view expanded + textTransform?: string; // is linear view expanded + letterSpacing?: string; // is linear view expanded } class EmptyBox { diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index f9a52d976..d980b0a91 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -11,4 +11,10 @@ position: absolute; width: 300px; height: 300px; +} + +.filter-cont { + position: absolute; + background-color: transparent; + border: 1px solid black; } \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 7fb8e7797..e44db4463 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -24,19 +24,25 @@ import { DocumentContentsView } from "./nodes/DocumentContentsView"; export default class GestureOverlay extends Touchable { static Instance: GestureOverlay; - @observable private _points: { X: number, Y: number }[] = []; - @observable private _palette?: JSX.Element; - @observable private _clipboardDoc?: JSX.Element; @observable public Color: string = "rgb(244, 67, 54)"; @observable public Width: number = 5; @observable public SavedColor?: string; @observable public SavedWidth?: number; - private _d1: Doc | undefined; - private _thumbDoc: Doc | undefined; @observable private _thumbX?: number; @observable private _thumbY?: number; + @observable private _pointerY?: number; + @observable private _points: { X: number, Y: number }[] = []; + @observable private _palette?: JSX.Element; + @observable private _clipboardDoc?: JSX.Element; + @observable private _showBounds: boolean = false; + + @computed private get height(): number { return Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 300, 300); } + + private _d1: Doc | undefined; + private _thumbDoc: Doc | undefined; private thumbIdentifier?: number; + private pointerIdentifier?: number; private _hands: Map = new Map(); protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @@ -178,7 +184,7 @@ export default class GestureOverlay extends Touchable { e.stopPropagation(); } - handleHandDown = (e: React.TouchEvent) => { + handleHandDown = async (e: React.TouchEvent) => { const fingers = new Array(); for (let i = 0; i < e.touches.length; i++) { const pt: any = e.touches.item(i); @@ -194,6 +200,22 @@ export default class GestureOverlay extends Touchable { } } const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); + const rightMost = Math.max(...fingers.map(f => f.clientX)); + const leftMost = Math.min(...fingers.map(f => f.clientX)); + let pointer: React.Touch | undefined; + // left hand + if (thumb.clientX === rightMost) { + pointer = fingers.reduce((a, v) => a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v); + } + // right hand + else if (thumb.clientX === leftMost) { + pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v); + } + else { + console.log("not hand"); + } + this.pointerIdentifier = pointer?.identifier; + runInAction(() => this._pointerY = pointer?.clientY); if (thumb.identifier === this.thumbIdentifier) { this._thumbX = thumb.clientX; this._thumbY = thumb.clientY; @@ -201,15 +223,12 @@ export default class GestureOverlay extends Touchable { return; } this.thumbIdentifier = thumb?.identifier; - // fingers.forEach((f) => this.prevPoints.delete(f.identifier)); this._hands.set(thumb.identifier, fingers); const others = fingers.filter(f => f !== thumb); const minX = Math.min(...others.map(f => f.clientX)); const minY = Math.min(...others.map(f => f.clientY)); - // const t = this.getTransform().transformPoint(minX, minY); - // const th = this.getTransform().transformPoint(thumb.clientX, thumb.clientY); - const thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc)); + const thumbDoc = await Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc); if (thumbDoc) { runInAction(() => { this._thumbDoc = thumbDoc; @@ -247,7 +266,7 @@ export default class GestureOverlay extends Touchable { } } const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]); - if (thumb?.identifier === this.thumbIdentifier) { + if (thumb?.identifier && thumb?.identifier === this.thumbIdentifier) { this._hands.set(thumb.identifier, fingers); } @@ -259,6 +278,9 @@ export default class GestureOverlay extends Touchable { this._thumbX = pt.clientX; } } + if (pt && pt.identifier === this.pointerIdentifier) { + this._pointerY = pt.clientY; + } } } @@ -329,8 +351,6 @@ export default class GestureOverlay extends Touchable { return actionPerformed; } - - @action onPointerUp = (e: PointerEvent) => { if (this._points.length > 1) { @@ -414,7 +434,6 @@ export default class GestureOverlay extends Touchable { @computed get elements() { return [ this.props.children, - // this._clipboardDoc, this._palette, this.currentStroke ]; @@ -422,8 +441,6 @@ export default class GestureOverlay extends Touchable { @action public openFloatingDoc = (doc: Doc) => { - // const t = new Transform(-(this._clipboardDoc ? (this._thumbX ?? 0) : -350), -(this._thumbY ?? 0) + 350, 1); - // let t = this._clipboardDoc = new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + 350, 1)} + ScreenToLocalTransform={() => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1)} ContentScaling={returnOne} PanelWidth={() => 300} PanelHeight={() => 300} @@ -462,13 +479,24 @@ export default class GestureOverlay extends Touchable {
{this.elements}
{this._clipboardDoc}
-
); +
+
+
); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 168d2ea18..14ee0dc53 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -46,7 +46,7 @@ import RichTextMenu from '../util/RichTextMenu'; @observer export class MainView extends React.Component { public static Instance: MainView; - private _buttonBarHeight = 75; + private _buttonBarHeight = 35; private _flyoutSizeOnDown = 0; private _urlState: HistoryUtil.DocUrl; private _docBtnRef = React.createRef(); diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 8b0638aa1..77af2dc0e 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -48,7 +48,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { (i) => runInAction(() => { this._selectedIndex = i; let selected: any = undefined; - this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { + this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map(async (pair, ind) => { const isSelected = this._selectedIndex === ind; if (isSelected) { selected = pair; diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index d1272c266..d29fe1711 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -80,7 +80,10 @@ export class ButtonBox extends DocComponent(Butt return (
-
+
{(this.Document.text || this.Document.title)}
diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 24f6224eb..d5c34e128 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -57,6 +57,8 @@ export const documentSchema = createSchema({ yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set LODarea: "number", // area (width*height) where CollectionFreeFormViews switch from a label to rendering contents LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews + letterSpacing: "string", + textTransform: "string" }); export const positionSchema = createSchema({ diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index adeee452c..3e6399d82 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -129,7 +129,7 @@ export class CurrentUserUtils { static setupThumbDoc(userDoc: Doc) { if (!userDoc.thumbDoc) { userDoc.thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { - width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true + width: 100, height: 50, ignoreClick: true, lockedPosition: true, chromeStatus: "disabled", title: "buttons", autoHeight: true, yMargin: 5, isExpanded: true, backgroundColor: "white" }); } return userDoc.thumbDoc; @@ -154,7 +154,8 @@ export class CurrentUserUtils { }); return Docs.Create.ButtonDocument({ - width: 35, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Tools", fontSize: 10, targetContainer: sidebarContainer, + width: 35, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Tools", fontSize: 10, targetContainer: sidebarContainer, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.StackingDocument([dragCreators, color], { width: 500, height: 800, lockedPosition: true, chromeStatus: "disabled", title: "tools stack" }), @@ -179,9 +180,10 @@ export class CurrentUserUtils { }); return Docs.Create.ButtonDocument({ - width: 50, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Library", fontSize: 10, + width: 50, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Library", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.TreeDocument([doc.workspaces as Doc, doc.documents as Doc, doc.recentlyClosed as Doc], { - title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true + title: "Library", xMargin: 5, yMargin: 5, gridGap: 5, forceActive: true, dropAction: "alias", lockedPosition: true, boxShadow: "0 0", }), targetContainer: sidebarContainer, onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel;") @@ -191,7 +193,8 @@ export class CurrentUserUtils { // setup the Search button which will display the search panel. static setupSearchPanel(sidebarContainer: Doc) { return Docs.Create.ButtonDocument({ - width: 50, height: 35, backgroundColor: "#222222", color: "lightgrey", title: "Search", fontSize: 10, + width: 50, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Search", fontSize: 10, + letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.QueryDocument({ title: "search stack", ignoreClick: true }), @@ -214,7 +217,8 @@ export class CurrentUserUtils { // Finally, setup the list of buttons to display in the sidebar doc.sidebarButtons = Docs.Create.StackingDocument([doc.SearchBtn as Doc, doc.LibraryBtn as Doc, doc.ToolsBtn as Doc], { width: 500, height: 80, boxShadow: "0 0", sectionFilter: "title", hideHeadings: true, ignoreClick: true, - backgroundColor: "lightgrey", chromeStatus: "disabled", title: "library stack" + backgroundColor: "rgb(100, 100, 100)", chromeStatus: "disabled", title: "library stack", + yMargin: 10, }); } -- cgit v1.2.3-70-g09d2 From 42d82210f3bbfce5d79c9fe93889ddfe8944d5be Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Mon, 20 Jan 2020 17:28:01 -0500 Subject: stufs --- src/client/views/GestureOverlay.tsx | 102 ++++++++++++--------- .../authentication/models/current_user_utils.ts | 3 +- 2 files changed, 63 insertions(+), 42 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index e44db4463..84d089b47 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -15,7 +15,7 @@ import { Scripting } from "../util/Scripting"; import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import Palette from "./Palette"; -import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from "../../Utils"; +import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange } from "../../Utils"; import { DocumentView } from "./nodes/DocumentView"; import { Transform } from "../util/Transform"; import { DocumentContentsView } from "./nodes/DocumentContentsView"; @@ -28,6 +28,7 @@ export default class GestureOverlay extends Touchable { @observable public Width: number = 5; @observable public SavedColor?: string; @observable public SavedWidth?: number; + @observable public Tool: ToolglassTools = ToolglassTools.None; @observable private _thumbX?: number; @observable private _thumbY?: number; @@ -35,9 +36,9 @@ export default class GestureOverlay extends Touchable { @observable private _points: { X: number, Y: number }[] = []; @observable private _palette?: JSX.Element; @observable private _clipboardDoc?: JSX.Element; - @observable private _showBounds: boolean = false; @computed private get height(): number { return Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 300, 300); } + @computed private get showBounds() { return this.Tool !== ToolglassTools.None; } private _d1: Doc | undefined; private _thumbDoc: Doc | undefined; @@ -357,51 +358,62 @@ export default class GestureOverlay extends Touchable { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); - const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); - let actionPerformed = false; - if (result && result.Score > 0.7) { - switch (result.Name) { - case GestureUtils.Gestures.Box: - const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); - target?.dispatchEvent(new CustomEvent("dashOnGesture", + const xInGlass = points[0].X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && points[0].X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height; + const yInGlass = points[0].Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && points[0].Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER); + + if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) { + switch (this.Tool) { + case ToolglassTools.InkToText: + break; + } + } + else { + const result = GestureUtils.GestureRecognizer.Recognize(new Array(points)); + let actionPerformed = false; + if (result && result.Score > 0.7) { + switch (result.Name) { + case GestureUtils.Gestures.Box: + const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); + target?.dispatchEvent(new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Box, + bounds: B + } + })); + actionPerformed = true; + break; + case GestureUtils.Gestures.Line: + actionPerformed = this.handleLineGesture(); + break; + case GestureUtils.Gestures.Scribble: + console.log("scribble"); + break; + } + if (actionPerformed) { + this._points = []; + } + } + + if (!actionPerformed) { + const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); + target?.dispatchEvent( + new CustomEvent("dashOnGesture", { bubbles: true, detail: { points: this._points, - gesture: GestureUtils.Gestures.Box, + gesture: GestureUtils.Gestures.Stroke, bounds: B } - })); - actionPerformed = true; - break; - case GestureUtils.Gestures.Line: - actionPerformed = this.handleLineGesture(); - break; - case GestureUtils.Gestures.Scribble: - console.log("scribble"); - break; - } - if (actionPerformed) { + } + ) + ); this._points = []; } } - - if (!actionPerformed) { - const target = document.elementFromPoint(this._points[0].X, this._points[0].Y); - target?.dispatchEvent( - new CustomEvent("dashOnGesture", - { - bubbles: true, - detail: { - points: this._points, - gesture: GestureUtils.Gestures.Stroke, - bounds: B - } - } - ) - ); - this._points = []; - } } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -491,16 +503,24 @@ export default class GestureOverlay extends Touchable { transform: `translate(${this._thumbX}px, ${(this._thumbY ?? 0) - this.height}px)`, height: this.height, width: this.height, - pointerEvents: this._showBounds ? "unset" : "none", - touchAction: this._showBounds ? "unset" : "none", - display: this._showBounds ? "unset" : "none", + pointerEvents: "none", + touchAction: "none", + display: this.showBounds ? "unset" : "none", }}>
); } } +export enum ToolglassTools { + InkToText = "inktotext", + None = "none", +} + Scripting.addGlobal("GestureOverlay", GestureOverlay); +Scripting.addGlobal(function setToolglass(tool: any) { + runInAction(() => GestureOverlay.Instance.Tool = tool); +}); Scripting.addGlobal(function setPen(width: any, color: any) { runInAction(() => { GestureOverlay.Instance.SavedColor = GestureOverlay.Instance.Color; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 3e6399d82..5375c8ac8 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -114,7 +114,8 @@ export class CurrentUserUtils { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, ischecked?: string, clipboard?: Doc, activePen?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, -- cgit v1.2.3-70-g09d2 From d528b9d19a685d8447a9d54715dc94cd66034852 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 20 Jan 2020 21:18:10 -0500 Subject: fixed linking to text region. fixed link displays. removed old tooltiptextmenu. fixed brushes in richTextMenu. --- src/client/util/ProsemirrorExampleTransfer.ts | 2 - src/client/util/RichTextMenu.tsx | 25 +- src/client/util/RichTextRules.ts | 4 +- src/client/util/RichTextSchema.tsx | 3 +- src/client/util/TooltipLinkingMenu.tsx | 68 -- src/client/util/TooltipTextMenu.scss | 372 ------- src/client/util/TooltipTextMenu.tsx | 1042 -------------------- src/client/views/DocumentButtonBar.tsx | 8 +- src/client/views/DocumentDecorations.tsx | 8 - src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 22 +- .../authentication/models/current_user_utils.ts | 4 +- 12 files changed, 37 insertions(+), 1523 deletions(-) delete mode 100644 src/client/util/TooltipLinkingMenu.tsx delete mode 100644 src/client/util/TooltipTextMenu.scss delete mode 100644 src/client/util/TooltipTextMenu.tsx (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index c028dbf8b..da3815181 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -5,9 +5,7 @@ import { Schema } from "prosemirror-model"; import { liftListItem, sinkListItem } from "./prosemirrorPatches.js"; import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; -import { TooltipTextMenu } from "./TooltipTextMenu"; import { SelectionManager } from "./SelectionManager"; -import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx index 419d7caf9..f070589ae 100644 --- a/src/client/util/RichTextMenu.tsx +++ b/src/client/util/RichTextMenu.tsx @@ -32,8 +32,9 @@ export default class RichTextMenu extends AntimodeMenu { public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable private view?: EditorView; - private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; + public editorProps: FieldViewProps & FormattedTextBoxProps | undefined; + public _brushMap: Map> = new Map(); private fontSizeOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; private fontFamilyOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; private listTypeOptions: { node: NodeType | any | null, title: string, label: string, command: any, style?: {} }[]; @@ -142,6 +143,17 @@ export default class RichTextMenu extends AntimodeMenu { this.updateFromDash(view, lastState, this.editorProps); } + + public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { + if (this.view) { + const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId }); + this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). + addMark(this.view.state.selection.from, this.view.state.selection.to, link)); + return this.view.state.selection.$from.nodeAfter?.text || ""; + } + return ""; + } + @action public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { if (!view) { @@ -417,6 +429,15 @@ export default class RichTextMenu extends AntimodeMenu { @action toggleBrushDropdown() { this.showBrushDropdown = !this.showBrushDropdown; } + // todo: add brushes to brushMap to save with a style name + onBrushNameKeyPress = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + RichTextMenu.Instance.brushMarks && RichTextMenu.Instance._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks); + this._brushNameRef.current!.style.background = "lightGray"; + } + } + _brushNameRef = React.createRef(); + createBrushButton() { const self = this; function onBrushClick(e: React.PointerEvent) { @@ -447,7 +468,7 @@ export default class RichTextMenu extends AntimodeMenu {

{label}

- {/* */} +
; return ( diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 29b378299..e20abb04c 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -5,11 +5,11 @@ import { NodeSelection, TextSelection } from "prosemirror-state"; import { NumCast, Cast } from "../../new_fields/Types"; import { Doc } from "../../new_fields/Doc"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; -import { TooltipTextMenuManager } from "../util/TooltipTextMenu"; import { Docs, DocUtils } from "../documents/Documents"; import { Id } from "../../new_fields/FieldSymbols"; import { DocServer } from "../DocServer"; import { returnFalse, Utils } from "../../Utils"; +import RichTextMenu from "./RichTextMenu"; export const inpRules = { rules: [ @@ -264,7 +264,7 @@ export const inpRules = { new RegExp(/%[a-z]+$/), (state, match, start, end) => { const color = match[0].substring(1, match[0].length); - const marks = TooltipTextMenuManager.Instance._brushMap.get(color); + const marks = RichTextMenu.Instance._brushMap.get(color); if (marks) { const tr = state.tr.deleteRange(start, end); return marks ? Array.from(marks).reduce((tr, m) => tr.addStoredMark(m), tr) : tr; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index ef90a7294..1f70cafc4 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -855,7 +855,8 @@ export class FootnoteView { }), new Plugin({ view(newView) { - return FormattedTextBox.getToolTip(newView); + // TODO -- make this work with RichTextMenu + // return FormattedTextBox.getToolTip(newView); } }) ], diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx deleted file mode 100644 index b46675a04..000000000 --- a/src/client/util/TooltipLinkingMenu.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { EditorState } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import { FieldViewProps } from "../views/nodes/FieldView"; -import "./TooltipTextMenu.scss"; - -//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. -export class TooltipLinkingMenu { - - private tooltip: HTMLElement; - private view: EditorView; - private editorProps: FieldViewProps; - - constructor(view: EditorView, editorProps: FieldViewProps) { - this.view = view; - this.editorProps = editorProps; - this.tooltip = document.createElement("div"); - this.tooltip.className = "tooltipMenu linking"; - - //add the div which is the tooltip - view.dom.parentNode!.parentNode!.appendChild(this.tooltip); - - const target = "https://www.google.com"; - - const link = document.createElement("a"); - link.href = target; - link.textContent = target; - link.target = "_blank"; - link.style.color = "white"; - this.tooltip.appendChild(link); - - this.update(view, undefined); - } - - //updates the tooltip menu when the selection changes - update(view: EditorView, lastState: EditorState | undefined) { - const state = view.state; - // Don't do anything if the document/selection didn't change - if (lastState && lastState.doc.eq(state.doc) && - lastState.selection.eq(state.selection)) return; - - // Hide the tooltip if the selection is empty - if (state.selection.empty) { - this.tooltip.style.display = "none"; - return; - } - - console.log("STORED:"); - console.log(state.doc.content.firstChild!.content); - - // Otherwise, reposition it and update its content - this.tooltip.style.display = ""; - const { from, to } = state.selection; - const start = view.coordsAtPos(from), end = view.coordsAtPos(to); - // The box in which the tooltip is positioned, to use as base - const box = this.tooltip.offsetParent!.getBoundingClientRect(); - // Find a center-ish x position from the selection endpoints (when - // crossing lines, end may be more to the left) - const left = Math.max((start.left + end.left) / 2, start.left + 3); - this.tooltip.style.left = (left - box.left) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - const width = Math.abs(start.left - end.left) / 2 * this.editorProps.ScreenToLocalTransform().Scale; - const mid = Math.min(start.left, end.left) + width; - - this.tooltip.style.width = "auto"; - this.tooltip.style.bottom = (box.bottom - start.top) * this.editorProps.ScreenToLocalTransform().Scale + "px"; - } - - destroy() { this.tooltip.remove(); } -} diff --git a/src/client/util/TooltipTextMenu.scss b/src/client/util/TooltipTextMenu.scss deleted file mode 100644 index 2a38fe118..000000000 --- a/src/client/util/TooltipTextMenu.scss +++ /dev/null @@ -1,372 +0,0 @@ -@import "../views/globalCssVariables"; -.ProseMirror-menu-dropdown-wrap { - display: inline-block; - position: relative; -} - -.ProseMirror-menu-dropdown { - vertical-align: 1px; - cursor: pointer; - position: relative; - padding: 0 15px 0 4px; - background: white; - border-radius: 2px; - text-align: left; - font-size: 12px; - white-space: nowrap; - margin-right: 4px; - - &:after { - content: ""; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid currentColor; - opacity: .6; - position: absolute; - right: 4px; - top: calc(50% - 2px); - } -} - -.ProseMirror-menu-submenu-wrap { - position: relative; - margin-right: -4px; -} - -.ProseMirror-menu-dropdown-menu, -.ProseMirror-menu-submenu { - font-size: 12px; - background: white; - border: 1px solid rgb(223, 223, 223); - min-width: 40px; - z-index: 50000; - position: absolute; - box-sizing: content-box; - - .ProseMirror-menu-dropdown-item { - cursor: pointer; - z-index: 100000; - text-align: left; - padding: 3px; - - &:hover { - background-color: $light-color-secondary; - } - } -} - - -.ProseMirror-menu-submenu-label:after { - content: ""; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - border-left: 4px solid currentColor; - opacity: .6; - position: absolute; - right: 4px; - top: calc(50% - 4px); -} - - .ProseMirror-icon { - display: inline-block; - // line-height: .8; - // vertical-align: -2px; /* Compensate for padding */ - // padding: 2px 8px; - cursor: pointer; - - &.ProseMirror-menu-disabled { - cursor: default; - } - - svg { - fill:white; - height: 1em; - } - - span { - vertical-align: text-top; - } - } - -.wrapper { - position: absolute; - pointer-events: all; - display: flex; - align-items: center; - transform: translateY(-85px); - - height: 35px; - background: #323232; - border-radius: 6px; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - -} - -.tooltipMenu, .basic-tools { - z-index: 20000; - pointer-events: all; - padding: 3px; - padding-bottom: 5px; - display: flex; - align-items: center; - - .ProseMirror-example-setup-style hr { - padding: 2px 10px; - border: none; - margin: 1em 0; - } - - .ProseMirror-example-setup-style hr:after { - content: ""; - display: block; - height: 1px; - background-color: silver; - line-height: 2px; - } -} - -.menuicon { - width: 25px; - height: 25px; - cursor: pointer; - text-align: center; - line-height: 25px; - margin: 0 2px; - border-radius: 3px; - - &:hover { - background-color: black; - - #link-drag { - background-color: black; - } - } - - &> * { - margin-top: 50%; - margin-left: 50%; - transform: translate(-50%, -50%); - } - - svg { - fill: inherit; - width: 18px; - height: 18px; - } -} - -.menuicon-active { - width: 25px; - height: 25px; - cursor: pointer; - text-align: center; - line-height: 25px; - margin: 0 2px; - border-radius: 3px; - - &:hover { - background-color: black; - } - - &> * { - margin-top: 50%; - margin-left: 50%; - transform: translate(-50%, -50%); - } - - svg { - fill: greenyellow; - width: 18px; - height: 18px; - } -} - -.colorPicker { - position: relative; - - svg { - width: 18px; - height: 18px; - // margin-top: 11px; - } - - .buttonColor { - position: absolute; - top: 24px; - left: 1px; - width: 24px; - height: 4px; - margin-top: 0; - } -} - -#link-drag { - background-color: #323232; -} - -.underline svg { - margin-top: 13px; -} - - .font-size-indicator { - font-size: 12px; - padding-right: 0px; - } - .summarize{ - color: white; - height: 20px; - text-align: center; - } - - -.brush{ - display: inline-block; - width: 1em; - height: 1em; - stroke-width: 0; - stroke: currentColor; - fill: currentColor; - margin-right: 15px; -} - -.brush-active{ - display: inline-block; - width: 1em; - height: 1em; - stroke-width: 3; - fill: greenyellow; - margin-right: 15px; -} - -.dragger-wrapper { - color: #eee; - height: 22px; - padding: 0 5px; - box-sizing: content-box; - cursor: grab; - - .dragger { - width: 18px; - height: 100%; - display: flex; - justify-content: space-evenly; - } - - .dragger-line { - width: 2px; - height: 100%; - background-color: black; - } -} - -.button-dropdown-wrapper { - display: flex; - align-content: center; - - &:hover { - background-color: black; - } -} - -.buttonSettings-dropdown { - - &.ProseMirror-menu-dropdown { - width: 10px; - height: 25px; - margin: 0; - padding: 0 2px; - background-color: #323232; - text-align: center; - - &:after { - border-top: 4px solid white; - right: 2px; - } - - &:hover { - background-color: black; - } - } - - &.ProseMirror-menu-dropdown-menu { - min-width: 150px; - left: -27px; - top: 31px; - background-color: #323232; - border: 1px solid #4d4d4d; - color: $light-color-secondary; - // border: none; - // border: 1px solid $light-color-secondary; - border-radius: 0 6px 6px 6px; - padding: 3px; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - - .ProseMirror-menu-dropdown-item{ - cursor: default; - - &:last-child { - border-bottom: none; - } - - &:hover { - background-color: #323232; - } - - .button-setting, .button-setting-disabled { - padding: 2px; - border-radius: 2px; - } - - .button-setting:hover { - cursor: pointer; - background-color: black; - } - - .separated-button { - border-top: 1px solid $light-color-secondary; - padding-top: 6px; - } - - input { - color: black; - border: none; - border-radius: 1px; - padding: 3px; - } - - button { - padding: 6px; - background-color: #323232; - border: 1px solid black; - border-radius: 1px; - - &:hover { - background-color: black; - } - } - } - - - } -} - -.colorPicker-wrapper { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - margin-top: 3px; - margin-left: -3px; - width: calc(100% + 6px); -} - -button.colorPicker { - width: 20px; - height: 20px; - border-radius: 15px !important; - margin: 3px; - border: none !important; - - &.active { - border: 2px solid white !important; - } -} \ No newline at end of file diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx deleted file mode 100644 index 1c15dca7f..000000000 --- a/src/client/util/TooltipTextMenu.tsx +++ /dev/null @@ -1,1042 +0,0 @@ -import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css -import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model"; -import { wrapInList } from 'prosemirror-schema-list'; -import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; -import { EditorView } from "prosemirror-view"; -import { Doc, Field, Opt } from "../../new_fields/Doc"; -import { Utils } from "../../Utils"; -import { DocServer } from "../DocServer"; -import { FieldViewProps } from "../views/nodes/FieldView"; -import { FormattedTextBoxProps } from "../views/nodes/FormattedTextBox"; -import { LinkManager } from "./LinkManager"; -import { schema } from "./RichTextSchema"; -import "./TooltipTextMenu.scss"; -import { Cast, NumCast, StrCast } from '../../new_fields/Types'; -import { updateBullets } from './ProsemirrorExampleTransfer'; -import { DocumentDecorations } from '../views/DocumentDecorations'; -import { SelectionManager } from './SelectionManager'; -import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../new_fields/SchemaHeaderField'; -const { toggleMark } = require("prosemirror-commands"); - -//appears above a selection of text in a RichTextBox to give user options such as Bold, Italics, etc. -export class TooltipTextMenu { - public static Toolbar: HTMLDivElement | undefined; - - // editor state properties - private view: EditorView; - private editorProps: FieldViewProps & FormattedTextBoxProps | undefined; - - private fontStyles: Mark[] = []; - private fontSizes: Mark[] = []; - private _marksToDoms: Map = new Map(); - private _collapsed: boolean = false; - - // editor doms - public tooltip: HTMLElement = document.createElement("div"); - private wrapper: HTMLDivElement = document.createElement("div"); - - // editor button doms - private colorDom?: Node; - private colorDropdownDom?: Node; - private linkDom?: Node; - private highighterDom?: Node; - private highlighterDropdownDom?: Node; - private linkDropdownDom?: Node; - private _brushdom?: Node; - private _brushDropdownDom?: Node; - private fontSizeDom?: Node; - private fontStyleDom?: Node; - private basicTools?: HTMLElement; - - static createDiv(className: string) { const div = document.createElement("div"); div.className = className; return div; } - static createSpan(className: string) { const div = document.createElement("span"); div.className = className; return div; } - constructor(view: EditorView) { - this.view = view; - - // initialize the tooltip -- sets this.tooltip - this.initTooltip(view); - - // initialize the wrapper - this.wrapper = TooltipTextMenu.createDiv("wrapper"); - this.wrapper.appendChild(this.tooltip); - - TooltipTextMenu.Toolbar = this.wrapper; - } - - private async initTooltip(view: EditorView) { - const self = this; - this.tooltip = TooltipTextMenu.createDiv("tooltipMenu"); - this.basicTools = TooltipTextMenu.createDiv("basic-tools"); - - const svgIcon = (name: string, title: string = name, dpath: string) => { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", dpath); - svg.appendChild(path); - - const span = TooltipTextMenu.createSpan(name + " menuicon"); - span.title = title; - span.appendChild(svg); - - return span; - }; - - const basicItems = [ // init basicItems in minimized toolbar -- paths to svgs are obtained from fontawesome - { mark: schema.marks.strong, dom: svgIcon("strong", "Bold", "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z") }, - { mark: schema.marks.em, dom: svgIcon("em", "Italic", "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z") }, - { mark: schema.marks.underline, dom: svgIcon("underline", "Underline", "M32 64h32v160c0 88.22 71.78 160 160 160s160-71.78 160-160V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H272a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h32v160a80 80 0 0 1-160 0V64h32a16 16 0 0 0 16-16V16a16 16 0 0 0-16-16H32a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm400 384H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z") }, - ]; - const items = [ // init items in full size toolbar - { mark: schema.marks.strikethrough, dom: svgIcon("strikethrough", "Strikethrough", "M496 224H293.9l-87.17-26.83A43.55 43.55 0 0 1 219.55 112h66.79A49.89 49.89 0 0 1 331 139.58a16 16 0 0 0 21.46 7.15l42.94-21.47a16 16 0 0 0 7.16-21.46l-.53-1A128 128 0 0 0 287.51 32h-68a123.68 123.68 0 0 0-123 135.64c2 20.89 10.1 39.83 21.78 56.36H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h480a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm-180.24 96A43 43 0 0 1 336 356.45 43.59 43.59 0 0 1 292.45 400h-66.79A49.89 49.89 0 0 1 181 372.42a16 16 0 0 0-21.46-7.15l-42.94 21.47a16 16 0 0 0-7.16 21.46l.53 1A128 128 0 0 0 224.49 480h68a123.68 123.68 0 0 0 123-135.64 114.25 114.25 0 0 0-5.34-24.36z") }, - { mark: schema.marks.superscript, dom: svgIcon("superscript", "Superscript", "M496 160h-16V16a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 64h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, - { mark: schema.marks.subscript, dom: svgIcon("subscript", "Subscript", "M496 448h-16V304a16 16 0 0 0-16-16h-48a16 16 0 0 0-14.29 8.83l-16 32A16 16 0 0 0 400 352h16v96h-16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h96a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM336 64h-67a16 16 0 0 0-13.14 6.87l-79.9 115-79.9-115A16 16 0 0 0 83 64H16A16 16 0 0 0 0 80v48a16 16 0 0 0 16 16h33.48l77.81 112-77.81 112H16a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h67a16 16 0 0 0 13.14-6.87l79.9-115 79.9 115A16 16 0 0 0 269 448h67a16 16 0 0 0 16-16v-48a16 16 0 0 0-16-16h-33.48l-77.81-112 77.81-112H336a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16z") }, - ]; - - basicItems.map(({ dom, mark }) => this.basicTools ?.appendChild(dom.cloneNode(true))); - basicItems.concat(items).forEach(({ dom, mark }) => { - this.tooltip.appendChild(dom); - this._marksToDoms.set(mark, dom); - - //pointer down handler to activate button effects - dom.addEventListener("pointerdown", e => { - this.view.focus(); - if (dom.contains(e.target as Node)) { - e.preventDefault(); - e.stopPropagation(); - toggleMark(mark)(this.view.state, this.view.dispatch, this.view); - this.updateHighlightStateOfButtons(); - } - }); - }); - - // summarize menu - this.highighterDom = this.createHighlightTool().render(this.view).dom; - this.highlighterDropdownDom = this.createHighlightDropdown().render(this.view).dom; - this.tooltip.appendChild(this.highighterDom); - this.tooltip.appendChild(this.highlighterDropdownDom); - - // color menu - this.colorDom = this.createColorTool().render(this.view).dom; - this.colorDropdownDom = this.createColorDropdown().render(this.view).dom; - this.tooltip.appendChild(this.colorDom); - this.tooltip.appendChild(this.colorDropdownDom); - - // link menu - this.linkDom = this.createLinkTool().render(this.view).dom; - this.linkDropdownDom = this.createLinkDropdown("").render(this.view).dom; - this.tooltip.appendChild(this.linkDom); - this.tooltip.appendChild(this.linkDropdownDom); - - // list of font styles - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 7 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 8 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 9 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 10 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 12 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 14 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 16 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 18 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 20 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 24 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 32 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 48 })); - this.fontSizes.push(schema.marks.pFontSize.create({ fontSize: 72 })); - - // font sizes - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Times New Roman" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Arial" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Georgia" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Comic Sans MS" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Tahoma" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Impact" })); - this.fontStyles.push(schema.marks.pFontFamily.create({ family: "Crimson Text" })); - - - // init brush tool - this._brushdom = this.createBrushTool().render(this.view).dom; - this.tooltip.appendChild(this._brushdom); - this._brushDropdownDom = this.createBrushDropdown().render(this.view).dom; - this.tooltip.appendChild(this._brushDropdownDom); - - // summarizer tool - const summarizer = new MenuItem({ - title: "Summarize", - label: "Summarize", - icon: icons.join, - css: "fill:white;", - class: "menuicon", - execEvent: "", - run: (state, dispatch) => TooltipTextMenu.insertSummarizer(state, dispatch) - }); - this.tooltip.appendChild(summarizer.render(this.view).dom); - - // list types dropdown - const listDropdownTypes = [{ mapStyle: "bullet", label: ":" }, { mapStyle: "decimal", label: "1.1" }, { mapStyle: "multi", label: "A.1" }, { label: "X" }]; - const listTypes = new Dropdown(listDropdownTypes.map(({ mapStyle, label }) => - new MenuItem({ - title: "Set Bullet Style", - label: label, - execEvent: "", - class: "dropdown-item", - css: "color: black; width: 40px;", - enable() { return true; }, - run() { - const marks = self.view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks()); - if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => { - const tx3 = updateBullets(tx2, schema, mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx2); - })) { - const tx2 = view.state.tr; - const tx3 = updateBullets(tx2, schema, mapStyle); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - - view.dispatch(tx3); - } - } - })), { label: ":", css: "color:black; width: 40px;" }); - this.tooltip.appendChild(listTypes.render(this.view).dom); - - await this.updateFromDash(view, undefined, undefined); - - const draggerWrapper = TooltipTextMenu.createDiv("dragger-wrapper"); - const dragger = TooltipTextMenu.createDiv("dragger"); - dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); - dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); - dragger.appendChild(TooltipTextMenu.createSpan("dragger-line")); - draggerWrapper.appendChild(dragger); - this.wrapper.appendChild(draggerWrapper); - this.setupDragElementInteractions(draggerWrapper); - } - - setupDragElementInteractions(elmnt: HTMLElement) { - var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - if (elmnt) { - // if present, the header is where you move the DIV from: - elmnt.onpointerdown = dragPointerDown; - elmnt.ondblclick = onClick; - } - const self = this; - - function dragPointerDown(e: PointerEvent) { - e = e || window.event; - e.preventDefault(); - // get the mouse cursor position at startup: - pos3 = e.clientX; - pos4 = e.clientY; - document.onpointerup = closeDragElement; - // call a function whenever the cursor moves: - document.onpointermove = elementDrag; - } - - function onClick(e: MouseEvent) { - self._collapsed = !self._collapsed; - const children = self.wrapper.childNodes; - if (self._collapsed && children.length > 0) { - self.wrapper.removeChild(self.tooltip); - self.basicTools && self.wrapper.prepend(self.basicTools); - } - else { - self.wrapper.prepend(self.tooltip); - self.basicTools && self.wrapper.removeChild(self.basicTools); - } - } - - function elementDrag(e: PointerEvent) { - e = e || window.event; - //e.preventDefault(); - // calculate the new cursor position: - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - // set the element's new position: - // elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; - // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; - - self.wrapper.style.top = (self.wrapper.offsetTop - pos2) + "px"; - self.wrapper.style.left = (self.wrapper.offsetLeft - pos1) + "px"; - } - - function closeDragElement() { - // stop moving when mouse button is released: - document.onpointerup = null; - document.onpointermove = null; - } - } - - //label of dropdown will change to given label - updateFontSizeDropdown(label: string) { - //font SIZES - const fontSizeBtns: MenuItem[] = []; - const self = this; - this.fontSizes.forEach(mark => - fontSizeBtns.push(new MenuItem({ - title: "Set Font Size", - label: String(mark.attrs.fontSize), - execEvent: "", - class: "dropdown-item", - css: "color: black; width: 50px;", - enable() { return true; }, - run() { - const size = mark.attrs.fontSize; - if (size) { self.updateFontSizeDropdown(String(size) + " pt"); } - if (self.editorProps) { - const ruleProvider = self.editorProps.ruleProvider; - const heading = NumCast(self.editorProps.Document.heading); - if (ruleProvider && heading) { - ruleProvider["ruleSize_" + heading] = size; - } - } - TooltipTextMenu.setMark(self.view.state.schema.marks.pFontSize.create({ fontSize: size }), self.view.state, self.view.dispatch); - } - }))); - - const newfontSizeDom = (new Dropdown(fontSizeBtns, { label: label, css: "color:black; min-width: 60px;" }) as MenuItem).render(this.view).dom; - if (this.fontSizeDom) { - this.tooltip.replaceChild(newfontSizeDom, this.fontSizeDom); - } - else { - this.tooltip.appendChild(newfontSizeDom); - } - this.fontSizeDom = newfontSizeDom; - } - - //label of dropdown will change to given label - updateFontStyleDropdown(label: string) { - //font STYLES - const fontBtns: MenuItem[] = []; - const self = this; - this.fontStyles.forEach(mark => - fontBtns.push(new MenuItem({ - title: "Set Font Family", - label: mark.attrs.family, - execEvent: "", - class: "dropdown-item", - css: "color: black; font-family: " + mark.attrs.family + ", sans-serif; width: 125px;", - enable() { return true; }, - run() { - const fontName = mark.attrs.family; - if (fontName) { self.updateFontStyleDropdown(fontName); } - if (self.editorProps) { - const ruleProvider = self.editorProps.ruleProvider; - const heading = NumCast(self.editorProps.Document.heading); - if (ruleProvider && heading) { - ruleProvider["ruleFont_" + heading] = fontName; - } - } - TooltipTextMenu.setMark(self.view.state.schema.marks.pFontFamily.create({ family: fontName }), self.view.state, self.view.dispatch); - } - }))); - - const newfontStyleDom = (new Dropdown(fontBtns, { label: label, css: "color:black; width: 125px;" }) as MenuItem).render(this.view).dom; - if (this.fontStyleDom) { - this.tooltip.replaceChild(newfontStyleDom, this.fontStyleDom); - } - else { - this.tooltip.appendChild(newfontStyleDom); - } - this.fontStyleDom = newfontStyleDom; - } - async getTextLinkTargetTitle() { - const node = this.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type.name === "link"); - if (link) { - const href = link.attrs.href; - if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - if (linkclicked) { - const linkDoc = await DocServer.GetRefField(linkclicked); - if (linkDoc instanceof Doc) { - const anchor1 = await Cast(linkDoc.anchor1, Doc); - const anchor2 = await Cast(linkDoc.anchor2, Doc); - const currentDoc = SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0].props.Document; - if (currentDoc && anchor1 && anchor2) { - if (Doc.AreProtosEqual(currentDoc, anchor1)) { - return StrCast(anchor2.title); - } - if (Doc.AreProtosEqual(currentDoc, anchor2)) { - return StrCast(anchor1.title); - } - } - } - } - } else { - return href; - } - } else { - return link.attrs.title; - } - } - } - - // LINK TOOL - createLinkTool(active: boolean = false) { - return new MenuItem({ - title: "Link tool", - label: "Link tool", - icon: icons.link, - css: "fill:white;", - class: active ? "menuicon-active" : "menuicon", - execEvent: "", - run: async (state, dispatch) => { }, - active: (state) => true - }); - } - - createLinkDropdown(targetTitle: string) { - const input = document.createElement("input"); - - // menu item for input for hyperlink url - // TODO: integrate search to allow users to search for a doc to link to - const linkInfo = new MenuItem({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - const p = document.createElement("p"); - p.textContent = "Linked to:"; - - input.type = "text"; - input.placeholder = "Enter URL"; - if (targetTitle) input.value = targetTitle; - input.onclick = (e: MouseEvent) => { - input.select(); - input.focus(); - }; - - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(input); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { event.stopPropagation(); } - }); - - // menu item to update/apply the hyperlink to the selected text - const linkApply = new MenuItem({ - title: "", - execEvent: "", - class: "", - css: "", - render() { - const button = document.createElement("button"); - button.className = "link-url-button"; - button.textContent = "Apply hyperlink"; - return button; - }, - enable() { return false; }, - run: async (state, dispatch, view, event) => { - event.stopPropagation(); - let node = this.view.state.selection.$from.nodeAfter; - let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: input.value, location: "onRight" }); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); - this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - node = this.view.state.selection.$from.nodeAfter; - link = node && node.marks.find(m => m.type.name === "link"); - - // update link menu - const linkDom = self.createLinkTool(true).render(self.view).dom; - const linkDropdownDom = self.createLinkDropdown(await self.getTextLinkTargetTitle()).render(self.view).dom; - self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom); - self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom); - self.linkDom = linkDom; - self.linkDropdownDom = linkDropdownDom; - } - }); - - // menu item to remove the link - // TODO: allow this to be undoable - const self = this; - const deleteLink = new MenuItem({ - title: "Delete link", - execEvent: "", - class: "separated-button", - css: "", - render() { - const button = document.createElement("button"); - button.textContent = "Remove link"; - - const wrapper = document.createElement("div"); - wrapper.appendChild(button); - return wrapper; - }, - enable() { return true; }, - async run() { - // delete the link - const node = self.view.state.selection.$from.nodeAfter; - const link = node && node.marks.find(m => m.type === self.view.state.schema.marks.link); - const href = link!.attrs.href; - if (href ?.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - linkclicked && DocServer.GetRefField(linkclicked).then(async linkDoc => { - if (linkDoc instanceof Doc) { - LinkManager.Instance.deleteLink(linkDoc); - self.view.dispatch(self.view.state.tr.removeMark(self.view.state.selection.from, self.view.state.selection.to, self.view.state.schema.marks.link)); - } - }); - } - // update link menu - const linkDom = self.createLinkTool(false).render(self.view).dom; - const linkDropdownDom = self.createLinkDropdown("").render(self.view).dom; - self.linkDom && self.tooltip.replaceChild(linkDom, self.linkDom); - self.linkDropdownDom && self.tooltip.replaceChild(linkDropdownDom, self.linkDropdownDom); - self.linkDom = linkDom; - self.linkDropdownDom = linkDropdownDom; - } - }); - - return new Dropdown(targetTitle ? [linkInfo, linkApply, deleteLink] : [linkInfo, linkApply], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { - const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, targetId: targetDocId }); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). - addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - return this.view.state.selection.$from.nodeAfter ?.text || ""; - } - - // SUMMARIZER TOOL - static insertSummarizer(state: EditorState, dispatch: any) { - if (!state.selection.empty) { - const mark = state.schema.marks.summarize.create(); - const tr = state.tr.addMark(state.selection.from, state.selection.to, mark); - const content = tr.selection.content(); - const newNode = state.schema.nodes.summary.create({ visibility: false, text: content, textslice: content.toJSON() }); - dispatch ?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); - } - } - - // HIGHLIGHTER TOOL - createHighlightTool() { - return new MenuItem({ - title: "Highlight", - css: "fill:white;", - class: "menuicon", - execEvent: "", - render() { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", "M0 479.98L99.92 512l35.45-35.45-67.04-67.04L0 479.98zm124.61-240.01a36.592 36.592 0 0 0-10.79 38.1l13.05 42.83-50.93 50.94 96.23 96.23 50.86-50.86 42.74 13.08c13.73 4.2 28.65-.01 38.15-10.78l35.55-41.64-173.34-173.34-41.52 35.44zm403.31-160.7l-63.2-63.2c-20.49-20.49-53.38-21.52-75.12-2.35L190.55 183.68l169.77 169.78L530.27 154.4c19.18-21.74 18.15-54.63-2.35-75.13z"); - svg.appendChild(path); - - const color = TooltipTextMenu.createDiv("buttonColor"); - color.style.backgroundColor = TooltipTextMenuManager.Instance.highlighter.toString(); - - const wrapper = TooltipTextMenu.createDiv("colorPicker"); - wrapper.appendChild(svg); - wrapper.appendChild(color); - return wrapper; - }, - run: (state, dispatch) => TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, state, dispatch) - }); - } - - static insertHighlight(color: String, state: EditorState, dispatch: any) { - if (!state.selection.empty) { - toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch); - } - } - - createHighlightDropdown() { - // menu item for color picker - const self = this; - const colors = new MenuItem({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - const p = document.createElement("p"); - p.textContent = "Change highlight:"; - - const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper"); - - const colors = [ - PastelSchemaPalette.get("pink2"), - PastelSchemaPalette.get("purple4"), - PastelSchemaPalette.get("bluegreen1"), - PastelSchemaPalette.get("yellow4"), - PastelSchemaPalette.get("red2"), - PastelSchemaPalette.get("bluegreen7"), - PastelSchemaPalette.get("bluegreen5"), - PastelSchemaPalette.get("orange1"), - "white", - "transparent" - ]; - - colors.forEach(color => { - const button = document.createElement("button"); - button.className = color === TooltipTextMenuManager.Instance.highlighter ? "colorPicker active" : "colorPicker"; - if (color) { - button.style.backgroundColor = color; - button.textContent = color === "transparent" ? "X" : ""; - button.onclick = e => { - TooltipTextMenuManager.Instance.highlighter = color; - - TooltipTextMenu.insertHighlight(TooltipTextMenuManager.Instance.highlighter, self.view.state, self.view.dispatch); - - // update color menu - const highlightDom = self.createHighlightTool().render(self.view).dom; - const highlightDropdownDom = self.createHighlightDropdown().render(self.view).dom; - self.highighterDom && self.tooltip.replaceChild(highlightDom, self.highighterDom); - self.highlighterDropdownDom && self.tooltip.replaceChild(highlightDropdownDom, self.highlighterDropdownDom); - self.highighterDom = highlightDom; - self.highlighterDropdownDom = highlightDropdownDom; - }; - } - colorsWrapper.appendChild(button); - }); - - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(colorsWrapper); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { - event.stopPropagation(); - } - }); - - return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - // COLOR TOOL - createColorTool() { - return new MenuItem({ - title: "Color", - css: "fill:white;", - class: "menuicon", - execEvent: "", - render() { - const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svg.setAttribute("viewBox", "-100 -100 650 650"); - const path = document.createElementNS('http://www.w3.org/2000/svg', "path"); - path.setAttributeNS(null, "d", "M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"); - svg.appendChild(path); - - const color = TooltipTextMenu.createDiv("buttonColor"); - color.style.backgroundColor = TooltipTextMenuManager.Instance.color.toString(); - - const wrapper = TooltipTextMenu.createDiv("colorPicker"); - wrapper.appendChild(svg); - wrapper.appendChild(color); - return wrapper; - }, - run: (state, dispatch) => TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, state, dispatch) - }); - } - - static insertColor(color: String, state: EditorState, dispatch: any) { - const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color }); - if (state.selection.empty) { - dispatch(state.tr.addStoredMark(colorMark)); - } else { - this.setMark(colorMark, state, dispatch); - } - } - - createColorDropdown() { - // menu item for color picker - const self = this; - const colors = new MenuItem({ - title: "", - execEvent: "", - class: "button-setting-disabled", - css: "", - render() { - const p = document.createElement("p"); - p.textContent = "Change color:"; - - const colorsWrapper = TooltipTextMenu.createDiv("colorPicker-wrapper"); - - const colors = [ - DarkPastelSchemaPalette.get("pink2"), - DarkPastelSchemaPalette.get("purple4"), - DarkPastelSchemaPalette.get("bluegreen1"), - DarkPastelSchemaPalette.get("yellow4"), - DarkPastelSchemaPalette.get("red2"), - DarkPastelSchemaPalette.get("bluegreen7"), - DarkPastelSchemaPalette.get("bluegreen5"), - DarkPastelSchemaPalette.get("orange1"), - "#757472", - "#000" - ]; - - colors.forEach(color => { - const button = document.createElement("button"); - button.className = color === TooltipTextMenuManager.Instance.color ? "colorPicker active" : "colorPicker"; - if (color) { - button.style.backgroundColor = color; - button.onclick = e => { - TooltipTextMenuManager.Instance.color = color; - - TooltipTextMenu.insertColor(TooltipTextMenuManager.Instance.color, self.view.state, self.view.dispatch); - - // update color menu - const colorDom = self.createColorTool().render(self.view).dom; - const colorDropdownDom = self.createColorDropdown().render(self.view).dom; - self.colorDom && self.tooltip.replaceChild(colorDom, self.colorDom); - self.colorDropdownDom && self.tooltip.replaceChild(colorDropdownDom, self.colorDropdownDom); - self.colorDom = colorDom; - self.colorDropdownDom = colorDropdownDom; - }; - } - colorsWrapper.appendChild(button); - }); - - const div = document.createElement("div"); - div.appendChild(p); - div.appendChild(colorsWrapper); - return div; - }, - enable() { return false; }, - run(p1, p2, p3, event) { event.stopPropagation(); } - }); - - return new Dropdown([colors], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - // BRUSH TOOL - createBrushTool(active: boolean = false) { - const icon = { - height: 32, width: 32, - path: "M30.828 1.172c-1.562-1.562-4.095-1.562-5.657 0l-5.379 5.379-3.793-3.793-4.243 4.243 3.326 3.326-14.754 14.754c-0.252 0.252-0.358 0.592-0.322 0.921h-0.008v5c0 0.552 0.448 1 1 1h5c0 0 0.083 0 0.125 0 0.288 0 0.576-0.11 0.795-0.329l14.754-14.754 3.326 3.326 4.243-4.243-3.793-3.793 5.379-5.379c1.562-1.562 1.562-4.095 0-5.657zM5.409 30h-3.409v-3.409l14.674-14.674 3.409 3.409-14.674 14.674z" - }; - const self = this; - return new MenuItem({ - title: "Brush tool", - label: "Brush tool", - icon: icon, - css: "fill:white;", - class: active ? "menuicon-active" : "menuicon", - execEvent: "", - run: (state, dispatch) => { - this.brush_function(state, dispatch); - - // update dropdown with marks - const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom; - self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom); - self._brushDropdownDom = newBrushDropdowndom; - }, - active: (state) => true - }); - } - - brush_function(state: EditorState, dispatch: any) { - if (TooltipTextMenuManager.Instance._brushIsEmpty) { - // get marks in the selection - const selected_marks = new Set(); - const { from, to } = state.selection as TextSelection; - state.doc.nodesBetween(from, to, (node) => node.marks ?.forEach(m => selected_marks.add(m))); - - if (this._brushdom && selected_marks.size >= 0) { - TooltipTextMenuManager.Instance._brushMarks = selected_marks; - const newbrush = this.createBrushTool(true).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; - } - } - else { - const { from, to, $from } = this.view.state.selection; - if (this._brushdom) { - if (!this.view.state.selection.empty && $from && $from.nodeAfter) { - if (TooltipTextMenuManager.Instance._brushMarks && to - from > 0) { - this.view.dispatch(this.view.state.tr.removeMark(from, to)); - Array.from(TooltipTextMenuManager.Instance._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => { - TooltipTextMenu.setMark(mark, this.view.state, this.view.dispatch); - }); - } - } - else { - const newbrush = this.createBrushTool(false).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - TooltipTextMenuManager.Instance._brushIsEmpty = !TooltipTextMenuManager.Instance._brushIsEmpty; - } - } - } - } - - createBrushDropdown(active: boolean = false) { - let label = "Stored marks: "; - if (TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0) { - TooltipTextMenuManager.Instance._brushMarks.forEach((mark: Mark) => label += mark.type.name + ", "); - label = label.substring(0, label.length - 2); - } else { - label = "No marks are currently stored"; - } - - const brushInfo = new MenuItem({ - title: "", - label: label, - execEvent: "", - class: "button-setting-disabled", - css: "", - enable() { return false; }, - run(p1, p2, p3, event) { event.stopPropagation(); } - }); - - const self = this; - const input = document.createElement("input"); - const clearBrush = new MenuItem({ - title: "Clear brush", - execEvent: "", - class: "separated-button", - css: "", - render() { - const button = document.createElement("button"); - button.textContent = "Clear brush"; - - input.textContent = "editme"; - input.style.width = "75px"; - input.style.height = "30px"; - input.style.background = "white"; - input.setAttribute("contenteditable", "true"); - input.style.whiteSpace = "nowrap"; - input.type = "text"; - input.placeholder = "Enter URL"; - input.onpointerdown = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - }; - input.onclick = (e: MouseEvent) => { - input.select(); - input.focus(); - }; - input.onkeypress = (e: KeyboardEvent) => { - if (e.key === "Enter") { - TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMap.set(input.value, TooltipTextMenuManager.Instance._brushMarks); - input.style.background = "lightGray"; - } - }; - - const wrapper = document.createElement("div"); - wrapper.appendChild(input); - wrapper.appendChild(button); - return wrapper; - }, - enable() { return true; }, - run() { - TooltipTextMenuManager.Instance._brushIsEmpty = true; - TooltipTextMenuManager.Instance._brushMarks = new Set(); - - // update brush tool - // TODO: this probably isn't very clean - const newBrushdom = self.createBrushTool().render(self.view).dom; - self._brushdom && self.tooltip.replaceChild(newBrushdom, self._brushdom); - self._brushdom = newBrushdom; - const newBrushDropdowndom = self.createBrushDropdown().render(self.view).dom; - self._brushDropdownDom && self.tooltip.replaceChild(newBrushDropdowndom, self._brushDropdownDom); - self._brushDropdownDom = newBrushDropdowndom; - } - }); - - const hasMarks = TooltipTextMenuManager.Instance._brushMarks && TooltipTextMenuManager.Instance._brushMarks.size > 0; - return new Dropdown(hasMarks ? [brushInfo, clearBrush] : [brushInfo], { class: "buttonSettings-dropdown" }) as MenuItem; - } - - static setMark = (mark: Mark, state: EditorState, dispatch: any) => { - if (mark) { - const node = (state.selection as NodeSelection).node; - if (node ?.type === schema.nodes.ordered_list) { - let attrs = node.attrs; - if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, setFontFamily: mark.attrs.family }; - if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, setFontSize: mark.attrs.fontSize }; - if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, setFontColor: mark.attrs.color }; - const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema); - dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from)))); - } else { - toggleMark(mark.type, mark.attrs)(state, (tx: any) => { - const { from, $from, to, empty } = tx.selection; - if (!tx.doc.rangeHasMark(from, to, mark.type)) { - toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch); - } else dispatch(tx); - }); - } - } - } - - // called by Prosemirror - update(view: EditorView, lastState: EditorState | undefined) { this.updateFromDash(view, lastState, this.editorProps); } - //updates the tooltip menu when the selection changes - public async updateFromDash(view: EditorView, lastState: EditorState | undefined, props: any) { - if (!view) { - console.log("no editor? why?"); - return; - } - this.view = view; - DocumentDecorations.Instance.showTextBar(); - props && (this.editorProps = props); - - // Don't do anything if the document/selection didn't change - if (!lastState || !lastState.doc.eq(view.state.doc) || !lastState.selection.eq(view.state.selection)) { - - // UPDATE LINK DROPDOWN - const linkTarget = await this.getTextLinkTargetTitle(); - const linkDom = this.createLinkTool(linkTarget ? true : false).render(this.view).dom; - const linkDropdownDom = this.createLinkDropdown(linkTarget).render(this.view).dom; - this.linkDom && this.tooltip.replaceChild(linkDom, this.linkDom); - this.linkDropdownDom && this.tooltip.replaceChild(linkDropdownDom, this.linkDropdownDom); - this.linkDom = linkDom; - this.linkDropdownDom = linkDropdownDom; - - //UPDATE FONT STYLE DROPDOWN - const activeStyles = this.activeFontFamilyOnSelection(); - this.updateFontStyleDropdown(activeStyles.length === 1 ? activeStyles[0] : activeStyles.length ? "various" : "default"); - - //UPDATE FONT SIZE DROPDOWN - const activeSizes = this.activeFontSizeOnSelection(); - this.updateFontSizeDropdown(activeSizes.length === 1 ? String(activeSizes[0]) + " pt" : activeSizes.length ? "various" : "default"); - - //UPDATE ALL OTHER BUTTONS - this.updateHighlightStateOfButtons(); - } - } - - updateHighlightStateOfButtons() { - Array.from(this._marksToDoms.values()).forEach(val => val.style.fill = "white"); - this.activeMarksOnSelection().filter(mark => this._marksToDoms.has(mark)).forEach(mark => - this._marksToDoms.get(mark)!.style.fill = "greenyellow"); - - // keeps brush tool highlighted if active when switching between textboxes - if (!TooltipTextMenuManager.Instance._brushIsEmpty && this._brushdom) { - const newbrush = this.createBrushTool(true).render(this.view).dom; - this.tooltip.replaceChild(newbrush, this._brushdom); - this._brushdom = newbrush; - } - } - - //finds fontSize at start of selection - activeFontSizeOnSelection() { - //current selection - const state = this.view.state; - const activeSizes: number[] = []; - const pos = this.view.state.selection.$from; - const ref_node: ProsNode = this.reference_node(pos); - if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) { - ref_node.marks.forEach(m => m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize)); - } - return activeSizes; - } - //finds fontSize at start of selection - activeFontFamilyOnSelection() { - //current selection - const state = this.view.state; - const activeFamilies: string[] = []; - const pos = this.view.state.selection.$from; - const ref_node: ProsNode = this.reference_node(pos); - if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) { - ref_node.marks.forEach(m => m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family)); - } - return activeFamilies; - } - //finds all active marks on selection in given group - activeMarksOnSelection() { - const markGroup = Array.from(this._marksToDoms.keys()); - if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); - //current selection - const { empty, ranges, $to } = this.view.state.selection as TextSelection; - const state = this.view.state; - let activeMarks: MarkType[] = []; - if (!empty) { - activeMarks = markGroup.filter(mark => { - const has = false; - for (let i = 0; !has && i < ranges.length; i++) { - return state.doc.rangeHasMark(ranges[i].$from.pos, ranges[i].$to.pos, mark); - } - return false; - }); - } - else { - const pos = this.view.state.selection.$from; - const ref_node: ProsNode = this.reference_node(pos); - if (ref_node !== null && ref_node !== this.view.state.doc) { - if (ref_node.isText) { - } - else { - return []; - } - activeMarks = markGroup.filter(mark_type => { - if (mark_type === state.schema.marks.pFontSize) { - return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); - } - const mark = state.schema.mark(mark_type); - return ref_node.marks.includes(mark); - }); - } - } - return activeMarks; - } - - reference_node(pos: ResolvedPos): ProsNode { - let ref_node: ProsNode = this.view.state.doc; - if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) { - ref_node = pos.nodeBefore; - } - else if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) { - ref_node = pos.nodeAfter; - } - else if (pos.pos > 0) { - let skip = false; - for (let i: number = pos.pos - 1; i > 0; i--) { - this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => { - if (node.isLeaf && !skip) { - ref_node = node; - skip = true; - } - - }); - } - } - if (!ref_node.isLeaf && ref_node.childCount > 0) { - ref_node = ref_node.child(0); - } - return ref_node; - } - - destroy() { - // this.wrapper.remove(); - } -} - - -export class TooltipTextMenuManager { - private static _instance: TooltipTextMenuManager; - private _isPinned: boolean = false; - - public pinnedX: number = 0; - public pinnedY: number = 0; - public unpinnedX: number = 0; - public unpinnedY: number = 0; - - public _brushMarks: Set | undefined; - public _brushMap: Map> = new Map(); - public _brushIsEmpty: boolean = true; - - public color: String = "#000"; - public highlighter: String = "transparent"; - - public activeMenu: TooltipTextMenu | undefined; - - static get Instance() { - if (!TooltipTextMenuManager._instance) { - TooltipTextMenuManager._instance = new TooltipTextMenuManager(); - } - return TooltipTextMenuManager._instance; - } - - public get isPinned() { return this._isPinned; } - - public toggleIsPinned() { this._isPinned = !this._isPinned; } -} diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 202bfe400..4441356dc 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -20,6 +20,7 @@ import React = require("react"); import { DocumentView } from './nodes/DocumentView'; import { ParentDocSelector } from './collections/ParentDocumentSelector'; import { CollectionDockingView } from './collections/CollectionDockingView'; +import RichTextMenu from '../util/RichTextMenu'; import { Id } from '../../new_fields/FieldSymbols'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -112,14 +113,15 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | const linkDrag = UndoManager.StartBatch("Drag Link"); this.view0 && DragManager.StartLinkDrag(this._linkButton.current, this.view0.props.Document, e.pageX, e.pageY, { dragComplete: dropEv => { - const linkDoc = dropEv.linkDragData?.linkDocument; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop - if (this.view0 && linkDoc && FormattedTextBox.ToolTipTextMenu) { + const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop + if (this.view0 && linkDoc) { const proto = Doc.GetProto(linkDoc); proto.sourceContext = this.view0.props.ContainingCollectionDoc; const anchor2Title = linkDoc.anchor2 instanceof Doc ? StrCast(linkDoc.anchor2.title) : "-untitled-"; + const anchor2Id = linkDoc.anchor2 instanceof Doc ? linkDoc.anchor2[Id] : ""; + const text = RichTextMenu.Instance.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", anchor2Id); if (linkDoc.anchor2 instanceof Doc) { - const text = FormattedTextBox.ToolTipTextMenu.MakeLinkToSelection(linkDoc[Id], anchor2Title, e.ctrlKey ? "onRight" : "inTab", linkDoc.anchor2[Id]); proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODO open to more descriptive descriptions of following in text link } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 799b3695c..32fea15fb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -14,7 +14,6 @@ import { Docs, DocUtils } from "../documents/Documents"; import { DocumentManager } from "../util/DocumentManager"; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; -import { TooltipTextMenu } from '../util/TooltipTextMenu'; import { undoBatch, UndoManager } from "../util/UndoManager"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { CollectionView } from "./collections/CollectionView"; @@ -26,8 +25,6 @@ import { IconBox } from "./nodes/IconBox"; import React = require("react"); import { DocumentType } from '../documents/DocumentTypes'; import { ScriptField } from '../../new_fields/ScriptField'; -import { render } from 'react-dom'; -import RichTextMenu from '../util/RichTextMenu'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -546,11 +543,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this.TextBar = ele; } } - public showTextBar = () => { - if (this.TextBar && TooltipTextMenu.Toolbar && Array.from(this.TextBar.childNodes).indexOf(TooltipTextMenu.Toolbar) === -1) { - this.TextBar.appendChild(TooltipTextMenu.Toolbar); - } - } render() { const bounds = this.Bounds; const seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 867be287f..b9f601a9a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -61,7 +61,7 @@ export class DocumentContentsView extends React.Componentawaiting layout

"; - const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, "layout")], "string"); + const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); if (layout === undefined) { return this.props.Document.data ? "" : diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 60842bcb0..1cd5cdcea 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -29,8 +29,6 @@ import buildKeymap from "../../util/ProsemirrorExampleTransfer"; import { inpRules } from "../../util/RichTextRules"; import { DashDocCommentView, FootnoteView, ImageResizeView, DashDocView, OrderedListView, schema, SummaryView } from "../../util/RichTextSchema"; import { SelectionManager } from "../../util/SelectionManager"; -import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu"; -import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocAnnotatableComponent } from "../DocComponent"; import { DocumentButtonBar } from '../DocumentButtonBar'; @@ -77,7 +75,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; - public static ToolTipTextMenu: TooltipTextMenu | undefined = undefined; public ProseRef?: HTMLDivElement; private _ref: React.RefObject = React.createRef(); private _scrollRef: React.RefObject = React.createRef(); @@ -127,10 +124,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & return ""; } - public static getToolTip(ev: EditorView) { - return this.ToolTipTextMenu ? this.ToolTipTextMenu : this.ToolTipTextMenu = new TooltipTextMenu(ev); - } - @undoBatch public setFontColor(color: string) { const view = this._editorView!; @@ -485,11 +478,10 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & schema, plugins: [ inputRules(inpRules), - this.tooltipTextMenuPlugin(), + this.richTextMenuPlugin(), history(), keymap(this._keymap), keymap(baseKeymap), - // this.tooltipLinkingMenuPlugin(), new Plugin({ props: { attributes: { class: "ProseMirror-example-setup-style" } @@ -1038,25 +1030,16 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & } } - tooltipTextMenuPlugin() { + richTextMenuPlugin() { const self = FormattedTextBox; return new Plugin({ view(newView) { - // return self.ToolTipTextMenu = FormattedTextBox.getToolTip(newView); RichTextMenu.Instance.changeView(newView); return RichTextMenu.Instance; } }); } - tooltipLinkingMenuPlugin() { - const myprops = this.props; - return new Plugin({ - view(_editorView) { - return new TooltipLinkingMenu(_editorView, myprops); - } - }); - } onBlur = (e: any) => { //DictationManager.Controls.stop(false); if (this._undoTyping) { @@ -1138,7 +1121,6 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & if (this.props.isSelected()) { // TODO: ftong --> update from dash in richtextmenu RichTextMenu.Instance.updateFromDash(this._editorView!, undefined, this.props); - // FormattedTextBox.ToolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props); } else if (FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); } diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 5375c8ac8..e502a06b8 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -151,14 +151,14 @@ export class CurrentUserUtils { }); // setup a color picker const color = Docs.Create.ColorDocument({ - title: "color picker", width: 400, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) + title: "color picker", width: 300, dropAction: "alias", forceActive: true, removeDropProperties: new List(["dropAction", "forceActive"]) }); return Docs.Create.ButtonDocument({ width: 35, height: 25, backgroundColor: "lightgrey", color: "rgb(34, 34, 34)", title: "Tools", fontSize: 10, targetContainer: sidebarContainer, letterSpacing: "0px", textTransform: "unset", borderRounding: "5px 5px 0px 0px", boxShadow: "3px 3px 0px rgb(34, 34, 34)", sourcePanel: Docs.Create.StackingDocument([dragCreators, color], { - width: 500, height: 800, lockedPosition: true, chromeStatus: "disabled", title: "tools stack" + width: 500, lockedPosition: true, chromeStatus: "disabled", title: "tools stack" }), onClick: ScriptField.MakeScript("this.targetContainer.proto = this.sourcePanel"), }); -- cgit v1.2.3-70-g09d2 From 0035bdb42367705293e8d00195075125f196f37f Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Tue, 21 Jan 2020 12:45:31 -0500 Subject: added extra button --- src/server/authentication/models/current_user_utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 5375c8ac8..b383bc8b6 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -115,7 +115,8 @@ export class CurrentUserUtils { { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, - { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", clipboard: Docs.Create.FreeformDocument([], { width: 300, height: 300 }), backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, + { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green", ischecked: `sameDocs(this.activePen.pen, this)`, activePen: doc }, ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ nativeWidth: 10, nativeHeight: 10, width: 10, height: 10, dropAction: data.pointerDown ? "copy" : undefined, title: data.title, icon: data.icon, ignoreClick: data.ignoreClick, -- cgit v1.2.3-70-g09d2 From 6b1e9b68c3d409df6ea2a8909b99d5d8d75226c5 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 22 Jan 2020 12:53:40 -0500 Subject: various fixes to pivot view related things. --- src/client/documents/Documents.ts | 3 +- .../views/collections/CollectionPivotView.tsx | 39 ++++++++++------------ .../views/collections/CollectionTreeView.tsx | 33 +++++++++--------- .../CollectionFreeFormLayoutEngines.tsx | 6 +--- .../collections/collectionFreeForm/MarqueeView.tsx | 1 + src/scraping/buxton/scraper.py | 6 +++- .../authentication/models/current_user_utils.ts | 2 +- 7 files changed, 45 insertions(+), 45 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 634c223a7..e86ed52f3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -88,7 +88,7 @@ export interface DocumentOptions { defaultBackgroundColor?: string; dropAction?: dropActionType; chromeStatus?: string; - disableLOD?: boolean; + LODdisable?: boolean; columnWidth?: number; fontSize?: number; curPage?: number; @@ -107,6 +107,7 @@ export interface DocumentOptions { ischecked?: ScriptField; // returns whether a font icon box is checked activePen?: Doc; // which pen document is currently active (used as the radio button state for the 'unhecked' pen tool scripts) onClick?: ScriptField; + onChildClick?: ScriptField; // script given to children of a collection to execute when they are clicked onPointerDown?: ScriptField; onPointerUp?: ScriptField; dragFactory?: Doc; // document to create when dragging with a suitable onDragStart script diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 8e712e370..f23540529 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -26,36 +26,33 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { componentDidMount() { this.props.Document.freeformLayoutEngine = "pivot"; if (!this.props.Document.facetCollection) { - const facetCollection = Docs.Create.FreeformDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); + const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; - const scriptText = "setDocFilter(context.target, heading, this.title, checked)"; + const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; const script = CompileScript(scriptText, { - params: { this: Doc.name, heading: "boolean", checked: "boolean", context: Doc.name }, + params: { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name }, typecheck: false, editable: true, }); if (script.compiled) { facetCollection.onCheckedClick = new ScriptField(script); } - this._narrativeDisposer = reaction(() => this.props.Document.childDetailed, - (childDetailed) => - DocCastAsync(childDetailed).then(childDetailed => { - if (childDetailed instanceof Doc) { - const captured: { [name: string]: Field } = {}; - captured.childDetailed = new PrefetchProxy(childDetailed); - const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; - const openDocScript = CompileScript(openDocText, { - params: { this: Doc.name, heading: "boolean", context: Doc.name }, - typecheck: false, - editable: true, - capturedVariables: captured - }); - if (openDocScript.compiled) { - this.props.Document.onChildClick = new ScriptField(openDocScript); - } + const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; + this._narrativeDisposer = reaction(() => DocCastAsync(this.props.Document.childDetailed), + (childDetailedPromise) => childDetailedPromise.then(childDetailed => { + if (childDetailed) { + const openDocScript = CompileScript(openDocText, { + params: { this: Doc.name, heading: "boolean", containingTreeView: Doc.name }, + capturedVariables: { childDetailed: new PrefetchProxy(childDetailed) }, + typecheck: false, + editable: true, + }); + if (openDocScript.compiled) { + this.props.Document.onChildClick = new ScriptField(openDocScript); } - }), { fireImmediately: true }); + } + }), { fireImmediately: true }); this.props.Document.facetCollection = facetCollection; this.props.Document.fitToBox = true; } @@ -84,7 +81,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { if (found !== -1) { (facetCollection.data as List).splice(found, 1); } else { - const newFacet = Docs.Create.FreeformDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2433cc40e..bef587786 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -32,6 +32,7 @@ import { Templates } from '../Templates'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); +import { CollectionViewType } from './CollectionView'; export interface TreeViewProps { @@ -377,7 +378,7 @@ class TreeView extends React.Component { this: this.props.document.isTemplateField && this.props.dataDoc ? this.props.dataDoc : this.props.document, heading: this.props.containingCollection.title, checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check", - context: this.props.treeViewId, + containingTreeView: this.props.treeViewId, }, console.log); } else { this.treeViewOpen = !this.treeViewOpen; @@ -635,32 +636,32 @@ export class CollectionTreeView extends CollectionSubView(Document) { description: "Buxton Layout", icon: "eye", event: () => { const { TextDocument, ImageDocument, MulticolumnDocument } = Docs.Create; const { Document } = this.props; - const fallback = "http://www.cs.brown.edu/~bcz/face.gif"; - const imageRack = MulticolumnDocument([], { title: "data", height: 100 }); - const wrapper = Docs.Create.StackingDocument([ - ImageDocument(fallback, { title: "hero" }), - imageRack, - ...["short_description", "year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key })) - ], { autoHeight: true, chromeStatus: "disabled", disableLOD: true, title: "detailed layout stack" }); - wrapper.isTemplateDoc = makeTemplate(wrapper); - imageRack.onChildClick = ScriptField.MakeFunction(`containingCollection.resolvedDataDoc.hero = copyField(this.data)`, { containingCollection: Doc.name }); - - const cardLayout = ImageDocument(fallback); + const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; + + const detailedLayout = Docs.Create.StackingDocument([ + ImageDocument(fallbackImg, { title: "activeHero" }), + MulticolumnDocument([], { title: "data", height: 100, onChildClick: ScriptField.MakeFunction(`containingCollection.resolvedDataDoc.activeHero = copyField(this.data)`, { containingCollection: Doc.name }) }), + TextDocument({ title: "short_description", autoHeight: true }), + ...["year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key, height: 30 })) + ], { autoHeight: true, chromeStatus: "disabled", title: "detailed layout stack" }); + detailedLayout.isTemplateDoc = makeTemplate(detailedLayout); + + const cardLayout = ImageDocument(fallbackImg, { isTemplateDoc: true, isTemplateField: true }); // this acts like a template doc and a template field ... a little weird, but seems to work? cardLayout.proto!.layout = ImageBox.LayoutString("hero"); cardLayout.showTitle = "title"; cardLayout.showTitleHover = "titlehover"; - cardLayout.isTemplateField = true; // make this document act like a template field - cardLayout.isTemplateDoc = true; // but it's also a template doc itself... a little weird Document.childLayout = cardLayout; - Document.childDetailed = wrapper; + Document.childDetailed = detailedLayout; + Document.viewType = CollectionViewType.Pivot; + Document.pivotField = "company"; } }); const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; onClicks.push({ description: "Edit onChecked Script", icon: "edit", event: (obj: any) => ScriptBox.EditButtonScript("On Checked Changed ...", this.props.Document, - "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", context: Doc.name }) + "onCheckedClick", obj.x, obj.y, { heading: "boolean", checked: "boolean", treeViewContainer: Doc.name }) }); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 5c6336248..32097816d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -59,11 +59,7 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo let numCols = NumCast(pivotDoc.pivotNumColumns, Math.ceil(Math.sqrt(minSize))); const docMap = new Map(); const groupNames: PivotData[] = []; - if (panelDim[0] < 2500) numCols = Math.min(5, numCols); - if (panelDim[0] < 2000) numCols = Math.min(4, numCols); - if (panelDim[0] < 1400) numCols = Math.min(3, numCols); - if (panelDim[0] < 1000) numCols = Math.min(2, numCols); - if (panelDim[0] < 600) numCols = 1; + numCols = Math.min(panelDim[0] / pivotAxisWidth, numCols) const expander = 1.05; const gap = .15; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 523edb918..584d18372 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -331,6 +331,7 @@ export class MarqueeView extends React.Component Date: Thu, 23 Jan 2020 18:24:38 -0500 Subject: got rid of extension docs. changed layout-specific keys to start with "_" which flags them to be written to the current layout document --- .../apis/google_docs/GooglePhotosClientUtils.ts | 2 +- src/client/apis/youtube/YoutubeBox.tsx | 2 +- src/client/documents/Documents.ts | 109 +++++++++++---------- src/client/util/DictationManager.ts | 6 +- src/client/util/DragManager.ts | 2 +- src/client/util/DropConverter.ts | 3 +- .../util/Import & Export/DirectoryImportBox.tsx | 4 +- src/client/util/RichTextRules.ts | 4 +- src/client/util/RichTextSchema.tsx | 4 +- src/client/views/DocComponent.tsx | 9 +- src/client/views/DocumentDecorations.tsx | 40 ++++---- src/client/views/MainView.tsx | 10 +- src/client/views/MetadataEntryMenu.tsx | 4 +- src/client/views/PreviewCursor.tsx | 4 +- src/client/views/TemplateMenu.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 20 ++-- .../views/collections/CollectionLinearView.tsx | 12 +-- .../collections/CollectionMasonryViewFieldRow.tsx | 4 +- .../views/collections/CollectionPivotView.tsx | 23 ++--- .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 34 +++---- .../CollectionStackingViewFieldColumn.tsx | 8 +- .../views/collections/CollectionStaffView.tsx | 4 - src/client/views/collections/CollectionSubView.tsx | 23 ++--- .../views/collections/CollectionTreeView.tsx | 36 +++---- src/client/views/collections/CollectionView.tsx | 50 ++++------ .../views/collections/CollectionViewChromes.tsx | 22 ++--- .../views/collections/ParentDocumentSelector.tsx | 18 ++-- .../CollectionFreeFormLayoutEngines.tsx | 8 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 100 ++++++++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 54 +++++----- src/client/views/linking/LinkEditor.tsx | 2 +- src/client/views/linking/LinkFollowBox.tsx | 48 ++++----- src/client/views/linking/LinkMenuGroup.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 101 +++++++++---------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 12 +-- .../views/nodes/ContentFittingDocumentView.tsx | 10 +- src/client/views/nodes/DocumentView.tsx | 106 ++++++++++---------- src/client/views/nodes/FormattedTextBox.tsx | 26 +++-- src/client/views/nodes/FormattedTextBoxComment.tsx | 2 +- src/client/views/nodes/IconBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 89 ++++++++--------- src/client/views/nodes/KeyValueBox.tsx | 8 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 22 ++--- src/client/views/nodes/PresBox.tsx | 4 +- src/client/views/nodes/VideoBox.tsx | 32 +++--- src/client/views/nodes/WebBox.tsx | 14 +-- src/client/views/pdf/Annotation.tsx | 10 +- src/client/views/pdf/PDFViewer.tsx | 67 +++++++------ .../views/presentationview/PresElementBox.tsx | 10 +- src/client/views/search/SearchBox.tsx | 16 +-- src/client/views/search/SearchItem.tsx | 10 +- src/mobile/ImageUpload.tsx | 2 +- src/new_fields/Doc.ts | 51 +++------- src/new_fields/RichTextUtils.ts | 2 +- src/new_fields/documentSchemas.ts | 13 +-- src/new_fields/util.ts | 34 ++++++- src/scraping/buxton/scraper.py | 14 +-- .../authentication/models/current_user_utils.ts | 70 ++++++------- 60 files changed, 696 insertions(+), 710 deletions(-) (limited to 'src/server/authentication/models/current_user_utils.ts') diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 966d8053a..21cd20e14 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -136,7 +136,7 @@ export namespace GooglePhotos { document.contentSize = upload.contentSize; return document; }); - const options = { width: 500, height: 500 }; + const options = { _width: 500, _height: 500 }; return constructor(children, options); }; diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index d654e2530..c940bba43 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -337,7 +337,7 @@ export class YoutubeBox extends React.Component { const newVideoX = NumCast(this.props.Document.x); const newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height); - addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, width: 400, height: 315, x: newVideoX, y: newVideoY })); + addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, _width: 400, _height: 315, x: newVideoX, y: newVideoY })); this.videoClicked = true; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6132f787b..c665a7fd3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -56,21 +56,28 @@ const requestImageSize = require('../util/request-image-size'); const path = require('path'); export interface DocumentOptions { + _panX?: number; + _panY?: number; + _width?: number; + _height?: number; + _nativeWidth?: number; + _nativeHeight?: number; + _fitWidth?: boolean; + _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents + _LODdisable?: boolean; + _dropAction?: dropActionType; + _chromeStatus?: string; + _viewType?: number; + _gridGap?: number; // gap between items in masonry view + _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts + _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts x?: number; y?: number; z?: number; type?: string; - width?: number; - height?: number; - nativeWidth?: number; - nativeHeight?: number; title?: string; - panX?: number; - panY?: number; page?: number; scale?: number; - fitWidth?: boolean; - fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents isDisplayPanel?: boolean; // whether the panel functions as GoldenLayout "stack" used to display documents forceActive?: boolean; preventTreeViewOpen?: boolean; // ignores the treeViewOpen Doc flag which allows a treeViewItem's expande/collapse state to be independent of other views of the same document in the tree view @@ -79,16 +86,12 @@ export interface DocumentOptions { isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; templates?: List; - viewType?: number; backgroundColor?: string | ScriptField; ignoreClick?: boolean; lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed opacity?: number; defaultBackgroundColor?: string; - dropAction?: dropActionType; - chromeStatus?: string; - LODdisable?: boolean; columnWidth?: number; fontSize?: number; curPage?: number; @@ -114,9 +117,6 @@ export interface DocumentOptions { onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop clipboard?: Doc; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop icon?: string; - gridGap?: number; // gap between items in masonry view - xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts - yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts sourcePanel?: Doc; // panel to display in 'targetContainer' as the result of a button onClick script targetContainer?: Doc; // document whose proto will be set to 'panel' as the result of a onClick click script dropConverter?: ScriptField; // script to run when documents are dropped on this Document. @@ -158,19 +158,19 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.TEXT, { layout: { view: FormattedTextBox, dataField: data }, - options: { height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" } + options: { _height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" } }], [DocumentType.HIST, { layout: { view: HistogramBox, dataField: data }, - options: { height: 300, backgroundColor: "black" } + options: { _height: 300, backgroundColor: "black" } }], [DocumentType.QUERY, { layout: { view: QueryBox, dataField: data }, - options: { width: 400 } + options: { _width: 400 } }], [DocumentType.COLOR, { layout: { view: ColorBox, dataField: data }, - options: { nativeWidth: 220, nativeHeight: 300 } + options: { _nativeWidth: 220, _nativeHeight: 300 } }], [DocumentType.IMG, { layout: { view: ImageBox, dataField: data }, @@ -178,19 +178,19 @@ export namespace Docs { }], [DocumentType.WEB, { layout: { view: WebBox, dataField: data }, - options: { height: 300 } + options: { _height: 300 } }], [DocumentType.COL, { layout: { view: CollectionView, dataField: data }, - options: { panX: 0, panY: 0, scale: 1, width: 500, height: 500 } + options: { _panX: 0, _panY: 0, scale: 1, _width: 500, _height: 500 } }], [DocumentType.KVP, { layout: { view: KeyValueBox, dataField: data }, - options: { height: 150 } + options: { _height: 150 } }], [DocumentType.DOCUMENT, { layout: { view: DocumentBox, dataField: data }, - options: { height: 250 } + options: { _height: 250 } }], [DocumentType.VID, { layout: { view: VideoBox, dataField: data }, @@ -198,7 +198,7 @@ export namespace Docs { }], [DocumentType.AUDIO, { layout: { view: AudioBox, dataField: data }, - options: { height: 35, backgroundColor: "lightGray" } + options: { _height: 35, backgroundColor: "lightGray" } }], [DocumentType.PDF, { layout: { view: PDFBox, dataField: data }, @@ -206,11 +206,11 @@ export namespace Docs { }], [DocumentType.ICON, { layout: { view: IconBox, dataField: data }, - options: { width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }, + options: { _width: Number(MINIMIZED_ICON_SIZE), _height: Number(MINIMIZED_ICON_SIZE) }, }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: data }, - options: { height: 150 } + options: { _height: 150 } }], [DocumentType.LINKDOC, { data: new List(), @@ -228,7 +228,7 @@ export namespace Docs { }], [DocumentType.FONTICON, { layout: { view: FontIconBox, dataField: data }, - options: { width: 40, height: 40, borderRounding: "100%" }, + options: { _width: 40, _height: 40, borderRounding: "100%" }, }], [DocumentType.LINKFOLLOW, { layout: { view: LinkFollowBox, dataField: data } @@ -243,7 +243,7 @@ export namespace Docs { ]); // All document prototypes are initialized with at least these values - const defaultOptions: DocumentOptions = { x: 0, y: 0, width: 300 }; + const defaultOptions: DocumentOptions = { x: 0, y: 0, _width: 300 }; const suffix = "Proto"; /** @@ -321,7 +321,8 @@ export namespace Docs { // whatever options pertain to this specific prototype const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; options.layout = layout.view.LayoutString(layout.dataField); - return Doc.assign(new Doc(prototypeId, true), { ...options }); + let doc = Doc.assign(new Doc(prototypeId, true), { layoutKey: "layout", ...options }); + return doc; } } @@ -332,7 +333,7 @@ export namespace Docs { */ export namespace Create { - const delegateKeys = ["x", "y", "width", "height", "panX", "panY", "nativeWidth", "nativeHeight", "dropAction", "annotationOn", "forceActive", "fitWidth", "diableLOD"]; + const delegateKeys = ["x", "y", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "_dropAction", "_annotationOn", "_chromeStatus", "_forceActive", "_fitWidth", "_LODdisable"]; /** * This function receives the relevant document prototype and uses @@ -402,11 +403,11 @@ export namespace Docs { requestImageSize(target) .then((size: any) => { const aspect = size.height / size.width; - if (!inst.nativeWidth) { - inst.nativeWidth = size.width; + if (!inst._nativeWidth) { + inst._nativeWidth = size.width; } - inst.nativeHeight = NumCast(inst.nativeWidth) * aspect; - inst.height = NumCast(inst.width) * aspect; + inst._nativeHeight = NumCast(inst._nativeWidth) * aspect; + inst._height = NumCast(inst._width) * aspect; }) .catch((err: any) => console.log(err)); // } @@ -465,7 +466,7 @@ export namespace Docs { const ctlog = await Gateway.Instance.GetSchema(url, schemaName); if (ctlog && ctlog.schemas) { const schema = ctlog.schemas[0]; - const schemaDoc = Docs.Create.TreeDocument([], { ...options, nativeWidth: undefined, nativeHeight: undefined, width: 150, height: 100, title: schema.displayName! }); + const schemaDoc = Docs.Create.TreeDocument([], { ...options, _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: schema.displayName! }); const schemaDocuments = Cast(schemaDoc.data, listSpec(Doc), []); if (!schemaDocuments) { return; @@ -482,13 +483,13 @@ export namespace Docs { new AttributeTransformationModel(atmod, AggregateFunction.None), new AttributeTransformationModel(atmod, AggregateFunction.Count), new AttributeTransformationModel(atmod, AggregateFunction.Count)); - docs.push(Docs.Create.HistogramDocument(histoOp, { ...columnOptions, width: 200, height: 200, title: attr.displayName! })); + docs.push(Docs.Create.HistogramDocument(histoOp, { ...columnOptions, _width: 200, _height: 200, title: attr.displayName! })); } })); }); return schemaDoc; } - return Docs.Create.TreeDocument([], { width: 50, height: 100, title: schemaName }); + return Docs.Create.TreeDocument([], { _width: 50, _height: 100, title: schemaName }); } export function WebDocument(url: string, options: DocumentOptions = {}) { @@ -508,31 +509,31 @@ export namespace Docs { } export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Freeform }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Freeform }, id); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Linear }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Linear }, id); } export function SchemaDocument(schemaColumns: SchemaHeaderField[], documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List(schemaColumns), ...options, viewType: CollectionViewType.Schema }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List(schemaColumns), ...options, _viewType: CollectionViewType.Schema }); } export function TreeDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Tree }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Tree }); } export function StackingDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Stacking }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Stacking }); } export function MulticolumnDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Multicolumn }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Multicolumn }); } export function MasonryDocument(documents: Array, options: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, viewType: CollectionViewType.Masonry }); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", schemaColumns: new List([new SchemaHeaderField("title", "#f1efeb")]), ...options, _viewType: CollectionViewType.Masonry }); } export function ButtonDocument(options?: DocumentOptions) { @@ -553,7 +554,7 @@ export namespace Docs { } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); + const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); Doc.GetProto(inst).data = new List(documents); return inst; } @@ -714,24 +715,24 @@ export namespace Docs { let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined; if (type.indexOf("image") !== -1) { ctor = Docs.Create.ImageDocument; - if (!options.width) options.width = 300; + if (!options._width) options._width = 300; } if (type.indexOf("video") !== -1) { ctor = Docs.Create.VideoDocument; - if (!options.width) options.width = 600; - if (!options.height) options.height = options.width * 2 / 3; + if (!options._width) options._width = 600; + if (!options._height) options._height = options._width * 2 / 3; } if (type.indexOf("audio") !== -1) { ctor = Docs.Create.AudioDocument; } if (type.indexOf("pdf") !== -1) { ctor = Docs.Create.PdfDocument; - if (!options.width) options.width = 400; - if (!options.height) options.height = options.width * 1200 / 927; + if (!options._width) options._width = 400; + if (!options._height) options._height = options._width * 1200 / 927; } if (type.indexOf("excel") !== -1) { ctor = Docs.Create.DBDocument; - options.dropAction = "copy"; + options._dropAction = "copy"; } if (type.indexOf("html") !== -1) { if (path.includes(window.location.hostname)) { @@ -742,15 +743,15 @@ export namespace Docs { const alias = Doc.MakeAlias(field); alias.x = options.x || 0; alias.y = options.y || 0; - alias.width = options.width || 300; - alias.height = options.height || options.width || 300; + alias._width = options._width || 300; + alias._height = options._height || options._width || 300; return alias; } return undefined; }); } ctor = Docs.Create.WebDocument; - options = { height: options.width, ...options, title: path, nativeWidth: undefined }; + options = { _height: options._width, ...options, title: path, _nativeWidth: undefined }; } return ctor ? ctor(path, options) : undefined; } diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 3d8f2d234..a99e3bc4f 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -325,14 +325,14 @@ export namespace DictationManager { ["open fields", { action: (target: DocumentView) => { - const kvp = Docs.Create.KVPDocument(target.props.Document, { width: 300, height: 300 }); + const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); target.props.addDocTab(kvp, target.props.DataDoc, "onRight"); } }], ["new outline", { action: (target: DocumentView) => { - const newBox = Docs.Create.TextDocument({ width: 400, height: 200, title: "My Outline" }); + const newBox = Docs.Create.TextDocument({ _width: 400, _height: 200, title: "My Outline" }); newBox.autoHeight = true; const proto = newBox.proto!; const prompt = "Press alt + r to start dictating here..."; @@ -379,7 +379,7 @@ export namespace DictationManager { expression: /view as (freeform|stacking|masonry|schema|tree)/g, action: (target: DocumentView, matches: RegExpExecArray) => { const mode = CollectionViewType.valueOf(matches[1]); - mode && (target.props.Document.viewType = mode); + mode && (target.props.Document._viewType = mode); }, restrictTo: [DocumentType.COL] } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index df2f5fe3c..601a7e856 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -205,7 +205,7 @@ export namespace DragManager { // drag a button template and drop a new button export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) { const finishDrag = (e: DragCompleteEvent) => { - const bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title }); + const bd = Docs.Create.ButtonDocument({ _width: 150, _height: 50, title: title }); bd.onClick = ScriptField.MakeScript(script); params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); initialize && initialize(bd); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 66caf52cb..9c068d2d7 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,5 +1,4 @@ import { DragManager } from "./DragManager"; -import { CollectionViewType } from "../views/collections/CollectionView"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { DocumentType } from "../documents/DocumentTypes"; import { ObjectField } from "../../new_fields/ObjectField"; @@ -34,7 +33,7 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { } else { layoutDoc.isTemplateDoc = (layoutDoc.type === DocumentType.TEXT || layoutDoc.layout instanceof Doc) && !data.userDropAction; } - dbox = Docs.Create.FontIconDocument({ nativeWidth: 100, nativeHeight: 100, width: 100, height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" }); + dbox = Docs.Create.FontIconDocument({ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, backgroundColor: StrCast(doc.backgroundColor), title: "Custom", icon: layoutDoc.isTemplateDoc ? "font" : "bolt" }); dbox.dragFactory = layoutDoc; dbox.removeDropProperties = doc.removeDropProperties instanceof ObjectField ? ObjectField.MakeCopy(doc.removeDropProperties) : undefined; dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 5b5bffd8c..c7c94abed 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -145,8 +145,8 @@ export default class DirectoryImportBox extends React.Component const offset: number = this.persistent ? (height === 0 ? 0 : height + 30) : 0; const options: DocumentOptions = { title: `Import of ${directory}`, - width: 1105, - height: 500, + _width: 1105, + _height: 500, x: NumCast(doc.x), y: NumCast(doc.y) + offset }; diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 0ee9481f0..fb22e0d18 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -79,7 +79,7 @@ export const inpRules = { const value = state.doc.textBetween(start, end); if (value) { DocServer.GetRefField(value).then(docx => { - const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500, }, value); + const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, _width: 500, _height: 500, }, value); DocUtils.Publish(target, value, returnFalse, returnFalse); DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", ""); }); @@ -196,7 +196,7 @@ export const inpRules = { new InputRule( new RegExp(/%#$/), (state, match, start, end) => { - const target = Docs.Create.TextDocument({ width: 75, height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, autoHeight: true, fontSize: 9, title: "inline comment" }); + const target = Docs.Create.TextDocument({ _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, autoHeight: true, fontSize: 9, title: "inline comment" }); const node = (state.doc.resolve(start) as any).nodeAfter; const newNode = schema.nodes.dashComment.create({ docid: target[Id] }); const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" }); diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index fe7ab31a8..d64098e5f 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -703,7 +703,7 @@ export class DashDocView { const { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer); return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale); } - contentScaling = () => NumCast(this._dashDoc!.nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!.nativeWidth) : 1; + contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1; outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { this._textBox = tbox; @@ -758,7 +758,7 @@ export class DashDocView { ReactDOM.render((schemaCt @computed get Document(): T { return schemaCtor(this.props.Document); } @computed get layoutDoc() { return Doc.Layout(this.props.Document); } @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools } return Component; @@ -59,14 +58,12 @@ export function DocAnnotatableComponent

(schema @computed get Document(): T { return schemaCtor(this.props.Document); } @computed get layoutDoc() { return Doc.Layout(this.props.Document); } @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - @computed get extensionDocSync() { return Doc.fieldExtensionDocSync(this.dataDoc, this.props.fieldKey); } - @computed get annotationsKey() { return "annotations"; } + @computed get annotationsKey() { return this.props.fieldKey+"-annotations"; } @action.bound removeDocument(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = undefined; - const value = this.extensionDoc && Cast(this.extensionDoc[this.annotationsKey], listSpec(Doc), []); + const value = Cast(this.dataDoc[this.props.fieldKey+"-"+this.annotationsKey], listSpec(Doc), []); const index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1; return index !== -1 && value && value.splice(index, 1) ? true : false; } @@ -79,7 +76,7 @@ export function DocAnnotatableComponent

(schema @action.bound addDocument(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = this.props.Document; - return this.extensionDoc && Doc.AddDocToList(this.extensionDoc, this.annotationsKey, doc) ? true : false; + return Doc.AddDocToList(this.dataDoc, this.annotationsKey, doc) ? true : false; } whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e6a119d1f..095b9b17c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -317,8 +317,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> IconBox.AutomaticTitle(iconDoc); //iconDoc.proto![this._fieldKey] = selected.length > 1 ? "collection" : undefined; - iconDoc.width = Number(MINIMIZED_ICON_SIZE); - iconDoc.height = Number(MINIMIZED_ICON_SIZE); + iconDoc._width = Number(MINIMIZED_ICON_SIZE); + iconDoc._height = Number(MINIMIZED_ICON_SIZE); iconDoc.x = NumCast(doc.x); iconDoc.y = NumCast(doc.y) - 24; iconDoc.maximizedDocs = new List(selected.map(s => s.props.Document)); @@ -454,10 +454,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { const doc = PositionDocument(element.props.Document); const layoutDoc = PositionDocument(Doc.Layout(element.props.Document)); - let nwidth = layoutDoc.nativeWidth || 0; - let nheight = layoutDoc.nativeHeight || 0; - const width = (layoutDoc.width || 0); - const height = (layoutDoc.height || (nheight / nwidth * width)); + let nwidth = layoutDoc._nativeWidth || 0; + let nheight = layoutDoc._nativeHeight || 0; + const width = (layoutDoc._width || 0); + const height = (layoutDoc._height || (nheight / nwidth * width)); const scale = element.props.ScreenToLocalTransform().Scale * element.props.ContentScaling(); const actualdW = Math.max(width + (dW * scale), 20); const actualdH = Math.max(height + (dH * scale), 20); @@ -466,33 +466,33 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { layoutDoc.ignoreAspect = false; - layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; - layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; + layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; } if (fixedAspect && (!nwidth || !nheight)) { - layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; - layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; + layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; } if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { - layoutDoc.nativeWidth = actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0); + layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0); } - layoutDoc.width = actualdW; - if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.height = nheight / nwidth * layoutDoc.width; - else layoutDoc.height = actualdH; + layoutDoc._width = actualdW; + if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width; + else layoutDoc._height = actualdH; } else { if (!fixedAspect) { - layoutDoc.nativeHeight = actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0); + layoutDoc._nativeHeight = actualdH / (layoutDoc._height || 1) * (doc._nativeHeight || 0); } - layoutDoc.height = actualdH; - if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.width = nwidth / nheight * layoutDoc.height; - else layoutDoc.width = actualdW; + layoutDoc._height = actualdH; + if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height; + else layoutDoc._width = actualdW; } } else { - dW && (layoutDoc.width = actualdW); - dH && (layoutDoc.height = actualdH); + dW && (layoutDoc._width = actualdW); + dH && (layoutDoc._height = actualdH); dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cd4e9a1a6..2297c2bb3 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -176,9 +176,9 @@ export class MainView extends React.Component { } else { if (received && this._urlState.sharing) { reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, - initialized => initialized && received && DocServer.GetRefField(received).then(field => { - if (field instanceof Doc && field.viewType !== CollectionViewType.Docking) { - CollectionDockingView.AddRightSplit(field, undefined); + initialized => initialized && received && DocServer.GetRefField(received).then(docField => { + if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { + CollectionDockingView.AddRightSplit(docField, undefined); } }), ); @@ -199,8 +199,8 @@ export class MainView extends React.Component { const freeformOptions: DocumentOptions = { x: 0, y: 400, - width: this._panelWidth * .7, - height: this._panelHeight, + _width: this._panelWidth * .7, + _height: this._panelHeight, title: "Collection " + workspaceCount, backgroundColor: "white" }; diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 243cdb8f6..a48e8f508 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -178,11 +178,11 @@ export class MetadataEntryMenu extends React.Component{ } docSource = docSource as Doc[] | Doc; if (docSource instanceof Doc) { - if (docSource.viewType === undefined) { + if (docSource._viewType === undefined) { return (null); } } else if (Array.isArray(docSource)) { - if (!docSource.every(doc => doc.viewType !== undefined)) { + if (!docSource.every(doc => doc._viewType !== undefined)) { return null; } } diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 9706d0f99..831d9ca0b 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -31,8 +31,8 @@ export class PreviewCursor extends React.Component<{}> { if (e.clipboardData.getData("text/plain").indexOf("www.youtube.com/watch") !== -1) { const url = e.clipboardData.getData("text/plain").replace("youtube.com/watch?v=", "youtube.com/embed/"); return PreviewCursor._addDocument(Docs.Create.VideoDocument(url, { - title: url, width: 400, height: 315, - nativeWidth: 600, nativeHeight: 472.5, + title: url, _width: 400, _height: 315, + _nativeWidth: 600, _nativeHeight: 472.5, x: newPoint[0], y: newPoint[1] })); } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index d953d3bab..d6cc394d8 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -104,7 +104,7 @@ export class TemplateMenu extends React.Component { toggleChrome = (): void => { this.props.docs.map(dv => { const layout = Doc.Layout(dv.Document); - layout.chromeStatus = (layout.chromeStatus !== "disabled" ? "disabled" : "enabled"); + layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : "enabled"); }); } onAliasButtonUp = (e: PointerEvent): void => { @@ -150,7 +150,7 @@ export class TemplateMenu extends React.Component { templateMenu.push(); templateMenu.push(); templateMenu.push(); - templateMenu.push(); + templateMenu.push(); return ( diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 125cd828f..75e43a875 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -235,7 +235,7 @@ export class CollectionDockingView extends React.Component([document]); } else { - const stackView = Docs.Create.FreeformDocument([document], { fitToBox: true, isDisplayPanel: true, title: "document viewer" }); + const stackView = Docs.Create.FreeformDocument([document], { _fitToBox: true, isDisplayPanel: true, title: "document viewer" }); CollectionDockingView.AddRightSplit(stackView, undefined, []); } } @@ -505,7 +505,7 @@ export class CollectionDockingView extends React.Component { if (e.target === stack.header.element[0] && e.button === 1) { - this.AddTab(stack, Docs.Create.FreeformDocument([], { width: this.props.PanelWidth(), height: this.props.PanelHeight(), title: "Untitled Collection" }), undefined); + this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" }), undefined); } }); @@ -665,19 +665,19 @@ export class DockedFrameRenderer extends React.Component { } get layoutDoc() { return this._document && Doc.Layout(this._document); } - panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc.width), NumCast(this.layoutDoc.nativeWidth)), this._panelWidth) : this._panelWidth; + panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : this._panelWidth; panelHeight = () => this._panelHeight; - nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeWidth) || this._panelWidth : 0; - nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!.fitWidth ? NumCast(this.layoutDoc!.nativeHeight) || this._panelHeight : 0; + nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0; contentScaling = () => { if (this.layoutDoc!.type === DocumentType.PDF) { - if ((this.layoutDoc && this.layoutDoc.fitWidth) || - this._panelHeight / NumCast(this.layoutDoc!.nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!.nativeWidth)) { - return this._panelWidth / NumCast(this.layoutDoc!.nativeWidth); + if ((this.layoutDoc && this.layoutDoc._fitWidth) || + this._panelHeight / NumCast(this.layoutDoc!._nativeHeight) > this._panelWidth / NumCast(this.layoutDoc!._nativeWidth)) { + return this._panelWidth / NumCast(this.layoutDoc!._nativeWidth); } else { - return this._panelHeight / NumCast(this.layoutDoc!.nativeHeight); + return this._panelHeight / NumCast(this.layoutDoc!._nativeHeight); } } const nativeH = this.nativeHeight(); @@ -745,7 +745,7 @@ export class DockedFrameRenderer extends React.Component { (

this._mainCont = ref} style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, - height: this.layoutDoc && this.layoutDoc.fitWidth ? undefined : "100%", + height: this.layoutDoc && this.layoutDoc._fitWidth ? undefined : "100%", width: this.widthpercent }}> {this.docView} diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 8bf6b4202..67062ae41 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -38,8 +38,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { componentDidMount() { // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported). - this._widthDisposer = reaction(() => NumCast(this.props.Document.height, 0) + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0), - () => this.props.Document.width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10), + this._widthDisposer = reaction(() => this.props.Document[HeightSym]() + this.childDocs.length + (this.props.Document.isExpanded ? 1 : 0), + () => this.props.Document._width = 5 + (this.props.Document.isExpanded ? this.childDocs.length * (this.props.Document[HeightSym]()) : 10), { fireImmediately: true } ); @@ -73,7 +73,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { public isCurrent(doc: Doc) { return !doc.isMinimized && (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } - dimension = () => NumCast(this.props.Document.height); // 2 * the padding + dimension = () => NumCast(this.props.Document._height); // 2 * the padding getTransform = (ele: React.RefObject) => () => { if (!ele.current) return Transform.Identity(); const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current); @@ -88,11 +88,11 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { onChange={action((e: any) => this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} /> -
+
{this.childLayoutPairs.filter((pair) => this.isCurrent(pair.layout)).map((pair, ind) => { - const nested = pair.layout.viewType === CollectionViewType.Linear; + const nested = pair.layout._viewType === CollectionViewType.Linear; const dref = React.createRef(); - const nativeWidth = NumCast(pair.layout.nativeWidth, this.dimension()); + const nativeWidth = NumCast(pair.layout._nativeWidth, this.dimension()); const deltaSize = nativeWidth * .15 / 2; return
{ this._createAliasSelected = false; const key = StrCast(this.props.parent.props.Document.sectionFilter); - const newDoc = Docs.Create.TextDocument({ height: 18, width: 200, title: value }); + const newDoc = Docs.Create.TextDocument({ _height: 18, _width: 200, title: value }); newDoc[key] = this.getValue(this.props.heading); return this.props.parent.props.addDocument(newDoc); } @@ -258,7 +258,7 @@ export class CollectionMasonryViewFieldRow extends React.Component "", SetValue: this.addDocument, diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 43a302af7..a44f990e1 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -24,11 +24,11 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { this._narrativeDisposer?.(); } componentDidMount() { - this.props.Document.freeformLayoutEngine = "pivot"; - if (!this.props.Document.facetCollection) { - const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); + this.props.Document._freeformLayoutEngine = "pivot"; + if (!this.props.Document._facetCollection) { + const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; - this.props.Document.excludeFields = new List(["facetCollection", "docFilter", "viewSpecScript"]); + this.props.Document.excludeFields = new List(["_facetCollection", "_docFilter", "viewSpecScript"]); const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; const script = CompileScript(scriptText, { @@ -54,15 +54,10 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { } } }), { fireImmediately: true }); - this.props.Document.facetCollection = facetCollection; - this.props.Document.fitToBox = true; + this.props.Document._facetCollection = facetCollection; + this.props.Document._fitToBox = true; } } - - @computed get fieldExtensionDoc() { - return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey); - } - bodyPanelWidth = () => this.props.PanelWidth() - this._facetWidth; getTransform = () => this.props.ScreenToLocalTransform().translate(-200, 0); @@ -76,7 +71,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { * Responds to clicking the check box in the flyout menu */ facetClick = (facetHeader: string) => { - const facetCollection = this.props.Document.facetCollection; + const facetCollection = this.props.Document._facetCollection; if (facetCollection instanceof Doc) { const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader); if (found !== -1) { @@ -120,11 +115,11 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { } render() { - const facetCollection = Cast(this.props.Document?.facetCollection, Doc, null); + const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null); const flyout = (
{this._allFacets.map(facet =>
} - {this.props.Document.chromeStatus !== 'disabled' ? : null} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index f0dcadb5b..29dfb60c5 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -136,7 +136,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { if (value === ":freeForm") { - return this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { width: 200, height: 200 })); + return this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200 })); } else if (value.startsWith(":")) { const { Document, addDocument } = this.props.parent.props; const fieldKey = value.substring(1); @@ -152,7 +152,7 @@ export class CollectionStackingViewFieldColumn extends React.Component NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0); const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; @@ -310,7 +310,7 @@ export class CollectionStackingViewFieldColumn extends React.Component
{/* the default bucket (no key value) has a tooltip that describes what it is. @@ -350,7 +350,7 @@ export class CollectionStackingViewFieldColumn extends React.Component
: (null); for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `; - const chromeStatus = this.props.parent.props.Document.chromeStatus; + const chromeStatus = this.props.parent.props.Document._chromeStatus; return (
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx index 105061f46..8c7e113b2 100644 --- a/src/client/views/collections/CollectionStaffView.tsx +++ b/src/client/views/collections/CollectionStaffView.tsx @@ -23,10 +23,6 @@ export class CollectionStaffView extends CollectionSubView(doc => doc) { this.props.Document.staves = 5; } - @computed get fieldExtensionDoc() { - return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey); - } - @computed get addStaffButton() { return
+
; } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 063b5c5df..db393918d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -82,7 +82,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : Doc.GetProto(this.props.Document); } - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc. // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through @@ -90,14 +89,10 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' @computed get dataField() { const { annotationsKey, fieldKey } = this.props; - const { extensionDoc, dataDoc } = this; if (annotationsKey) { - if (extensionDoc) { - return extensionDoc[annotationsKey]; - } - return undefined; + return this.dataDoc[fieldKey + "-" + annotationsKey]; } - return dataDoc[fieldKey]; + return this.dataDoc[fieldKey]; } get childLayoutPairs(): { layout: Doc; data: Doc; }[] { @@ -216,7 +211,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); } } else if (text) { - this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, width: 100, height: 25, documentText: "@@@" + text })); + this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument({ ...options, _width: 100, _height: 25, documentText: "@@@" + text })); } return; } @@ -226,7 +221,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { const img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : ""; if (img) { const split = img.split("src=\"")[1].split("\"")[0]; - const doc = Docs.Create.ImageDocument(split, { ...options, width: 300 }); + const doc = Docs.Create.ImageDocument(split, { ...options, _width: 300 }); ImageUtils.ExtractExif(doc); this.props.addDocument(doc); return; @@ -241,7 +236,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } }); } else { - const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", width: 300, height: 300, documentText: text }); + const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300, documentText: text }); this.props.addDocument(htmlDoc); } return; @@ -249,12 +244,12 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } if (text && text.indexOf("www.youtube.com/watch") !== -1) { const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/"); - this.props.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, width: 400, height: 315, nativeWidth: 600, nativeHeight: 472.5 })); + this.props.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5 })); return; } let matches: RegExpExecArray | null; if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) { - const newBox = Docs.Create.TextDocument({ ...options, width: 400, height: 200, title: "Awaiting title from Google Docs..." }); + const newBox = Docs.Create.TextDocument({ ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." }); const proto = newBox.proto!; const documentId = matches[2]; proto[GoogleRef] = documentId; @@ -303,7 +298,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { const dropFileName = file ? file.name : "-empty-"; promises.push(Networking.PostFormDataToServer("/upload", formData).then(results => { results.map(action(({ clientAccessPath }: any) => { - const full = { ...options, width: 300, title: dropFileName }; + const full = { ...options, _width: 300, title: dropFileName }; const pathname = Utils.prepend(clientAccessPath); Docs.Get.DocumentFromType(type, pathname, full).then(doc => { doc && (Doc.GetProto(doc).fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "")); @@ -318,7 +313,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { Promise.all(promises).finally(() => { completed && completed(); batch.end(); }); } else { if (text && !text.includes("https://")) { - this.props.addDocument(Docs.Create.TextDocument({ ...options, documentText: "@@@" + text, width: 400, height: 315 })); + this.props.addDocument(Docs.Create.TextDocument({ ...options, documentText: "@@@" + text, _width: 400, _height: 315 })); } batch.end(); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ec348e74b..21f2bc326 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -179,7 +179,7 @@ class TreeView extends React.Component { OnFillDown={undoBatch((value: string) => { Doc.SetInPlace(this.props.document, key, value, false); const layoutDoc = this.props.document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.document.layoutCustom)) : undefined; - const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; return this.props.addDocument(doc); })} @@ -210,7 +210,7 @@ class TreeView extends React.Component { ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); } - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { width: 300, height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" }); ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); @@ -256,21 +256,21 @@ class TreeView extends React.Component { } docWidth = () => { const layoutDoc = Doc.Layout(this.props.document); - const aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth); + const aspect = NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth); if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); - return NumCast(layoutDoc.nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; + return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; } docHeight = () => { const layoutDoc = Doc.Layout(this.props.document); const bounds = this.boundsOfCollectionDocument; return Math.min(this.MAX_EMBED_HEIGHT, (() => { - const aspect = NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth, 1); + const aspect = NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth, 1); if (aspect) return this.docWidth() * aspect; if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); - return layoutDoc.fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection.height) : - Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc.nativeHeight)) / NumCast(layoutDoc.nativeWidth, - NumCast(this.props.containingCollection.height)))) : - NumCast(layoutDoc.height) ? NumCast(layoutDoc.height) : 50; + return layoutDoc._fitWidth ? (!this.props.document.nativeHeight ? NumCast(this.props.containingCollection._height) : + Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, + NumCast(this.props.containingCollection._height)))) : + NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; })()); } @@ -547,7 +547,7 @@ class TreeView extends React.Component { }; const childLayout = Doc.Layout(pair.layout); const rowHeight = () => { - const aspect = NumCast(childLayout.nativeWidth, 0) / NumCast(childLayout.nativeHeight, 0); + const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0); return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); }; return !(child instanceof Doc) ? (null) : TextDocument({ title: key, height: 30 })) - ], { autoHeight: true, chromeStatus: "disabled", title: "detailed layout stack" }); + ...["year", "company", "degrees_of_freedom"].map(key => TextDocument({ title: key, _height: 30 })) + ], { autoHeight: true, _chromeStatus: "disabled", title: "detailed layout stack" }); detailedLayout.isTemplateDoc = makeTemplate(detailedLayout); const cardLayout = ImageDocument(fallbackImg, { isTemplateDoc: true, isTemplateForField: "hero", }); // this acts like a template doc and a template field ... a little weird, but seems to work? @@ -650,7 +650,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { Document.childLayout = cardLayout; Document.childDetailed = detailedLayout; - Document.viewType = CollectionViewType.Pivot; + Document._viewType = CollectionViewType.Pivot; Document.pivotField = "company"; } }); @@ -675,12 +675,12 @@ export class CollectionTreeView extends CollectionSubView(Document) { } render() { - const dropAction = StrCast(this.props.Document.dropAction) as dropActionType; + const dropAction = StrCast(this.props.Document._dropAction) as dropActionType; const addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before, false, false, false); const moveDoc = (d: Doc, target: Doc | undefined, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); return !this.childDocs ? (null) : (
this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} onDrop={this.onTreeDrop} @@ -695,7 +695,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { OnFillDown={undoBatch((value: string) => { Doc.SetInPlace(this.dataDoc, "title", value, false); const layoutDoc = this.props.Document.layoutCustom instanceof Doc ? Doc.ApplyTemplate(Doc.GetProto(this.props.Document.layoutCustom)) : undefined; - const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + const doc = layoutDoc || Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true, false, false, false); })} />)} @@ -734,7 +734,7 @@ Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey }); Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { - const docFilters = Cast(layoutDoc.docFilter, listSpec("string"), []); + const docFilters = Cast(layoutDoc._docFilter, listSpec("string"), []); for (let i = 0; i < docFilters.length; i += 3) { const [header, value, state] = docFilters.slice(i, i + 3); if (header === facetHeader && value === facetValue) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 430aecef4..715094bf5 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -91,17 +91,8 @@ export class CollectionView extends Touchable { @observable private static _safeMode = false; public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.props.Document, this.props.fieldKey); } - get collectionViewType(): CollectionViewType | undefined { - if (!this.extensionDoc) return CollectionViewType.Invalid; - NumCast(this.props.Document.viewType) && setTimeout(() => { - if (this.props.Document.viewType) { - this.extensionDoc!.viewType = NumCast(this.props.Document.viewType); - } - Doc.GetProto(this.props.Document).viewType = this.props.Document.viewType = undefined; - }); - const viewField = NumCast(this.extensionDoc.viewType, Cast(this.props.Document.viewType, "number")); + const viewField = NumCast(this.props.Document._viewType); if (CollectionView._safeMode) { if (viewField === CollectionViewType.Freeform) { return CollectionViewType.Tree; @@ -110,15 +101,15 @@ export class CollectionView extends Touchable { return CollectionViewType.Freeform; } } - return viewField === undefined ? CollectionViewType.Invalid : viewField; + return viewField; } componentDidMount = () => { - this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus), + this._reactionDisposer = reaction(() => StrCast(this.props.Document._chromeStatus), () => { // chrome status is one of disabled, collapsed, or visible. this determines initial state from document // chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar. - const chromeStatus = this.props.Document.chromeStatus; + const chromeStatus = this.props.Document._chromeStatus; if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed")) { runInAction(() => this._collapsed = true); } @@ -128,7 +119,7 @@ export class CollectionView extends Touchable { componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer(); // bcz: Argh? What's the height of the collection chromes?? - chromeHeight = () => (this.props.Document.chromeStatus === "enabled" ? -60 : 0); + chromeHeight = () => (this.props.Document._chromeStatus === "enabled" ? -60 : 0); active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0; @@ -138,8 +129,7 @@ export class CollectionView extends Touchable { addDocument(doc: Doc): boolean { const targetDataDoc = Doc.GetProto(this.props.Document); Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); - const extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field - extension && (extension.lastModified = new DateField(new Date(Date.now()))); + targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); Doc.GetProto(doc).lastOpened = new DateField; return true; } @@ -192,19 +182,19 @@ export class CollectionView extends Touchable { case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } case CollectionViewType.Pivot: { return (); } case CollectionViewType.Freeform: - default: { this.props.Document.freeformLayoutEngine = undefined; return (); } + default: { this.props.Document._freeformLayoutEngine = undefined; return (); } } } @action private collapse = (value: boolean) => { this._collapsed = value; - this.props.Document.chromeStatus = value ? "collapsed" : "enabled"; + this.props.Document._chromeStatus = value ? "collapsed" : "enabled"; } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip - const chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) : + const chrome = this.props.Document._chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) : ; return [chrome, this.SubViewHelper(type, renderProps)]; } @@ -214,24 +204,24 @@ export class CollectionView extends Touchable { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 const existingVm = ContextMenu.Instance.findByDescription("View Modes..."); const subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; - subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; }, icon: "signature" }); + subItems.push({ description: "Freeform", event: () => { this.props.Document._viewType = CollectionViewType.Freeform; }, icon: "signature" }); if (CollectionView._safeMode) { - ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); + ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document._viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } - subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" }); - subItems.push({ description: "Treeview", event: () => this.props.Document.viewType = CollectionViewType.Tree, icon: "tree" }); - subItems.push({ description: "Stacking", event: () => this.props.Document.viewType = CollectionViewType.Stacking, icon: "ellipsis-v" }); + subItems.push({ description: "Schema", event: () => this.props.Document._viewType = CollectionViewType.Schema, icon: "th-list" }); + subItems.push({ description: "Treeview", event: () => this.props.Document._viewType = CollectionViewType.Tree, icon: "tree" }); + subItems.push({ description: "Stacking", event: () => this.props.Document._viewType = CollectionViewType.Stacking, icon: "ellipsis-v" }); subItems.push({ description: "Stacking (AutoHeight)", event: () => { - this.props.Document.viewType = CollectionViewType.Stacking; + this.props.Document._viewType = CollectionViewType.Stacking; this.props.Document.autoHeight = true; }, icon: "ellipsis-v" }); - subItems.push({ description: "Staff", event: () => this.props.Document.viewType = CollectionViewType.Staff, icon: "music" }); - subItems.push({ description: "Multicolumn", event: () => this.props.Document.viewType = CollectionViewType.Multicolumn, icon: "columns" }); - subItems.push({ description: "Masonry", event: () => this.props.Document.viewType = CollectionViewType.Masonry, icon: "columns" }); - subItems.push({ description: "Pivot", event: () => this.props.Document.viewType = CollectionViewType.Pivot, icon: "columns" }); - switch (this.props.Document.viewType) { + subItems.push({ description: "Staff", event: () => this.props.Document._viewType = CollectionViewType.Staff, icon: "music" }); + subItems.push({ description: "Multicolumn", event: () => this.props.Document._viewType = CollectionViewType.Multicolumn, icon: "columns" }); + subItems.push({ description: "Masonry", event: () => this.props.Document._viewType = CollectionViewType.Masonry, icon: "columns" }); + subItems.push({ description: "Pivot", event: () => this.props.Document._viewType = CollectionViewType.Pivot, icon: "columns" }); + switch (this.props.Document._viewType) { case CollectionViewType.Freeform: { subItems.push({ description: "Custom", icon: "fingerprint", event: AddCustomFreeFormLayout(this.props.Document, this.props.fieldKey) }); break; diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 42c563fe2..59d7a2671 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -55,9 +55,9 @@ export class CollectionViewBaseChrome extends React.Component Doc.GetProto(this.props.CollectionView.props.Document).data = new List(draggedDocs.map((d: any) => Doc.MakeAlias(d))) }; _viewCommand = { - title: "restore view", script: "this.target.panX = this.restoredPanX; this.target.panY = this.restoredPanY; this.target.scale = this.restoredScale;", params: ["target"], - immediate: (draggedDocs: Doc[]) => { this.props.CollectionView.props.Document.panX = 0; this.props.CollectionView.props.Document.panY = 0; this.props.CollectionView.props.Document.scale = 1; }, - initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document.panX; button.restoredPanY = this.props.CollectionView.props.Document.panY; button.restoredScale = this.props.CollectionView.props.Document.scale; } + title: "restore view", script: "this.target._panX = this.restoredPanX; this.target._panY = this.restoredPanY; this.target.scale = this.restoredScale;", params: ["target"], + immediate: (draggedDocs: Doc[]) => { this.props.CollectionView.props.Document._panX = 0; this.props.CollectionView.props.Document._panY = 0; this.props.CollectionView.props.Document.scale = 1; }, + initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document._panX; button.restoredPanY = this.props.CollectionView.props.Document._panY; button.restoredScale = this.props.CollectionView.props.Document.scale; } }; _freeform_commands = [this._contentCommand, this._templateCommand, this._narrativeCommand, this._viewCommand]; _stacking_commands = [this._contentCommand, this._templateCommand]; @@ -132,7 +132,7 @@ export class CollectionViewBaseChrome extends React.Component { this.addKeyRestrictions(fields); // chrome status is one of disabled, collapsed, or visible. this determines initial state from document - const chromeStatus = this.props.CollectionView.props.Document.chromeStatus; + const chromeStatus = this.props.CollectionView.props.Document._chromeStatus; if (chromeStatus) { if (chromeStatus === "disabled") { throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!"); @@ -149,7 +149,7 @@ export class CollectionViewBaseChrome extends React.Component { //@ts-ignore - this.props.CollectionView.props.Document.viewType = parseInt(e.target.selectedOptions[0].value); + this.props.CollectionView.props.Document._viewType = parseInt(e.target.selectedOptions[0].value); } @action @@ -223,7 +223,7 @@ export class CollectionViewBaseChrome extends React.Component { - this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "collapsed" : "enabled"; + this.props.CollectionView.props.Document._chromeStatus = this.props.CollectionView.props.Document._chromeStatus === "enabled" ? "collapsed" : "enabled"; if (this.props.collapse) { - this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled"); + this.props.collapse(this.props.CollectionView.props.Document._chromeStatus !== "enabled"); } } @@ -272,7 +272,7 @@ export class CollectionViewBaseChrome extends React.Component { - if (StrCast(this.document.freeformLayoutEngine) !== "pivot") { + if (StrCast(this.document._freeformLayoutEngine) !== "pivot") { return (null); } return (
@@ -402,7 +402,7 @@ export class CollectionViewBaseChrome extends React.Component + value={NumCast(this.props.CollectionView.props.Document._viewType)}> diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 1cc05f92c..18f1baf44 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -21,7 +21,13 @@ export const Flyout = higflyout.default; library.add(faEdit); -type SelectorProps = { Document: Doc, Views: DocumentView[], Stack?: any, addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void }; +type SelectorProps = { + Document: Doc, + Views: DocumentView[], + Stack?: any, + addDocTab(doc: Doc, dataDoc: Doc | undefined, location: string): void +}; + @observer export class SelectorContextMenu extends React.Component { @observable private _docs: { col: Doc, target: Doc }[] = []; @@ -49,11 +55,11 @@ export class SelectorContextMenu extends React.Component { getOnClick({ col, target }: { col: Doc, target: Doc }) { return () => { col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; - if (NumCast(col.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(target.x) + NumCast(target.width) / 2; - const newPanY = NumCast(target.y) + NumCast(target.height) / 2; - col.panX = newPanX; - col.panY = newPanY; + if (NumCast(col._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { + const newPanX = NumCast(target.x) + NumCast(target._width) / 2; + const newPanY = NumCast(target.y) + NumCast(target._height) / 2; + col._panX = newPanX; + col._panY = newPanY; } this.props.addDocTab(col, undefined, "inTab"); // bcz: dataDoc? }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 32097816d..8bf56338b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -79,10 +79,10 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo for (const doc of val) { const layoutDoc = Doc.Layout(doc); let wid = pivotAxisWidth; - let hgt = layoutDoc.nativeWidth ? (NumCast(layoutDoc.nativeHeight) / NumCast(layoutDoc.nativeWidth)) * pivotAxisWidth : pivotAxisWidth; + let hgt = layoutDoc._nativeWidth ? (NumCast(layoutDoc._nativeHeight) / NumCast(layoutDoc._nativeWidth)) * pivotAxisWidth : pivotAxisWidth; if (hgt > pivotAxisWidth) { hgt = pivotAxisWidth; - wid = layoutDoc.nativeHeight ? (NumCast(layoutDoc.nativeWidth) / NumCast(layoutDoc.nativeHeight)) * pivotAxisWidth : pivotAxisWidth; + wid = layoutDoc.nativeHeight ? (NumCast(layoutDoc._nativeWidth) / NumCast(layoutDoc._nativeHeight)) * pivotAxisWidth : pivotAxisWidth; } docMap.set(doc, { x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.length < numCols ? (numCols - val.length) * pivotAxisWidth / 2 : 0), @@ -104,8 +104,8 @@ export function computePivotLayout(poolData: ObservableMap, pivotDo x: NumCast(pair.layout.x), y: NumCast(pair.layout.y), z: NumCast(pair.layout.z), - width: NumCast(pair.layout.width), - height: NumCast(pair.layout.height) + width: NumCast(pair.layout._width), + height: NumCast(pair.layout._height) }; const pos = docMap.get(pair.layout) || defaultPosition; const data = poolData.get(pair.layout[Id]); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 65953dbdc..c89fb92a5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -41,15 +41,13 @@ import React = require("react"); import { computedFn } from "mobx-utils"; import { TraceMobx } from "../../../../new_fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; -import { LinkManager } from "../../../util/LinkManager"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; -import CollectionPaletteVIew from "../../Palette"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); export const panZoomSchema = createSchema({ - panX: "number", - panY: "number", + _panX: "number", + _panY: "number", scale: "number", arrangeScript: ScriptField, arrangeInit: ScriptField, @@ -81,16 +79,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables @observable _clusterSets: (Doc[])[] = []; - @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } + @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc.xPadding, 10), NumCast(this.layoutDoc.yPadding, 10)); } - @computed get nativeWidth() { return this.Document.fitToContent ? 0 : this.Document.nativeWidth || 0; } - @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; } + @computed get nativeWidth() { return this.Document._fitToContent ? 0 : this.Document._nativeWidth || 0; } + @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document._nativeHeight || 0; } private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } private easing = () => this.props.Document.panTransformType === "Ease"; - private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document.panX || 0; - private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document.panY || 0; + private panX = () => this.fitToContent ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0; + private panY = () => this.fitToContent ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0; private zoomScaling = () => (1 / this.parentScaling) * (this.fitToContent ? Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) : this.Document.scale || 1) @@ -146,13 +144,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const layoutDoc = Doc.Layout(d); d.x = x + NumCast(d.x) - dropX; d.y = y + NumCast(d.y) - dropY; - if (!NumCast(layoutDoc.width)) { - layoutDoc.width = 300; + if (!NumCast(layoutDoc._width)) { + layoutDoc._width = 300; } - if (!NumCast(layoutDoc.height)) { - const nw = NumCast(layoutDoc.nativeWidth); - const nh = NumCast(layoutDoc.nativeHeight); - layoutDoc.height = nw && nh ? nh / nw * NumCast(layoutDoc.width) : 300; + if (!NumCast(layoutDoc._height)) { + const nw = NumCast(layoutDoc._nativeWidth); + const nh = NumCast(layoutDoc._nativeHeight); + layoutDoc._height = nw && nh ? nh / nw * NumCast(layoutDoc._width) : 300; } this.bringToFront(d); })); @@ -182,8 +180,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const layoutDoc = Doc.Layout(cd); const cx = NumCast(cd.x) - this._clusterDistance; const cy = NumCast(cd.y) - this._clusterDistance; - const cw = NumCast(layoutDoc.width) + 2 * this._clusterDistance; - const ch = NumCast(layoutDoc.height) + 2 * this._clusterDistance; + const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance; + const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance; return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? NumCast(cd.cluster) : cluster; }, -1); @@ -353,7 +351,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { case GestureUtils.Gestures.Stroke: const points = ge.points; const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); - const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { x: B.x, y: B.y, width: B.width, height: B.height }); + const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { x: B.x, y: B.y, _width: B.width, _height: B.height }); this.addDocument(inkDoc); e.stopPropagation(); break; @@ -375,7 +373,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return pass; }); - this.addDocument(Docs.Create.FreeformDocument(sel, { x: bounds.x, y: bounds.y, width: bWidth, height: bHeight, panX: 0, panY: 0 })); + this.addDocument(Docs.Create.FreeformDocument(sel, { x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 })); sel.forEach(d => this.props.removeDocument(d)); break; @@ -397,11 +395,11 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // I think it makes sense for the marquee menu to go away when panned. -syip2 MarqueeOptionsMenu.Instance.fadeOut(true); - let x = this.Document.panX || 0; - let y = this.Document.panY || 0; + let x = this.Document._panX || 0; + let y = this.Document._panY || 0; const docs = this.childLayoutPairs.map(pair => pair.layout); const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); - if (!this.isAnnotationOverlay) { + if (!this.isAnnotationOverlay && docs.length) { PDFMenu.Instance.fadeOut(true); const minx = this.childDataProvider(docs[0]).x;//docs.length ? NumCast(docs[0].x) : 0; const miny = this.childDataProvider(docs[0]).y;//docs.length ? NumCast(docs[0].y) : 0; @@ -591,8 +589,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY)); - this.Document.panX = this.isAnnotationOverlay ? newPanX : panX; - this.Document.panY = this.isAnnotationOverlay ? newPanY : panY; + this.Document._panX = this.isAnnotationOverlay ? newPanX : panX; + this.Document._panY = this.isAnnotationOverlay ? newPanY : panY; } } @@ -616,14 +614,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // TODO This technically isn't correct if type !== "doc", as // currently nothing is done, but we should probably push a new state - if (state.type === "doc" && this.Document.panX !== undefined && this.Document.panY !== undefined) { + if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) { const init = state.initializers![this.Document[Id]]; if (!init) { - state.initializers![this.Document[Id]] = { panX: this.Document.panX, panY: this.Document.panY }; + state.initializers![this.Document[Id]] = { panX: this.Document._panX, panY: this.Document._panY }; HistoryUtil.pushState(state); } else if (init.panX !== this.Document.panX || init.panY !== this.Document.panY) { - init.panX = this.Document.panX; - init.panY = this.Document.panY; + init.panX = this.Document._panX; + init.panY = this.Document._panY; HistoryUtil.pushState(state); } } @@ -639,13 +637,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } else { const layoutdoc = Doc.Layout(doc); - const newPanX = NumCast(doc.x) + NumCast(layoutdoc.width) / 2; - const newPanY = NumCast(doc.y) + NumCast(layoutdoc.height) / 2; + const newPanX = NumCast(doc.x) + NumCast(layoutdoc._width) / 2; + const newPanY = NumCast(doc.y) + NumCast(layoutdoc._height) / 2; const newState = HistoryUtil.getState(); newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); - const savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType }; + const savedState = { px: this.Document._panX, py: this.Document._panY, s: this.Document.scale, pt: this.Document.panTransformType }; if (!doc.z) this.setPan(newPanX, newPanY, "Ease"); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow Doc.BrushDoc(this.props.Document); @@ -655,8 +653,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { afterFocus && setTimeout(() => { if (afterFocus && afterFocus()) { - this.Document.panX = savedState.px; - this.Document.panY = savedState.py; + this.Document._panX = savedState.px; + this.Document._panY = savedState.py; this.Document.scale = savedState.s; this.Document.panTransformType = savedState.pt; } @@ -666,7 +664,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } setScaleToZoom = (doc: Doc, scale: number = 0.5) => { - this.Document.scale = scale * Math.min(this.props.PanelWidth() / NumCast(doc.width), this.props.PanelHeight() / NumCast(doc.height)); + this.Document.scale = scale * Math.min(this.props.PanelWidth() / NumCast(doc._width), this.props.PanelHeight() / NumCast(doc._height)); } zoomToScale = (scale: number) => { @@ -709,7 +707,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return { ...result, transition: "transform 1s" }; } const layoutDoc = Doc.Layout(params.doc); - return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc.width, "number"), height: Cast(layoutDoc.height, "number") }; + return { x: Cast(params.doc.x, "number"), y: Cast(params.doc.y, "number"), z: Cast(params.doc.z, "number"), width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number") }; } viewDefsToJSX = (views: any[]) => { @@ -735,7 +733,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc) { return this._layoutPoolData.get(doc[Id]); }.bind(this)); + childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc) { + if (!doc) { + console.log(doc); + } + return this._layoutPoolData.get(doc[Id]); + }.bind(this)); doPivotLayout(poolData: ObservableMap) { return computePivotLayout(poolData, this.props.Document, this.childDocs, @@ -761,7 +764,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { get doLayoutComputation() { let computedElementData: { elements: ViewDefResult[] }; - switch (this.Document.freeformLayoutEngine) { + switch (this.Document._freeformLayoutEngine) { case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break; default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break; } @@ -770,7 +773,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ele: , + fitToBox={this.props.fitToBox || this.Document._freeformLayoutEngine === "pivot"} />, bounds: this.childDataProvider(pair.layout) })); @@ -797,12 +800,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { layoutDocsInGrid = () => { UndoManager.RunInBatch(() => { const docs = DocListCast(this.Document[this.props.fieldKey]); - const startX = this.Document.panX || 0; + const startX = this.Document._panX || 0; let x = startX; - let y = this.Document.panY || 0; + let y = this.Document._panY || 0; let i = 0; - const width = Math.max(...docs.map(doc => NumCast(doc.width))); - const height = Math.max(...docs.map(doc => NumCast(doc.height))); + const width = Math.max(...docs.map(doc => NumCast(doc._width))); + const height = Math.max(...docs.map(doc => NumCast(doc._height))); for (const doc of docs) { doc.x = x; doc.y = y; @@ -880,8 +883,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { const layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); - layoutItems.push({ description: `${this.Document.LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document.LODdisable = !this.Document.LODdisable, icon: "table" }); - layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document.fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); + layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); + layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); layoutItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); @@ -918,7 +921,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { description: "Add Note ...", subitems: DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({ description: (i + 1) + ": " + StrCast(note.title), - event: (args: { x: number, y: number }) => this.addLiveTextBox(Docs.Create.TextDocument({ width: 200, height: 100, x: this.getTransform().transformPoint(args.x, args.y)[0], y: this.getTransform().transformPoint(args.x, args.y)[1], autoHeight: true, layout: note, title: StrCast(note.title) })), + event: (args: { x: number, y: number }) => this.addLiveTextBox(Docs.Create.TextDocument({ _width: 200, _height: 100, x: this.getTransform().transformPoint(args.x, args.y)[0], y: this.getTransform().transformPoint(args.x, args.y)[1], autoHeight: true, layout: note, title: StrCast(note.title) })), icon: "eye" })) as ContextMenuProps[], icon: "eye" @@ -939,7 +942,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { children = () => { const eles: JSX.Element[] = []; - this.extensionDoc && (eles.push(...this.childViews())); + eles.push(...this.childViews()); // this._palette && (eles.push(this._palette)); // this.currentStroke && (eles.push(this.currentStroke)); eles.push(); @@ -951,7 +954,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
; } @computed get marqueeView() { - return @@ -973,7 +976,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y); // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey. // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document - if (!this.extensionDoc) return (null); // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale; return
- {!this.Document.LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? + {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? this.placeholder : this.marqueeView}
; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 584d18372..0e02d1ef2 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -29,7 +29,6 @@ interface MarqueeViewProps { removeDocument: (doc: Doc) => boolean; addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; - extensionDoc: Doc; isAnnotationOverlay?: boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } @@ -85,7 +84,7 @@ export class MarqueeView extends React.Component { const indent = line.search(/\S|$/); - const newBox = Docs.Create.TextDocument({ width: 200, height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); + const newBox = Docs.Create.TextDocument({ _width: 200, _height: 35, x: x + indent / 3 * 10, y: y, documentText: "@@@" + line, title: line }); this.props.addDocument(newBox); y += 40 * this.props.getTransform().Scale; }); @@ -95,17 +94,17 @@ export class MarqueeView extends React.Component { const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== ""); if (ns.length === 1 && text.startsWith("http")) { - this.props.addDocument(Docs.Create.ImageDocument(text, { nativeWidth: 300, width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer + this.props.addDocument(Docs.Create.ImageDocument(text, { _nativeWidth: 300, _width: 300, x: x, y: y }));// paste an image from its URL in the paste buffer } else { this.pasteTable(ns, x, y); } }); } else if (!e.ctrlKey) { this.props.addLiveTextDocument( - Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" })); + Docs.Create.TextDocument({ _width: 200, _height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" })); } else if (e.keyCode > 48 && e.keyCode <= 57) { const notes = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data); - const text = Docs.Create.TextDocument({ width: 200, height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" }); + const text = Docs.Create.TextDocument({ _width: 200, _height: 100, x: x, y: y, autoHeight: true, title: "-typed text-" }); text.layout = notes[(e.keyCode - 49) % notes.length]; this.props.addLiveTextDocument(text); } @@ -128,7 +127,7 @@ export class MarqueeView extends React.Component c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", width: 300, height: 100 }); + const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", _width: 300, _height: 100 }); this.props.addDocument(newCol); } @@ -267,15 +266,15 @@ export class MarqueeView extends React.Component([newCollection]); newCollection.x = bounds.left + bounds.width; Doc.GetProto(newCollection).summaryDoc = summary; Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`); if (e instanceof KeyboardEvent ? e.key === "s" : true) { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view. - const container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" }); - container.viewType = CollectionViewType.Stacking; - container.autoHeight = true; + const container = Docs.Create.FreeformDocument([summary, newCollection], { + x: bounds.left, y: bounds.top, _width: 300, _height: 200, autoHeight: true, + _viewType: CollectionViewType.Stacking, _chromeStatus: "disabled", title: "-summary-" + }); Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight" this.props.addLiveTextDocument(container); } else if (e instanceof KeyboardEvent ? e.key === "S" : false) { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them @@ -468,8 +468,8 @@ export class MarqueeView extends React.Component { if (index > -1) keys.splice(index, 1); const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); - const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); + const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { _width: 500, _height: 300, title: groupType + " table" })); const ref = React.createRef(); return
; } diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 29e167ff7..325c92413 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -89,7 +89,7 @@ export class LinkFollowBox extends React.Component { async resetPan() { if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionDoc) { runInAction(() => this.canPan = false); - if (this.sourceView.props.ContainingCollectionDoc.viewType === CollectionViewType.Freeform) { + if (this.sourceView.props.ContainingCollectionDoc._viewType === CollectionViewType.Freeform) { const docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []); const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc)); @@ -165,11 +165,11 @@ export class LinkFollowBox extends React.Component { @undoBatch openColFullScreen = (options: { context: Doc }) => { if (LinkFollowBox.destinationDoc) { - if (NumCast(options.context.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc.width) / 2; - const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc.height) / 2; - options.context.panX = newPanX; - options.context.panY = newPanY; + if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { + const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2; + const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2; + options.context._panX = newPanX; + options.context._panY = newPanY; } const view = DocumentManager.Instance.getDocumentView(options.context); view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view); @@ -193,11 +193,11 @@ export class LinkFollowBox extends React.Component { openLinkColRight = (options: { context: Doc, shouldZoom: boolean }) => { if (LinkFollowBox.destinationDoc) { options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context; - if (NumCast(options.context.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc.width) / 2; - const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc.height) / 2; - options.context.panX = newPanX; - options.context.panY = newPanY; + if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { + const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2; + const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2; + options.context._panX = newPanX; + options.context._panY = newPanY; } (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, undefined, "onRight"); @@ -245,11 +245,11 @@ export class LinkFollowBox extends React.Component { openLinkColTab = (options: { context: Doc, shouldZoom: boolean }) => { if (LinkFollowBox.destinationDoc) { options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context; - if (NumCast(options.context.viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc.width) / 2; - const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc.height) / 2; - options.context.panX = newPanX; - options.context.panY = newPanY; + if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { + const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2; + const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2; + options.context._panX = newPanX; + options.context._panY = newPanY; } (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, undefined, "inTab"); if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom }); @@ -270,13 +270,13 @@ export class LinkFollowBox extends React.Component { const y = NumCast(LinkFollowBox.sourceDoc.y); const x = NumCast(LinkFollowBox.sourceDoc.x); - const width = NumCast(LinkFollowBox.sourceDoc.width); - const height = NumCast(LinkFollowBox.sourceDoc.height); + const width = NumCast(LinkFollowBox.sourceDoc._width); + const height = NumCast(LinkFollowBox.sourceDoc._height); alias.x = x + width + 30; alias.y = y; - alias.width = width; - alias.height = height; + alias._width = width; + alias._height = height; this.sourceView.props.addDocument(alias); } @@ -361,7 +361,7 @@ export class LinkFollowBox extends React.Component { get canOpenInPlace() { if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) { const colDoc = this.sourceView.props.ContainingCollectionDoc; - if (colDoc.viewType && colDoc.viewType === CollectionViewType.Freeform) return true; + if (colDoc._viewType === CollectionViewType.Freeform) return true; } return false; } @@ -481,7 +481,7 @@ export class LinkFollowBox extends React.Component { let contextMatch: boolean = false; if (this.selectedContextAliases) { this.selectedContextAliases.forEach(alias => { - if (alias.viewType === CollectionViewType.Freeform) contextMatch = true; + if (alias._viewType === CollectionViewType.Freeform) contextMatch = true; }); } if (contextMatch) return true; @@ -523,7 +523,7 @@ export class LinkFollowBox extends React.Component { render() { return ( -
+
{LinkFollowBox.linkDoc ? "Link Title: " + StrCast(LinkFollowBox.linkDoc.title) : "No Link Selected"} @@ -533,7 +533,7 @@ export class LinkFollowBox extends React.Component { LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc ? "Source: " + StrCast(LinkFollowBox.sourceDoc.title) + ", Destination: " + StrCast(LinkFollowBox.destinationDoc.title) : "" : ""}
-
+
Mode
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index ace9a9e4c..0c38ff45c 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -58,7 +58,7 @@ export class LinkMenuGroup extends React.Component { if (index > -1) keys.splice(index, 1); const cols = ["anchor1", "anchor2", ...[...keys]].map(c => new SchemaHeaderField(c, "#f1efeb")); const docs: Doc[] = LinkManager.Instance.getAllMetadataDocsInGroup(groupType); - const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { width: 500, height: 300, title: groupType + " table" })); + const createTable = action(() => Docs.Create.SchemaDocument(cols, docs, { _width: 500, _height: 300, title: groupType + " table" })); const ref = React.createRef(); return
; } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 95c765e8a..8ede79edc 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -68,7 +68,7 @@ export class AudioBox extends DocExtendableComponent AudioBox._scrubTime, timeInMillisecondsFrom1970 => { - const start = this.extensionDoc && DateCast(this.extensionDoc.recordingStart); + const start = DateCast(this.dataDoc[this.props.fieldKey + "-recordingStart"]); start && this.playFrom((timeInMillisecondsFrom1970 - start.date.getTime()) / 1000); }); } @@ -128,13 +128,12 @@ export class AudioBox extends DocExtendableComponent { let gumStream: any; const self = this; - const extensionDoc = this.extensionDoc; - extensionDoc && navigator.mediaDevices.getUserMedia({ + navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) { gumStream = stream; self._recorder = new MediaRecorder(stream); - extensionDoc.recordingStart = new DateField(new Date()); + self.dataDoc[self.props.fieldKey + "-recordingStart"] = new DateField(new Date()); AudioBox.ActiveRecordings.push(self.props.Document); self._recorder.ondataavailable = async function (e: any) { const formData = new FormData(); @@ -213,55 +212,53 @@ export class AudioBox extends DocExtendableComponent -
- {!this.path ? - : -
-
-
-
-
e.stopPropagation()} - onPointerDown={e => { - if (e.button === 0 && !e.ctrlKey) { - const rect = (e.target as any).getBoundingClientRect(); - this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); - this.pause(); - e.stopPropagation(); - } - }} > - {DocListCast(this.dataDoc.links).map((l, i) => { - let la1 = l.anchor1 as Doc; - let la2 = l.anchor2 as Doc; - let linkTime = NumCast(l.anchor2Timecode); - if (Doc.AreProtosEqual(la1, this.dataDoc)) { - la1 = l.anchor2 as Doc; - la2 = l.anchor1 as Doc; - linkTime = NumCast(l.anchor1Timecode); - } - return !linkTime ? (null) : -
-
- -
-
Doc.linkFollowHighlight(la1)} - onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(linkTime); e.stopPropagation(); } }} - onClick={e => { if (e.button === 0 && !e.ctrlKey) { this.pause(); e.stopPropagation(); } }} /> -
; - })} -
- {this.audio} -
+ return
+
+ {!this.path ? + : +
+
+
+
+
e.stopPropagation()} + onPointerDown={e => { + if (e.button === 0 && !e.ctrlKey) { + const rect = (e.target as any).getBoundingClientRect(); + this._ele!.currentTime = this.Document.currentTimecode = (e.clientX - rect.x) / rect.width * NumCast(this.dataDoc.duration); + this.pause(); + e.stopPropagation(); + } + }} > + {DocListCast(this.dataDoc.links).map((l, i) => { + let la1 = l.anchor1 as Doc; + let la2 = l.anchor2 as Doc; + let linkTime = NumCast(l.anchor2Timecode); + if (Doc.AreProtosEqual(la1, this.dataDoc)) { + la1 = l.anchor2 as Doc; + la2 = l.anchor1 as Doc; + linkTime = NumCast(l.anchor1Timecode); + } + return !linkTime ? (null) : +
+
+ +
+
Doc.linkFollowHighlight(la1)} + onPointerDown={e => { if (e.button === 0 && !e.ctrlKey) { this.playFrom(linkTime); e.stopPropagation(); } }} + onClick={e => { if (e.button === 0 && !e.ctrlKey) { this.pause(); e.stopPropagation(); } }} /> +
; + })} +
+ {this.audio}
- } -
- ); +
+ } +
; } } \ No newline at end of file diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index cd78f5d49..ea3c505d8 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -38,18 +38,18 @@ export class CollectionFreeFormDocumentView extends DocComponent{ public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); } - private get nativeWidth() { return NumCast(this.layoutDoc?.nativeWidth, this.props.PanelWidth()); } - private get nativeHeight() { return NumCast(this.layoutDoc?.nativeHeight, this.props.PanelHeight()); } + private get nativeWidth() { return NumCast(this.layoutDoc?._nativeWidth, this.props.PanelWidth()); } + private get nativeHeight() { return NumCast(this.layoutDoc?._nativeHeight, this.props.PanelHeight()); } private contentScaling = () => { const wscale = this.props.PanelWidth() / (this.nativeWidth || this.props.PanelWidth() || 1); if (wscale * this.nativeHeight > this.props.PanelHeight()) { @@ -68,10 +68,10 @@ export class ContentFittingDocumentView extends React.Component this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); - private PanelHeight = () => this.nativeHeight && (!this.props.Document || !this.props.Document.fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); + private PanelWidth = () => this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); + private PanelHeight = () => this.nativeHeight && (!this.props.Document || !this.props.Document._fitWidth) ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, -this.centeringYOffset).scale(1 / this.contentScaling()); - private get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document.fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; } + private get centeringOffset() { return this.nativeWidth && (!this.props.Document || !this.props.Document._fitWidth) ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; } private get centeringYOffset() { return Math.abs(this.centeringOffset) < 0.001 ? (this.props.PanelHeight() - this.nativeHeight * this.contentScaling()) / 2 : 0; } @computed get borderRounding() { return StrCast(this.props.Document?.borderRounding); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2570d232a..5663e5e3c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -108,8 +108,8 @@ export class DocumentView extends DocComponent(Docu public get ContentDiv() { return this._mainCont.current; } @computed get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } @computed get topMost() { return this.props.renderDepth === 0; } - @computed get nativeWidth() { return this.layoutDoc.nativeWidth || 0; } - @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } + @computed get nativeWidth() { return this.layoutDoc._nativeWidth || 0; } + @computed get nativeHeight() { return this.layoutDoc._nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } @computed get onPointerDownHandler() { return this.props.onPointerDown ? this.props.onPointerDown : this.Document.onPointerDown; } @computed get onPointerUpHandler() { return this.props.onPointerUp ? this.props.onPointerUp : this.Document.onPointerUp; } @@ -343,7 +343,7 @@ export class DocumentView extends DocComponent(Docu if (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick)) { this.cleanUpInteractions(); - this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); + this.startDragging(this._downX, this._downY, this.Document._dropAction ? this.Document._dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); } } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -387,10 +387,10 @@ export class DocumentView extends DocComponent(Docu if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { const doc = PositionDocument(this.props.Document); const layoutDoc = PositionDocument(Doc.Layout(this.props.Document)); - let nwidth = layoutDoc.nativeWidth || 0; - let nheight = layoutDoc.nativeHeight || 0; - const width = (layoutDoc.width || 0); - const height = (layoutDoc.height || (nheight / nwidth * width)); + let nwidth = layoutDoc._nativeWidth || 0; + let nheight = layoutDoc._nativeHeight || 0; + const width = (layoutDoc._width || 0); + const height = (layoutDoc._height || (nheight / nwidth * width)); const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling(); const actualdW = Math.max(width + (dW * scale), 20); const actualdH = Math.max(height + (dH * scale), 20); @@ -400,33 +400,33 @@ export class DocumentView extends DocComponent(Docu if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { layoutDoc.ignoreAspect = false; - layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; - layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; + layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; } if (fixedAspect && (!nwidth || !nheight)) { - layoutDoc.nativeWidth = nwidth = layoutDoc.width || 0; - layoutDoc.nativeHeight = nheight = layoutDoc.height || 0; + layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; + layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; } if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { - layoutDoc.nativeWidth = actualdW / (layoutDoc.width || 1) * (layoutDoc.nativeWidth || 0); + layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0); } - layoutDoc.width = actualdW; - if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.height = nheight / nwidth * layoutDoc.width; - else layoutDoc.height = actualdH; + layoutDoc._width = actualdW; + if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width; + else layoutDoc._height = actualdH; } else { if (!fixedAspect) { - layoutDoc.nativeHeight = actualdH / (layoutDoc.height || 1) * (doc.nativeHeight || 0); + layoutDoc._nativeHeight = actualdH / (layoutDoc._height || 1) * (doc._nativeHeight || 0); } - layoutDoc.height = actualdH; - if (fixedAspect && !layoutDoc.fitWidth) layoutDoc.width = nwidth / nheight * layoutDoc.height; - else layoutDoc.width = actualdW; + layoutDoc._height = actualdH; + if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height; + else layoutDoc._width = actualdW; } } else { - dW && (layoutDoc.width = actualdW); - dH && (layoutDoc.height = actualdH); + dW && (layoutDoc._width = actualdW); + dH && (layoutDoc._height = actualdH); dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } @@ -484,7 +484,7 @@ export class DocumentView extends DocComponent(Docu if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); + this.startDragging(this._downX, this._downY, this.Document._dropAction ? this.Document._dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); } } e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -520,12 +520,13 @@ export class DocumentView extends DocComponent(Docu undoBatch(() => doc.layoutKey = "layout")(); } - static makeCustomViewClicked = (doc: Doc, dataDoc: Opt) => { + static makeCustomViewClicked = (doc: Doc, dataDoc: Opt, name: string = "custom") => { const batch = UndoManager.StartBatch("CustomViewClicked"); - if (doc.layoutCustom === undefined) { - const width = NumCast(doc.width); - const height = NumCast(doc.height); - const options = { title: "data", width, x: -width / 2, y: - height / 2, }; + const customName = "layout-" + name; + if (doc[customName] === undefined) { + const _width = NumCast(doc.width); + const _height = NumCast(doc.height); + const options = { title: "data", _width, x: -_width / 2, y: - _height / 2, }; let fieldTemplate: Doc; switch (doc.type) { @@ -549,12 +550,12 @@ export class DocumentView extends DocComponent(Docu fieldTemplate.heading = 1; fieldTemplate.autoHeight = true; - const docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: doc.title + "_layout", width: width + 20, height: Math.max(100, height + 45) }); + const docTemplate = Docs.Create.FreeformDocument([fieldTemplate], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(docTemplate)); - Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, "layoutCustom", undefined); + Doc.ApplyTemplateTo(docTemplate, dataDoc || doc, customName, undefined); } else { - doc.layoutKey = "layoutCustom"; + doc.layoutKey = customName; } batch.end(); } @@ -622,9 +623,9 @@ export class DocumentView extends DocComponent(Docu freezeNativeDimensions = (): void => { this.layoutDoc.autoHeight = this.layoutDoc.autoHeight = false; this.layoutDoc.ignoreAspect = !this.layoutDoc.ignoreAspect; - if (!this.layoutDoc.ignoreAspect && !this.layoutDoc.nativeWidth) { - this.layoutDoc.nativeWidth = this.props.PanelWidth(); - this.layoutDoc.nativeHeight = this.props.PanelHeight(); + if (!this.layoutDoc.ignoreAspect && !this.layoutDoc._nativeWidth) { + this.layoutDoc._nativeWidth = this.props.PanelWidth(); + this.layoutDoc._nativeHeight = this.props.PanelHeight(); } } @@ -635,7 +636,7 @@ export class DocumentView extends DocComponent(Docu if (!anchors.find(anchor2 => anchor2 && anchor2.title === this.Document.title + ".portal" ? true : false)) { const portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, ""); DocServer.GetRefField(portalID).then(existingPortal => { - const portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.layoutDoc.width || 0) + 10, height: this.layoutDoc.height || 0, title: portalID }); + const portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { _width: (this.layoutDoc._width || 0) + 10, _height: this.layoutDoc._height || 0, title: portalID }); DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link"); this.Document.isButton = true; }); @@ -646,13 +647,14 @@ export class DocumentView extends DocComponent(Docu @action setNarrativeView = (custom: boolean): void => { if (custom) { - this.props.Document.layout_narrative = CollectionView.LayoutString("narrative"); - this.props.Document.nativeWidth = this.props.Document.nativeHeight = undefined; - !this.props.Document.narrative && (Doc.GetProto(this.props.Document).narrative = new List([])); - this.props.Document.viewType = CollectionViewType.Stacking; - this.props.Document.layoutKey = "layout_narrative"; - } else { - DocumentView.makeNativeViewClicked(this.props.Document); + custom ? DocumentView.makeCustomViewClicked(this.props.Document, this.props.DataDoc, "narrative") : DocumentView.makeNativeViewClicked(this.props.Document); + // this.props.Document.layout_narrative = CollectionView.LayoutString("narrative"); + // this.props.Document.layoutKey = "layout_narrative"; + // this.props.Document._nativeWidth = this.props.Document._nativeHeight = undefined; + // !this.props.Document.narrative && (Doc.GetProto(this.props.Document).narrative = new List([])); + // this.props.Document._viewType = CollectionViewType.Stacking; + // } else { + // DocumentView.makeNativeViewClicked(this.props.Document); } } @@ -709,7 +711,7 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Right ", event: () => this.props.addDocTab(this.props.Document, this.props.DataDoc, "onRight", this.props.LibraryPath), icon: "caret-square-right" }); subitems.push({ description: "Open Alias Tab ", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "inTab"), icon: "folder" }); subitems.push({ description: "Open Alias Right", event: () => this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.props.DataDoc, "onRight"), icon: "caret-square-right" }); - subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); + subitems.push({ description: "Open Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), undefined, "onRight"), icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); @@ -737,9 +739,9 @@ export class DocumentView extends DocComponent(Docu if (this.props.DataDoc) { layoutItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc!), icon: "concierge-bell" }); } - layoutItems.push({ description: `${this.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document.chromeStatus = (this.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); + layoutItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); layoutItems.push({ description: `${this.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc.autoHeight = !this.layoutDoc.autoHeight, icon: "plus" }); - layoutItems.push({ description: this.Document.ignoreAspect || !this.Document.nativeWidth || !this.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); + layoutItems.push({ description: this.Document.ignoreAspect || !this.Document._nativeWidth || !this.Document._nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); layoutItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); layoutItems.push({ description: this.Document.lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); @@ -793,13 +795,13 @@ export class DocumentView extends DocComponent(Docu const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground; DocServer.setFieldWriteMode("x", mode1); DocServer.setFieldWriteMode("y", mode1); - DocServer.setFieldWriteMode("width", mode1); - DocServer.setFieldWriteMode("height", mode1); + DocServer.setFieldWriteMode("_width", mode1); + DocServer.setFieldWriteMode("_height", mode1); - DocServer.setFieldWriteMode("panX", mode2); - DocServer.setFieldWriteMode("panY", mode2); + DocServer.setFieldWriteMode("_panX", mode2); + DocServer.setFieldWriteMode("_panY", mode2); DocServer.setFieldWriteMode("scale", mode2); - DocServer.setFieldWriteMode("viewType", mode2); + DocServer.setFieldWriteMode("_viewType", mode2); }; const aclsMenu: ContextMenuProps[] = []; aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" }); @@ -858,7 +860,7 @@ export class DocumentView extends DocComponent(Docu const fallback = Cast(this.props.Document.layoutKey, "string"); return typeof fallback === "string" ? fallback : "layout"; } - childScaling = () => (this.layoutDoc.fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); + childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this._nativeWidth : this.props.ContentScaling()); @computed get contents() { TraceMobx(); return ((Docu const highlightColors = ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"]; const highlightStyles = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"]; - let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear; + let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc._viewType !== CollectionViewType.Linear; highlighting = highlighting && this.props.focus !== emptyFunction; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way return
(Docu outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, boxShadow: this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined, - background: this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc.viewType === CollectionViewType.Linear ? undefined : backgroundColor, + background: this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor, width: animwidth, height: animheight, opacity: this.Document.opacity diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0a1a79bc4..9303ca0fa 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -152,7 +152,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & const id = Utils.GenerateDeterministicGuid(this.dataDoc[Id] + key); DocServer.GetRefField(value).then(doc => { DocServer.GetRefField(id).then(linkDoc => { - this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); + this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, _width: 500, _height: 500 }, value); DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument); if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id); @@ -193,8 +193,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & const tsel = this._editorView.state.selection.$from; tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 5000 - 1000))); this._applyingChange = true; - this.extensionDoc && !this.extensionDoc.lastModified && (this.extensionDoc.backgroundColor = "lightGray"); - this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now()))); + this.dataDoc[this.props.fieldKey + "-lastModified"] && (this.dataDoc[this.props.fieldKey + "-backgroundColor"] = "lightGray"); + this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON()), state.doc.textBetween(0, state.doc.content.size, "\n\n")); this._applyingChange = false; this.updateTitle(); @@ -271,7 +271,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & const target = de.complete.docDragData.droppedDocuments[0]; // const link = DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "Embedded Doc:" + target.title); // if (link) { - target.fitToBox = true; + target._fitToBox = true; const node = schema.nodes.dashDoc.create({ width: target[WidthSym](), height: target[HeightSym](), title: "dashDoc", docid: target[Id], @@ -704,11 +704,8 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & DocServer.GetRefField(pdfRegionId).then(pdfRegion => { if ((pdfDoc instanceof Doc) && (pdfRegion instanceof Doc)) { setTimeout(async () => { - const extension = Doc.fieldExtensionDoc(pdfDoc, "data"); - if (extension) { - const targetAnnotations = await DocListCastAsync(extension.annotations);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations - targetAnnotations && targetAnnotations.push(pdfRegion); - } + const targetAnnotations = await DocListCastAsync(pdfDoc["data-annotations"]);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations + targetAnnotations && targetAnnotations.push(pdfRegion); }); const link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link"); @@ -1069,12 +1066,12 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & this.layoutDoc.limitHeight = undefined; this.layoutDoc.autoHeight = false; } - const nh = this.Document.isTemplateForField ? 0 : NumCast(this.dataDoc.nativeHeight, 0); - const dh = NumCast(this.layoutDoc.height, 0); + const nh = this.Document.isTemplateForField ? 0 : NumCast(this.dataDoc._nativeHeight, 0); + const dh = NumCast(this.layoutDoc._height, 0); const newHeight = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)); if (Math.abs(newHeight - dh) > 1) { // bcz: Argh! without this, we get into a React crash if the same document is opened in a freeform view and in the treeview. no idea why, but after dragging the freeform document, selecting it, and selecting text, it will compute to 1 pixel higher than the treeview which causes a cycle - this.layoutDoc.height = newHeight; - this.dataDoc.nativeHeight = nh ? scrollHeight : undefined; + this.layoutDoc._height = newHeight; + this.dataDoc._nativeHeight = nh ? scrollHeight : undefined; } } } @@ -1082,6 +1079,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & @computed get sidebarWidthPercent() { return StrCast(this.props.Document.sidebarWidthPercent, "0%"); } @computed get sidebarWidth() { return Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); } @computed get annotationsKey() { return "annotations"; } + @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); } render() { TraceMobx(); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; @@ -1122,7 +1120,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & {this.props.Document.hideSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
this.toggleSidebar()} /> :
+ style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> this.sidebarWidth} diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx index 8dedfda9b..fda3e3285 100644 --- a/src/client/views/nodes/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/FormattedTextBoxComment.tsx @@ -86,7 +86,7 @@ export class FormattedTextBoxComment { DocumentManager.Instance.FollowLink(FormattedTextBoxComment.linkDoc, textBox.props.Document, (doc: Doc, maxLocation: string) => textBox.props.addDocTab(doc, undefined, e.ctrlKey ? "inTab" : "onRight")); } else if (textBox && (FormattedTextBoxComment.tooltipText as any).href) { - textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, width: 200, height: 400 }), undefined, "onRight"); + textBox.props.addDocTab(Docs.Create.WebDocument((FormattedTextBoxComment.tooltipText as any).href, { title: (FormattedTextBoxComment.tooltipText as any).href, _width: 200, _height: 400 }), undefined, "onRight"); } keep && textBox && FormattedTextBoxComment.start !== undefined && textBox.adoptAnnotation( FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 9462ff024..172338eb6 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -79,7 +79,7 @@ export class IconBox extends React.Component { runInAction(() => { if (r.offset!.width || this.props.Document.hideLabel) { this.props.Document.iconWidth = (r.offset!.width + Number(MINIMIZED_ICON_SIZE)); - if (this.props.Document.height === Number(MINIMIZED_ICON_SIZE)) this.props.Document.width = this.props.Document.iconWidth; + if (this.props.Document._height === Number(MINIMIZED_ICON_SIZE)) this.props.Document._width = this.props.Document.iconWidth; } })}> {({ measureRef }) => diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index b7e904283..1a07c1598 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -39,7 +39,6 @@ library.add(faFileAudio, faAsterisk); export const pageSchema = createSchema({ curPage: "number", fitWidth: "boolean", - rotation: "number", googlePhotosUrl: "string", googlePhotosTags: "string" }); @@ -78,7 +77,7 @@ export class ImageBox extends DocAnnotatableComponent { - this.extensionDoc && Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop); + Doc.AddDocToList(this.dataDoc, this.props.fieldKey + "-alternates", drop); e.stopPropagation(); })); } @@ -88,8 +87,7 @@ export class ImageBox extends DocAnnotatableComponent { - const nw = this.Document.nativeWidth; - const nh = this.Document.nativeHeight; - const w = this.Document.width; - const h = this.Document.height; - this.Document.rotation = ((this.Document.rotation || 0) + 90) % 360; - this.Document.nativeWidth = nh; - this.Document.nativeHeight = nw; - this.Document.width = h; - this.Document.height = w; + const nw = NumCast(this.Document[this.props.fieldKey + "-nativeWidth"]); + const nh = NumCast(this.Document[this.props.fieldKey + "-nativeHeight"]); + const w = this.Document._width; + const h = this.Document._height; + this.dataDoc[this.props.fieldKey + "-rotation"] = (NumCast(this.dataDoc[this.props.fieldKey + "-rotation"]) + 90) % 360; + this.dataDoc[this.props.fieldKey + "-nativeWidth"] = nh; + this.dataDoc[this.props.fieldKey + "-nativeHeight"] = nw; + this.Document._width = h; + this.Document._height = w; }); specificContextMenu = (e: React.MouseEvent): void => { @@ -159,7 +157,7 @@ export class ImageBox extends DocAnnotatableComponent) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List()); return faceDocs; }; - this.url && this.extensionDoc && CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["faces"], this.url, Service.Face, converter); + this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.props.fieldKey + "-faces"], this.url, Service.Face, converter); } generateMetadata = (threshold: Confidence = Confidence.Excellent) => { @@ -171,12 +169,12 @@ export class ImageBox extends DocAnnotatableComponent= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`); }); - this.extensionDoc && (this.extensionDoc.generatedTags = tagsList); + this.dataDoc[this.props.fieldKey + "-generatedTags"] = tagsList; tagDoc.title = "Generated Tags Doc"; tagDoc.confidence = threshold; return tagDoc; }; - this.url && this.extensionDoc && CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter); + this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.props.fieldKey + "-generatedTagsDoc"], this.url, Service.ComputerVision, converter); } @computed private get url() { @@ -216,29 +214,30 @@ export class ImageBox extends DocAnnotatableComponent { - requestImageSize(imgPath) - .then((size: any) => { - const rotation = NumCast(this.dataDoc.rotation) % 180; - const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; - const aspect = realsize.height / realsize.width; - if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) { - setTimeout(action(() => { - if (this.paths[NumCast(this.props.Document.curPage)] === imgPath && (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc)) { - this._resized = imgPath; - this.Document.height = this.Document[WidthSym]() * aspect; - this.Document.nativeHeight = realsize.height; - this.Document.nativeWidth = realsize.width; - } - }), 0); - } else this._resized = imgPath; - }) - .catch((err: any) => console.log(err)); + if (!this.dataDoc[this.props.fieldKey + "-nativeHeight"]) + requestImageSize(imgPath) + .then((size: any) => { + const rotation = NumCast(this.dataDoc[this.props.fieldKey + "-rotation"]) % 180; + const realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; + const aspect = realsize.height / realsize.width; + if (this.Document._width && (Math.abs(1 - NumCast(this.Document._height) / NumCast(this.Document._width) / (realsize.height / realsize.width)) > 0.1)) { + setTimeout(action(() => { + if (this.paths[NumCast(this.props.Document.curPage)] === imgPath && (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc)) { + this._resized = imgPath; + this.Document._height = this.Document[WidthSym]() * aspect; + this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.dataDoc._nativeHeight = realsize.height; + this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.dataDoc._nativeWidth = realsize.width; + } + }), 0); + } else this._resized = imgPath; + }) + .catch((err: any) => console.log(err)); } @action onPointerEnter = () => { const self = this; - const audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations); + const audioAnnos = DocListCast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"]); if (audioAnnos && audioAnnos.length && this._audioState === 0) { const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)]; anno.data instanceof AudioField && new Howl({ @@ -273,17 +272,16 @@ export class ImageBox extends DocAnnotatableComponent 20) { - const alts = DocListCast(extensionDoc.Alternates); + const alts = DocListCast(this.dataDoc[this.props.fieldKey + "-alternates"]); const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url)); const field = this.dataDoc[this.props.fieldKey]; // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; @@ -296,13 +294,11 @@ export class ImageBox extends DocAnnotatableComponent + style={{ color: [DocListCast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }} + icon={!DocListCast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"]).length ? "microphone" : faFileAudio} size="sm" />
{this.considerGooglePhotosLink()} - +
; } diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 234a6a9d3..499cd2877 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -179,7 +179,7 @@ export class KeyValueBox extends React.Component { } getTemplate = async () => { - const parent = Docs.Create.StackingDocument([], { width: 800, height: 800, title: "Template" }); + const parent = Docs.Create.StackingDocument([], { _width: 800, _height: 800, title: "Template" }); parent.singleColumn = false; parent.columnWidth = 100; for (const row of this.rows.filter(row => row.isChecked)) { @@ -200,15 +200,15 @@ export class KeyValueBox extends React.Component { if (!fieldTemplate) { return; } - const previousViewType = fieldTemplate.viewType; + const previousViewType = fieldTemplate._viewType; Doc.MakeMetadataFieldTemplate(fieldTemplate, Doc.GetProto(parentStackingDoc)); - previousViewType && (fieldTemplate.viewType = previousViewType); + previousViewType && (fieldTemplate._viewType = previousViewType); Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate); } inferType = async (data: FieldResult, metaKey: string) => { - const options = { width: 300, height: 300, title: metaKey }; + const options = { _width: 300, _height: 300, title: metaKey }; if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") { return Docs.Create.TextDocument(options); } else if (data instanceof List) { diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 46466aa0f..e6b512adf 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -46,7 +46,7 @@ export class KeyValuePair extends React.Component { if (value instanceof Doc) { e.stopPropagation(); e.preventDefault(); - ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); + ContextMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(value, { _width: 300, _height: 300 }), undefined, "onRight"), icon: "layer-group" }); ContextMenu.Instance.displayMenu(e.clientX, e.clientY); } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 8370df6ba..49fdf56e6 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -50,9 +50,9 @@ export class PDFBox extends DocAnnotatableComponent constructor(props: any) { super(props); this._initialScale = this.props.ScreenToLocalTransform().Scale; - const nw = this.Document.nativeWidth = NumCast(this.extensionDocSync.nativeWidth, NumCast(this.Document.nativeWidth, 927)); - const nh = this.Document.nativeHeight = NumCast(this.extensionDocSync.nativeHeight, NumCast(this.Document.nativeHeight, 1200)); - !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); + const nw = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 927)); + const nh = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, 1200)); + !this.Document._fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); const backup = "oldPath"; const { Document } = this.props; @@ -90,10 +90,10 @@ export class PDFBox extends DocAnnotatableComponent } loaded = (nw: number, nh: number, np: number) => { - this.extensionDocSync.numPages = np; - this.extensionDocSync.nativeWidth = this.Document.nativeWidth = nw * 96 / 72; - this.extensionDocSync.nativeHeight = this.Document.nativeHeight = nh * 96 / 72; - !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); + this.dataDoc[this.props.fieldKey + "-numPages"] = np; + this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = nw * 96 / 72; + this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = nh * 96 / 72; + !this.Document._fitWidth && !this.Document.ignoreAspect && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); } public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); } @@ -211,7 +211,7 @@ export class PDFBox extends DocAnnotatableComponent const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); const funcs: ContextMenuProps[] = []; pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" }); - funcs.push({ description: "Toggle Fit Width " + (this.Document.fitWidth ? "Off" : "On"), event: () => this.Document.fitWidth = !this.Document.fitWidth, icon: "expand-arrows-alt" }); + funcs.push({ description: "Toggle Fit Width " + (this.Document._fitWidth ? "Off" : "On"), event: () => this.Document._fitWidth = !this.Document._fitWidth, icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } @@ -220,8 +220,8 @@ export class PDFBox extends DocAnnotatableComponent @computed get renderTitleBox() { const classname = "pdfBox" + (this.active() ? "-interactive" : ""); return
@@ -252,7 +252,7 @@ export class PDFBox extends DocAnnotatableComponent render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null); if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true; - if (pdfUrl && this.extensionDoc && (this._everActive || (this.extensionDoc.nativeWidth && this.props.ScreenToLocalTransform().Scale < 2.5))) { + if (pdfUrl && (this._everActive || (this.dataDoc[this.props.fieldKey + "-nativeWidth"] && this.props.ScreenToLocalTransform().Scale < 2.5))) { if (pdfUrl instanceof PdfField && this._pdf) { return this.renderPdfView; } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 1e6894f37..eda12d6fb 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -336,7 +336,7 @@ export class PresBox extends React.Component { */ @action initializeScaleViews = (docList: Doc[], viewtype: number) => { - this.props.Document.chromeStatus = "disabled"; + this.props.Document._chromeStatus = "disabled"; const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72; docList.forEach((doc: Doc) => { doc.presBox = this.props.Document; @@ -360,7 +360,7 @@ export class PresBox extends React.Component { return this.props.ScreenToLocalTransform().translate(-10, -50);// listBox padding-left and pres-box-cont minHeight } render() { - this.initializeScaleViews(this.childDocs, NumCast(this.props.Document.viewType)); + this.initializeScaleViews(this.childDocs, NumCast(this.props.Document._viewType)); return (
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 52b4f391a..066c51654 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -55,12 +55,12 @@ export class VideoBox extends DocAnnotatableComponent { const aspect = this.player!.videoWidth / this.player!.videoHeight; - const nativeWidth = (this.Document.nativeWidth || 0); - const nativeHeight = (this.Document.nativeHeight || 0); + const nativeWidth = (this.Document._nativeWidth || 0); + const nativeHeight = (this.Document._nativeHeight || 0); if (!nativeWidth || !nativeHeight) { - if (!this.Document.nativeWidth) this.Document.nativeWidth = this.player!.videoWidth; - this.Document.nativeHeight = (this.Document.nativeWidth || 0) / aspect; - this.Document.height = (this.Document.width || 0) / aspect; + if (!this.Document._nativeWidth) this.Document._nativeWidth = this.player!.videoWidth; + this.Document._nativeHeight = (this.Document._nativeWidth || 0) / aspect; + this.Document._height = (this.Document._width || 0) / aspect; } if (!this.Document.duration) this.Document.duration = this.player!.duration; } @@ -101,11 +101,11 @@ export class VideoBox extends DocAnnotatableComponent Math.round(this.Document.currentTimecode || 0)); return