From 461af95c312fc7aff85551c014fb93dc7a89cecd Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sun, 10 Nov 2019 16:28:48 -0500 Subject: first commit --- src/client/views/MainView.tsx | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 773da05df..294e781b7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import { PreviewCursor } from './PreviewCursor'; import { Scripting } from '../util/Scripting'; import { LinkManager } from '../util/LinkManager'; import { AudioBox } from './nodes/AudioBox'; +import SettingsManager from '../util/SettingsManager'; @observer export class MainView extends React.Component { @@ -412,6 +413,9 @@ export class MainView extends React.Component { zoomToScale={emptyFunction} getScale={returnOne}> + @@ -507,6 +511,7 @@ export class MainView extends React.Component { return (
+ {this.mainContent} -- cgit v1.2.3-70-g09d2 From cef6852d597ce67637466afb36c3498dc84211f6 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Wed, 8 Jan 2020 19:48:28 -0500 Subject: gestures are now overlayed and can span collections/panes! --- src/client/documents/Documents.ts | 3 +- src/client/views/CollectionLinearView.tsx | 4 +- src/client/views/GestureOverlay.scss | 8 + src/client/views/GestureOverlay.tsx | 159 +++++++++++++++++++ src/client/views/InkSelectDecorations.scss | 5 - src/client/views/InkSelectDecorations.tsx | 55 ------- src/client/views/InkingStroke.tsx | 6 +- src/client/views/MainView.scss | 7 +- src/client/views/MainView.tsx | 7 +- .../views/collections/CollectionStackingView.tsx | 6 +- src/client/views/collections/CollectionSubView.tsx | 13 +- .../views/collections/CollectionTreeView.scss | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 171 +++++++-------------- src/client/views/nodes/DocumentView.tsx | 19 ++- src/pen-gestures/GestureUtils.ts | 28 +++- 15 files changed, 299 insertions(+), 198 deletions(-) create mode 100644 src/client/views/GestureOverlay.scss create mode 100644 src/client/views/GestureOverlay.tsx delete mode 100644 src/client/views/InkSelectDecorations.scss delete mode 100644 src/client/views/InkSelectDecorations.tsx (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e149963b9..1853be529 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -29,7 +29,7 @@ import { listSpec } from "../../new_fields/Schema"; import { DocServer } from "../DocServer"; import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; -import { UndoManager } from "../util/UndoManager"; +import { UndoManager, undoBatch } from "../util/UndoManager"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { LinkManager } from "../util/LinkManager"; @@ -730,6 +730,7 @@ export namespace DocUtils { }); } + @undoBatch export function MakeLink(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, title: string = "", description: string = "", id?: string) { const sv = DocumentManager.Instance.getDocumentView(source.doc); if (sv && sv.props.ContainingCollectionDoc === target.doc) return; diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 5ca861f71..5d6a58656 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -36,7 +36,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { { fireImmediately: true } ); } - protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); if (ele) { this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); @@ -55,7 +55,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { render() { const guid = Utils.GenerateGuid(); return
-
+
this.props.Document.isExpanded = this.addMenuToggle.current!.checked)} /> diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss new file mode 100644 index 000000000..cbc1b6e7d --- /dev/null +++ b/src/client/views/GestureOverlay.scss @@ -0,0 +1,8 @@ +.gestureOverlay-cont { + width: 100vw; + height: 100vw; + position: absolute; + top: 0; + left: 0; + touch-action: none; +} \ No newline at end of file diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx new file mode 100644 index 000000000..c88e3f7ae --- /dev/null +++ b/src/client/views/GestureOverlay.tsx @@ -0,0 +1,159 @@ +import React = require("react"); +import { Touchable } from "./Touchable"; +import { observer } from "mobx-react"; +import "./GestureOverlay.scss" +import { computed, observable, action } from "mobx"; +import { CreatePolyline } from "./InkingStroke"; +import { GestureUtils } from "../../pen-gestures/GestureUtils"; +import { InteractionUtils } from "../util/InteractionUtils"; +import { InkingControl } from "./InkingControl"; +import { InkTool } from "../../new_fields/InkField"; +import { Doc } from "../../new_fields/Doc"; +import { LinkManager } from "../util/LinkManager"; +import { DocUtils } from "../documents/Documents"; +import { undoBatch } from "../util/UndoManager"; + +@observer +export default class GestureOverlay extends Touchable { + static Instance: GestureOverlay; + + @observable private _points: { X: number, Y: number }[] = []; + + private _d1: Doc | undefined; + + constructor(props: Readonly<{}>) { + super(props); + + GestureOverlay.Instance = this; + } + + @action + onPointerDown = (e: React.PointerEvent) => { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + this._points.push({ X: e.clientX, Y: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + } + } + + @action + onPointerMove = (e: PointerEvent) => { + this._points.push({ X: e.clientX, Y: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + } + + @action + onPointerUp = (e: PointerEvent) => { + if (this._points.length > 1) { + 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", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Box, + bounds: B + } + })); + actionPerformed = true; + break; + case GestureUtils.Gestures.Line: + const ep1 = this._points[0]; + const ep2 = this._points[this._points.length - 1]; + const target1 = document.elementFromPoint(ep1.X, ep1.Y); + const target2 = document.elementFromPoint(ep2.X, ep2.Y); + const callback = (doc: Doc) => { + if (!this._d1) { + this._d1 = doc; + } + else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { + DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); + actionPerformed = true; + } + } + const ge = new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Line, + bounds: B, + callbackFn: callback + } + }) + target1?.dispatchEvent(ge); + target2?.dispatchEvent(ge); + 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); + } + + @computed get svgBounds() { + const xs = this._points.map(p => p.X); + const ys = this._points.map(p => p.Y); + const right = Math.max(...xs); + const left = Math.min(...xs); + const bottom = Math.max(...ys); + const top = Math.min(...ys); + return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top }; + } + + @computed get currentStroke() { + if (this._points.length <= 1) { + return (null); + } + + const B = this.svgBounds; + + return ( + + {CreatePolyline(this._points, B.left, B.top)} + + ); + } + + render() { + return ( +
+ {this.props.children} + {this.currentStroke} +
); + } +} \ No newline at end of file diff --git a/src/client/views/InkSelectDecorations.scss b/src/client/views/InkSelectDecorations.scss deleted file mode 100644 index daff58fd6..000000000 --- a/src/client/views/InkSelectDecorations.scss +++ /dev/null @@ -1,5 +0,0 @@ -.inkSelectDecorations { - position: absolute; - border: black 1px solid; - z-index: 9001; -} \ No newline at end of file diff --git a/src/client/views/InkSelectDecorations.tsx b/src/client/views/InkSelectDecorations.tsx deleted file mode 100644 index 3ad50762d..000000000 --- a/src/client/views/InkSelectDecorations.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React = require("react"); -import { Touchable } from "./Touchable"; -import { PointData } from "../../new_fields/InkField"; -import { observer } from "mobx-react"; -import { computed, observable, action, runInAction } from "mobx"; -import "./InkSelectDecorations.scss"; - -@observer -export default class InkSelectDecorations extends Touchable { - static Instance: InkSelectDecorations; - - @observable private _selectedInkNodes: Map = new Map(); - - constructor(props: Readonly<{}>) { - super(props); - - InkSelectDecorations.Instance = this; - } - - @action - public SetSelected = (inkNodes: Map, keepOld: boolean = false) => { - if (!keepOld) { - this._selectedInkNodes = new Map(); - } - inkNodes.forEach((value: any, key: any) => { - runInAction(() => this._selectedInkNodes.set(key, value)); - }); - } - - @computed - get Bounds(): { x: number, y: number, b: number, r: number } { - const left = Number.MAX_VALUE; - const top = Number.MAX_VALUE; - const right = -Number.MAX_VALUE; - const bottom = -Number.MAX_VALUE; - this._selectedInkNodes.forEach((value: PointData, key: string) => { - // value.pathData.map(val => { - // left = Math.min(val.x, left); - // top = Math.min(val.y, top); - // right = Math.max(val.x, right); - // bottom = Math.max(val.y, bottom); - // }); - }); - return { x: left, y: top, b: bottom, r: right }; - } - - render() { - const bounds = this.Bounds; - return
; - } -} \ No newline at end of file diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index a413eebc9..8b346d5d9 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -9,6 +9,7 @@ import { InkingControl } from "./InkingControl"; import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); +import { TraceMobx } from "../../new_fields/util"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -35,6 +36,7 @@ export class InkingStroke extends DocExtendableComponent p.X); const ys = data.map(p => p.Y); @@ -42,7 +44,7 @@ export class InkingStroke extends DocExtendableComponent diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 4c8c95529..2a1cc3303 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -9,8 +9,8 @@ .mainContent-div { position: relative; - width:100%; - height:100%; + width: 100%; + height: 100%; } // add nodes menu. Note that the + button is actually an input label, not an actual button. @@ -28,6 +28,7 @@ top: 0; left: 0; z-index: 1; + touch-action: none; } .mainView-mainContent { @@ -66,7 +67,7 @@ .mainView-libraryFlyout { height: 100%; - width:100%; + width: 100%; position: absolute; display: flex; flex-direction: column; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a1196ee1c..14c1803c0 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -36,7 +36,7 @@ import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu'; -import InkSelectDecorations from './InkSelectDecorations'; +import GestureOverlay from './GestureOverlay'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; import { TraceMobx } from '../../new_fields/util'; @@ -510,8 +510,9 @@ export class MainView extends React.Component { - - {this.mainContent} + + {this.mainContent} + diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index e71e11b48..992820fc7 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -153,7 +153,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; - this.createDropTarget(ele!); //so the whole grid is the drop target? + this.createDropAndGestureTarget(ele!); //so the whole grid is the drop target? } overlays = (doc: Doc) => { @@ -309,7 +309,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropTarget} + createDropTarget={this.createDropAndGestureTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} />; } @@ -342,7 +342,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { docList={docList} parent={this} type={type} - createDropTarget={this.createDropTarget} + createDropTarget={this.createDropAndGestureTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} setDocHeight={this.setDocHeight} />; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 062521690..73dc7edc6 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -23,6 +23,7 @@ import { basename } from 'path'; import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { Networking } from "../../Network"; +import { GestureUtils } from "../../../pen-gestures/GestureUtils"; export interface CollectionViewProps extends FieldViewProps { addDocument: (document: Doc) => boolean; @@ -47,15 +48,18 @@ export interface SubCollectionViewProps extends CollectionViewProps { export function CollectionSubView(schemaCtor: (doc: Doc) => T) { class CollectionSubView extends DocComponent(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; + private gestureDisposer?: GestureUtils.GestureEventDisposer; private _childLayoutDisposer?: IReactionDisposer; - protected createDropTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + protected createDropAndGestureTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer && this.dropDisposer(); + this.gestureDisposer && this.gestureDisposer(); if (ele) { this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); } } protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view - this.createDropTarget(ele); + this.createDropAndGestureTarget(ele); } componentDidMount() { @@ -129,6 +133,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { } } + @undoBatch + protected onGesture(e: Event, ge: GestureUtils.GestureEvent) { + + } + @undoBatch @action protected drop(e: Event, de: DragManager.DropEvent): boolean { diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 0b9dc2eb2..2fa6813d7 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -7,9 +7,9 @@ border-radius: inherit; box-sizing: border-box; height: 100%; - width:100%; + width: 100%; position: relative; - top:0; + top: 0; padding-left: 10px; padding-right: 10px; background: $light-color-secondary; @@ -17,6 +17,7 @@ overflow: auto; user-select: none; cursor: default; + touch-action: pan-y; ul { list-style: none; @@ -115,6 +116,7 @@ .treeViewItem-header { border: transparent 1px solid; display: flex; + .editableView-container-editing-oneLine { min-width: 15px; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eb5a074bb..9656debf3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -272,11 +272,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return clusterColor; } - @observable private _points: { X: number, Y: number }[] = []; @action onPointerDown = (e: React.PointerEvent): void => { - if (e.nativeEvent.cancelBubble) return; + if (e.nativeEvent.cancelBubble || 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; if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { document.removeEventListener("pointermove", this.onPointerMove); @@ -284,14 +285,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); // if physically using a pen or we're in pen or highlighter mode - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { - e.stopPropagation(); - e.preventDefault(); - const point = this.getTransform().transformPoint(e.pageX, e.pageY); - this._points.push({ X: point[0], Y: point[1] }); - } + // if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + // e.stopPropagation(); + // e.preventDefault(); + // const point = this.getTransform().transformPoint(e.pageX, e.pageY); + // this._points.push({ X: point[0], Y: point[1] }); + // } // if not using a pen and in no ink mode - else if (InkingControl.Instance.selectedTool === InkTool.None) { + if (InkingControl.Instance.selectedTool === InkTool.None) { this._lastX = e.pageX; this._lastY = e.pageY; } @@ -332,13 +333,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { document.addEventListener("touchmove", this.onTouch); document.removeEventListener("touchend", this.onTouchEnd); document.addEventListener("touchend", this.onTouchEnd); - if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { - e.stopPropagation(); - e.preventDefault(); - const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); - this._points.push({ X: point[0], Y: point[1] }); - } - else if (InkingControl.Instance.selectedTool === InkTool.None) { + // if (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen) { + // e.stopPropagation(); + // e.preventDefault(); + // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); + // this._points.push({ X: point[0], Y: point[1] }); + // } + if (InkingControl.Instance.selectedTool === InkTool.None) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.stopPropagation(); @@ -352,72 +353,44 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - @action - onPointerUp = (e: PointerEvent): void => { - if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && this._points.length <= 1) return; - - if (this._points.length > 1) { - 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 bounds = { x: Math.min(...this._points.map(p => p.X)), r: Math.max(...this._points.map(p => p.X)), y: Math.min(...this._points.map(p => p.Y)), b: Math.max(...this._points.map(p => p.Y)) }; - const sel = this.getActiveDocuments().filter(doc => { - const l = NumCast(doc.x); - const r = l + doc[WidthSym](); - const t = NumCast(doc.y); - const b = t + doc[HeightSym](); - const pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t); - if (pass) { - doc.x = l - B.left - B.width / 2; - doc.y = t - B.top - B.height / 2; - } - return pass; - }); - this.addDocument(Docs.Create.FreeformDocument(sel, { x: B.left, y: B.top, width: B.width, height: B.height, panX: 0, panY: 0 })); - sel.forEach(d => this.props.removeDocument(d)); - actionPerformed = true; - break; - case GestureUtils.Gestures.Line: - const ep1 = this._points[0]; - const ep2 = this._points[this._points.length - 1]; - let d1: Doc | undefined; - let d2: Doc | undefined; - this.getActiveDocuments().map(doc => { - const l = NumCast(doc.x); - const r = l + doc[WidthSym](); - const t = NumCast(doc.y); - const b = t + doc[HeightSym](); - if (!d1 && l < ep1.X && r > ep1.X && t < ep1.Y && b > ep1.Y) { - d1 = doc; - } - else if (!d2 && l < ep2.X && r > ep2.X && t < ep2.Y && b > ep2.Y) { - d2 = doc; - } - }); - if (d1 && d2) { - if (!LinkManager.Instance.doesLinkExist(d1, d2)) { - DocUtils.MakeLink({ doc: d1 }, { doc: d2 }); - actionPerformed = true; - } - } - break; - } - if (actionPerformed) { - this._points = []; - } - } - - if (!actionPerformed) { - const inkDoc = Docs.Create.InkDocument(InkingControl.Instance.selectedColor, InkingControl.Instance.selectedTool, parseInt(InkingControl.Instance.selectedWidth), points, { width: B.width, height: B.height, x: B.left, y: B.top }); + @undoBatch + onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { + switch (ge.gesture) { + 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 }); this.addDocument(inkDoc); - this._points = []; - } + e.stopPropagation(); + break; + case GestureUtils.Gestures.Box: + const lt = this.getTransform().transformPoint(Math.min(...ge.points.map(p => p.X)), Math.min(...ge.points.map(p => p.Y))); + const rb = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y))); + const bounds = { x: lt[0], r: rb[0], y: lt[1], b: rb[1] }; + const bWidth = bounds.r - bounds.x; + const bHeight = bounds.b - bounds.y; + const sel = this.getActiveDocuments().filter(doc => { + const l = NumCast(doc.x); + const r = l + doc[WidthSym](); + const t = NumCast(doc.y); + const b = t + doc[HeightSym](); + const pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t); + if (pass) { + doc.x = l - bounds.x - bWidth / 2; + doc.y = t - bounds.y - bHeight / 2; + } + return pass; + }); + 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; + } + } + + @action + onPointerUp = (e: PointerEvent): void => { + if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) return; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -473,11 +446,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } if (!e.cancelBubble) { const selectedTool = InkingControl.Instance.selectedTool; - if (selectedTool === InkTool.Highlighter || selectedTool === InkTool.Pen || InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - const point = this.getTransform().transformPoint(e.clientX, e.clientY); - this._points.push({ X: point[0], Y: point[1] }); - } - else if (selectedTool === InkTool.None) { + if (selectedTool === InkTool.None) { if (this._hitCluster && this.tryDragCluster(e)) { 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 e.preventDefault(); @@ -508,10 +477,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } this.pan(pt); } - else if (InkingControl.Instance.selectedTool !== InkTool.Eraser && InkingControl.Instance.selectedTool !== InkTool.Scrubber) { - const point = this.getTransform().transformPoint(pt.clientX, pt.clientY); - this._points.push({ X: point[0], Y: point[1] }); - } } e.stopPropagation(); e.preventDefault(); @@ -942,34 +907,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ]; } - @computed get svgBounds() { - const xs = this._points.map(p => p.X); - const ys = this._points.map(p => p.Y); - const right = Math.max(...xs); - const left = Math.min(...xs); - const bottom = Math.max(...ys); - const top = Math.min(...ys); - return { right: right, left: left, bottom: bottom, top: top, width: right - left, height: bottom - top }; - } - - @computed get currentStroke() { - if (this._points.length <= 1) { - return (null); - } - - const B = this.svgBounds; - - return ( - - {CreatePolyline(this._points, B.left, B.top)} - - ); - } - children = () => { const eles: JSX.Element[] = []; this.extensionDoc && (eles.push(...this.childViews())); - this.currentStroke && (eles.push(this.currentStroke)); + // this.currentStroke && (eles.push(this.currentStroke)); eles.push(); return eles; } @@ -998,7 +939,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // 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) ? diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a14f69f71..b544319d7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -45,6 +45,7 @@ import { InkTool } from '../../../new_fields/InkField'; import { TraceMobx } from '../../../new_fields/util'; import { List } from '../../../new_fields/List'; import { FormattedTextBoxComment } from './FormattedTextBoxComment'; +import { GestureUtils } from '../../../pen-gestures/GestureUtils'; library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -94,6 +95,7 @@ export class DocumentView extends DocComponent(Docu private _hitTemplateDrag = false; private _mainCont = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; + private _gestureEventDisposer?: GestureUtils.GestureEventDisposer; private _titleRef = React.createRef(); public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -107,6 +109,7 @@ export class DocumentView extends DocComponent(Docu @action componentDidMount() { this._mainCont.current && (this._dropDisposer = DragManager.MakeDropTarget(this._mainCont.current, this.drop.bind(this))); + this._mainCont.current && (this._gestureEventDisposer = GestureUtils.MakeGestureTarget(this._mainCont.current, this.onGesture.bind(this))); !this.props.dontRegisterView && DocumentManager.Instance.DocumentViews.push(this); } @@ -365,10 +368,11 @@ export class DocumentView extends DocComponent(Docu // 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) + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + return; + } if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) { - if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - e.stopPropagation(); - } + e.stopPropagation(); return; } if ((!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart)) { @@ -424,6 +428,15 @@ export class DocumentView extends DocComponent(Docu this._lastTap = Date.now(); } + onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { + switch (ge.gesture) { + case GestureUtils.Gestures.Line: + ge.callbackFn && ge.callbackFn(this.props.Document); + e.stopPropagation(); + break; + } + } + @undoBatch deleteClicked = (): void => { SelectionManager.DeselectAll(); this.props.removeDocument && this.props.removeDocument(this.props.Document); } diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index 59a85b66b..4edcfa623 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -1,19 +1,43 @@ import { NDollarRecognizer } from "./ndollar"; import { Type } from "typescript"; -import { InkField } from "../new_fields/InkField"; +import { InkField, PointData } from "../new_fields/InkField"; import { Docs } from "../client/documents/Documents"; import { Doc, WidthSym, HeightSym } from "../new_fields/Doc"; import { NumCast } from "../new_fields/Types"; import { CollectionFreeFormView } from "../client/views/collections/collectionFreeForm/CollectionFreeFormView"; +import { Rect } from "react-measure"; export namespace GestureUtils { namespace GestureDataTypes { export type BoxData = Array; } + export class GestureEvent { + constructor( + readonly gesture: Gestures, + readonly points: PointData[], + readonly bounds: Rect, + readonly callbackFn?: Function + ) { } + } + + export interface GestureEventDisposer { (): void; } + + export function MakeGestureTarget( + element: HTMLElement, + func: (e: Event, ge: GestureEvent) => void + ): GestureEventDisposer { + const handler = (e: Event) => func(e, (e as CustomEvent).detail); + element.addEventListener("dashOnGesture", handler); + return () => { + element.removeEventListener("dashOnGesture", handler); + } + } + export enum Gestures { Box = "box", - Line = "line" + Line = "line", + Stroke = "stroke" } export const GestureRecognizer = new NDollarRecognizer(false); -- cgit v1.2.3-70-g09d2 From e3466bdb86fdc3096e86f2437a0c8e2000b2252a Mon Sep 17 00:00:00 2001 From: Andy Rickert Date: Wed, 8 Jan 2020 22:32:09 -0800 Subject: menu appears on click --- src/client/views/MainView.tsx | 2 + src/client/views/Touchable.tsx | 9 ++++- src/client/views/globalCssVariables.scss | 2 + src/client/views/nodes/DocumentView.tsx | 69 +++++++++++++++++++++++++++++--- 4 files changed, 75 insertions(+), 7 deletions(-) (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a1196ee1c..5bf5dbcc1 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -40,6 +40,7 @@ import InkSelectDecorations from './InkSelectDecorations'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; import { TraceMobx } from '../../new_fields/util'; +import { RadialMenu } from './nodes/RadialMenu'; @observer export class MainView extends React.Component { @@ -514,6 +515,7 @@ export class MainView extends React.Component { {this.mainContent} + diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 251cd41e5..49b2116f1 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -1,11 +1,13 @@ import * as React from 'react'; import { action } from 'mobx'; import { InteractionUtils } from '../util/InteractionUtils'; +import { RadialMenu } from './nodes/RadialMenu'; const HOLD_DURATION = 1000; export abstract class Touchable extends React.Component { - private holdTimer: NodeJS.Timeout | undefined; + //private holdTimer: NodeJS.Timeout | undefined; + holdTimer: NodeJS.Timeout | undefined; protected _touchDrag: boolean = false; protected prevPoints: Map = new Map(); @@ -54,6 +56,7 @@ export abstract class Touchable extends React.Component { if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return; this._touchDrag = true; if (this.holdTimer) { + console.log("CLEAR") clearTimeout(this.holdTimer); } switch (myTouches.length) { @@ -89,6 +92,7 @@ export abstract class Touchable extends React.Component { } if (this.holdTimer) { clearTimeout(this.holdTimer); + console.log("clear"); } this._touchDrag = false; e.stopPropagation(); @@ -136,5 +140,8 @@ export abstract class Touchable extends React.Component { console.log("Hold"); e.stopPropagation(); e.preventDefault(); + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchend", this.onTouchEnd); } + } \ No newline at end of file diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss index 6dffee586..019f931f9 100644 --- a/src/client/views/globalCssVariables.scss +++ b/src/client/views/globalCssVariables.scss @@ -25,6 +25,8 @@ $search-thumnail-size: 175; // dragged items $contextMenu-zindex: 100000; // context menu shows up over everything +$radialMenu-zindex: 100000; // context menu shows up over everything + $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc $docDecorations-zindex: 998; // then doc decorations appear over everything else $remoteCursors-zindex: 997; // ... not sure what level the remote cursors should go -- is this right? diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e1b68cdf7..d5442bd48 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -45,6 +45,9 @@ import { InkTool } from '../../../new_fields/InkField'; import { TraceMobx } from '../../../new_fields/util'; import { List } from '../../../new_fields/List'; import { FormattedTextBoxComment } from './FormattedTextBoxComment'; +import { RadialMenu } from './RadialMenu'; +import { RadialMenuProps } from './RadialMenuItem'; + library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -85,7 +88,6 @@ export interface DocumentViewProps { radialMenu?: String[]; } - @observer export class DocumentView extends DocComponent(Document) { private _downX: number = 0; @@ -105,12 +107,65 @@ export class DocumentView extends DocComponent(Docu @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } - constructor(props: any) { - super(props); - } handle1PointerHoldStart= (e: React.TouchEvent): any =>{ - console.log("yeet") + this.onRadialMenu(e); + + document.removeEventListener("touchmove", this.onTouch); + document.removeEventListener("touchend", this.onTouchEnd); + document.addEventListener("touchmove", this.handle1PointerHoldMove); + document.addEventListener("touchend", this.handle1PointerHoldEnd); + } + + handle1PointerHoldMove = (e: TouchEvent): void => { + console.log("YUH") + document.removeEventListener("touchmove", this.handle1PointerHoldMove); + document.addEventListener("touchmove", this.handle1PointerHoldMove); + document.removeEventListener("touchend", this.handle1PointerHoldEnd); + document.addEventListener("touchend", this.handle1PointerHoldEnd); + } + + handle1PointerHoldEnd = (e: TouchEvent): void => { + RadialMenu.Instance.closeMenu(); + document.removeEventListener("touchmove", this.handle1PointerHoldMove); + document.removeEventListener("touchend", this.handle1PointerHoldEnd); + } + + @action + onRadialMenu = async (event: React.TouchEvent): Promise => { + console.log("YUH") + // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 + // if (e.button === 0) { + // e.preventDefault(); + // return; + // } + let e = event.touches[0]; + // if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3 || + // // event.isDefaultPrevented()) { + // // event.preventDefault(); + // return; + // } + // event.preventDefault(); + + let rm = RadialMenu.Instance; + rm.openMenu(); + + rm.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { width: 300, height: 300 }), undefined, "onRight"), icon: "layer-group" }); + runInAction(() => { + // cm.addItem({ + // description: "Share", + // event: () => SharingManager.Instance.open(this), + // icon: "external-link-alt" + // }); + + if (!this.topMost) { + // DocumentViews should stop propagation of this event + } + RadialMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); + if (!SelectionManager.IsSelected(this, true)) { + SelectionManager.SelectDoc(this, false); + } + }); } @action @@ -371,6 +426,7 @@ export class DocumentView extends DocComponent(Docu } onPointerDown = (e: React.PointerEvent): void => { + // 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) @@ -388,7 +444,6 @@ export class DocumentView extends DocComponent(Docu // || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) { // return; // } - this._downX = e.clientX; this._downY = e.clientY; this._hitTemplateDrag = false; @@ -404,11 +459,13 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); + if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); } } } onPointerMove = (e: PointerEvent): void => { + if ((e as any).formattedHandled) { e.stopPropagation(); return; } if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) -- cgit v1.2.3-70-g09d2 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 --- package-lock.json | 27 +++++++++ package.json | 1 + src/client/views/GestureOverlay.scss | 2 +- src/client/views/GestureOverlay.tsx | 69 +++++++++++++--------- src/client/views/InkingControl.tsx | 21 ++++--- src/client/views/MainView.tsx | 4 +- src/client/views/Palette.scss | 18 ++++++ src/client/views/Palette.tsx | 25 ++++++++ src/client/views/Touchable.tsx | 18 ++++++ .../views/collections/CollectionSchemaView.scss | 14 +++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 54 +++++++++++++++++ src/client/views/nodes/DocumentView.tsx | 17 +----- src/mobile/ImageUpload.tsx | 31 +++++++--- src/mobile/MobileInterface.tsx | 69 ++++++++++++++++++++++ src/new_fields/InkField.ts | 3 +- src/pen-gestures/GestureUtils.ts | 3 +- .../authentication/models/current_user_utils.ts | 26 ++++++++ 17 files changed, 335 insertions(+), 67 deletions(-) create mode 100644 src/client/views/Palette.scss create mode 100644 src/client/views/Palette.tsx create mode 100644 src/mobile/MobileInterface.tsx (limited to 'src/client/views/MainView.tsx') diff --git a/package-lock.json b/package-lock.json index d807d75a1..244b2e7e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2040,6 +2040,11 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "avl": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/avl/-/avl-1.4.4.tgz", + "integrity": "sha512-vMjF4jhT2N0naO1fsBTA/lZ2AB70ECBv41na7oBe8B5PCgMVvNrWG+VRKMlbG5Mk5UGbMxzUD9fXK/IBsmmO5A==" + }, "awesome-typescript-loader": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", @@ -2314,6 +2319,14 @@ "tweetnacl": "^0.14.3" } }, + "bentley-ottman-sweepline": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/bentley-ottman-sweepline/-/bentley-ottman-sweepline-0.1.4.tgz", + "integrity": "sha512-Qptw9uoNLM0zLiJfA+9PqFrF5b6pMiJNyoWgqONq4/wVml4NC2N8S4RXYXn4xXR4JEBhHF+euVwwML7D9X1LqA==", + "requires": { + "avl": "^1.4.0" + } + }, "better-assert": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", @@ -4316,6 +4329,14 @@ "minimalistic-crypto-utils": "^1.0.0" } }, + "emit-logger": { + "version": "github:chocolateboy/emit-logger#b9d25a2d939e42f29c940861e9648bd0fb810070", + "from": "github:chocolateboy/emit-logger#better-emitter-name", + "requires": { + "chalk": "^1.1.1", + "moment": "^2.10.6" + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -7065,6 +7086,7 @@ "babel-runtime": "^6.22.0", "bluebird": "^3.4.7", "boolify-string": "^2.0.2", + "emit-logger": "github:chocolateboy/emit-logger#better-emitter-name", "lodash": "^4.17.4", "semver": "^5.0.3", "source-map-support": "^0.5.9" @@ -8545,6 +8567,11 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, "mongodb": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.7.tgz", diff --git a/package.json b/package.json index 9bd2273f8..2a72fac80 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "async": "^2.6.2", "babel-runtime": "^6.26.0", "bcrypt-nodejs": "0.0.3", + "bentley-ottman-sweepline": "^0.1.4", "bluebird": "^3.5.3", "body-parser": "^1.18.3", "bootstrap": "^4.3.1", diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index cbc1b6e7d..31601efd4 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -1,6 +1,6 @@ .gestureOverlay-cont { width: 100vw; - height: 100vw; + height: 100vh; position: absolute; top: 0; left: 0; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index c88e3f7ae..848927912 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -43,9 +43,43 @@ export default class GestureOverlay extends Touchable { @action onPointerMove = (e: PointerEvent) => { - this._points.push({ X: e.clientX, Y: e.clientY }); - e.stopPropagation(); - e.preventDefault(); + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) { + this._points.push({ X: e.clientX, Y: e.clientY }); + e.stopPropagation(); + e.preventDefault(); + } + } + + handleLineGesture = (): boolean => { + let actionPerformed = false; + const B = this.svgBounds; + const ep1 = this._points[0]; + const ep2 = this._points[this._points.length - 1]; + + const target1 = document.elementFromPoint(ep1.X, ep1.Y); + const target2 = document.elementFromPoint(ep2.X, ep2.Y); + const callback = (doc: Doc) => { + if (!this._d1) { + this._d1 = doc; + } + else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { + DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); + actionPerformed = true; + } + } + const ge = new CustomEvent("dashOnGesture", + { + bubbles: true, + detail: { + points: this._points, + gesture: GestureUtils.Gestures.Line, + bounds: B, + callbackFn: callback + } + }); + target1?.dispatchEvent(ge); + target2?.dispatchEvent(ge); + return actionPerformed; } @action @@ -72,31 +106,10 @@ export default class GestureOverlay extends Touchable { actionPerformed = true; break; case GestureUtils.Gestures.Line: - const ep1 = this._points[0]; - const ep2 = this._points[this._points.length - 1]; - const target1 = document.elementFromPoint(ep1.X, ep1.Y); - const target2 = document.elementFromPoint(ep2.X, ep2.Y); - const callback = (doc: Doc) => { - if (!this._d1) { - this._d1 = doc; - } - else if (this._d1 !== doc && !LinkManager.Instance.doesLinkExist(this._d1, doc)) { - DocUtils.MakeLink({ doc: this._d1 }, { doc: doc }); - actionPerformed = true; - } - } - const ge = new CustomEvent("dashOnGesture", - { - bubbles: true, - detail: { - points: this._points, - gesture: GestureUtils.Gestures.Line, - bounds: B, - callbackFn: callback - } - }) - target1?.dispatchEvent(ge); - target2?.dispatchEvent(ge); + actionPerformed = this.handleLineGesture(); + break; + case GestureUtils.Gestures.Scribble: + console.log("scribble"); break; } if (actionPerformed) { diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index e33f193b8..243123352 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -4,19 +4,18 @@ import { Doc } from "../../new_fields/Doc"; import { InkTool } from "../../new_fields/InkField"; import { List } from "../../new_fields/List"; import { listSpec } from "../../new_fields/Schema"; -import { Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { Cast, NumCast, StrCast, FieldValue } from "../../new_fields/Types"; import { Utils } from "../../Utils"; 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"; - export class InkingControl { @observable static Instance: InkingControl; - @observable private _selectedTool: InkTool = InkTool.None; - @observable private _selectedColor: string = "rgb(244, 67, 54)"; - @observable private _selectedWidth: string = "5"; + @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"; } @observable public _open: boolean = false; constructor() { @@ -24,7 +23,8 @@ export class InkingControl { } switchTool = action((tool: InkTool): void => { - this._selectedTool = tool; + // this._selectedTool = tool; + CurrentUserUtils.UserDocument.inkTool = tool; }); decimalToHexString(number: number) { if (number < 0) { @@ -36,7 +36,7 @@ export class InkingControl { @undoBatch switchColor = action((color: ColorState): void => { - this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); + CurrentUserUtils.UserDocument.inkColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { const selected = SelectionManager.SelectedDocuments(); @@ -99,7 +99,8 @@ export class InkingControl { }); @action switchWidth = (width: string): void => { - this._selectedWidth = width; + // this._selectedWidth = width; + CurrentUserUtils.UserDocument.inkWidth = width; } @computed @@ -114,7 +115,8 @@ export class InkingControl { @action updateSelectedColor(value: string) { - this._selectedColor = value; + // this._selectedColor = value; + CurrentUserUtils.UserDocument.inkColor = value; } @computed @@ -127,6 +129,7 @@ Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) { Ink Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Highlighter : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); }); Scripting.addGlobal(function activateEraser(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Eraser : InkTool.None); }); Scripting.addGlobal(function activateScrubber(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Scrubber : InkTool.None); }); +Scripting.addGlobal(function activateStamp(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Stamp : InkTool.None); }); Scripting.addGlobal(function deactivateInk() { return InkingControl.Instance.switchTool(InkTool.None); }); Scripting.addGlobal(function setInkWidth(width: any) { return InkingControl.Instance.switchWidth(width); }); Scripting.addGlobal(function setInkColor(color: any) { return InkingControl.Instance.updateSelectedColor(color); }); \ No newline at end of file diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 14c1803c0..f463ca306 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 + faMusic, faObjectGroup, faPause, faMousePointer, faPenNib, faFileAudio, faPen, faEraser, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt, faHighlighter, faMicrophone, faCompressArrowsAlt, faPhone, faStamp } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; @@ -135,6 +135,8 @@ export class MainView extends React.Component { library.add(faChevronRight); library.add(faEllipsisV); library.add(faMusic); + library.add(faPhone); + library.add(faStamp); this.initEventListeners(); this.initAuthenticationRouters(); } diff --git a/src/client/views/Palette.scss b/src/client/views/Palette.scss new file mode 100644 index 000000000..60004c81f --- /dev/null +++ b/src/client/views/Palette.scss @@ -0,0 +1,18 @@ +.palette-container { + .palette-thumb { + width: 300px; + height: 300px; + touch-action: pan-x; + overflow: scroll; + + .palette-thumbContent { + width: 100%; + height: 100%; + + .palette-button { + width: 100px; + height: 100px; + } + } + } +} \ No newline at end of file diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx new file mode 100644 index 000000000..390b7e2c2 --- /dev/null +++ b/src/client/views/Palette.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; +import "./Palette.scss"; +import { PointData } from "../../new_fields/InkField"; + +export interface PaletteProps { + x: number; + y: number; + thumb: number[]; +} + +export default class Palette extends React.Component { + render() { + return ( +
+
+
+
console.log("hi")}>1
+
2
+
3
+
+
+
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 251cd41e5..3eb66ff72 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { action } from 'mobx'; import { InteractionUtils } from '../util/InteractionUtils'; +import { SelectionManager } from '../util/SelectionManager'; const HOLD_DURATION = 1000; @@ -34,11 +35,19 @@ export abstract class Touchable extends React.Component { case 1: this.handle1PointerDown(e); e.persist(); + if (this.holdTimer) { + clearTimeout(this.holdTimer) + this.holdTimer = undefined; + } this.holdTimer = setTimeout(() => this.handle1PointerHoldStart(e), HOLD_DURATION); + console.log(this.holdTimer); break; case 2: this.handle2PointersDown(e); break; + case 5: + this.handleHandDown(e); + break; } } } @@ -54,7 +63,9 @@ export abstract class Touchable extends React.Component { 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; } switch (myTouches.length) { case 1: @@ -88,7 +99,9 @@ export abstract class Touchable extends React.Component { } } if (this.holdTimer) { + console.log("clear"); clearTimeout(this.holdTimer); + this.holdTimer = undefined; } this._touchDrag = false; e.stopPropagation(); @@ -137,4 +150,9 @@ export abstract class Touchable extends React.Component { e.stopPropagation(); e.preventDefault(); } + + handleHandDown = (e: React.TouchEvent) => { + e.stopPropagation(); + e.preventDefault(); + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index cb95dcbbc..8b3d332af 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -15,6 +15,11 @@ display: flex; justify-content: space-between; flex-wrap: nowrap; + touch-action: none; + + div { + touch-action: none; + } .collectionSchemaView-tableContainer { @@ -49,7 +54,7 @@ .rt-table { height: 100%; display: -webkit-inline-box; - direction: ltr; + direction: ltr; overflow: visible; } @@ -202,7 +207,7 @@ button.add-column { border-bottom: 1px solid lightgray; &:first-child { - padding-top : 0; + padding-top: 0; } &:last-child { @@ -231,7 +236,7 @@ button.add-column { transition: background-color 0.2s; &:hover { - background-color: $light-color-secondary; + background-color: $light-color-secondary; } &.active { @@ -267,7 +272,7 @@ button.add-column { overflow-y: scroll; position: absolute; top: 28px; - box-shadow: 0 10px 16px rgba(0,0,0,0.1); + box-shadow: 0 10px 16px rgba(0, 0, 0, 0.1); .key-option { background-color: $light-color; @@ -380,6 +385,7 @@ button.add-column { &.editing { padding: 0; + input { outline: 0; border: none; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9656debf3..d7cccc036 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,6 +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"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -444,6 +445,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } return; } + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + return; + } if (!e.cancelBubble) { const selectedTool = InkingControl.Instance.selectedTool; if (selectedTool === InkTool.None) { @@ -844,6 +848,53 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], inkData); } + 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); + } + } + onContextMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; @@ -907,9 +958,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ]; } + @observable private _palette?: JSX.Element; + children = () => { const eles: JSX.Element[] = []; this.extensionDoc && (eles.push(...this.childViews())); + 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 7d6b8bf01..415d0e2ef 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -256,7 +256,6 @@ export class DocumentView extends DocComponent(Docu document.removeEventListener("touchend", this.onTouchEnd); document.addEventListener("touchend", this.onTouchEnd); if ((e.nativeEvent as any).formattedHandled) e.stopPropagation(); - console.log("down") } } @@ -327,6 +326,7 @@ export class DocumentView extends DocComponent(Docu 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; } @@ -357,8 +357,6 @@ export class DocumentView extends DocComponent(Docu dH && layoutDoc.autoHeight && (layoutDoc.autoHeight = false); } } - // let newWidth = Math.max(Math.abs(oldPoint1!.clientX - oldPoint2!.clientX), Math.abs(pt1.clientX - pt2.clientX)) - // this.props.Document.width = newWidth; e.stopPropagation(); e.preventDefault(); } @@ -374,19 +372,7 @@ export class DocumentView extends DocComponent(Docu } return; } - // if (!InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE)) { - // e.stopPropagation(); - // return; - // } if (!e.nativeEvent.cancelBubble || this.Document.onClick || this.Document.onDragStart) { - // if ((e.nativeEvent.cancelBubble && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) - // // return if we're inking, and not selecting a button document - // || (InkingControl.Instance.selectedTool !== InkTool.None && !this.Document.onClick) - // // return if using pen or eraser - // || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) { - // return; - // } - this._downX = e.clientX; this._downY = e.clientY; this._hitTemplateDrag = false; @@ -408,6 +394,7 @@ export class DocumentView extends DocComponent(Docu onPointerMove = (e: PointerEvent): void => { if ((e as any).formattedHandled) { e.stopPropagation(); return; } + if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || InkingControl.Instance.selectedTool === InkTool.Highlighter || InkingControl.Instance.selectedTool === InkTool.Pen)) return; if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index ec698b151..0b0280519 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -11,6 +11,8 @@ import { List } from '../new_fields/List'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; import { Utils } from '../Utils'; +import MobileInterface from './MobileInterface'; +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; @@ -104,10 +106,25 @@ class Uploader extends React.Component { } -DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload"); - -ReactDOM.render(( - -), - document.getElementById('root') -); \ No newline at end of file +// DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload"); +(async () => { + const info = await CurrentUserUtils.loadCurrentUser(); + DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + "mobile"); + await Docs.Prototypes.initialize(); + if (info.id !== "__guest__") { + // a guest will not have an id registered + await CurrentUserUtils.loadUserDocument(info); + } + document.getElementById('root')!.addEventListener('wheel', event => { + if (event.ctrlKey) { + event.preventDefault(); + } + }, true); + ReactDOM.render(( + // + + ), + document.getElementById('root') + ); +} +)(); \ No newline at end of file diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx new file mode 100644 index 000000000..ea2fc917f --- /dev/null +++ b/src/mobile/MobileInterface.tsx @@ -0,0 +1,69 @@ +import React = require('react'); +import { observer } from 'mobx-react'; +import { computed, action } from 'mobx'; +import { CurrentUserUtils } from '../server/authentication/models/current_user_utils'; +import { FieldValue, Cast } from '../new_fields/Types'; +import { Doc } from '../new_fields/Doc'; +import { Docs } from '../client/documents/Documents'; +import { CollectionView } from '../client/views/collections/CollectionView'; +import { DocumentView } from '../client/views/nodes/DocumentView'; +import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue } from '../Utils'; +import { Transform } from '../client/util/Transform'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faPenNib, faHighlighter, faEraser, faMousePointer } from '@fortawesome/free-solid-svg-icons'; + +@observer +export default class MobileInterface extends React.Component { + @computed private get userDoc() { return CurrentUserUtils.UserDocument; } + @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; } + + @action + componentDidMount = () => { + library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer]); + + if (this.userDoc && !this.mainContainer) { + const doc = CurrentUserUtils.setupMobileDoc(this.userDoc); + this.userDoc.activeMobile = doc; + } + } + + @computed + get mainContent() { + if (this.mainContainer) { + return 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}> + ; + } + return "hello"; + } + + render() { + return ( +
+ {this.mainContent} +
+ ); + } +} \ No newline at end of file diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts index 83d631958..20dbf4b17 100644 --- a/src/new_fields/InkField.ts +++ b/src/new_fields/InkField.ts @@ -9,7 +9,8 @@ export enum InkTool { Pen, Highlighter, Eraser, - Scrubber + Scrubber, + Stamp } export interface PointData { diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index 4edcfa623..062604458 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -37,7 +37,8 @@ export namespace GestureUtils { export enum Gestures { Box = "box", Line = "line", - Stroke = "stroke" + Stroke = "stroke", + Scribble = "scribble" } export const GestureRecognizer = new NDollarRecognizer(false); 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 acf72eb45bdcdda5a87a7017f698793d2feea1c4 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 16 Jan 2020 11:46:12 -0500 Subject: extra fixes after pull from master --- src/client/views/MainView.tsx | 3 --- src/server/ApiManagers/UserManager.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 05bfee95b..b300b0471 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,12 +39,9 @@ import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsM import InkSelectDecorations from './InkSelectDecorations'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; -<<<<<<< HEAD import SettingsManager from '../util/SettingsManager'; -======= import { TraceMobx } from '../../new_fields/util'; import RichTextMenu from '../util/RichTextMenu'; ->>>>>>> e410cde0e430553002d4e1a2f64364b57b65fdbc @observer export class MainView extends React.Component { diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 36d48e366..b0d868918 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -41,7 +41,7 @@ export default class UserManager extends ApiManager { register({ method: Method.POST, subscription: '/internalResetPassword', - onValidation: async ({ user, req, res }) => { + secureHandler: async ({ user, req, res }) => { const result: any = {}; const { curr_pass, new_pass, new_confirm } = req.body; // perhaps should assert whether curr password is entered correctly -- 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/client/views/MainView.tsx') 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 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/client/views/MainView.tsx') 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 613c2c8de636927fb38a7a32ec6b0427b238db2b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 21 Jan 2020 21:33:42 -0500 Subject: added filter resizer for pivot viewer. fixed click/drag confusion with mainview divider. --- src/client/views/MainView.tsx | 5 +- .../views/collections/CollectionPivotView.scss | 13 +++- .../views/collections/CollectionPivotView.tsx | 90 +++++++++++++--------- 3 files changed, 70 insertions(+), 38 deletions(-) (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d7656aba4..0958c434a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -307,8 +307,10 @@ export class MainView extends React.Component { ; } + _canClick = false; onPointerDown = (e: React.PointerEvent) => { if (this._flyoutTranslate) { + this._canClick = true; this._flyoutSizeOnDown = e.clientX; document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -339,11 +341,12 @@ export class MainView extends React.Component { @action onPointerMove = (e: PointerEvent) => { this.flyoutWidth = Math.max(e.clientX, 0); + Math.abs(this.flyoutWidth - this._flyoutSizeOnDown) > 6 && (this._canClick = false); this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30; } @action onPointerUp = (e: PointerEvent) => { - if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4) { + if (Math.abs(e.clientX - this._flyoutSizeOnDown) < 4 && this._canClick) { this.flyoutWidth = this.flyoutWidth < 15 ? 250 : 0; this.flyoutWidth && (this.sidebarButtonsDoc.columnWidth = this.flyoutWidth / 3 - 30); } diff --git a/src/client/views/collections/CollectionPivotView.scss b/src/client/views/collections/CollectionPivotView.scss index bd3d6c77b..2ddcc97ea 100644 --- a/src/client/views/collections/CollectionPivotView.scss +++ b/src/client/views/collections/CollectionPivotView.scss @@ -45,7 +45,7 @@ } .collectionPivotView-tree { display:inline-block; - width: 200px; + width: 100%; height: calc(100% - 30px); } } @@ -54,4 +54,15 @@ width: calc(100% - 200px); height: 100%; } + .collectionPivotView-dragger { + background-color: lightgray; + height: 40px; + width: 20px; + position: absolute; + border-radius: 10px; + top: 55%; + border: 1px black solid; + z-index: 2; + left:-10px; + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index f31f1aba6..8e712e370 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -65,7 +65,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { return Doc.fieldExtensionDoc(this.props.DataDoc || this.props.Document, this.props.fieldKey); } - bodyPanelWidth = () => this.props.PanelWidth() - 200; + bodyPanelWidth = () => this.props.PanelWidth() - this._facetWidth; getTransform = () => this.props.ScreenToLocalTransform().translate(-200, 0); @computed get _allFacets() { @@ -78,39 +78,53 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { * Responds to clicking the check box in the flyout menu */ facetClick = (facetHeader: string) => { - const { Document, fieldKey } = this.props; - const facetCollection = 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) { - //Doc.RemoveDocFromList(facetCollection, "data", DocListCast(facetCollection.data)[found]); (facetCollection.data as List).splice(found, 1); } else { const newFacet = Docs.Create.FreeformDocument([], { 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); Doc.AddDocToList(facetCollection, "data", newFacet); - const { dataDoc } = this; - const capturedVariables = { - layoutDoc: Document, - dataDoc, - dataKey: fieldKey, - facetHeader - }; - const params = { - layoutDoc: Doc.name, - dataDoc: Doc.name, - dataKey: "string", - facetHeader: "string" - }; - newFacet.container = dataDoc; - newFacet.data = ComputedField.MakeFunction("readFacetData(layoutDoc, dataDoc, dataKey, facetHeader)", params, capturedVariables); } } } + _canClick = false; + _facetWidthOnDown = 0; + @observable _facetWidth = 200; + onPointerDown = (e: React.PointerEvent) => { + this._canClick = true; + this._facetWidthOnDown = e.screenX; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + e.preventDefault(); + } + + + @action + onPointerMove = (e: PointerEvent) => { + this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0); + Math.abs(e.movementX) > 6 && (this._canClick = false); + } + @action + onPointerUp = (e: PointerEvent) => { + if (Math.abs(e.screenX - this._facetWidthOnDown) < 6 && this._canClick) { + this._facetWidth = this._facetWidth < 15 ? 200 : 0; + } + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } render() { const facetCollection = Cast(this.props.Document?.facetCollection, Doc, null); const flyout = ( -
+
{this._allFacets.map(facet => )}
); - return !facetCollection ? (null) :
-
-
e.stopPropagation()}> - -
- Facet Filters - -
-
+ return !facetCollection ? (null) : +
+
+
-
- +
+
e.stopPropagation()}> + +
+ Facet Filters + +
+
+
+
+ +
-
-
- -
-
; +
+ +
+
; } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From bfebed91e12abca324d7a638adeb1da9755a9057 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 22 Jan 2020 15:41:04 -0500 Subject: basic cleanup for warnings --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- src/client/util/ProseMirrorEditorView.tsx | 74 ---------------------- src/client/util/RichTextMenu.tsx | 68 ++++++++++---------- src/client/util/RichTextSchema.tsx | 2 +- src/client/views/EditableView.tsx | 2 +- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionPivotView.tsx | 11 ++-- .../views/collections/CollectionTreeView.tsx | 2 +- .../views/collections/CollectionViewChromes.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../collectionFreeForm/MarqueeOptionsMenu.tsx | 3 + src/client/views/linking/LinkMenu.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 1 - src/new_fields/Doc.ts | 10 +-- src/new_fields/documentSchemas.ts | 1 + src/server/Websocket/Websocket.ts | 2 - 17 files changed, 59 insertions(+), 132 deletions(-) delete mode 100644 src/client/util/ProseMirrorEditorView.tsx (limited to 'src/client/views/MainView.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index fd3d9e2f1..d654e2530 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -46,7 +46,7 @@ export class YoutubeBox extends React.Component { * When component mounts, last search's results are laoded in based on the back up stored * in the document of the props. */ - async componentWillMount() { + async componentDidMount() { //DocServer.getYoutubeChannels(); const castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc); const awaitedBackUp = await castedSearchBackUp; diff --git a/src/client/util/ProseMirrorEditorView.tsx b/src/client/util/ProseMirrorEditorView.tsx deleted file mode 100644 index b42adfbb4..000000000 --- a/src/client/util/ProseMirrorEditorView.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from "react"; -import { EditorView } from "prosemirror-view"; -import { EditorState } from "prosemirror-state"; - -export interface ProseMirrorEditorViewProps { - /* EditorState instance to use. */ - editorState: EditorState; - /* Called when EditorView produces new EditorState. */ - onEditorState: (editorState: EditorState) => any; -} - -/** - * This wraps ProseMirror's EditorView into React component. - * This code was found on https://discuss.prosemirror.net/t/using-with-react/904 - */ -export class ProseMirrorEditorView extends React.Component { - - private _editorView?: EditorView; - - _createEditorView = (element: HTMLDivElement | null) => { - if (element !== null) { - this._editorView = new EditorView(element, { - state: this.props.editorState, - dispatchTransaction: this.dispatchTransaction, - }); - } - } - - dispatchTransaction = (tx: any) => { - // In case EditorView makes any modification to a state we funnel those - // modifications up to the parent and apply to the EditorView itself. - const editorState = this.props.editorState.apply(tx); - if (this._editorView) { - this._editorView.updateState(editorState); - } - this.props.onEditorState(editorState); - } - - focus() { - if (this._editorView) { - this._editorView.focus(); - } - } - - componentWillReceiveProps(nextProps: { editorState: EditorState; }) { - // In case we receive new EditorState through props — we apply it to the - // EditorView instance. - if (this._editorView) { - if (nextProps.editorState !== this.props.editorState) { - this._editorView.updateState(nextProps.editorState); - } - } - } - - componentWillUnmount() { - if (this._editorView) { - this._editorView.destroy(); - } - } - - shouldComponentUpdate() { - // Note that EditorView manages its DOM itself so we'd ratrher don't mess - // with it. - return false; - } - - render() { - // Render just an empty div which is then used as a container for an - // EditorView instance. - return ( -
- ); - } -} \ No newline at end of file diff --git a/src/client/util/RichTextMenu.tsx b/src/client/util/RichTextMenu.tsx index f070589ae..eb5c90654 100644 --- a/src/client/util/RichTextMenu.tsx +++ b/src/client/util/RichTextMenu.tsx @@ -73,7 +73,7 @@ export default class RichTextMenu extends AntimodeMenu { this.fontSizeOptions = [ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8pt", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "8pt", command: this.changeFontSize }, + { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12pt", command: this.changeFontSize }, { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14pt", command: this.changeFontSize }, @@ -312,22 +312,22 @@ export default class RichTextMenu extends AntimodeMenu { } return ( - ); } - createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[]): JSX.Element { + createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string): JSX.Element { const items = options.map(({ title, label, hidden, style }) => { if (hidden) { return label === activeOption ? - : - ; + : + ; } return label === activeOption ? - : - ; + : + ; }); const self = this; @@ -340,19 +340,19 @@ export default class RichTextMenu extends AntimodeMenu { } }); } - return ; + return ; } - createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[]): JSX.Element { + createNodesDropdown(activeOption: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[], key: string): JSX.Element { const items = options.map(({ title, label, hidden, style }) => { if (hidden) { return label === activeOption ? - : - ; + : + ; } return label === activeOption ? - : - ; + : + ; }); const self = this; @@ -363,7 +363,7 @@ export default class RichTextMenu extends AntimodeMenu { } }); } - return ; + return ; } changeFontSize = (mark: Mark, view: EditorView) => { @@ -472,7 +472,7 @@ export default class RichTextMenu extends AntimodeMenu {
; return ( - + ); } @@ -535,21 +535,21 @@ export default class RichTextMenu extends AntimodeMenu { ; const dropdownContent = -
+

Change font color:

{this.fontColors.map(color => { if (color) { return this.activeFontColor === color ? - : - ; + : + ; } })}
; return ( - + ); } @@ -582,7 +582,7 @@ export default class RichTextMenu extends AntimodeMenu { } const button = - ; @@ -594,15 +594,15 @@ export default class RichTextMenu extends AntimodeMenu { {this.highlightColors.map(color => { if (color) { return this.activeHighlightColor === color ? - : - ; + : + ; } })}
; return ( - + ); } @@ -635,7 +635,7 @@ export default class RichTextMenu extends AntimodeMenu {
; return ( - + ); } @@ -773,7 +773,7 @@ export default class RichTextMenu extends AntimodeMenu { render() { - const row1 =
{[ + const row1 =
{[ this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), @@ -787,14 +787,14 @@ export default class RichTextMenu extends AntimodeMenu { this.createButton("indent", "Summarize", undefined, this.insertSummarizer), ]}
; - const row2 =
-
- {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions), - this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions), - this.createNodesDropdown(this.activeListType, this.listTypeOptions)]} + const row2 =
+
+ {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size"), + this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family"), + this.createNodesDropdown(this.activeListType, this.listTypeOptions, "nodes")]}
-
- {this.getDragger()} @@ -864,12 +864,12 @@ class ButtonDropdown extends React.Component { : <> {this.props.button} - } - {this.showDropdown ? this.props.dropdownContent : <>} + {this.showDropdown ? this.props.dropdownContent : (null)}
); } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 1f70cafc4..5751c8c7e 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -306,7 +306,7 @@ export const marks: { [index: string]: MarkSpec } = { } }], toDOM(node: any) { - return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', { style: 'color: black' }]; + return node.attrs.color ? ['span', { style: 'color:' + node.attrs.color }] : ['span', 0]; } }, diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 0d677b8ce..394caba72 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -66,7 +66,7 @@ export class EditableView extends React.Component { } @action - componentWillReceiveProps(nextProps: EditableProps) { + componentDidUpdate(nextProps: EditableProps) { // this is done because when autosuggest is turned on, the suggestions are passed in as a prop, // so when the suggestions are passed in, and no editing prop is passed in, it used to set it // to false. this will no longer do so -syip diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 0958c434a..79e0cfea5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -64,7 +64,7 @@ export class MainView extends React.Component { public isPointerDown = false; - componentWillMount() { + componentDidMount() { const tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index f23540529..aa68cc18f 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -28,6 +28,7 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { if (!this.props.Document.facetCollection) { const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; + facetCollection.dontCopyOnAlias = true; const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; const script = CompileScript(scriptText, { @@ -122,8 +123,8 @@ export class CollectionPivotView extends CollectionSubView(doc => doc) { const facetCollection = Cast(this.props.Document?.facetCollection, Doc, null); const flyout = (
- {this._allFacets.map(facet =>