From 05fe7bb89e51f3d8b643726f3283ba8adee740e4 Mon Sep 17 00:00:00 2001 From: Melissa Zhang Date: Thu, 4 Jun 2020 16:22:27 -0700 Subject: hover to show autoscroll button --- src/client/views/DocumentDecorations.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 04f02c683..7cdf955a1 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,5 +1,5 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faTextHeight, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes, faAngleLeft, faAngleRight, faAngleDoubleLeft, faAngleDoubleRight, faPause } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; @@ -38,6 +38,11 @@ library.add(faCheckCircle); library.add(faCloudUploadAlt); library.add(faSyncAlt); library.add(faShare); +library.add(faAngleDoubleLeft); +library.add(faAngleDoubleRight); +library.add(faAngleLeft); +library.add(faAngleRight); +library.add(faPause); @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { -- cgit v1.2.3-70-g09d2 From 639be55eaff422e1e982cf41d09edd9e96f014a4 Mon Sep 17 00:00:00 2001 From: yunahi <60233430+yunahi@users.noreply.github.com> Date: Thu, 18 Jun 2020 14:10:34 +0900 Subject: updated menu --- package-lock.json | 44 +++-- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 4 + src/client/util/InteractionUtils.tsx | 64 +++++-- src/client/views/DocumentDecorations.scss | 50 +++-- src/client/views/DocumentDecorations.tsx | 81 +++++++- src/client/views/GestureOverlay.tsx | 21 ++- src/client/views/InkingStroke.tsx | 41 ++++- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../collectionFreeForm/InkOptionsMenu.tsx | 203 +++++++++++++++++++-- src/fields/InkField.ts | 4 +- 11 files changed, 459 insertions(+), 67 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/package-lock.json b/package-lock.json index c956fd3e9..cd80ecc5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2832,7 +2832,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2850,11 +2851,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2867,15 +2870,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2978,7 +2984,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2988,6 +2995,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3000,17 +3008,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3027,6 +3038,7 @@ "mkdirp": { "version": "0.5.3", "bundled": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3082,7 +3094,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -3107,7 +3120,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3117,6 +3131,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3185,7 +3200,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3215,6 +3231,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3232,6 +3249,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3270,11 +3288,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8d867348f..cbf97e8e8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -37,7 +37,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { QueryBox } from "../views/nodes/QueryBox"; import { ColorBox } from "../views/nodes/ColorBox"; import { DocHolderBox } from "../views/nodes/DocHolderBox"; -import { InkingStroke, ActiveInkColor, ActiveInkWidth, ActiveInkBezierApprox } from "../views/InkingStroke"; +import { InkingStroke, ActiveInkColor, ActiveInkWidth, ActiveInkBezierApprox, ActiveFillColor, ActiveArrowStart, ActiveArrowEnd, ActiveDash } from "../views/InkingStroke"; import { InkField } from "../../fields/InkField"; import { RichTextField } from "../../fields/RichTextField"; import { extname } from "path"; @@ -631,13 +631,17 @@ export namespace Docs { return doc; } - export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { + export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { const I = new Doc(); I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString("data"); I.color = color; I.strokeWidth = strokeWidth; I.strokeBezier = strokeBezier; + I.fillColor = fillColor; + I.arrowStart = arrowStart; + I.arrowEnd = arrowEnd; + I.dash = dash; I.tool = tool; I.title = "ink"; I.x = options.x; @@ -852,7 +856,7 @@ export namespace DocUtils { created = Docs.Create.AudioDocument((field).url.href, resolved); layout = AudioBox.LayoutString; } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), (field).inkData, resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); layout = InkingStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b0cea9947..3d0c0ef4a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -691,6 +691,10 @@ export class CurrentUserUtils { doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); doc.activeInkWidth = StrCast(doc.activeInkWidth, "1"); doc.activeInkBezier = StrCast(doc.activeInkBezier, ""); + doc.activeFillColor = StrCast(doc.activeFillColor, "none"); + doc.activeArrowStart = StrCast(doc.activeArrowStart, "none"); + doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "none"); + doc.activeDash = StrCast(doc.activeDash, "0"); doc.fontSize = NumCast(doc.fontSize, 12); doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); // diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 81f9b9362..c6b96df45 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -1,6 +1,7 @@ import React = require("react"); import * as beziercurve from 'bezier-curve'; import * as fitCurve from 'fit-curve'; +import InkOptionsMenu from "../views/collections/collectionFreeForm/InkOptionsMenu"; export namespace InteractionUtils { export const MOUSETYPE = "mouse"; @@ -89,7 +90,8 @@ export namespace InteractionUtils { return myTouches; } - export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean) { + + export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string, fill: string, arrowStart: string, arrowEnd: string, dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean) { var pts = ""; if (shape) { //if any of the shape are true @@ -116,22 +118,58 @@ export namespace InteractionUtils { //in the middle of drawing pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X * scalex - left * scalex},${pt.Y * scaley - top * scaley} `, ""); } + const dashArray = String(Number(width) * Number(dash)); + return ( - + + + + {/* {makeArrow()} */} + + + + + {/* */} + + + + {/* */} + + + + + + + ); } + // export function makeArrow() { + // return ( + // InkOptionsMenu.Instance.getColors().map(color => { + // const id1 = "arrowHeadTest" + color; + // console.log(color); + // + // + // ; + // }) + // ); + // } + export function makePolygon(shape: string, points: { X: number, Y: number }[]) { if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) { //pointer is up (first and last points are the same) diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 0a96b058b..cf5871bda 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -22,11 +22,20 @@ $linkGap : 3px; } + .documentDecorations-rotation { + pointer-events: auto; + // cursor: grabbing; + cursor: ns-resize; + width: 10px; + height: 10px; + } + .documentDecorations-resizer { pointer-events: auto; background: $alt-accent; opacity: 0.1; } + .documentDecorations-resizer:hover { opacity: 1; } @@ -87,14 +96,17 @@ $linkGap : 3px; background: unset; opacity: 1; } + #documentDecorations-topLeftResizer { - border-left: 2px solid; - border-top: solid 2px; + border-left: 2px solid; + border-top: solid 2px; } + #documentDecorations-bottomRightResizer { - border-right: 2px solid; - border-bottom: solid 2px; + border-right: 2px solid; + border-bottom: solid 2px; } + #documentDecorations-topLeftResizer:hover, #documentDecorations-bottomRightResizer:hover { opacity: 1; @@ -110,14 +122,17 @@ $linkGap : 3px; background: unset; opacity: 1; } + #documentDecorations-topRightResizer { - border-right: 2px solid; - border-top: 2px solid; + border-right: 2px solid; + border-top: 2px solid; } + #documentDecorations-bottomLeftResizer { - border-left: 2px solid; - border-bottom: 2px solid; + border-left: 2px solid; + border-bottom: 2px solid; } + #documentDecorations-topRightResizer:hover, #documentDecorations-bottomLeftResizer:hover { cursor: nesw-resize; @@ -139,10 +154,11 @@ $linkGap : 3px; width: 25px; height: calc(100% + 8px); // 8px for the height of the top resizer bar grid-column-start: 2; - grid-column-end : 2; + grid-column-end: 2; pointer-events: all; padding-left: 5px; } + .documentDecorations-title { opacity: 1; grid-column-start: 3; @@ -150,14 +166,16 @@ $linkGap : 3px; pointer-events: auto; overflow: hidden; text-align: center; - display: flex; + display: flex; border-bottom: solid 1px; - margin-left:10px; + margin-left: 10px; width: calc(100% - 10px); } + .focus-visible { - margin-left:0px; + margin-left: 0px; } + .publishBox { width: 20px; height: 22px; @@ -194,8 +212,9 @@ $linkGap : 3px; width: 8px; height: $MINIMIZED_ICON_SIZE; max-height: 20px; - > svg { - margin:0; + + >svg { + margin: 0; } } @@ -332,7 +351,8 @@ $linkGap : 3px; padding: 2px 12px; list-style: none; - .templateToggle, .chromeToggle { + .templateToggle, + .chromeToggle { text-align: left; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6ca7331d6..8cb0c83ed 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -17,11 +17,13 @@ import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); -import { Id } from '../../fields/FieldSymbols'; +import { Id, Copy, Update } from '../../fields/FieldSymbols'; import e = require('express'); import { CollectionDockingView } from './collections/CollectionDockingView'; import { SnappingManager } from '../util/SnappingManager'; import { HtmlField } from '../../fields/HtmlField'; +import { InkData, InkField, InkTool } from "../../fields/InkField"; +import { update } from 'serializr'; library.add(faCaretUp); library.add(faObjectGroup); @@ -50,6 +52,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> private _resizeUndo?: UndoManager.Batch; private _offX = 0; _offY = 0; // offset from click pt to inner edge of resize border private _snapX = 0; _snapY = 0; // last snapped location of resize border + private _prevX = 0; + private _prevY = 0; + private _centerPoints: { X: number, Y: number }[] = []; + @observable private _accumulatedTitle = ""; @observable private _titleControlString: string = "#title"; @observable private _edtingTitle = false; @@ -237,6 +243,77 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> return false; } + @action + onRotateDown = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, this.onRotateMove, this.onRotateUp, (e) => { }); + this._prevX = e.clientX; + this._prevY = e.clientY; + SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { + const doc = Document(element.rootDoc); + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const xs = ink.map(p => p.X); + const ys = ink.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + this._centerPoints.push({ X: ((right - left) / 2) + left, Y: ((bottom - top) / 2) + bottom }); + } + } + })); + + } + @action + onRotateMove = (e: PointerEvent, down: number[]): boolean => { + // const distance = Math.sqrt((this._prevY - e.clientY) * (this._prevY - e.clientY) + (this._prevX - e.clientX) * (this._prevX - e.clientX)); + const distance = Math.abs(this._prevY - e.clientY); + var angle = 0; + //think of a better condition later... + // if ((down[0] < e.clientX && this._prevY < e.clientY) || (down[0] > e.clientX && this._prevY > e.clientY)) { + if (e.clientY > this._prevY) { + angle = distance * (Math.PI / 180); + // } else if ((down[0] < e.clientX && this._prevY > e.clientY) || (down[0] > e.clientX && this._prevY <= e.clientY)) { + } else if (e.clientY < this._prevY) { + angle = - distance * (Math.PI / 180); + } + this._prevX = e.clientX; + this._prevY = e.clientY; + var index = 0; + SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { + const doc = Document(element.rootDoc); + if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { + const ink = Cast(doc.data, InkField)?.inkData; + if (ink) { + const newPoints: { X: number, Y: number }[] = []; + for (var i = 0; i < ink.length; i++) { + const newX = Math.cos(angle) * (ink[i].X - this._centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - this._centerPoints[index].Y) + this._centerPoints[index].X; + const newY = Math.sin(angle) * (ink[i].X - this._centerPoints[index].X) + Math.cos(angle) * (ink[i].Y - this._centerPoints[index].Y) + this._centerPoints[index].Y; + newPoints.push({ X: newX, Y: newY }); + } + doc.data = new InkField(newPoints); + const xs = newPoints.map(p => p.X); + const ys = newPoints.map(p => p.Y); + const left = Math.min(...xs); + const top = Math.min(...ys); + const right = Math.max(...xs); + const bottom = Math.max(...ys); + doc._height = bottom - top; + doc._width = right - left; + } + index++; + } + })); + return false; + } + + onRotateUp = (e: PointerEvent) => { + this._centerPoints = []; + } + + + _initialAutoHeight = false; _dragHeights = new Map(); @action @@ -521,6 +598,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
+
e.preventDefault()}>
{ const b = this.getBounds(l); return - {InteractionUtils.CreatePolyline(l, b.left, b.top, ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), 1, 1, this.InkShape)} + {InteractionUtils.CreatePolyline(l, b.left, b.top, ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), 1, 1, this.InkShape, "none", false)} ; }), this._points.length <= 1 ? (null) : - {InteractionUtils.CreatePolyline(this._points, B.left, B.top, ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), 1, 1, this.InkShape)} + {InteractionUtils.CreatePolyline(this._points, B.left, B.top, ActiveInkColor(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), 1, 1, this.InkShape, "none", false)} ] ]; } @@ -901,12 +906,20 @@ Scripting.addGlobal("GestureOverlay", GestureOverlay); Scripting.addGlobal(function setToolglass(tool: any) { runInAction(() => GestureOverlay.Instance.Tool = tool); }); -Scripting.addGlobal(function setPen(width: any, color: any) { +Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { runInAction(() => { GestureOverlay.Instance.SavedColor = ActiveInkColor(); SetActiveInkColor(color); GestureOverlay.Instance.SavedWidth = ActiveInkWidth(); SetActiveInkWidth(width); + GestureOverlay.Instance.SavedFill = ActiveFillColor(); + SetActiveFillColor(fill); + GestureOverlay.Instance.SavedArrowStart = ActiveArrowStart(); + SetActiveArrowStart(arrowStart); + GestureOverlay.Instance.SavedArrowEnd = ActiveArrowEnd(); + SetActiveArrowStart(arrowEnd); + GestureOverlay.Instance.SavedDash = ActiveDash(); + SetActiveDash(dash); }); }); Scripting.addGlobal(function resetPen() { diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index a7650163f..324665aea 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -58,11 +58,13 @@ export class InkingStroke extends ViewBoxBaseComponent 5 ? strokeColor : "transparent", + // strokeColor, (strokeWidth + 15).toString(), - StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), scaleX, scaleY, "", this.props.active() ? "visiblestroke" : "none", false); + StrCast(this.layoutDoc.strokeBezier, ActiveInkBezierApprox()), StrCast(this.layoutDoc.fillColor, ActiveFillColor()), StrCast(this.layoutDoc.arrowStart, ActiveArrowStart()), StrCast(this.layoutDoc.arrowEnd, ActiveArrowEnd()), StrCast(this.layoutDoc.dash, ActiveDash()), scaleX, scaleY, "", this.props.active() ? "visiblestroke" : "none", false); + console.log("#" + strokeColor); return ( { ContextMenu.Instance.addItem({ description: "Analyze Stroke", event: this.analyzeStrokes, icon: "paint-brush" }); @@ -93,6 +96,18 @@ export class InkingStroke extends ViewBoxBaseComponent + {/* + + */} + {/* + + + + + */} + {/* + + */} {hpoints} {points} @@ -105,17 +120,33 @@ export class InkingStroke extends ViewBoxBaseComponent) { super(props); @@ -29,18 +43,103 @@ export default class InkOptionsMenu extends AntimodeMenu { this._canFade = false; // don't let the inking menu fade away } + getColors = () => { + return this._palette; + } + + @action + changeArrow = (arrowStart: string, arrowEnd: string) => { + SetActiveArrowStart(arrowStart); + SetActiveArrowEnd(arrowEnd); + } + @action - changeColor = (color: string) => { + changeColor = (color: string, type: string) => { const col: ColorState = { hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" }, rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "", }; - SetActiveInkColor(Utils.colorString(col)); + if (type === "color") { + SetActiveInkColor(Utils.colorString(col)); + } else if (type === "fill") { + SetActiveFillColor(Utils.colorString(col)); + } } + @action + editProperties = (value: any, field: string) => { + SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { + const doc = Document(element.rootDoc); + if (doc.type === DocumentType.INK) { + switch (field) { + case "width": + doc.strokeWidth = Number(value); + break; + case "color": + doc.color = String(value); + break; + case "fill": + doc.fillColor = String(value); + break; + case "bezier": + // doc.strokeBezier === 300 ? doc.strokeBezier = 0 : doc.strokeBezier = 300; + break; + case "arrowStart": + doc.arrowStart = String(value); + break; + case "arrowEnd": + doc.arrowEnd = String(value); + break; + case "dash": + doc.dash = Number(value); + default: + break; + } + } + })); + } + + @action changeBezier = (e: React.PointerEvent): void => { SetActiveBezierApprox(!ActiveInkBezierApprox() ? "300" : ""); + this.editProperties(0, "bezier"); + } + @action + changeDash = (e: React.PointerEvent): void => { + SetActiveDash(ActiveDash() === "0" ? "2" : "0"); + this.editProperties(ActiveDash(), "dash"); + } + + @computed get arrowPicker() { + var currIcon; + for (var i = 0; i < this._arrowStart.length; i++) { + if (this._arrowStart[i] === ActiveArrowStart() && this._arrowEnd[i] === ActiveArrowEnd()) { + currIcon = this._arrowIcons[i]; + } + } + var arrowPicker = ; + if (this._arrowBtn) { + arrowPicker =
+ {arrowPicker} + {this._arrowStart.map((arrowStart, i) => { + return ; + })} +
; + } + return arrowPicker; } @computed get widthPicker() { @@ -58,7 +157,7 @@ export default class InkOptionsMenu extends AntimodeMenu { return ; @@ -68,6 +167,8 @@ export default class InkOptionsMenu extends AntimodeMenu { return widthPicker; } + + @computed get colorPicker() { var colorPicker = ; @@ -94,6 +195,35 @@ export default class InkOptionsMenu extends AntimodeMenu { return colorPicker; } + @computed get fillPicker() { + var fillPicker = ; + if (this._fillBtn) { + fillPicker =
+ {fillPicker} + {this._palette.map(color => { + return ; + })} + +
; + } + return fillPicker; + } + + + @computed get shapeButtons() { return <> {this._buttons.map((btn, i) => )}, + )} ; } + @computed get shapePicker() { + var currIcon; + if (GestureOverlay.Instance.InkShape === "") { + currIcon = "S"; + } else { + for (var i = 0; i < this._icons.length; i++) { + if (GestureOverlay.Instance.InkShape === this._buttons[i]) { + currIcon = this._icons[i]; + } + } + } + var shapePicker = ; + if (this._shapeBtn) { + shapePicker =
+ {shapePicker} + {this._buttons.map((btn, i) => { + return ; + })} +
; + } + return shapePicker; + } + @computed get bezierButton() { return ; } + @computed get dashButton() { + return ; + } + render() { const buttons = [ , - this.shapeButtons, + this.shapePicker, + // this.shapeButtons, this.bezierButton, this.widthPicker, this.colorPicker, + this.fillPicker, + this.arrowPicker, + this.dashButton ]; return this.getElement(buttons); } diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 51a5768bf..7cfd74cc4 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -1,7 +1,7 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; +import { Copy, ToScriptString, ToString, Update } from "./FieldSymbols"; export enum InkTool { None = "none", @@ -31,6 +31,8 @@ const strokeDataSchema = createSimpleSchema({ export class InkField extends ObjectField { @serializable(list(object(strokeDataSchema))) readonly inkData: InkData; + // inkData: InkData; + constructor(data: InkData) { super(); -- cgit v1.2.3-70-g09d2 From 780ef6fec5add6a0b6b05f5fa64f47b1a2b71df4 Mon Sep 17 00:00:00 2001 From: yunahi <60233430+yunahi@users.noreply.github.com> Date: Sat, 27 Jun 2020 02:44:31 +0900 Subject: fixed rotation resize --- src/client/views/DocumentDecorations.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c091b2315..6e8eed287 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -24,6 +24,7 @@ import { SnappingManager } from '../util/SnappingManager'; import { HtmlField } from '../../fields/HtmlField'; import { InkData, InkField, InkTool } from "../../fields/InkField"; import { update } from 'serializr'; +import { Transform } from "../util/Transform"; library.add(faCaretUp); library.add(faObjectGroup); @@ -260,7 +261,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const top = Math.min(...ys); const right = Math.max(...xs); const bottom = Math.max(...ys); - this._centerPoints.push({ X: ((right - left) / 2) + left, Y: ((bottom - top) / 2) + bottom }); + // this._centerPoints.push({ X: ((right - left) / 2) + left, Y: ((bottom - top) / 2) + bottom }); + this._centerPoints.push({ X: left, Y: top }); } } })); @@ -268,6 +270,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } @action onRotateMove = (e: PointerEvent, down: number[]): boolean => { + // const distance = Math.sqrt((this._prevY - e.clientY) * (this._prevY - e.clientY) + (this._prevX - e.clientX) * (this._prevX - e.clientX)); const distance = Math.abs(this._prevY - e.clientY); var angle = 0; @@ -287,6 +290,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) { const ink = Cast(doc.data, InkField)?.inkData; if (ink) { + const newPoints: { X: number, Y: number }[] = []; for (var i = 0; i < ink.length; i++) { const newX = Math.cos(angle) * (ink[i].X - this._centerPoints[index].X) - Math.sin(angle) * (ink[i].Y - this._centerPoints[index].Y) + this._centerPoints[index].X; @@ -300,8 +304,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const top = Math.min(...ys); const right = Math.max(...xs); const bottom = Math.max(...ys); - doc._height = bottom - top; - doc._width = right - left; + + doc._height = (bottom - top) * element.props.ScreenToLocalTransform().Scale; + doc._width = (right - left) * element.props.ScreenToLocalTransform().Scale; + } index++; } -- cgit v1.2.3-70-g09d2 From 99d2b19dba130353ac3b021bc7eab079bb125408 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 27 Jun 2020 02:21:24 -0400 Subject: fixed icon button of document deoratins. cleaned up several compile errors and warnings --- package-lock.json | 57 ++++++++++------------ package.json | 1 + src/client/documents/Documents.ts | 17 ++----- src/client/util/LinkManager.ts | 2 +- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.scss | 9 ++-- src/client/views/DocumentDecorations.tsx | 7 +-- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/LinkDocPreview.tsx | 4 +- .../formattedText/ProsemirrorExampleTransfer.ts | 2 +- src/fields/List.ts | 7 +-- src/fields/ObjectField.ts | 4 +- src/fields/Schema.ts | 5 +- 13 files changed, 55 insertions(+), 64 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/package-lock.json b/package-lock.json index 67d572ee2..928c59df4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -637,6 +637,14 @@ "@types/node": "*" } }, + "@types/google-maps": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/google-maps/-/google-maps-3.2.2.tgz", + "integrity": "sha512-/XPVfS28VzUdE/HlmBRoe5ii1nNMyWujyRfRY08bD/JgmPlWSiY8enB2dqTe9mlc3kULq7LfFa1wcupM+lQfqA==", + "requires": { + "@types/googlemaps": "*" + } + }, "@types/google-maps-react": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/google-maps-react/-/google-maps-react-2.0.5.tgz", @@ -646,6 +654,11 @@ "google-maps-react": "*" } }, + "@types/googlemaps": { + "version": "3.39.8", + "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.39.8.tgz", + "integrity": "sha512-z03u79t1v8QIktoUXypWD06Fzl499/hA162hurA+eCDlWXxFynuU+hMZIaferILF5Gzr4PMX1ShHszT666sUHQ==" + }, "@types/jquery": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.0.tgz", @@ -2922,8 +2935,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -2941,13 +2953,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2960,18 +2970,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -3074,8 +3081,7 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -3085,7 +3091,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3098,20 +3103,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.9.0", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3128,7 +3130,6 @@ "mkdirp": { "version": "0.5.3", "bundled": true, - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3184,8 +3185,7 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "npm-packlist": { "version": "1.4.8", @@ -3210,8 +3210,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -3221,7 +3220,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -3290,8 +3288,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -3321,7 +3318,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3339,7 +3335,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3378,13 +3373,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.1.1", - "bundled": true, - "optional": true + "bundled": true } } } diff --git a/package.json b/package.json index 79c33bd99..683f181c3 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "@hig/flyout": "^1.2.1", "@hig/theme-context": "^2.1.3", "@hig/theme-data": "^2.16.1", + "@types/google-maps": "^3.2.2", "@types/webscopeio__react-textarea-autocomplete": "^4.6.1", "@webscopeio/react-textarea-autocomplete": "^4.7.0", "adm-zip": "^0.4.16", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0fc58c39d..d5b7f07b5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -13,28 +13,23 @@ import { Cast, NumCast, StrCast } from "../../fields/Types"; import { AudioField, ImageField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; import { MessageStore } from "../../server/Message"; import { OmitKeys, Utils } from "../../Utils"; +import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { DocServer } from "../DocServer"; +import { DocumentManager } from "../util/DocumentManager"; import { dropActionType } from "../util/DragManager"; +import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox"; import { LinkManager } from "../util/LinkManager"; import { Scripting } from "../util/Scripting"; import { UndoManager } from "../util/UndoManager"; -import { DocumentType } from "./DocumentTypes"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { ContextMenu } from "../views/ContextMenu"; import { ContextMenuProps } from "../views/ContextMenuItem"; -import { ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke"; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke"; import { AudioBox } from "../views/nodes/AudioBox"; import { ColorBox } from "../views/nodes/ColorBox"; import { ComparisonBox } from "../views/nodes/ComparisonBox"; import { DocHolderBox } from "../views/nodes/DocHolderBox"; -import { InkingStroke, ActiveInkColor, ActiveInkWidth, ActiveInkBezierApprox, ActiveFillColor, ActiveArrowStart, ActiveArrowEnd, ActiveDash } from "../views/InkingStroke"; -import { InkField } from "../../fields/InkField"; -import { RichTextField } from "../../fields/RichTextField"; -import { extname } from "path"; -import { MessageStore } from "../../server/Message"; -import { ContextMenuProps } from "../views/ContextMenuItem"; -import { ContextMenu } from "../views/ContextMenu"; import { FontIconBox } from "../views/nodes/FontIconBox"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { ImageBox } from "../views/nodes/ImageBox"; @@ -52,9 +47,7 @@ import { WebBox } from "../views/nodes/WebBox"; import { PresElementBox } from "../views/presentationview/PresElementBox"; import { RecommendationsBox } from "../views/RecommendationsBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; -import { YoutubeBox } from "../apis/youtube/YoutubeBox"; -import { DocumentManager } from "../util/DocumentManager"; -import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox"; +import { DocumentType } from "./DocumentTypes"; const path = require('path'); export interface DocumentOptions { diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 438f3eb9b..0aec81ab0 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -72,7 +72,7 @@ export class LinkManager { }); DocListCast(anchor[Doc.LayoutFieldKey(anchor) + "-annotations"]).map(anno => { related.push(...LinkManager.Instance.getAllRelatedLinks(anno)); - }) + }); return related; } diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index fcaea0f0b..c05ca33fb 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -271,7 +271,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const considerPush = isText && this.considerGoogleDocsPush; return
- +
{this.templateButton} diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index c8347165d..5948ada88 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -193,10 +193,10 @@ $linkGap : 3px; .documentDecorations-iconifyButton { opacity: 1; grid-column-start: 4; - grid-column-end: 6; + grid-column-end: 5; pointer-events: all; text-align: center; - left: -20px; + left: -25px; top: -2px; cursor: pointer; position: absolute; @@ -207,10 +207,13 @@ $linkGap : 3px; .documentDecorations-closeButton { opacity: 1; grid-column-start: 4; - grid-column-end: 6; + grid-column-end: 5; pointer-events: all; text-align: center; cursor: pointer; + width: 15px; + margin-left: -8px; + margin-top: auto; } .documentDecorations-minimizeButton { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6e8eed287..f5454216a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -603,9 +603,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> }}> {maximizeIcon} {titleArea} -
- {"_"} -
+ {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : +
+ {"_"} +
}
{SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8f139e39e..a2bb9700e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -742,7 +742,7 @@ export class DocumentView extends DocComponent(Docu this.props.contextMenuItems?.().forEach(item => cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, self: this.rootDoc }), icon: "sticky-note" })); - let options = cm.findByDescription("Options..."); + const options = cm.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); templateDoc && optionItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 126dc240a..92b443d3b 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -25,8 +25,8 @@ export class LinkDocPreview extends React.Component { @observable _targetDoc: Opt; @observable _toolTipText = ""; - componentDidUpdate() { this.updatePreview() } - componentDidMount() { this.updatePreview() } + componentDidUpdate() { this.updatePreview(); } + componentDidMount() { this.updatePreview(); } async updatePreview() { const linkDoc = this.props.linkDoc; const linkSrc = this.props.linkSrc; diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 0abaf3df6..1bbcb9fa8 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -175,7 +175,7 @@ export default function buildKeymap>(schema: S, props: any } }); - var backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); + // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { if (!deleteSelection(state, (tx: Transaction>) => { dispatch(updateBullets(tx, schema)); diff --git a/src/fields/List.ts b/src/fields/List.ts index fdabea365..a9da75abb 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -291,9 +291,10 @@ class ListImpl extends ObjectField { this.___fields = value; for (const key in value) { const field = value[key]; - if (!(field instanceof ObjectField)) continue; - (field as ObjectField)[Parent] = this[Self]; - (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); + if (field instanceof ObjectField) { + field[Parent] = this[Self]; + field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); + } } } diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts index 9aa1c9b04..92b2cfa60 100644 --- a/src/fields/ObjectField.ts +++ b/src/fields/ObjectField.ts @@ -3,8 +3,8 @@ import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols import { Scripting } from "../client/util/Scripting"; export abstract class ObjectField { - protected [OnUpdate](diff?: any) { } - private [Parent]?: RefField | ObjectField; + public [OnUpdate](diff?: any) { } + public [Parent]?: RefField | ObjectField; abstract [Copy](): ObjectField; abstract [ToScriptString](): string; diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts index 72bce283d..98ef3e087 100644 --- a/src/fields/Schema.ts +++ b/src/fields/Schema.ts @@ -65,9 +65,8 @@ export function makeInterface(...schemas: T): InterfaceFu return obj; }; return function (doc?: Doc | Doc[]) { - doc = doc || new Doc; - if (doc instanceof Doc) { - return fn(doc); + if (doc instanceof Doc || doc === undefined) { + return fn(doc || new Doc); } else { return doc.map(fn); } -- cgit v1.2.3-70-g09d2 From f2bbf54077432852b2a078e70d675bada8b54961 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 27 Jun 2020 19:47:32 -0400 Subject: cleaned up a bunch of stuff related to TreeViews and link dots. added option for link dots to show up in tree views. changed titles in tree views to be truncatable. fixed edting of treeview titles when an alias is in the tree view as well. --- src/client/documents/Documents.ts | 1 + src/client/util/CurrentUserUtils.ts | 19 +- src/client/util/DragManager.ts | 6 +- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/EditableView.tsx | 60 ++-- .../views/collections/CollectionTreeView.scss | 3 +- .../views/collections/CollectionTreeView.tsx | 391 +++++++++++---------- .../CollectionFreeFormLinksView.tsx | 5 +- src/client/views/nodes/DocumentView.scss | 11 + src/client/views/nodes/DocumentView.tsx | 44 +-- src/client/views/nodes/LinkAnchorBox.tsx | 10 +- src/client/views/search/SearchItem.tsx | 2 +- 12 files changed, 287 insertions(+), 267 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d5b7f07b5..b175c018e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -168,6 +168,7 @@ export interface DocumentOptions { treeViewOpen?: boolean; // whether this document is expanded in a tree view treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked + treeViewTruncateTitleWidth?: number; 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 diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9a4c95571..2b0b2c738 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -306,21 +306,17 @@ export class CurrentUserUtils { // setup templates for different document types when they are iconified from Document Decorations static setupDefaultIconTemplates(doc: Doc) { if (doc["template-icon-view"] === undefined) { - const iconView = Docs.Create.TextDocument("", { - title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + const iconView = Docs.Create.LabelDocument({ + title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") }); - Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); + // Docs.Create.TextDocument("", { + // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + // }); + // Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); iconView.isTemplateDoc = makeTemplate(iconView); doc["template-icon-view"] = new PrefetchProxy(iconView); } - if (doc["template-icon-view-pdf"] === undefined) { - const iconPdfView = Docs.Create.LabelDocument({ - title: "icon_" + DocumentType.PDF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") - }); - iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); - doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView); - } if (doc["template-icon-view-rtf"] === undefined) { const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), @@ -605,6 +601,7 @@ export class CurrentUserUtils { if (doc["tabs-button-library"] === undefined) { const libraryStack = new PrefetchProxy(Docs.Create.TreeDocument([workspaces, documents, recentlyClosed, doc], { title: "Library", _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same" })) as any as Doc; doc["tabs-button-library"] = new PrefetchProxy(Docs.Create.ButtonDocument({ diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 417ddf989..0db3963b2 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -19,8 +19,6 @@ export function SetupDrag( docFunc: () => Doc | Promise | undefined, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType, - treeViewId?: string, - dontHideOnDrop?: boolean, dragStarted?: () => void ) { const onRowMove = async (e: PointerEvent) => { @@ -34,8 +32,6 @@ export function SetupDrag( const dragData = new DragManager.DocumentDragData([doc]); dragData.dropAction = dropAction; dragData.moveDocument = moveFunc; - dragData.treeViewId = treeViewId; - dragData.dontHideOnDrop = dontHideOnDrop; DragManager.StartDocumentDrag([_reference.current!], dragData, e.x, e.y); dragStarted?.(); } @@ -128,7 +124,7 @@ export namespace DragManager { draggedDocuments: Doc[]; droppedDocuments: Doc[]; dragDivName?: string; - treeViewId?: string; + treeViewDoc?: Doc; dontHideOnDrop?: boolean; offset: number[]; dropAction: dropActionType; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index f5454216a..6e28ad654 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -77,7 +77,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> get Bounds(): { x: number, y: number, b: number, r: number } { return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => { if (documentView.props.renderDepth === 0 || - documentView.props.treeViewId || + documentView.props.treeViewDoc || Doc.AreProtosEqual(documentView.props.Document, Doc.UserDoc())) { return bounds; } diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index ee3ce1cf3..bab3a1634 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -31,6 +31,7 @@ export interface EditableProps { fontStyle?: string; fontSize?: number; height?: number | "auto"; + sizeToContent?: boolean; maxHeight?: number; display?: string; autosuggestProps?: { @@ -60,13 +61,11 @@ export interface EditableProps { */ @observer export class EditableView extends React.Component { - public static loadId = ""; @observable _editing: boolean = false; constructor(props: EditableProps) { super(props); this._editing = this.props.editing ? true : false; - EditableView.loadId = ""; } // @action @@ -150,37 +149,44 @@ export class EditableView extends React.Component { @action setIsFocused = (value: boolean) => { const wasFocused = this._editing; - this._editing = value; + //this._editing = value; return wasFocused !== this._editing; } _ref = React.createRef(); + renderEditor() { + return this.props.autosuggestProps + ? this.finalizeEdit(e.currentTarget.value, false, true), + onPointerDown: this.stopPropagation, + onClick: this.stopPropagation, + onPointerUp: this.stopPropagation, + value: this.props.autosuggestProps.value, + onChange: this.props.autosuggestProps.onChange + }} + /> + : this.finalizeEdit(e.currentTarget.value, false, true)} + onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} + style={{ display: this.props.display, fontSize: this.props.fontSize, minWidth: 20 }} + placeholder={this.props.placeholder} + />; + } render() { if (this._editing && this.props.GetValue() !== undefined) { - return this.props.autosuggestProps - ? this.finalizeEdit(e.currentTarget.value, false, true), - onPointerDown: this.stopPropagation, - onClick: this.stopPropagation, - onPointerUp: this.stopPropagation, - value: this.props.autosuggestProps.value, - onChange: this.props.autosuggestProps.onChange - }} - /> - : this.finalizeEdit(e.currentTarget.value, false, true)} - onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} - style={{ display: this.props.display, fontSize: this.props.fontSize }} - placeholder={this.props.placeholder} - />; + return this.props.sizeToContent ? +
+
{this.props.GetValue()}
+ {this.renderEditor()} +
: this.renderEditor(); } else { this.props.autosuggestProps?.resetValue(); return (this.props.contents instanceof ObjectField ? (null) : diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 2aac81146..50f0534bd 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -82,8 +82,6 @@ text-overflow: ellipsis; white-space: pre-wrap; min-width: 10px; - // width:100%;//width: max-content; - } .treeViewItem-openRight { @@ -100,6 +98,7 @@ border-left: dashed 1px #00000042; } +.treeViewItem-header-editing, .treeViewItem-header { border: transparent 1px solid; display: flex; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 68dc0ced2..7ffecdc26 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -13,7 +13,7 @@ import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; import { SnappingManager } from '../../util/SnappingManager'; -import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; +import { DragManager, dropActionType } from "../../util/DragManager"; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; @@ -55,7 +55,7 @@ export interface TreeViewProps { ScreenToLocalTransform: () => Transform; backgroundColor?: (doc: Doc) => string | undefined; outerXf: () => { translateX: number, translateY: number }; - treeViewId: Doc; + treeViewDoc: Doc; parentKey: string; active: (outsideReaction?: boolean) => boolean; treeViewHideHeaderFields: () => boolean; @@ -76,31 +76,32 @@ export interface TreeViewProps { * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree */ class TreeView extends React.Component { - static _editTitleScript: ScriptField | undefined; + private _editTitleScript: ScriptField | undefined; private _header?: React.RefObject = React.createRef(); private _treedropDisposer?: DragManager.DragDropDisposer; private _dref = React.createRef(); private _tref = React.createRef(); private _docRef = React.createRef(); + private _uniqueId = Utils.GenerateGuid(); + private _editMaxWidth: number | string = 0; + get doc() { return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } - get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive - get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.props.document.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive + get defaultExpandedView() { return this.childDocs ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state - set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = this._overrideTreeViewOpen = c; } - @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.props.document.treeViewPreventOpen && BoolCast(this.props.document.treeViewOpen)) || this._overrideTreeViewOpen; } - @computed get treeViewExpandedView() { return StrCast(this.props.document.treeViewExpandedView, this.defaultExpandedView); } + set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; } + @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; } + @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } - @computed get dataDoc() { return this.props.document[DataSym]; } - @computed get fieldKey() { - const splits = StrCast(Doc.LayoutField(this.props.document)).split("fieldKey={\'"); - return splits.length > 1 ? splits[1].split("\'")[0] : "data"; - } + @computed get dataDoc() { return this.doc[DataSym]; } + @computed get layoutDoc() { return Doc.Layout(this.doc); } + @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } childDocList(field: string) { - const layout = Doc.LayoutField(this.props.document) instanceof Doc ? Doc.LayoutField(this.props.document) as Doc : undefined; + const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field (layout ? Cast(layout[field], listSpec(Doc)) : undefined) || // else if there's a layout doc, display it's fields - Cast(this.props.document[field], listSpec(Doc))) as Doc[]; // otherwise use the document's data field + Cast(this.doc[field], listSpec(Doc))) as Doc[]; // otherwise use the document's data field } @computed get childDocs() { return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } @@ -110,21 +111,27 @@ class TreeView extends React.Component { } @undoBatch openRight = () => this.props.addDocTab(this.props.document, "onRight", this.props.libraryPath); + @undoBatch openRight = () => this.props.addDocTab(this.doc, "onRight", this.props.libraryPath); @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { - return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); + return this.doc !== target && this.props.deleteDoc(doc) && addDoc(doc); } @undoBatch @action remove = (doc: Doc | Doc[], key: string) => { - return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => - flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); + return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); } @undoBatch @action removeDoc = (doc: Doc | Doc[]) => { return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.props.containingCollection, Doc.LayoutFieldKey(this.props.containingCollection), doc), true); } + constructor(props: any) { + super(props); + this._editTitleScript = ScriptField.MakeFunction(`setInPlace(self, 'editTitle', '${this._uniqueId}')`); + if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false); + } + protected createTreeDropTarget = (ele: HTMLDivElement) => { this._treedropDisposer?.(); - ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.props.document); + ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc); } onPointerEnter = (e: React.PointerEvent): void => { @@ -136,7 +143,9 @@ class TreeView extends React.Component { } onPointerLeave = (e: React.PointerEvent): void => { Doc.UnBrushDoc(this.dataDoc); - this._header!.current!.className = "treeViewItem-header"; + if (this._header?.current?.className !== "treeViewItem-header-editing") { + this._header!.current!.className = "treeViewItem-header"; + } document.removeEventListener("pointermove", this.onDragMove, true); } onDragMove = (e: PointerEvent): void => { @@ -144,7 +153,7 @@ class TreeView extends React.Component { const pt = [e.clientX, e.clientY]; const rect = this._header!.current!.getBoundingClientRect(); const before = pt[1] < rect.top + rect.height / 2; - const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length); + const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); this._header!.current!.className = "treeViewItem-header"; if (inside) this._header!.current!.className += " treeViewItem-header-inside"; else if (before) this._header!.current!.className += " treeViewItem-header-above"; @@ -155,42 +164,38 @@ class TreeView extends React.Component { editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} + GetValue={() => StrCast(this.doc[key])} SetValue={undoBatch((value: string) => { - Doc.SetInPlace(this.props.document, key, value, false) || true; - Doc.SetInPlace(this.props.document, "editTitle", undefined, false); + Doc.SetInPlace(this.doc, key, value, false) || true; + Doc.SetInPlace(this.doc, "editTitle", undefined, false); })} OnFillDown={undoBatch((value: string) => { - Doc.SetInPlace(this.props.document, key, value, false); + Doc.SetInPlace(this.doc, key, value, false); const doc = Docs.Create.FreeformDocument([], { title: "-", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List([Templates.Title.Layout]) }); - Doc.SetInPlace(this.props.document, "editTitle", undefined, false); - Doc.SetInPlace(doc, "editTitle", true, false); + Doc.SetInPlace(this.doc, "editTitle", undefined, false); + Doc.SetInPlace(doc, "editTitle", "*", false); return this.props.addDocument(doc); })} onClick={() => { SelectionManager.DeselectAll(); - Doc.UserDoc().activeSelection = new List([this.props.document]); + Doc.UserDoc().activeSelection = new List([this.doc]); return false; }} OnTab={undoBatch((shift?: boolean) => { - EditableView.loadId = this.dataDoc[Id]; shift ? this.props.outdentDocument?.() : this.props.indentDocument?.(); - setTimeout(() => { // unsetting/setting brushing for this doc will recreate & refocus this editableView after all other treeview changes have been made to the Dom (which may remove focus from this document). - Doc.UnBrushDoc(this.props.document); - Doc.BrushDoc(this.props.document); - EditableView.loadId = ""; - }, 0); + setTimeout(() => Doc.SetInPlace(this.doc, "editTitle", "*", false), 0); })} />) preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { const dragData = de.complete.docDragData; - dragData && (dragData.dropAction = this.props.treeViewId[Id] === dragData.treeViewId ? "same" : dragData.dropAction); + dragData && (dragData.dropAction = this.props.treeViewDoc === dragData.treeViewDoc ? "same" : dragData.dropAction); } @undoBatch @@ -198,18 +203,18 @@ class TreeView extends React.Component { const pt = [de.x, de.y]; const rect = this._header!.current!.getBoundingClientRect(); const before = pt[1] < rect.top + rect.height / 2; - const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && DocListCast(this.dataDoc[this.fieldKey]).length); + const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); const complete = de.complete; if (complete.linkDragData) { const sourceDoc = complete.linkDragData.linkSourceDocument; - const destDoc = this.props.document; + const destDoc = this.doc; DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link"); e.stopPropagation(); } const docDragData = complete.docDragData; if (docDragData) { e.stopPropagation(); - if (docDragData.draggedDocuments[0] === this.props.document) return true; + if (docDragData.draggedDocuments[0] === this.doc) return true; const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before); let addDoc = parentAddDoc; if (inside) { @@ -222,35 +227,28 @@ class TreeView extends React.Component { return false; } - docTransform = () => { - const { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); - const outerXf = this.props.outerXf(); - const offset = this.props.ScreenToLocalTransform().transformDirection((outerXf.translateX - translateX), outerXf.translateY - translateY); - const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); - - return finalXf; - } - getTransform = () => { - const { scale, translateX, translateY } = Utils.GetScreenTransform(this._tref.current!); + refTransform = (ref: HTMLDivElement) => { + const { scale, translateX, translateY } = Utils.GetScreenTransform(ref); const outerXf = this.props.outerXf(); const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - const finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); - return finalXf; + return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); } + docTransform = () => this.refTransform(this._dref.current!) + getTransform = () => this.refTransform(this._tref.current!); docWidth = () => { - const layoutDoc = Doc.Layout(this.props.document); + const layoutDoc = this.layoutDoc; const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); return NumCast(layoutDoc._nativeWidth) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; } docHeight = () => { - const layoutDoc = Doc.Layout(this.props.document); + const layoutDoc = this.layoutDoc; const bounds = this.boundsOfCollectionDocument; return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => { const aspect = NumCast(layoutDoc._nativeHeight, layoutDoc._fitWidth ? 0 : layoutDoc[HeightSym]()) / NumCast(layoutDoc._nativeWidth, layoutDoc._fitWidth ? 1 : layoutDoc[WidthSym]()); if (aspect) return this.docWidth() * aspect; if (bounds) return this.docWidth() * (bounds.b - bounds.y) / (bounds.r - bounds.x); - return layoutDoc._fitWidth ? (!this.props.document._nativeHeight ? NumCast(this.props.containingCollection._height) : + return layoutDoc._fitWidth ? (!this.doc._nativeHeight ? NumCast(this.props.containingCollection._height) : Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, NumCast(layoutDoc._nativeHeight)) / NumCast(layoutDoc._nativeWidth, NumCast(this.props.containingCollection._height)))) : NumCast(layoutDoc._height) ? NumCast(layoutDoc._height) : 50; @@ -259,7 +257,7 @@ class TreeView extends React.Component { @computed get expandedField() { const ids: { [key: string]: string } = {}; - const doc = this.props.document; + const doc = this.doc; doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); const rows: JSX.Element[] = []; @@ -273,13 +271,12 @@ class TreeView extends React.Component { const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce( (flg, doc) => flg && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true), true); contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : - DocListCast(contents), this.props.treeViewId, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, + DocListCast(contents), this.props.treeViewDoc, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, [...this.props.renderedIds, doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields); } else { - contentElement = { return rows; } - rtfWidth = () => Math.min(Doc.Layout(this.props.document)?.[WidthSym](), this.props.panelWidth() - 20); - rtfHeight = () => this.rtfWidth() < Doc.Layout(this.props.document)?.[WidthSym]() ? Math.min(Doc.Layout(this.props.document)?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; + rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); + rtfHeight = () => this.rtfWidth() < this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; @computed get renderContent() { TraceMobx(); @@ -320,32 +317,32 @@ class TreeView extends React.Component { const docs = expandKey === "links" ? this.childLinks : this.childDocs; const sortKey = `${this.fieldKey}-sortAscending`; return
    { - this.props.document[sortKey] = (this.props.document[sortKey] ? false : (this.props.document[sortKey] === false ? undefined : true)); + this.doc[sortKey] = (this.doc[sortKey] ? false : (this.doc[sortKey] === false ? undefined : true)); e.stopPropagation(); }}> {!docs ? (null) : - TreeView.GetChildElements(docs, this.props.treeViewId, Doc.Layout(this.props.document), + TreeView.GetChildElements(docs, this.props.treeViewDoc, this.layoutDoc, this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - StrCast(this.props.document.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, + StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, this.props.document[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)} + [...this.props.renderedIds, this.doc[Id]], this.props.libraryPath, this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields)}
; } else if (this.treeViewExpandedView === "fields") { - return
    + return
      {this.expandedField}
    ; } else { - const layoutDoc = Doc.Layout(this.props.document); + const layoutDoc = this.layoutDoc; const panelHeight = layoutDoc.type === DocumentType.RTF ? this.rtfHeight : this.docHeight; const panelWidth = layoutDoc.type === DocumentType.RTF ? this.rtfWidth : this.docWidth; - return
    + return
    { } } - get onCheckedClick() { return this.props.onCheckedClick || ScriptCast(this.props.document.onCheckedClick); } + get onCheckedClick() { return this.props.onCheckedClick || ScriptCast(this.doc.onCheckedClick); } @action bulletClick = (e: React.MouseEvent) => { - if (this.onCheckedClick && this.props.document.type !== DocumentType.COL) { + if (this.onCheckedClick && this.doc.type !== DocumentType.COL) { // this.props.document.treeViewChecked = this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check"; this.onCheckedClick.script.run({ - this: this.props.document.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.props.document, + this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc, heading: this.props.containingCollection.title, - checked: this.props.document.treeViewChecked === "check" ? "x" : this.props.document.treeViewChecked === "x" ? undefined : "check", - containingTreeView: this.props.treeViewId, + checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check", + containingTreeView: this.props.treeViewDoc, }, console.log); } else { this.treeViewOpen = !this.treeViewOpen; @@ -392,104 +389,110 @@ class TreeView extends React.Component { @computed get renderBullet() { - const checked = this.props.document.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.props.document.treeViewChecked ? this.props.document.treeViewChecked : "unchecked") : undefined; + const checked = this.doc.type === DocumentType.COL ? undefined : this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; return
    + style={{ color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}> {}
    ; } showContextMenu = (e: React.MouseEvent) => { this._docRef.current?.ContentDiv && simulateMouseClick(this._docRef.current.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - e.stopPropagation(); } focusOnDoc = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(doc)?.props.focus(doc, true); - contextMenuItems = () => { - const focusScript = ScriptField.MakeFunction(`DocFocus(self)`); - return [{ script: focusScript!, label: "Focus" }]; - } + contextMenuItems = () => [{ script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }] + truncateTitleWidth = () => NumCast(this.props.treeViewDoc.treeViewTruncateTitleWidth, 0); + showTitleEdit = () => ["*", this._uniqueId].includes(Doc.GetT(this.doc, "editTitle", "string", true) || "") /** * Renders the EditableView title element for placement into the tree. */ @computed get renderTitle() { TraceMobx(); - (!TreeView._editTitleScript) && (TreeView._editTitleScript = ScriptField.MakeFunction("setInPlace(self, 'editTitle', true)")); - const headerElements = ( + const headerElements = this.props.treeViewHideHeaderFields() ? (null) : <> - this.showContextMenu(e)}> + { this.showContextMenu(e); e.stopPropagation(); }} /> { if (this.treeViewOpen) { - this.props.document.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : - this.treeViewExpandedView === "fields" && Doc.Layout(this.props.document) ? "layout" : - this.treeViewExpandedView === "layout" && this.props.document.links ? "links" : + this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : + this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : + this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> {this.treeViewExpandedView} - ); - const openRight = (
    - -
    ); + ; + const view = this.showTitleEdit() ? this.editableView("title") : + ; return <>
    - {Doc.GetT(this.props.document, "editTitle", "boolean", true) ? - this.editableView("title") : - } + {view}
    - {this.props.treeViewHideHeaderFields() ? (null) : headerElements} - {openRight} + {headerElements} +
    + +
    ; } render() { TraceMobx(); - const sorting = this.props.document[`${this.fieldKey}-sortAscending`]; - //setTimeout(() => runInAction(() => untracked(() => this._overrideTreeViewOpen = this.treeViewOpen)), 0); + const sorting = this.doc[`${this.fieldKey}-sortAscending`]; + if (this.showTitleEdit()) { // find containing CollectionTreeView and set our maximum width so the containing tree view won't have to scroll + let par: any = this._header?.current; + if (par) { + while (par && par.className !== "collectionTreeView-dropTarget") par = par.parentNode; + if (par) { + const par_rect = (par as HTMLElement).getBoundingClientRect(); + const my_recct = this._docRef.current?.ContentDiv?.getBoundingClientRect(); + this._editMaxWidth = Math.max(100, par_rect.right - (my_recct?.left || 0)); + } + } + } else this._editMaxWidth = ""; return
    this.props.active() && SelectionManager.DeselectAll()}>
  • -
    { +
    { if (this.props.active(true)) { e.stopPropagation(); e.preventDefault(); @@ -507,14 +510,14 @@ class TreeView extends React.Component { {this.renderTitle}
    - {!this.treeViewOpen || this.props.renderedIds.indexOf(this.props.document[Id]) !== -1 ? (null) : this.renderContent} + {!this.treeViewOpen || this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (null) : this.renderContent}
  • ; } public static GetChildElements( childDocs: Doc[], - treeViewId: Doc, + treeViewDoc: Doc, containingCollection: Doc, dataDoc: Doc | undefined, key: string, @@ -624,7 +627,7 @@ class TreeView extends React.Component { libraryPath={libraryPath ? [...libraryPath, containingCollection] : undefined} containingCollection={containingCollection} prevSibling={docs[i]} - treeViewId={treeViewId} + treeViewDoc={treeViewDoc} key={child[Id]} indentDocument={indent} outdentDocument={outdent} @@ -665,21 +668,22 @@ export class CollectionTreeView extends CollectionSubView { this.treedropDisposer?.(); if (this._mainEle = ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.props.Document, this.onInternalPreDrop.bind(this)); + this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this)); } } protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => { const dragData = de.complete.docDragData; if (dragData) { - if (targetAction && !dragData.draggedDocuments.some(d => d.context === this.props.Document && this.childDocs.includes(d))) { + if (targetAction && !dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d))) { dragData.dropAction = targetAction; - } else dragData.dropAction = this.props.Document[Id] === dragData?.treeViewId ? "same" : dragData.dropAction; + } else dragData.dropAction = this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction; } } @@ -691,7 +695,7 @@ export class CollectionTreeView extends CollectionSubView { const docs = doc instanceof Doc ? [doc] : doc; - const targetDataDoc = this.props.Document[DataSym]; + const targetDataDoc = this.doc[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); const result = value.filter(v => !docs.includes(v)); if (result.length !== value.length) { @@ -704,9 +708,9 @@ export class CollectionTreeView extends CollectionSubView, before?: boolean): boolean => { const doAddDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => - flg && Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true); - if (this.props.Document.resolvedDataDoc instanceof Promise) { - this.props.Document.resolvedDataDoc.then((resolved: any) => doAddDoc(doc)); + flg && Doc.AddDocToList(this.doc[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false), true); + if (this.doc.resolvedDataDoc instanceof Promise) { + this.doc.resolvedDataDoc.then((resolved: any) => doAddDoc(doc)); } else { doAddDoc(doc); } @@ -714,22 +718,23 @@ export class CollectionTreeView extends CollectionSubView { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout - if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myWorkspaces) { + if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myWorkspaces) { ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.props.Document), icon: "minus" }); + ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.doc), icon: "minus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - } else if (!e.isPropagationStopped() && this.props.Document === Doc.UserDoc().myRecentlyClosed) { + } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) { ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List(), icon: "plus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); } else { const layoutItems: ContextMenuProps[] = []; - layoutItems.push({ description: (this.props.Document.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.props.Document.treeViewPreventOpen = !this.props.Document.treeViewPreventOpen, icon: "paint-brush" }); - layoutItems.push({ description: (this.props.Document.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.props.Document.treeViewHideHeaderFields = !this.props.Document.treeViewHideHeaderFields, icon: "paint-brush" }); - layoutItems.push({ description: (this.props.Document.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.props.Document.treeViewHideTitle = !this.props.Document.treeViewHideTitle, icon: "paint-brush" }); + layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" }); + layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" }); + layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" }); + layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); } !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ @@ -745,7 +750,7 @@ export class CollectionTreeView extends CollectionSubView { + this.childDocs?.map(d => { DocListCast(d.data).map((img, i) => { const caption = (d.captions as any)[i]; if (caption) { @@ -774,7 +779,7 @@ export class CollectionTreeView extends CollectionSubView UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" + description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" }); !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } @@ -784,7 +789,7 @@ export class CollectionTreeView extends CollectionSubView
    ; @@ -795,51 +800,51 @@ export class CollectionTreeView extends CollectionSubView this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc); const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; return !childDocs ? (null) : ( -
    this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} - onDrop={this.onTreeDrop} - ref={this.createTreeDropTarget}> - {(this.props.treeViewHideTitle || this.props.Document.treeViewHideTitle ? (null) : StrCast(this.dataDoc.title)} - SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)} - OnFillDown={undoBatch((value: string) => { - Doc.SetInPlace(this.dataDoc, "title", value, false); - const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List([Templates.Title.Layout]) }); - EditableView.loadId = doc[Id]; - Doc.SetInPlace(doc, "editTitle", true, false); - this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true); - })} />)} - {this.props.Document.allowClear ? this.renderClearButton : (null)} -
      - { - TreeView.GetChildElements(childDocs, this.props.Document, this.props.Document, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, - this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.props.Document.treeViewHideHeaderFields), - BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick, - this.props.onChildClick || ScriptCast(this.props.Document.onChildClick), this.props.ignoreFields) - } -
    -
    +
    +
    this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} + onDrop={this.onTreeDrop} + ref={this.createTreeDropTarget}> + {this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : StrCast(this.dataDoc.title)} + SetValue={undoBatch((value: string) => Doc.SetInPlace(this.dataDoc, "title", value, false) || true)} + OnFillDown={undoBatch((value: string) => { + Doc.SetInPlace(this.dataDoc, "title", value, false); + const doc = Docs.Create.FreeformDocument([], { title: "", x: 0, y: 0, _width: 100, _height: 25, _LODdisable: true, templates: new List([Templates.Title.Layout]) }); + Doc.SetInPlace(doc, "editTitle", "*", false); + this.addDoc(doc, childDocs.length ? childDocs[0] : undefined, true); + })} />} + {this.doc.allowClear ? this.renderClearButton : (null)} +
      + { + TreeView.GetChildElements(childDocs, this.doc, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, + moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, + this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), + BoolCast(this.doc.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick, + this.props.onChildClick || ScriptCast(this.doc.onChildClick), this.props.ignoreFields) + } +
    +
    +
    ); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index ae81b4b36..1a2421bfd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -10,6 +10,7 @@ import React = require("react"); import { Utils, emptyFunction } from "../../../../Utils"; import { DocumentType } from "../../../documents/DocumentTypes"; import { SnappingManager } from "../../../util/SnappingManager"; +import { Cast } from "../../../../fields/Types"; @observer export class CollectionFreeFormLinksView extends React.Component { @@ -30,8 +31,8 @@ export class CollectionFreeFormLinksView extends React.Component { return drawnPairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]); return connections.filter(c => - c.a.props.Document.type === DocumentType.LINK && - c.a.props.pinToPres !== emptyFunction && c.b.props.pinToPres !== emptyFunction // bcz: this prevents links to be drawn to anchors in CollectionTree views -- this is a hack that should be fixed + c.a.props.Document.type === DocumentType.LINK + && !c.a.props.treeViewDoc?.treeViewHideLinkLines && !c.b.props.treeViewDoc?.treeViewHideLinkLines ).map(c => ); } diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index b7726f7ba..b978f6245 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -42,6 +42,17 @@ width:10px !important; } } + .documentView-treeView { + max-height: 1.5em; + text-overflow: ellipsis; + display: inline-block; + white-space: pre; + width: 100%; + overflow: hidden; + > .documentView-node { + position: absolute; + } + } .documentView-lock { width: 20; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a2bb9700e..22917f18b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -69,7 +69,7 @@ export interface DocumentViewProps { onDoubleClick?: ScriptField; onPointerDown?: ScriptField; onPointerUp?: ScriptField; - treeViewId?: string; + treeViewDoc?: Doc; dropAction?: dropActionType; dragDivName?: string; nudge?: (x: number, y: number) => void; @@ -237,7 +237,7 @@ export class DocumentView extends DocComponent(Docu dragData.removeDocument = this.props.removeDocument; dragData.moveDocument = this.props.moveDocument;// this.layoutDoc.onDragStart ? undefined : this.props.moveDocument; dragData.dragDivName = this.props.dragDivName; - dragData.treeViewId = this.props.treeViewId; + dragData.treeViewDoc = this.props.treeViewDoc; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart }); } } @@ -1071,30 +1071,34 @@ export class DocumentView extends DocComponent(Docu anchorPanelHeight = () => this.props.PanelHeight() || 1; @computed get anchors() { TraceMobx(); - return this.props.forcedBackgroundColor?.(this.Document) === "transparent" || this.layoutDoc.presBox || this.props.dontRegisterView ? (null) : DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => - ); + return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots + this.layoutDoc.presBox || // presentationbox nodes + this.props.dontRegisterView ? (null) : // view that are not registered + DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => + ); } @computed get innards() { TraceMobx(); - if (!this.props.PanelWidth()) { // this happens when the document is a tree view label - return
    + if (this.props.treeViewDoc && !this.props.LayoutTemplateString) { // this happens when the document is a tree view label (but not an anchor dot) + return
    {StrCast(this.props.Document.title)} {this.anchors}
    ; } + const showTitle = StrCast(this.layoutDoc._showTitle); const showTitleHover = StrCast(this.layoutDoc._showTitleHover); const showCaption = StrCast(this.layoutDoc._showCaption); @@ -1156,7 +1160,7 @@ export class DocumentView extends DocComponent(Docu renderLock() { return (this.Document.isBackground !== undefined || this.isSelected(false)) && ((this.Document.type === DocumentType.COL && this.Document._viewType !== CollectionViewType.Pile) || this.Document.type === DocumentType.IMG) && - this.props.renderDepth > 0 && this.props.PanelWidth() > 0 ? + this.props.renderDepth > 0 && !this.props.treeViewDoc ?
    this.toggleBackground(true)}>
    diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 2bcc6168b..d4ab70200 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -115,8 +115,9 @@ export class LinkAnchorBox extends ViewBoxBaseComponent 1 ? NumCast(this.rootDoc[this.fieldKey + "_x"], 100) : 0; - const y = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_y"], 100) : 0; + const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView + const x = NumCast(this.rootDoc[this.fieldKey + "_x"], 100); + const y = NumCast(this.rootDoc[this.fieldKey + "_y"], 100); const c = StrCast(this.layoutDoc._backgroundColor, StrCast(this.layoutDoc.backgroundColor, StrCast(this.dataDoc.backgroundColor, "lightBlue"))); // note this is not where the typical lightBlue default color comes from. See Documents.Create.LinkDocument() const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1"; const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25; @@ -131,7 +132,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent}
    ); - const small = this.props.PanelWidth() <= 1; return
    LinkDocPreview.LinkInfo = undefined)} onPointerEnter={action(e => LinkDocPreview.LinkInfo = { @@ -143,8 +143,8 @@ export class LinkAnchorBox extends ViewBoxBaseComponent {!this._editing && !this._forceOpen ? (null) : diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 74262d81a..2436bf418 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -85,7 +85,7 @@ export class SelectorContextMenu extends React.Component { const item = React.createRef(); return
    doc.col, undefined, undefined, undefined, undefined, () => SearchBox.Instance.closeSearch())}> + SetupDrag(item, () => doc.col, undefined, undefined, () => SearchBox.Instance.closeSearch())}>
    {doc.col.title} -- cgit v1.2.3-70-g09d2 From 973e49dc36f208cc7f9a261cbe03e8d65d001b92 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 1 Jul 2020 01:53:42 -0400 Subject: added isPushpin. added 'hidden' for DocumentViews to stop displaying them. made pushpin's FontIconBox's. made Esc close context menu. --- src/client/util/DocumentManager.ts | 8 +- src/client/views/ContextMenu.tsx | 2 + src/client/views/DocumentDecorations.tsx | 92 +++++++++++----------- src/client/views/GlobalKeyHandler.ts | 6 +- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 12 +-- src/client/views/nodes/DocumentView.tsx | 3 +- src/client/views/nodes/FontIconBox.tsx | 9 ++- 8 files changed, 74 insertions(+), 60 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fb5d1717e..1fa5faeb3 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -146,7 +146,7 @@ export class DocumentManager { }; const docView = getFirstDocView(targetDoc, originatingDoc); let annotatedDoc = await Cast(targetDoc.annotationOn, Doc); - if (annotatedDoc) { + if (annotatedDoc && !linkDoc?.isPushpin) { const first = getFirstDocView(annotatedDoc); if (first) { annotatedDoc = first.props.Document; @@ -156,7 +156,11 @@ export class DocumentManager { } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? - docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); + if (linkDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden; + else { + docView.props.Document.hidden && (docView.props.Document.hidden = undefined); + docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); + } highlight(); } else { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 941d7b44a..07f7b8e6d 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -155,9 +155,11 @@ export class ContextMenu extends React.Component { @action closeMenu = () => { + const wasOpen = this._display; this.clearItems(); this._display = false; this._shouldDisplay = false; + return wasOpen; } @computed get filteredItems(): (OriginalMenuProps | string[])[] { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4fda10926..a45ef8862 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -600,52 +600,54 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0, }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} >
    -
    - {maximizeIcon} - {titleArea} - {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : -
    - {"_"} -
    } -
    - {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."} + {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <> +
    + {maximizeIcon} + {titleArea} + {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : +
    + {"_"} +
    } +
    + {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."} +
    +
    +
    e.preventDefault()}>
    +
    e.preventDefault()}>
    +
    e.preventDefault()}>
    +
    e.preventDefault()}>
    +
    +
    e.preventDefault()}>
    +
    e.preventDefault()}>
    +
    e.preventDefault()}>
    +
    e.preventDefault()}>
    + {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : +
    e.preventDefault()}> + +
    } +
    e.preventDefault()}>
    + +
    +
    +
    -
    -
    e.preventDefault()}>
    -
    e.preventDefault()}>
    -
    e.preventDefault()}>
    -
    e.preventDefault()}>
    -
    -
    e.preventDefault()}>
    -
    e.preventDefault()}>
    -
    e.preventDefault()}>
    -
    e.preventDefault()}>
    - {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : -
    e.preventDefault()}> - -
    } -
    e.preventDefault()}>
    - -
    -
    - -
    + }
    ); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index e3546dece..c85849adb 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -20,6 +20,7 @@ import { MainView } from "./MainView"; import { DocumentView } from "./nodes/DocumentView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import PDFMenu from "./pdf/PDFMenu"; +import { ContextMenu } from "./ContextMenu"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; @@ -81,16 +82,17 @@ export default class KeyManager { DocumentLinksButton.StartLink = undefined; const main = MainView.Instance; Doc.SetSelectedTool(InkTool.None); + var doDeselect = true; if (main.isPointerDown) { DragManager.AbortDrag(); } else { if (CollectionDockingView.Instance.HasFullScreen()) { CollectionDockingView.Instance.CloseFullScreen(); } else { - SelectionManager.DeselectAll(); + doDeselect = !ContextMenu.Instance.closeMenu(); } } - SelectionManager.DeselectAll(); + doDeselect && SelectionManager.DeselectAll(); DictationManager.Controls.stop(); // RecommendationsBox.Instance.closeMenu(); GoogleAuthenticationManager.Instance.cancel(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d107db86b..95c4898cc 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -225,7 +225,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d); const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d); const res = addedDocs.length ? this.addDocument(addedDocs) : true; - added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res; + added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document) || de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res; } else { added = this.addDocument(docDragData.droppedDocuments); } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index dab82185a..705fbdd34 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -141,15 +141,17 @@ export class CollectionView extends Touchable { const context = Cast(doc.context, Doc, null); - if (context && (context?.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { - const pushpin = Docs.Create.FreeformDocument([], { - title: "pushpin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), - _width: 10, _height: 10, _backgroundColor: "red", isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null) + if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG) && + !DocListCast(doc.links).some(d => d.isPushpin)) { + const pushpin = Docs.Create.FontIconDocument({ + icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7", + _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null) }); Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin); - DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin"); + const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin"); const first = DocListCast(pushpin.links).find(d => d instanceof Doc); first && (first.hidden = true); + pushpinLink && (Doc.GetProto(pushpinLink).isPushpin = true); doc.displayTimecode = undefined; } doc.context = this.props.Document; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 21b6d8310..09eeaee36 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1169,8 +1169,9 @@ export class DocumentView extends DocComponent(Docu } render() { - if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); if (!(this.props.Document instanceof Doc)) return (null); + if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); + if (this.props.Document.hidden) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index cf0b16c7c..5e8dd2497 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -5,7 +5,7 @@ import { createSchema, makeInterface } from '../../../fields/Schema'; import { DocComponent } from '../DocComponent'; import './FontIconBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; -import { StrCast, Cast } from '../../../fields/Types'; +import { StrCast, Cast, NumCast } from '../../../fields/Types'; import { Utils } from "../../../Utils"; import { runInAction, observable, reaction, IReactionDisposer } from 'mobx'; import { Doc } from '../../../fields/Doc'; @@ -59,13 +59,14 @@ export class FontIconBox extends DocComponent( render() { const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc); - const referenceLayout = Doc.Layout(referenceDoc); + const refLayout = Doc.Layout(referenceDoc); return ; } -- cgit v1.2.3-70-g09d2