diff options
Diffstat (limited to 'src/client/util')
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 51 | ||||
-rw-r--r-- | src/client/util/DocumentManager.ts | 7 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 15 | ||||
-rw-r--r-- | src/client/util/InteractionUtils.tsx | 149 | ||||
-rw-r--r-- | src/client/util/LinkManager.ts | 33 | ||||
-rw-r--r-- | src/client/util/SelectionManager.ts | 14 | ||||
-rw-r--r-- | src/client/util/SettingsManager.tsx | 19 | ||||
-rw-r--r-- | src/client/util/SharingManager.scss | 1 | ||||
-rw-r--r-- | src/client/util/UndoManager.ts | 19 |
9 files changed, 132 insertions, 176 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f40cae676..435d40d2a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -20,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { TreeView } from "../views/collections/TreeView"; import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; @@ -38,7 +39,6 @@ import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; -import { TreeView } from "../views/collections/TreeView"; interface Button { title?: string; @@ -226,9 +226,9 @@ export class CurrentUserUtils { const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([ - new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false), - new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true), - new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true), + new SchemaHeaderField("[A Short Description]", "dimgray", undefined, undefined, undefined, false), + new SchemaHeaderField("[Long Description]", "dimgray", undefined, undefined, undefined, true), + new SchemaHeaderField("[Details]", "dimgray", undefined, undefined, undefined, true), ]); const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts, _chromeHidden: true, system: true }); detailView.isTemplateDoc = makeTemplate(detailView); @@ -349,7 +349,7 @@ export class CurrentUserUtils { static setupDefaultIconTemplates(doc: Doc) { if (doc["template-icon-view"] === undefined) { const iconView = Docs.Create.LabelDocument({ - title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", + 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)"), system: true }); // Docs.Create.TextDocument("", { @@ -815,22 +815,25 @@ export class CurrentUserUtils { const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); const newDashboardButton: Doc = Docs.Create.FontIconDocument({ onClick: newDashboard, _forceActive: true, toolTip: "Create new dashboard", _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true }); doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, + title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true, explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." })); - // const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); - // const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - // const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); + const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`); + const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); + const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); - (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]); - (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); - (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]); - // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); - // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); + const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()'); + // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]); + // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); + // (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]); + (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); + (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); + (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "chalkboard", "tv", "camera", "users", "times"]); + (doc.myDashboards as any as Doc).childContextMenuFilters = new List<ScriptField>([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]); } return doc.myDashboards as any as Doc; } @@ -1021,8 +1024,6 @@ export class CurrentUserUtils { title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, - switchToggle: true, - width: 100, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview()', @@ -1242,7 +1243,7 @@ export class CurrentUserUtils { static setupSearchSidebar(doc: Doc) { if (doc.mySearchPanel === undefined) { doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({ - backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, + backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true, childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true })) as any as Doc; } @@ -1307,7 +1308,6 @@ export class CurrentUserUtils { }, { fireImmediately: true }); // Document properties on load doc.system = true; - doc.darkScheme = ColorScheme.Dark; doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; doc._raiseWhenDragged = true; @@ -1326,7 +1326,6 @@ export class CurrentUserUtils { doc.fontColor = StrCast(doc.fontColor, "black"); doc.fontHighlight = StrCast(doc.fontHighlight, ""); doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); - doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // @@ -1334,6 +1333,7 @@ export class CurrentUserUtils { Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]); doc.savedFilters = new List<Doc>(); doc.filterDocCount = 0; + doc.freezeChildren = "remove|add"; this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupDocTemplates(doc); // sets up the template menu of templates this.setupImportSidebar(doc); // sets up the import sidebar @@ -1363,6 +1363,13 @@ export class CurrentUserUtils { // }); setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); + if (doc.activeDashboard instanceof Doc) { + // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) + doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; + } + if (doc.activeCollectionBackground === "white") { // temporary to avoid having to rebuild the databse for old accounts that have this set by default. + doc.activeCollectionBackground = undefined; + } return doc; } @@ -1532,8 +1539,7 @@ export class CurrentUserUtils { public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { const tbox = Docs.Create.TextDocument("", { _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor, - _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), - _fontFamily: StrCast(Doc.UserDoc().fontFamily), title + _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, title }); const template = Doc.UserDoc().defaultTextLayout; if (template instanceof Doc) { @@ -1642,4 +1648,7 @@ Scripting.addGlobal(function makeTopLevelFolder() { const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder); +}); +Scripting.addGlobal(function toggleComicMode() { + Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; });
\ No newline at end of file diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index dfec9823b..66b6a1e44 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,6 +10,7 @@ import { LightboxView } from '../views/LightboxView'; import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { Scripting } from './Scripting'; +import { SelectionManager } from './SelectionManager'; export class DocumentManager { @@ -62,6 +63,7 @@ export class DocumentManager { const index = this.DocumentViews.indexOf(view); index !== -1 && this.DocumentViews.splice(index, 1); } + SelectionManager.DeselectView(view); }); //gets all views @@ -159,7 +161,8 @@ export class DocumentManager { originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc finished?: () => void, originalTarget?: Doc, - noSelect?: boolean + noSelect?: boolean, + presZoom?: number ): Promise<void> => { originalTarget = originalTarget ?? targetDoc; const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; @@ -192,7 +195,7 @@ export class DocumentManager { if (focusView) { !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox focusView.focus(targetDoc, { - originalTarget, willZoom, afterFocus: (didFocus: boolean) => + originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => { focusAndFinish(didFocus); res(); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 421e4c6bb..f5704d2bf 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -365,6 +365,21 @@ export namespace DragManager { const dragElements = eles.map(ele => { if (!ele.parentNode) dragDiv.appendChild(ele); const dragElement = ele.parentNode === dragDiv ? ele : ele.cloneNode(true) as HTMLElement; + const children = Array.from(dragElement.children); + while (children.length) { // need to replace all the maker node reference ids with new unique ids. otherwise, the clone nodes will reference the original nodes which are all hidden during the drag + const next = children.pop(); + next && children.push(...Array.from(next.children)); + if (next) { + ["marker-start", "marker-mid", "marker-end"].forEach(field => { + if (next.localName.startsWith("path") && (next.attributes as any)[field]) { + next.setAttribute(field, (next.attributes as any)[field].value.replace("#", "#X")); + } + }); + if (next.localName.startsWith("marker")) { + next.id = "X" + next.id; + } + } + } const rect = ele.getBoundingClientRect(); const scaleX = rect.width / ele.offsetWidth; const scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX; diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 4a8011e3c..a32a8eecc 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -1,10 +1,6 @@ import React = require("react"); -import * as beziercurve from 'bezier-curve'; -import * as fitCurve from 'fit-curve'; -import "./InteractionUtils.scss"; import { Utils } from "../../Utils"; -import { CurrentUserUtils } from "./CurrentUserUtils"; -import { InkTool } from "../../fields/InkField"; +import "./InteractionUtils.scss"; export namespace InteractionUtils { export const MOUSETYPE = "mouse"; @@ -93,122 +89,43 @@ export namespace InteractionUtils { return myTouches; } - export function CreatePoints(points: { X: number, Y: number }[], left: number, top: number, - color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string, - dash: string, scalex: number, scaley: number, shape: string, pevents: string, drawHalo: boolean, nodefs: boolean) { - let pts: { X: number; Y: number; }[] = []; - if (shape) { //if any of the shape are true - pts = makePolygon(shape, points); - } - else if ((points.length >= 5 && points[3].X === points[4].X) || (points.length === 4)) { - for (var i = 0; i < points.length - 3; i += 4) { - const array = [[points[i].X, points[i].Y], [points[i + 1].X, points[i + 1].Y], [points[i + 2].X, points[i + 2].Y], [points[i + 3].X, points[i + 3].Y]]; - for (var t = 0; t < 1; t += 0.01) { - const point = beziercurve(t, array); - pts.push({ X: point[0], Y: point[1] }); - } - } - } - else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) { - //pointer is up (first and last points are the same) - const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]); - newPoints.pop(); - - const bezierCurves = fitCurve(newPoints, parseInt(bezier)); - for (const curve of bezierCurves) { - for (var t = 0; t < 1; t += 0.01) { - const point = beziercurve(t, curve); - pts.push({ X: point[0], Y: point[1] }); - } - } - } else { - pts = points.slice(); - // bcz: Ugh... this is ugly, but shapes apprently have an extra point added that is = (p[0].x,p[0].y+1) as some sort of flag. need to remove it here. - if (pts.length > 2 && pts[pts.length - 2].X === pts[0].X && pts[pts.length - 2].Y === pts[0].Y) { - pts.pop(); - } - } - if (isNaN(scalex)) { - scalex = 1; - } - if (isNaN(scaley)) { - scaley = 1; - } - return pts; - } + export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, + color: string, width: number, strokeWidth: number, lineJoin: string, lineCap: string, bezier: string, fill: string, arrowStart: string, arrowEnd: string, + dash: string | undefined, scalex: number, scaley: number, shape: string, pevents: string, opacity: number, nodefs: boolean) { + const pts = shape ? makePolygon(shape, points) : points; + if (isNaN(scalex)) scalex = 1; + if (isNaN(scaley)) scaley = 1; + const toScr = (p: { X: number, Y: number }) => ` ${!p ? 0 : (p.X - left - width / 2) * scalex + width / 2}, ${!p ? 0 : (p.Y - top - width / 2) * scaley + width / 2} `; + const strpts = bezier ? + pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? "" : (i === 0 ? "M" + toScr(pt) : "") + "C" + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), "") : + pts.reduce((acc: string, pt) => acc + `${toScr(pt)} `, ""); - export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, - color: string, width: number, strokeWidth: number, bezier: string, fill: string, arrowStart: string, arrowEnd: string, - dash: string | undefined, scalex: number, scaley: number, shape: string, pevents: string, opacity: number, nodefs: boolean) { - let pts: { X: number; Y: number; }[] = []; - if (shape) { //if any of the shape are true - pts = makePolygon(shape, points); - } - else if ((points.length >= 5 && points[3].X === points[4].X) || (points.length === 4)) { - for (var i = 0; i < points.length - 3; i += 4) { - const array = [[points[i].X, points[i].Y], [points[i + 1].X, points[i + 1].Y], [points[i + 2].X, points[i + 2].Y], [points[i + 3].X, points[i + 3].Y]]; - for (var t = 0; t < 1; t += 0.01) { - const point = beziercurve(t, array); - pts.push({ X: point[0], Y: point[1] }); - } - } - } - else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) { - //pointer is up (first and last points are the same) - const newPoints = points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]); - newPoints.pop(); - - const bezierCurves = fitCurve(newPoints, parseInt(bezier)); - for (const curve of bezierCurves) { - for (var t = 0; t < 1; t += 0.01) { - const point = beziercurve(t, curve); - pts.push({ X: point[0], Y: point[1] }); - } - } - } else { - pts = points.slice(); - // bcz: Ugh... this is ugly, but shapes apprently have an extra point added that is = (p[0].x,p[0].y+1) as some sort of flag. need to remove it here. - if (pts.length > 2 && pts[pts.length - 2].X === pts[0].X && pts[pts.length - 2].Y === pts[0].Y) { - pts.pop(); - } - } - if (isNaN(scalex)) { - scalex = 1; - } - if (isNaN(scaley)) { - scaley = 1; - } - const strpts = pts.reduce((acc: string, pt: { X: number, Y: number }) => acc + - `${(pt.X - left - width / 2) * scalex + width / 2}, - ${(pt.Y - top - width / 2) * scaley + width / 2} `, ""); const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined; const defGuid = Utils.GenerateGuid(); - const arrowDim = Math.max(0.5, 8 / Math.log(Math.max(2, strokeWidth))); - - const addables = pts.map((pts, i) => - <svg height="10" width="10"> - <circle cx={(pts.X - left - width / 2) * scalex + width / 2} cy={(pts.Y - top - width / 2) * scaley + width / 2} r={strokeWidth / 2} stroke="black" strokeWidth={1} fill="blue" - onDoubleClick={(e) => { console.log(i); }} pointerEvents="all" cursor="all-scroll" - /> - </svg>); - + const Tag = (bezier ? "path" : "polyline") as keyof JSX.IntrinsicElements; + const makerStrokeWidth = strokeWidth / 2; return (<svg fill={color}> {/* setting the svg fill sets the arrowStart fill */} {nodefs ? (null) : <defs> - {arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : <marker id={`dot${defGuid}`} orient="auto" overflow="visible"> - <circle r={1} fill="context-stroke" /> - </marker>} - {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowStart${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7"> - <polygon points={`${arrowDim} ${-Math.max(1, arrowDim / 2)}, ${arrowDim} ${Math.max(1, arrowDim / 2)}, -1 0`} /> - </marker>} - {arrowStart !== "arrow" && arrowEnd !== "arrow" ? (null) : <marker id={`arrowEnd${defGuid}`} orient="auto" overflow="visible" refX="1.6" refY="0" markerWidth="10" markerHeight="7"> - <polygon points={`${2 - arrowDim} ${-Math.max(1, arrowDim / 2)}, ${2 - arrowDim} ${Math.max(1, arrowDim / 2)}, 3 0`} /> - </marker>} + {arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : + <marker id={`dot${defGuid}`} orient="auto" markerUnits="userSpaceOnUse" refX={0} refY="0" overflow="visible"> + <circle r={strokeWidth * 1.5} fill="context-stroke" /> + </marker>} + {arrowStart !== "arrow" ? (null) : + <marker id={`arrowStart${defGuid}`} markerUnits="userSpaceOnUse" orient="auto" overflow="visible" refX={makerStrokeWidth * 1.5} refY={0} markerWidth="10" markerHeight="7"> + <polygon style={{ stroke: color }} strokeLinejoin={lineJoin as any} strokeWidth={makerStrokeWidth * 2 / 3} points={`${3 * makerStrokeWidth} ${-makerStrokeWidth * 1.5}, ${makerStrokeWidth * 2} 0, ${3 * makerStrokeWidth} ${makerStrokeWidth * 1.5}, 0 0`} /> + </marker>} + {arrowEnd !== "arrow" ? (null) : + <marker id={`arrowEnd${defGuid}`} markerUnits="userSpaceOnUse" orient="auto" overflow="visible" refX={makerStrokeWidth * 1.5} refY={0} markerWidth="10" markerHeight="7"> + <polygon style={{ stroke: color }} strokeLinejoin={lineJoin as any} strokeWidth={makerStrokeWidth * 2 / 3} points={`0 ${-makerStrokeWidth * 1.5}, ${makerStrokeWidth} 0, 0 ${makerStrokeWidth * 1.5}, ${3 * makerStrokeWidth} 0`} /> + </marker>} </defs>} - <polyline - points={strpts} + + <Tag + d={bezier ? strpts : undefined} + points={bezier ? undefined : strpts} style={{ // filter: drawHalo ? "url(#inkSelectionHalo)" : undefined, fill: fill && fill !== "transparent" ? fill : "none", @@ -217,14 +134,12 @@ export namespace InteractionUtils { pointerEvents: pevents as any, stroke: color ?? "rgb(0, 0, 0)", strokeWidth: strokeWidth, - strokeLinejoin: color === "rgba(245, 230, 95, 0.75)" ? "miter" : "round", - strokeLinecap: color === "rgba(245, 230, 95, 0.75)" ? "square" : "round", + strokeLinecap: lineCap as any, strokeDasharray: dashArray }} - markerStart={`url(#${arrowStart + "Start" + defGuid})`} - markerEnd={`url(#${arrowEnd + "End" + defGuid})`} + markerStart={`url(#${arrowStart === "dot" ? arrowStart + defGuid : arrowStart + "Start" + defGuid})`} + markerEnd={`url(#${arrowEnd === "dot" ? arrowEnd + defGuid : arrowEnd + "End" + defGuid})`} /> - {/* {addables} */} </svg>); } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 64da68f59..62b13e2c6 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -38,15 +38,19 @@ export class LinkManager { const addLinkToDoc = (link: Doc) => { const a1Prom = link?.anchor1; const a2Prom = link?.anchor2; - Promise.all([a1Prom, a2Prom]).then(action((all) => { + Promise.all([a1Prom, a2Prom]).then((all) => { const a1 = all[0]; const a2 = all[1]; - if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { - Doc.GetProto(a1)[DirectLinksSym].add(link); - Doc.GetProto(a2)[DirectLinksSym].add(link); - Doc.GetProto(link)[DirectLinksSym].add(link); - } - })); + const a1ProtoProm = (link?.anchor1 as Doc)?.proto; + const a2ProtoProm = (link?.anchor2 as Doc)?.proto; + Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => { + if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { + Doc.GetProto(a1)[DirectLinksSym].add(link); + Doc.GetProto(a2)[DirectLinksSym].add(link); + Doc.GetProto(link)[DirectLinksSym].add(link); + } + })); + }); }; const remLinkFromDoc = (link: Doc) => { const a1 = link?.anchor1; @@ -100,12 +104,9 @@ export class LinkManager { public createLinkrelationshipLists = () => { //create new lists for link relations and their associated colors if the lists don't already exist - if (!Doc.UserDoc().linkRelationshipList && !Doc.UserDoc().linkColorList) { - const linkRelationshipList = new List<string>(); - const linkColorList = new List<string>(); - Doc.UserDoc().linkRelationshipList = linkRelationshipList; - Doc.UserDoc().linkColorList = linkColorList; - } + !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>()); + !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>()); + !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>()); } public addLink(linkDoc: Doc, checkExists = false) { @@ -233,8 +234,10 @@ export class LinkManager { setTimeout(LightboxView.Next); finished?.(); } else { - const containerDoc = Cast(target.annotationOn, Doc, null) || target; - const targetContext = Cast(containerDoc?.context, Doc, null); + const containerAnnoDoc = Cast(target.annotationOn, Doc, null); + const containerDoc = containerAnnoDoc || target; + const containerDocContext = Cast(containerDoc?.context, Doc, null); + const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext; const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, finished); } diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index bac13373c..e507ec3bf 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -2,7 +2,6 @@ import { action, observable, ObservableMap } from "mobx"; import { computedFn } from "mobx-utils"; import { Doc, Opt } from "../../fields/Doc"; import { DocumentType } from "../documents/DocumentTypes"; -import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView"; import { CollectionViewType } from "../views/collections/CollectionView"; import { DocumentView } from "../views/nodes/DocumentView"; @@ -13,12 +12,10 @@ export namespace SelectionManager { @observable IsDragging: boolean = false; SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap(); @observable SelectedSchemaDocument: Doc | undefined; - @observable SelectedSchemaCollection: CollectionSchemaView | undefined; @action - SelectSchemaView(collectionView: Opt<CollectionSchemaView>, doc: Opt<Doc>) { + SelectSchemaViewDoc(doc: Opt<Doc>) { manager.SelectedSchemaDocument = doc; - manager.SelectedSchemaCollection = collectionView; } @action SelectView(docView: DocumentView, ctrlPressed: boolean): void { @@ -33,7 +30,6 @@ export namespace SelectionManager { } else if (!ctrlPressed && Array.from(manager.SelectedViews.entries()).length > 1) { Array.from(manager.SelectedViews.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false)); manager.SelectedSchemaDocument = undefined; - manager.SelectedSchemaCollection = undefined; manager.SelectedViews.clear(); manager.SelectedViews.set(docView, docView.rootDoc); } @@ -47,7 +43,6 @@ export namespace SelectionManager { } @action DeselectAll(): void { - manager.SelectedSchemaCollection = undefined; manager.SelectedSchemaDocument = undefined; Array.from(manager.SelectedViews.keys()).map(dv => dv.props.whenChildContentsActiveChanged(false)); manager.SelectedViews.clear(); @@ -62,8 +57,8 @@ export namespace SelectionManager { export function SelectView(docView: DocumentView, ctrlPressed: boolean): void { manager.SelectView(docView, ctrlPressed); } - export function SelectSchemaView(colSchema: Opt<CollectionSchemaView>, document: Opt<Doc>): void { - manager.SelectSchemaView(colSchema, document); + export function SelectSchemaViewDoc(document: Opt<Doc>): void { + manager.SelectSchemaViewDoc(document); } const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) { // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed @@ -96,9 +91,6 @@ export namespace SelectionManager { export function SelectedSchemaDoc(): Doc | undefined { return manager.SelectedSchemaDocument; } - export function SelectedSchemaCollection(): CollectionSchemaView | undefined { - return manager.SelectedSchemaCollection; - } export function Docs(): Doc[] { return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking); } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index bd91db779..6a26dfdc7 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -19,9 +19,9 @@ export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; export enum ColorScheme { - Dark = "Dark", - Light = "Light", - System = "Match System" + Dark = "-Dark", + Light = "-Light", + System = "-MatchSystem" } @observer @@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> { @observable activeTab = "Accounts"; @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; } - @computed get colorScheme() { return Doc.UserDoc().colorScheme; } + @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; } constructor(props: {}) { super(props); @@ -81,16 +81,16 @@ export class SettingsManager extends React.Component<{}> { const scheme: ColorScheme = (e.currentTarget as any).value; switch (scheme) { case ColorScheme.Light: - Doc.UserDoc().colorScheme = ColorScheme.Light; + CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" }); break; case ColorScheme.Dark: - Doc.UserDoc().colorScheme = ColorScheme.Dark; + CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark; addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" }); break; case ColorScheme.System: default: window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { - Doc.UserDoc().colorScheme = e.matches ? ColorScheme.Dark : ColorScheme.Light; + CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) }); break; } @@ -119,6 +119,7 @@ export class SettingsManager extends React.Component<{}> { </div>; const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System]; + const schemeMap = ["Light", "Dark", "Match system"]; return <div className="colors-content"> <div className="preferences-color"> @@ -132,8 +133,8 @@ export class SettingsManager extends React.Component<{}> { <div className="preferences-colorScheme"> <div className="preferences-color-text">Color Scheme</div> <div className="preferences-color-controls"> - <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.UserDoc().colorScheme)}> - {colorSchemes.map(scheme => <option key={scheme} value={scheme}> {scheme} </option>)} + <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}> + {colorSchemes.map((scheme, i) => <option key={scheme} value={scheme}> {schemeMap[i]} </option>)} </select> </div> </div> diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 9dc57dd1e..2de636f21 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -40,7 +40,6 @@ .permissions-select { z-index: 1; - margin-left: -115; border: none; outline: none; text-align: justify; // for Edge diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 05fb9f378..44e44d335 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -70,6 +70,7 @@ export namespace UndoManager { export interface UndoEvent { undo: () => void; redo: () => void; + prop: string; } type UndoBatch = UndoEvent[]; @@ -104,6 +105,23 @@ export namespace UndoManager { export function GetOpenBatches(): Without<Batch, 'end'>[] { return openBatches; } + export function FilterBatches(fieldTypes: string[]) { + const fieldCounts: { [key: string]: number } = {}; + const lastStack = UndoManager.undoStack.lastElement(); + if (lastStack) { + lastStack.forEach(ev => fieldTypes.includes(ev.prop) && (fieldCounts[ev.prop] = (fieldCounts[ev.prop] || 0) + 1)); + const fieldCount2: { [key: string]: number } = {}; + runInAction(() => + UndoManager.undoStack[UndoManager.undoStack.length - 1] = lastStack.filter(ev => { + if (fieldTypes.includes(ev.prop)) { + fieldCount2[ev.prop] = (fieldCount2[ev.prop] || 0) + 1; + if (fieldCount2[ev.prop] === 1 || fieldCount2[ev.prop] === fieldCounts[ev.prop]) return true; + return false; + } + return true; + })); + } + } export function TraceOpenBatches() { console.log(`Open batches:\n\t${openBatches.map(batch => batch.batchName).join("\n\t")}\n`); } @@ -140,6 +158,7 @@ export namespace UndoManager { batchCounter--; // console.log("End " + batchCounter); if (batchCounter === 0 && currentBatch?.length) { + // console.log("------ended----") if (!cancel) { undoStack.push(currentBatch); } |