From 810f86195188503b04d64f9d58ea4dfc3a639398 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 22 Mar 2022 11:32:47 -0400 Subject: fixed temporal media merge that had reverted a lot of things. --- src/client/util/CurrentUserUtils.ts | 248 ++++++--------- src/client/util/DictationManager.ts | 353 +++++++++++---------- src/client/util/DocumentManager.ts | 8 +- src/client/util/DragManager.ts | 2 +- src/client/util/DropConverter.ts | 4 +- src/client/util/History.ts | 6 +- .../util/Import & Export/DirectoryImportBox.tsx | 7 +- src/client/util/LinkManager.ts | 2 +- src/client/util/ScriptManager.ts | 12 +- src/client/util/Scripting.ts | 109 +------ src/client/util/ScriptingGlobals.ts | 81 +++++ src/client/util/UndoManager.ts | 3 +- 12 files changed, 384 insertions(+), 451 deletions(-) create mode 100644 src/client/util/ScriptingGlobals.ts (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a8b0da369..c0e3f66eb 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -7,7 +7,6 @@ import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; -import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; @@ -16,9 +15,7 @@ import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; -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"; @@ -32,7 +29,7 @@ import { DragManager } from "./DragManager"; import { makeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; import { LinkManager } from "./LinkManager"; -import { Scripting } from "./Scripting"; +import { ScriptingGlobals } from "./ScriptingGlobals"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { ColorScheme } from "./SettingsManager"; @@ -51,7 +48,6 @@ interface Button { numBtnMax?: number; switchToggle?: boolean; script?: string; - checkResult?: string; width?: number; list?: string[]; ignoreClick?: boolean; @@ -162,95 +158,41 @@ export class CurrentUserUtils { }); } - if (doc["template-button-switch"] === undefined) { - const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; - - const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); - const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); - const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); - const labelTemplate = { - doc: { - type: "doc", content: [{ - type: "paragraph", - content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] - }] - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); - Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); - // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); - // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); - Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); - // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); - const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); - box.isTemplateDoc = makeTemplate(box, true, "switch"); - - doc["template-button-switch"] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, - btnType: ButtonType.ToolButton - }); - } - - if (doc["template-button-detail"] === undefined) { - const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; - - const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)"); - const carousel = CarouselDocument([], { - title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10, - onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true - }); - - const details = TextDocument("", { title: "details", _height: 200, _autoHeight: true, system: true }); - const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 75, _autoHeight: true, system: true }); - const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 150, _autoHeight: true, system: true }); - - const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"]; - const detailedTemplate = { - doc: { - type: "doc", content: buxtonFieldKeys.map(fieldKey => ({ - type: "paragraph", - content: [{ type: "dashField", attrs: { fieldKey } }] - })) - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" ")); - - const shared = { _autoHeight: true, _xMargin: 0 }; - const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" }; - const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true }; - - const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); - descriptionWrapper._columnHeaders = new List([ - 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); - - details.title = "Details"; - short.title = "A Short Description"; - long.title = "Long Description"; - - doc["template-button-detail"] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(detailView) as any as Doc, - title: "detailView", - icon: "window-maximize", - system: true, - btnType: ButtonType.ToolButton, - }); - } + // if (doc["template-button-switch"] === undefined) { + // const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; + + // const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); + // const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); + // const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); + // const labelTemplate = { + // doc: { + // type: "doc", content: [{ + // type: "paragraph", + // content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] + // }] + // }, + // selection: { type: "text", anchor: 1, head: 1 }, + // storedMarks: [] + // }; + // Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); + // Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); + // // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); + // // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); + // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); + // // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); + // const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); + // box.isTemplateDoc = makeTemplate(box, true, "switch"); + + // doc["template-button-switch"] = CurrentUserUtils.createToolButton({ + // onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + // dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, + // btnType: ButtonType.ToolButton + // }); + // } const requiredTypes = [ doc["template-button-slides"] as Doc, doc["template-mobile-button"] as Doc, - doc["template-button-detail"] as Doc, doc["template-button-link"] as Doc, //doc["template-button-switch"] as Doc] ]; @@ -538,11 +480,11 @@ export class CurrentUserUtils { // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools static async setupCreatorButtons(doc: Doc) { let alreadyCreatedButtons: string[] = []; - const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null); + const dragCreatorSet = Cast(doc.myItemCreators, Doc, null); if (dragCreatorSet) { - const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc)); + const dragCreators = Cast(dragCreatorSet.data, listSpec(Doc)); if (dragCreators) { - const dragDocs = await Promise.all(dragCreators); + const dragDocs = await Promise.all(Array.from(dragCreators)); alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title)); } } @@ -735,12 +677,16 @@ export class CurrentUserUtils { ]; return docProtoData.map(data => Docs.Create.FontIconDocument({ _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon, - _dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick, + _dropAction: data.pointerDown ? "copy" : undefined, + ignoreClick: data.ignoreClick, onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, clipboard: data.clipboard, - onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, + onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, + onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, backgroundColor: data.backgroundColor, - _removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, system: true + _removeDropProperties: new List(["dropAction"]), + dragFactory: data.dragFactory, + system: true })); } @@ -987,37 +933,37 @@ export class CurrentUserUtils { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], - script: 'setFont' + script: 'setFont(value, _readOnly_)' }, - { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize' }, - { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor' }, - { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', checkResult: 'toggleBold(true)' }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', checkResult: 'toggleItalic(true)' }, - { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', checkResult: 'toggleUnderline(true)' }, - { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet")', checkResult: 'setBulletList("bullet", true)' }, - { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal")', checkResult: 'setBulletList("decimal", true)' }, + { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' }, + { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' }, + { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' }, + { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' }, + { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' }, + { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' }, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'}, // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'}, // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'}, - { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left")', checkResult: 'setAlignment("left", true)' }, - { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center")', checkResult: 'setAlignment("center", true)' }, - { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right")', checkResult: 'setAlignment("right", true)' }, + { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' }, + { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' }, + { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' }, ]; return tools; } static inkTools(doc: Doc) { const tools: Button[] = [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', checkResult: 'setActiveInkTool("pen" , true)' }, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser")', checkResult: 'setActiveInkTool("eraser" , true)' }, - // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', checkResult: 'setActiveInkTool("highlighter", true)' }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' }, - // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', checkResult: 'setActiveInkTool("line" , true)' }, - { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor" }, - { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth' }, - { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor' }, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' }, + // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' }, + // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' }, + { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" }, + { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' }, + { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' }, ]; return tools; } @@ -1031,8 +977,7 @@ export class CurrentUserUtils { btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", - click: 'toggleSchemaPreview()', - checkResult: 'toggleSchemaPreview(true)' + click: 'toggleSchemaPreview(_readOnly_)', }, ]; return tools; @@ -1041,10 +986,10 @@ export class CurrentUserUtils { static webTools(doc: Doc) { const tools: Button[] = [ - { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack()' }, - { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward()' }, + { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' }, + { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' }, //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' }, - { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL' }, + { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' }, ]; return tools; @@ -1059,17 +1004,17 @@ export class CurrentUserUtils { CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid], - script: 'setView', + script: 'setView(value, _readOnly_)', }, // Always show { title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", - script: "setBackgroundColor", hidden: 'selectedDocumentType()' + script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected { title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", - script: "setHeaderColor", hidden: 'selectedDocumentType()', + script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', }, // Only when a document is selected - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available @@ -1083,7 +1028,7 @@ export class CurrentUserUtils { if (doc.contextMenuBtns === undefined) { const docList: Doc[] = []; - (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded, checkResult }) => { + (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => { const menuDocList: Doc[] = []; if (subMenu) { // default is textTools @@ -1105,7 +1050,8 @@ export class CurrentUserUtils { tools = CurrentUserUtils.textTools(doc); break; } - tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle, checkResult }) => { + tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => { + const computed = click ? ComputedField.MakeFunction(click) as any : "transparent"; menuDocList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 25, _nativeHeight: 25, @@ -1116,7 +1062,7 @@ export class CurrentUserUtils { numBtnType, numBtnMin, numBtnMax, - script, + script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined, btnType: btnType, btnList: new List(list), ignoreClick: ignoreClick, @@ -1128,10 +1074,10 @@ export class CurrentUserUtils { title, switchToggle, color: Colors.WHITE, - backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent", + backgroundColor: computed, _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined + onClick: click ? ScriptField.MakeScript(click) : undefined })); }); docList.push(CurrentUserUtils.linearButtonList({ @@ -1141,7 +1087,7 @@ export class CurrentUserUtils { linearViewExpandable: true, icon: title, _height: 30, - backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent", + // backgroundColor: hidden ? ComputedField.MakeFunction(hidden, { }, { _readOnly_: true }) as any : "transparent", linearViewIsExpanded: expanded ? !(ComputedField.MakeFunction(expanded) as any) : undefined, hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, }, menuDocList)); @@ -1153,7 +1099,7 @@ export class CurrentUserUtils { _height: 25, icon, toolTip, - script, + script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined, btnType, btnList: new List(list), ignoreClick, @@ -1164,11 +1110,11 @@ export class CurrentUserUtils { dontUndo: true, title, color: Colors.WHITE, - backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent", + // backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult, {}, {_readOnly_:true}) as any : "transparent", _dropAction: "alias", hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined + onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }, { _readOnly_: false }) : undefined })); } }); @@ -1383,7 +1329,7 @@ export class CurrentUserUtils { if (response) { const result: { id: string, email: string, cacheDocumentIds: string } = JSON.parse(response); Doc.CurrentUserEmail = result.email; - resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts")); + resolvedPorts = JSON.parse(await (await fetch("/resolvedPorts")).text()); DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email); result.cacheDocumentIds && (await DocServer.GetRefFields(result.cacheDocumentIds.split(";"))); return result; @@ -1566,7 +1512,7 @@ export class CurrentUserUtils { @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } } -Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { +ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { const copy = Doc.copyDragFactory(dragFactory); if (copy) { CollectionDockingView.AddSplit(copy, "right"); @@ -1574,27 +1520,27 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { view && SelectionManager.SelectView(view, false); } }); -Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); }, +ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, "document containing all shared Docs"); -Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, +ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode"); -Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, +ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard"); -Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, +ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called"); -Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, +ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); -Scripting.addGlobal(function createNewFolder() { return MainView.Instance.createNewFolder(); }, +ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); -Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, +ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); -Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, +ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); -Scripting.addGlobal(function shareDashboard(dashboard: Doc) { +ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); -Scripting.addGlobal(async function removeDashboard(dashboard: Doc) { +ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); if (dashboards && dashboards.length > 1) { if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!); @@ -1602,7 +1548,7 @@ Scripting.addGlobal(async function removeDashboard(dashboard: Doc) { } }, "Remove Dashboard from Dashboards"); -Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { +ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]); @@ -1626,7 +1572,7 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { /** * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard. */ -Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { +ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) { if (dashboard[DataSym] instanceof Doc) { const allDocs = DocListCast(dashboard["data-all"]); const onScreenTab = DocListCast(dashboard.data)[0]; @@ -1638,7 +1584,7 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { } return []; }); -Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) { +ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) { let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined; if (selected && checkParent) { const parentDoc: Doc = Cast(selected.context, Doc, null); @@ -1649,11 +1595,11 @@ Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colTyp else if (selected && !colType && !docType) return false; else return true; }); -Scripting.addGlobal(function makeTopLevelFolder() { +ScriptingGlobals.add(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() { +ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); \ No newline at end of file diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index a93b2f573..a6dcda4bc 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -1,4 +1,6 @@ import * as interpreter from "words-to-numbers"; +// @ts-ignore bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module? +import type { } from "@types/dom-speech-recognition"; import { Doc, Opt } from "../../fields/Doc"; import { List } from "../../fields/List"; import { RichTextField } from "../../fields/RichTextField"; @@ -13,6 +15,7 @@ import { DocumentView } from "../views/nodes/DocumentView"; import { SelectionManager } from "./SelectionManager"; import { UndoManager } from "./UndoManager"; + /** * This namespace provides a singleton instance of a manager that * handles the listening and text-conversion of user speech. @@ -102,17 +105,17 @@ export namespace DictationManager { try { results = await (pendingListen = listenImpl(options)); pendingListen = undefined; - if (results) { - Utils.CopyText(results); - if (overlay) { - DictationOverlay.Instance.isListening = false; - const execute = options?.tryExecute; - DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results; - DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; - } - options?.tryExecute && await DictationManager.Commands.execute(results); - } - } catch (e) { + // if (results) { + // Utils.CopyText(results); + // if (overlay) { + // DictationOverlay.Instance.isListening = false; + // const execute = options?.tryExecute; + // DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results; + // DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; + // } + // options?.tryExecute && await DictationManager.Commands.execute(results); + // } + } catch (e: any) { console.log(e); if (overlay) { DictationOverlay.Instance.isListening = false; @@ -188,7 +191,7 @@ export namespace DictationManager { current && sessionResults.push(current); sessionResults.length && resolve(sessionResults.join(inter || interSession)); } else { - resolve(current); + resolve(current || ""); } current = undefined; sessionResults = []; @@ -222,168 +225,168 @@ export namespace DictationManager { } - export namespace Commands { - - export const dictationFadeDuration = 2000; - - export type IndependentAction = (target: DocumentView) => any | Promise; - export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] }; - - export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise; - export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] }; - - export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value); - export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry); - - export const execute = async (phrase: string) => { - return UndoManager.RunInBatch(async () => { - const targets = SelectionManager.Views(); - if (!targets || !targets.length) { - return; - } - - phrase = phrase.toLowerCase(); - const entry = Independent.get(phrase); - - if (entry) { - let success = false; - const restrictTo = entry.restrictTo; - for (const target of targets) { - if (!restrictTo || validate(target, restrictTo)) { - await entry.action(target); - success = true; - } - } - return success; - } - - for (const entry of Dependent) { - const regex = entry.expression; - const matches = regex.exec(phrase); - regex.lastIndex = 0; - if (matches !== null) { - let success = false; - const restrictTo = entry.restrictTo; - for (const target of targets) { - if (!restrictTo || validate(target, restrictTo)) { - await entry.action(target, matches); - success = true; - } - } - return success; - } - } - - return false; - }, "Execute Command"); - }; - - const ConstructorMap = new Map([ - [DocumentType.COL, listSpec(Doc)], - [DocumentType.AUDIO, AudioField], - [DocumentType.IMG, ImageField], - [DocumentType.IMPORT, listSpec(Doc)], - [DocumentType.RTF, "string"] - ]); - - const tryCast = (view: DocumentView, type: DocumentType) => { - const ctor = ConstructorMap.get(type); - if (!ctor) { - return false; - } - return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined; - }; - - const validate = (target: DocumentView, types: DocumentType[]) => { - for (const type of types) { - if (tryCast(target, type)) { - return true; - } - } - return false; - }; - - const interpretNumber = (number: string) => { - const initial = parseInt(number); - if (!isNaN(initial)) { - return initial; - } - const converted = interpreter.wordsToNumbers(number, { fuzzy: true }); - if (converted === null) { - return NaN; - } - return typeof converted === "string" ? parseInt(converted) : converted; - }; - - const Independent = new Map([ - - ["clear", { - action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(), - restrictTo: [DocumentType.COL] - }], - - ["open fields", { - action: (target: DocumentView) => { - const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); - target.props.addDocTab(kvp, "add:right"); - } - }], - - ["new outline", { - action: (target: DocumentView) => { - const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true }); - const proto = newBox.proto!; - const prompt = "Press alt + r to start dictating here..."; - const head = 3; - const anchor = head + prompt.length; - const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; - proto.data = new RichTextField(proseMirrorState); - proto.backgroundColor = "#eeffff"; - target.props.addDocTab(newBox, "add:right"); - } - }] - - ]); - - const Dependent = new Array( - - { - expression: /create (\w+) documents of type (image|nested collection)/g, - action: (target: DocumentView, matches: RegExpExecArray) => { - const count = interpretNumber(matches[1]); - const what = matches[2]; - const dataDoc = Doc.GetProto(target.props.Document); - const fieldKey = "data"; - if (isNaN(count)) { - return; - } - for (let i = 0; i < count; i++) { - let created: Doc | undefined; - switch (what) { - case "image": - created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"); - break; - case "nested collection": - created = Docs.Create.FreeformDocument([], {}); - break; - } - created && Doc.AddDocToList(dataDoc, fieldKey, created); - } - }, - restrictTo: [DocumentType.COL] - }, - - { - expression: /view as (freeform|stacking|masonry|schema|tree)/g, - action: (target: DocumentView, matches: RegExpExecArray) => { - const mode = matches[1]; - mode && (target.props.Document._viewType = mode); - }, - restrictTo: [DocumentType.COL] - } - - ); - - } + // export namespace Commands { + + // export const dictationFadeDuration = 2000; + + // export type IndependentAction = (target: DocumentView) => any | Promise; + // export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] }; + + // export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise; + // export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] }; + + // export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value); + // export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry); + + // export const execute = async (phrase: string) => { + // return UndoManager.RunInBatch(async () => { + // const targets = SelectionManager.Views(); + // if (!targets || !targets.length) { + // return; + // } + + // phrase = phrase.toLowerCase(); + // const entry = Independent.get(phrase); + + // if (entry) { + // let success = false; + // const restrictTo = entry.restrictTo; + // for (const target of targets) { + // if (!restrictTo || validate(target, restrictTo)) { + // await entry.action(target); + // success = true; + // } + // } + // return success; + // } + + // for (const entry of Dependent) { + // const regex = entry.expression; + // const matches = regex.exec(phrase); + // regex.lastIndex = 0; + // if (matches !== null) { + // let success = false; + // const restrictTo = entry.restrictTo; + // for (const target of targets) { + // if (!restrictTo || validate(target, restrictTo)) { + // await entry.action(target, matches); + // success = true; + // } + // } + // return success; + // } + // } + + // return false; + // }, "Execute Command"); + // }; + + // const ConstructorMap = new Map([ + // [DocumentType.COL, listSpec(Doc)], + // [DocumentType.AUDIO, AudioField], + // [DocumentType.IMG, ImageField], + // [DocumentType.IMPORT, listSpec(Doc)], + // [DocumentType.RTF, "string"] + // ]); + + // const tryCast = (view: DocumentView, type: DocumentType) => { + // const ctor = ConstructorMap.get(type); + // if (!ctor) { + // return false; + // } + // return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined; + // }; + + // const validate = (target: DocumentView, types: DocumentType[]) => { + // for (const type of types) { + // if (tryCast(target, type)) { + // return true; + // } + // } + // return false; + // }; + + // const interpretNumber = (number: string) => { + // const initial = parseInt(number); + // if (!isNaN(initial)) { + // return initial; + // } + // const converted = interpreter.wordsToNumbers(number, { fuzzy: true }); + // if (converted === null) { + // return NaN; + // } + // return typeof converted === "string" ? parseInt(converted) : converted; + // }; + + // const Independent = new Map([ + + // ["clear", { + // action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(), + // restrictTo: [DocumentType.COL] + // }], + + // ["open fields", { + // action: (target: DocumentView) => { + // const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); + // target.props.addDocTab(kvp, "add:right"); + // } + // }], + + // ["new outline", { + // action: (target: DocumentView) => { + // const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true }); + // const proto = newBox.proto!; + // const prompt = "Press alt + r to start dictating here..."; + // const head = 3; + // const anchor = head + prompt.length; + // const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; + // proto.data = new RichTextField(proseMirrorState); + // proto.backgroundColor = "#eeffff"; + // target.props.addDocTab(newBox, "add:right"); + // } + // }] + + // ]); + + // const Dependent = new Array( + + // { + // expression: /create (\w+) documents of type (image|nested collection)/g, + // action: (target: DocumentView, matches: RegExpExecArray) => { + // const count = interpretNumber(matches[1]); + // const what = matches[2]; + // const dataDoc = Doc.GetProto(target.props.Document); + // const fieldKey = "data"; + // if (isNaN(count)) { + // return; + // } + // for (let i = 0; i < count; i++) { + // let created: Doc | undefined; + // switch (what) { + // case "image": + // created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"); + // break; + // case "nested collection": + // created = Docs.Create.FreeformDocument([], {}); + // break; + // } + // created && Doc.AddDocToList(dataDoc, fieldKey, created); + // } + // }, + // restrictTo: [DocumentType.COL] + // }, + + // { + // expression: /view as (freeform|stacking|masonry|schema|tree)/g, + // action: (target: DocumentView, matches: RegExpExecArray) => { + // const mode = matches[1]; + // mode && (target.props.Document._viewType = mode); + // }, + // restrictTo: [DocumentType.COL] + // } + + // ); + + // } } \ No newline at end of file diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 66b6a1e44..0a00ab6e0 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,7 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; -import { Scripting } from './Scripting'; +import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; export class DocumentManager { @@ -198,7 +198,7 @@ export class DocumentManager { originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) => new Promise(res => { focusAndFinish(didFocus); - res(); + res(ViewAdjustment.doNothing); }) }); } else { @@ -227,7 +227,7 @@ export class DocumentManager { willZoom, afterFocus: (didFocus: boolean) => new Promise(res => { !noSelect && focusAndFinish(didFocus); - res(); + res(ViewAdjustment.doNothing); }) }); // focus on the target in the context } else if (delay > 1500) { @@ -252,7 +252,7 @@ export class DocumentManager { } } -Scripting.addGlobal(function DocFocusOrOpen(doc: any) { +ScriptingGlobals.add(function DocFocusOrOpen(doc: any) { const dv = DocumentManager.Instance.getDocumentView(doc); if (dv && dv.props.Document === doc) { dv.props.focus(doc, { willZoom: true }); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ae3fa3170..c9c499fff 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -499,7 +499,7 @@ export namespace DragManager { screenX: e.screenX, screenY: e.screenY, detail: e.detail, - view: e.view ? e.view : new Window, + view: e.view ? e.view : new Window as any, nativeEvent: new DragEvent("dashDragAutoScroll"), currentTarget: target, target: target, diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 32817eefd..082b6d8bd 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -7,7 +7,7 @@ import { Docs } from "../documents/Documents"; import { ScriptField, ComputedField } from "../../fields/ScriptField"; import { RichTextField } from "../../fields/RichTextField"; import { ImageField } from "../../fields/URLField"; -import { Scripting } from "./Scripting"; +import { ScriptingGlobals } from "./ScriptingGlobals"; import { listSpec } from "../../fields/Schema"; // @@ -81,5 +81,5 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data.droppedDocuments[i] = dbox; }); } -Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }, +ScriptingGlobals.add(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }, "converts the dropped data to buttons", "(dragData: any)"); \ No newline at end of file diff --git a/src/client/util/History.ts b/src/client/util/History.ts index cbe36b401..e6f75a7db 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -103,8 +103,7 @@ export namespace HistoryUtil { return undefined; } const parser = requiredFields[required]; - let value = opts[required]; - value = parse(parser, value); + const value = parse(parser, opts[required]); if (value !== null && value !== undefined) { current[required] = value; } @@ -114,8 +113,7 @@ export namespace HistoryUtil { continue; } const parser = optionalFields[opt]; - let value = opts[opt]; - value = parse(parser, value); + const value = parse(parser, opts[opt]); if (value !== undefined) { current[opt] = value; } diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 925b74efa..39e9251a5 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -1,9 +1,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { BatchedArray } from "array-batcher"; -import "fs"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import * as path from 'path'; +import { extname } from "path"; import Measure, { ContentRect } from "react-measure"; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; @@ -86,7 +85,7 @@ export class DirectoryImportBox extends React.Component { for (let i = 0; i < files.length; i++) { const file = files.item(i); if (file && !unsupported.includes(file.type)) { - const ext = path.extname(file.name).toLowerCase(); + const ext = extname(file.name).toLowerCase(); if (AcceptableMedia.imageFormats.includes(ext)) { validated.push(file); } @@ -120,7 +119,7 @@ export class DirectoryImportBox extends React.Component { } const { accessPaths, exifData } = result; const path = Utils.prepend(accessPaths.agnostic.client); - const document = await DocUtils.DocumentFromType(type, path, { _width: 300, title: name }); + const document = type && await DocUtils.DocumentFromType(type, path, { _width: 300 }); const { data, error } = exifData; if (document) { Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data }); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 62b13e2c6..df2c02a8d 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -194,7 +194,7 @@ export class LinkManager { afterFocus: (didFocus: boolean) => { finished?.(); res(ViewAdjustment.resetView); - return new Promise(res2 => res2()); + return new Promise(res2 => res2(ViewAdjustment.doNothing)); } }); } else { diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts index 94806a7ba..42a6493ea 100644 --- a/src/client/util/ScriptManager.ts +++ b/src/client/util/ScriptManager.ts @@ -1,9 +1,9 @@ import { Doc, DocListCast } from "../../fields/Doc"; import { List } from "../../fields/List"; -import { Scripting } from "./Scripting"; -import { StrCast, Cast } from "../../fields/Types"; import { listSpec } from "../../fields/Schema"; +import { Cast, StrCast } from "../../fields/Types"; import { Docs } from "../documents/Documents"; +import { ScriptingGlobals } from "./ScriptingGlobals"; export class ScriptManager { @@ -44,7 +44,7 @@ export class ScriptManager { public deleteScript(scriptDoc: Doc): boolean { if (scriptDoc.name) { - Scripting.removeGlobal(StrCast(scriptDoc.name)); + ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name)); } const scriptList = this.getAllScripts(); const index = scriptList.indexOf(scriptDoc); @@ -60,7 +60,7 @@ export class ScriptManager { public static addScriptToGlobals(scriptDoc: Doc): void { - Scripting.removeGlobal(StrCast(scriptDoc.name)); + ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name)); const params = Cast(scriptDoc["data-params"], listSpec("string"), []); const paramNames = params.reduce((o: string, p: string) => { @@ -86,9 +86,9 @@ export class ScriptManager { }); if (parameters === "(") { - Scripting.addGlobal(f, StrCast(scriptDoc.description)); + ScriptingGlobals.add(f, StrCast(scriptDoc.description)); } else { - Scripting.addGlobal(f, StrCast(scriptDoc.description), parameters); + ScriptingGlobals.add(f, StrCast(scriptDoc.description), parameters); } } } \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 40b94024e..3b0a47b54 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,16 +1,15 @@ -import * as ts from "typescript"; -export { ts }; - // export const ts = (window as any).ts; - // // @ts-ignore // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // // @ts-ignore // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' - // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; -import { Doc, Field } from '../../fields/Doc'; +import * as ts from "typescript"; +import { Doc, Field } from "../../fields/Doc"; +import { scriptingGlobals, ScriptingGlobals } from "./ScriptingGlobals"; +export { ts }; + export interface ScriptSuccess { success: true; @@ -47,98 +46,6 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is return false; } -export namespace Scripting { - export function addGlobal(global: { name: string }): void; - export function addGlobal(name: string, global: any): void; - - export function addGlobal(global: { name: string }, decription?: string, params?: string): void; - - export function addGlobal(first: any, second?: any, third?: string) { - let n: any; - let obj: any; - - if (second !== undefined) { - if (typeof first === "string") { - n = first; - obj = second; - } else { - obj = first; - n = first.name; - _scriptingDescriptions[n] = second; - if (third !== undefined) { - _scriptingParams[n] = third; - } - } - } else if (first && typeof first.name === "string") { - n = first.name; - obj = first; - } else { - throw new Error("Must either register an object with a name, or give a name and an object"); - } - if (n === undefined || n === "undefined") { - return false; - } else if (_scriptingGlobals.hasOwnProperty(n)) { - throw new Error(`Global with name ${n} is already registered, choose another name`); - } - _scriptingGlobals[n] = obj; - } - - export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) { - return { ..._scriptingGlobals, ...(globals || {}) }; - } - - export function setScriptingGlobals(globals: { [key: string]: any }) { - scriptingGlobals = globals; - } - - export function removeGlobal(name: string) { - if (getGlobals().includes(name)) { - delete _scriptingGlobals[name]; - if (_scriptingDescriptions[name]) { - delete _scriptingDescriptions[name]; - } - if (_scriptingParams[name]) { - delete _scriptingParams[name]; - } - return true; - } - return false; - } - - export function resetScriptingGlobals() { - scriptingGlobals = _scriptingGlobals; - } - - // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]); - export function printNodeType(node: any, indentation = "") { - console.log(indentation + ts.SyntaxKind[node.kind]); - } - - export function getGlobals() { - return Object.keys(_scriptingGlobals); - } - - export function getGlobalObj() { - return _scriptingGlobals; - } - - export function getDescriptions() { - return _scriptingDescriptions; - } - - export function getParameters() { - return _scriptingParams; - } -} - -export function scriptingGlobal(constructor: { new(...args: any[]): any }) { - Scripting.addGlobal(constructor); -} - -export const _scriptingGlobals: { [name: string]: any } = {}; -let scriptingGlobals: { [name: string]: any } = _scriptingGlobals; -const _scriptingDescriptions: { [name: string]: any } = {}; -const _scriptingParams: { [name: string]: any } = {}; function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error); @@ -273,7 +180,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp if (options.params && !options.params.this) options.params.this = Doc.name; if (options.params && !options.params.self) options.params.self = Doc.name; if (options.globals) { - Scripting.setScriptingGlobals(options.globals); + ScriptingGlobals.setScriptingGlobals(options.globals); } const host = new ScriptingCompilerHost; if (options.traverser) { @@ -331,9 +238,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp const result = Run(outputText, paramNames, diagnostics, script, options); if (options.globals) { - Scripting.resetScriptingGlobals(); + ScriptingGlobals.resetScriptingGlobals(); } return result; } -Scripting.addGlobal(CompileScript); \ No newline at end of file +ScriptingGlobals.add(CompileScript); diff --git a/src/client/util/ScriptingGlobals.ts b/src/client/util/ScriptingGlobals.ts new file mode 100644 index 000000000..f151acd81 --- /dev/null +++ b/src/client/util/ScriptingGlobals.ts @@ -0,0 +1,81 @@ + +import * as ts from "typescript"; +export { ts }; + +export namespace ScriptingGlobals { + export function add(global: { name: string }): void; + export function add(name: string, global: any): void; + export function add(global: { name: string }, decription?: string, params?: string): void; + export function add(first: any, second?: any, third?: string) { + let n: any; + let obj: any; + + if (second !== undefined) { + if (typeof first === "string") { + n = first; + obj = second; + } else { + obj = first; + n = first.name; + _scriptingDescriptions[n] = second; + if (third !== undefined) { + _scriptingParams[n] = third; + } + } + } else if (first && typeof first.name === "string") { + n = first.name; + obj = first; + } else { + throw new Error("Must either register an object with a name, or give a name and an object"); + } + if (n === undefined || n === "undefined") { + return false; + } else if (_scriptingGlobals.hasOwnProperty(n)) { + throw new Error(`Global with name ${n} is already registered, choose another name`); + } + _scriptingGlobals[n] = obj; + } + export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) { + return { ..._scriptingGlobals, ...(globals || {}) }; + } + + export function setScriptingGlobals(globals: { [key: string]: any }) { + scriptingGlobals = globals; + } + + export function removeGlobal(name: string) { + if (getGlobals().includes(name)) { + delete _scriptingGlobals[name]; + if (_scriptingDescriptions[name]) { + delete _scriptingDescriptions[name]; + } + if (_scriptingParams[name]) { + delete _scriptingParams[name]; + } + return true; + } + return false; + } + + export function resetScriptingGlobals() { scriptingGlobals = _scriptingGlobals; } + + // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]); + export function printNodeType(node: any, indentation = "") { console.log(indentation + ts.SyntaxKind[node.kind]); } + + export function getGlobals() { return Object.keys(_scriptingGlobals); } + + export function getGlobalObj() { return _scriptingGlobals; } + + export function getDescriptions() { return _scriptingDescriptions; } + + export function getParameters() { return _scriptingParams; } +} + +export function scriptingGlobal(constructor: { new(...args: any[]): any }) { + ScriptingGlobals.add(constructor); +} + +const _scriptingGlobals: { [name: string]: any } = {}; +export let scriptingGlobals: { [name: string]: any } = _scriptingGlobals; +const _scriptingDescriptions: { [name: string]: any } = {}; +const _scriptingParams: { [name: string]: any } = {}; \ No newline at end of file diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index 44e44d335..d1f1a0099 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -1,5 +1,4 @@ import { observable, action, runInAction } from "mobx"; -import 'source-map-support/register'; import { Without } from "../../Utils"; function getBatchName(target: any, key: string | symbol): string { @@ -107,7 +106,7 @@ export namespace UndoManager { } export function FilterBatches(fieldTypes: string[]) { const fieldCounts: { [key: string]: number } = {}; - const lastStack = UndoManager.undoStack.lastElement(); + const lastStack = UndoManager.undoStack.slice(-1)[0];//.lastElement(); if (lastStack) { lastStack.forEach(ev => fieldTypes.includes(ev.prop) && (fieldCounts[ev.prop] = (fieldCounts[ev.prop] || 0) + 1)); const fieldCount2: { [key: string]: number } = {}; -- cgit v1.2.3-70-g09d2 From f61f6be6771415391d9e6733e4cdb1cde32e626d Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Tue, 22 Mar 2022 17:36:35 -0400 Subject: Stop drag in present mode if element isn't specifically selected to drag. --- src/client/util/DragManager.ts | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c9c499fff..8d4b98d88 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -214,6 +214,10 @@ export namespace DragManager { options?: DragOptions, dropEvent?: () => any ) { + // stop an 'accidental' on-click drag that may have occured if the user is in presenting mode + // note: dragData.dropAction is only undefined when the element itself being dragged without being selected + if (Doc.UserDoc()?.isPresenting && dragData.dropAction === undefined) return false; + const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField); dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); -- cgit v1.2.3-70-g09d2 From 6e61005b3b6dd8deeb3ca43a8c6241d7778a1998 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 24 Mar 2022 09:58:01 -0400 Subject: fixed videoBox play issue. cleaned up global referencing a bit. --- src/client/util/CurrentUserUtils.ts | 1 + src/client/views/MainView.tsx | 2 +- .../collectionLinear/CollectionLinearView.tsx | 54 +++++++++++----------- .../CollectionMulticolumnView.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 6 +-- src/client/views/nodes/VideoBox.tsx | 6 +-- src/client/views/nodes/WebBox.tsx | 2 +- 7 files changed, 37 insertions(+), 36 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c0e3f66eb..22ab4beb8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1501,6 +1501,7 @@ export class CurrentUserUtils { return tbox; } + public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); } public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); } public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8c0795881..7cb40f701 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -184,7 +184,7 @@ export class MainView extends React.Component { fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, - fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]); + fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]); this.initAuthenticationRouters(); } diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 160134b60..1d142d595 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -8,6 +8,7 @@ import { Id } from '../../../../fields/FieldSymbols'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils'; import { DocUtils } from '../../../documents/Documents'; +import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; @@ -206,36 +207,35 @@ export class CollectionLinearView extends CollectionSubView() { }}> {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))} - {DocumentLinksButton.StartLink && StrCast(this.layoutDoc.title) === "docked buttons" ? e.stopPropagation()} > - - Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'} - - -
{"Toggle description pop-up"}
} placement="top"> - - Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"} + {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? null : + e.stopPropagation()} > + + Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'} -
-
Exit linking mode
} placement="top"> - - Stop +
{"Toggle description pop-up"}
} placement="top"> + + Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"} + +
+ +
Exit linking mode
} placement="top"> + + Stop + +
+
} + {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) : + + + Currently playing: + {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => + DocumentManager.Instance.jumpToDocument(clip, true)}> + {clip.title + (i == CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")} + )} -
- -
: null} - {CollectionStackedTimeline.CurrentlyPlaying && CollectionStackedTimeline.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? - - Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => - { - DocumentManager.Instance.jumpToDocument(clip, true); - }}>{clip.title + (i == CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")} - )} - - : null} + } ; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 2bdf92417..6929a1cd8 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -214,7 +214,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { } return this.props.addDocTab(doc, where); } - getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { + getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return { if (CollectionStackedTimeline.CurrentlyPlaying) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc); + const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc); index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); } } @@ -212,8 +212,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent { if (CollectionStackedTimeline.CurrentlyPlaying) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc); + const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc); index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); } } @@ -488,8 +488,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then( + VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime()).then( returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500)); }) .catch(function (error: any) { -- cgit v1.2.3-70-g09d2 From 2c3264bca271af170b620cfc44ecdfafb77aacc0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 28 Mar 2022 12:20:07 -0400 Subject: enabled dragging documents onto tab bar without shift. modified link pivot document to automatically update when links are added and be a stacking view --- src/client/util/DragManager.ts | 4 +-- src/client/views/nodes/DocumentLinksButton.tsx | 35 ++++++++------------------ src/client/views/nodes/DocumentView.tsx | 26 ++++++++++++++++++- src/client/views/nodes/WebBox.scss | 2 +- 4 files changed, 39 insertions(+), 28 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c9c499fff..0d6a77f71 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -458,9 +458,9 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction; } - if (e?.shiftKey && dragData.draggedDocuments.length === 1) { + if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { dragData.dropAction = dragData.userDropAction || "same"; - if (dragData.dropAction === "move") { + if (dragData.dropAction === "move" || dragData.dropAction === "same") { dragData.removeDocument?.(dragData.draggedDocuments[0]); } AbortDrag(); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 7e6ca4248..804e82856 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -2,9 +2,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DocListCastAsync, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; -import { Cast, StrCast } from "../../../fields/Types"; +import { ScriptField } from "../../../fields/ScriptField"; +import { StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; import { DocServer } from "../../DocServer"; @@ -20,7 +21,6 @@ import { DocumentView } from "./DocumentView"; import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; import { TaskCompletionBox } from "./TaskCompletedBox"; import React = require("react"); -import { Transform } from "../../util/Transform"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -72,31 +72,18 @@ export class DocumentLinksButton extends React.Component { setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { if (doubleTap) { - const rootDoc = this.props.View.rootDoc; - const docid = Doc.CurrentUserEmail + Doc.GetProto(rootDoc)[Id] + "-pivotish"; - DocServer.GetRefField(docid).then(async docx => { + const linkSource = this.props.View.rootDoc; + const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + "-pivotish"; + DocServer.GetRefField(docid).then(docx => { const rootAlias = () => { - const rootAlias = Doc.MakeAlias(rootDoc); + const rootAlias = Doc.MakeAlias(linkSource); rootAlias.x = rootAlias.y = 0; return rootAlias; }; - let wid = rootDoc[WidthSym](); - const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([rootAlias()], { title: this.props.View.Document.title + "-pivot", _width: 500, _height: 500, }, docid); - const docs = await DocListCastAsync(Doc.GetProto(target).data); - if (!target.pivotFocusish) (Doc.GetProto(target).pivotFocusish = target); - DocListCast(rootDoc.links).forEach(link => { - const other = LinkManager.getOppositeAnchor(link, rootDoc); - const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; - if (otherdoc && !docs?.some(d => Doc.AreProtosEqual(d, otherdoc))) { - const alias = Doc.MakeAlias(otherdoc); - alias.x = wid; - alias.y = 0; - alias._lockedPosition = false; - wid += otherdoc[WidthSym](); - Doc.AddDocToList(Doc.GetProto(target), "data", alias); - } - }); - LightboxView.SetLightboxDoc(target); + const linkCollection = ((docx instanceof Doc) && docx) || Docs.Create.StackingDocument([/*rootAlias()*/], { title: this.props.View.Document.title + "-pivot", _width: 500, _height: 500, }, docid); + linkCollection.linkSource = linkSource; + if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript("updateLinkCollection(self)"); + LightboxView.SetLightboxDoc(linkCollection); }); } else DocumentLinksButton.LinkEditorDocView = this.props.View; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e7de87ea5..955698190 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -2,7 +2,7 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc"; +import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, DocListCastAsync, Field, Opt, StrListCast, WidthSym } from "../../../fields/Doc"; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -1247,6 +1247,10 @@ export class DocumentView extends React.Component { return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling); } componentDidMount() { + this._disposers.reactionScript = reaction( + () => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result, + output => output + ); this._disposers.height = reaction( () => NumCast(this.layoutDoc._height), action(height => { @@ -1298,4 +1302,24 @@ export class DocumentView extends React.Component { ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) { if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout"); else dv.switchViews(true, detailLayoutKeySuffix); +}); + +ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) { + const linkSource = Cast(linkCollection.linkSource, Doc, null); + const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data); + let wid = linkSource[WidthSym](); + const links = DocListCast(linkSource.links); + links.forEach(link => { + const other = LinkManager.getOppositeAnchor(link, linkSource); + const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; + if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) { + const alias = Doc.MakeAlias(otherdoc); + alias.x = wid; + alias.y = 0; + alias._lockedPosition = false; + wid += otherdoc[WidthSym](); + Doc.AddDocToList(Doc.GetProto(linkCollection), "data", alias); + } + }); + return links; }); \ No newline at end of file diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index 9d8825ad1..ff38e37dd 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -10,7 +10,7 @@ width: 100%; height: 100%; cursor: ew-resize; - background: yellow; + background: darkgray; } .webBox-background { width: 100%; -- cgit v1.2.3-70-g09d2 From 85c24ec4dcc39551e8c7de83d3482ec15472c030 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 31 Mar 2022 12:01:37 -0400 Subject: added autoLinks to formattedTextBoxes. Set auto link target by titling documents with a prefix '@'. --- src/client/util/CurrentUserUtils.ts | 11 +++++ src/client/views/DocumentDecorations.tsx | 6 +++ src/client/views/nodes/LinkDocPreview.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 48 ++++++++++++++-------- .../formattedText/FormattedTextBoxComment.tsx | 10 +++-- src/client/views/nodes/formattedText/marks_rts.ts | 27 ++++++++++++ 6 files changed, 83 insertions(+), 21 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 22ab4beb8..e51abf63b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1141,6 +1141,17 @@ export class CurrentUserUtils { // Sharing sidebar is where shared documents are contained static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + if (doc.myPublishedDocs === undefined) { + let pubDocs = Docs.newAccount ? undefined : Cast((await doc.myPublishedDocs), Doc, null); + if (!pubDocs) { + pubDocs = new Doc(linkDatabaseId, true); + (pubDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail; + (pubDocs as Doc).author = Doc.CurrentUserEmail; + (pubDocs as Doc).data = new List([]); + (pubDocs as Doc)["acl-Public"] = SharingPermissions.Augment; + doc.myPublishedDocs = new PrefetchProxy(pubDocs); + } + } if (doc.myLinkDatabase === undefined) { let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); if (!linkDocs) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4c9f15fef..328b8ab88 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -86,7 +86,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P titleFieldKey === "title" && (d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-")); //@ts-ignore const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle); + if (titleFieldKey === "title" && StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) { + Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", SelectionManager.Docs().lastElement()); + } Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true); + if (titleFieldKey === "title" && this._accumulatedTitle.startsWith("@")) { + Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", SelectionManager.Docs().lastElement()); + } if (d.rootDoc.syncLayoutFieldWithTitle) { const title = titleField.toString(); diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 2e29c0656..46736aa4e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -83,7 +83,7 @@ export class LinkDocPreview extends React.Component { const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0]; anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { - this._linkDoc = DocListCast(anchor.links)[0]; + this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; this._linkSrc = anchor; const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); this._targetDoc = linkTarget?.type === DocumentType.MARKER && linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c12c4acf0..ab2d84893 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -63,6 +63,7 @@ import React = require("react"); import { SidebarAnnos } from '../../SidebarAnnos'; import { Colors } from '../../global/globalEnums'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { LinkManager } from '../../../util/LinkManager'; const translateGoogleApi = require("translate-google-api"); export interface FormattedTextBoxProps { @@ -346,7 +347,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } + autoLink = () => { + if (this._editorView) { + const newAutoLinks = new Set(); + const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship = "automatic"); + const f = this._editorView.state.selection.from; + const t = this._editorView.state.selection.to; + var tr = this._editorView.state.tr as any; + tr = tr.removeMark(0, tr.doc.content.size, this._editorView!.state.schema.marks.autoLinkAnchor); + DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks)); + tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); + this._editorView?.dispatch(tr); + oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink)).forEach(LinkManager.Instance.deleteLink); + } + } + updateTitle = () => { + const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship = "automatic"); if (!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] && (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) { @@ -357,26 +374,24 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } - // needs a better API for taking in a set of words with target documents instead of just one target - hyperlinkTerms = (terms: string[], target: Doc) => { - if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) { - const res1 = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); - let tr = this._editorView.state.tr; - const flattened1: TextSelection[] = []; - res1.map(r => r.map(h => flattened1.push(h))); + // creates links between terms in a document and documents which have a matching Id + hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set) => { + if (this._editorView && (this._editorView as any).docView) { + const flattened1 = this.findInNode(this._editorView, this._editorView.state.doc, StrCast(target.title).replace(/^@/, "")); + var alink: Doc | undefined; flattened1.forEach((flat, i) => { - const flattened: TextSelection[] = []; - const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); - res.map(r => r.map(h => flattened.push(h))); + const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, StrCast(target.title).replace(/^@/, "")); this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; - const anchor = Docs.Create.TextanchorDocument(); - const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!; - const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }]; - const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location }); + alink = alink ?? (DocListCast(this.Document.links).find(link => + Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && + Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, "automatic")!); + newAutoLinks.add(alink); + const allAnchors = [{ href: Doc.localServerPath(this.props.Document), title: "a link", anchorId: this.props.Document[Id] }]; + const link = this._editorView!.state.schema.marks.autoLinkAnchor.create({ allAnchors, linkDoc: alink[Id], title: "auto link", location }); tr = tr.addMark(flattened[i].from, flattened[i].to, link); }); - this._editorView.dispatch(tr); } + return tr; } highlightSearchTerms = (terms: string[], backward: boolean) => { if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) { @@ -1256,7 +1271,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); let target = (e.target as any).parentElement; // hrefs are stored on the database of the node that wraps the hyerlink while (target && !target.dataset?.targethrefs) target = target.parentElement; - FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs); + FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc); } (e.nativeEvent as any).formattedHandled = true; @@ -1399,6 +1414,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @action onBlur = (e: any) => { + this.autoLink(); FormattedTextBox.Focused === this && (FormattedTextBox.Focused = undefined); if (RichTextMenu.Instance?.view === this._editorView && !this.props.isSelected(true)) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined); diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 1fde6e5f0..3e673c0b2 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -2,6 +2,7 @@ import { Mark, ResolvedPos } from "prosemirror-model"; import { EditorState } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc } from "../../../../fields/Doc"; +import { DocServer } from "../../../DocServer"; import { LinkDocPreview } from "../LinkDocPreview"; import { FormattedTextBox } from "./FormattedTextBox"; import './FormattedTextBoxComment.scss'; @@ -9,7 +10,7 @@ import { schema } from "./schema_rts"; export function findOtherUserMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail); } export function findUserMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid); } -export function findLinkMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.type === schema.marks.linkAnchor); } +export function findLinkMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.type === schema.marks.autoLinkAnchor || m.type === schema.marks.linkAnchor); } export function findStartOfMark(rpos: ResolvedPos, view: EditorView, finder: (marks: Mark[]) => Mark | undefined) { let before = 0, nbef = rpos.nodeBefore; while (nbef && finder(nbef.marks)) { @@ -82,14 +83,14 @@ export class FormattedTextBoxComment { FormattedTextBoxComment.tooltip.style.display = ""; } - static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = "") { + static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = "", linkDoc: string = "") { FormattedTextBoxComment.textBox = textBox; if ((hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection))) { - FormattedTextBoxComment.setupPreview(view, textBox, hrefs?.trim().split(" ").filter(h => h)); + FormattedTextBoxComment.setupPreview(view, textBox, hrefs?.trim().split(" ").filter(h => h), linkDoc); } } - static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[]) { + static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string) { const state = view.state; // this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date if (state.selection.$from) { @@ -115,6 +116,7 @@ export class FormattedTextBoxComment { nbef && naft && LinkDocPreview.SetLinkInfo({ docProps: textBox.props, linkSrc: textBox.rootDoc, + linkDoc: linkDoc ? DocServer.GetCachedRefField(linkDoc) as Doc : undefined, location: ((pos) => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))), hrefs, showHeader: true diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 6103a28d6..52ef06b7f 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -17,6 +17,33 @@ export const marks: { [index: string]: MarkSpec } = { return ["div", { className: "dummy" }, 0]; } }, + + + // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each link has an href URL and a title for use in menus and hover (Dash links have linkIDs & targetIDs). `title` + // defaults to the empty string. Rendered and parsed as an `` + // element. + autoLinkAnchor: { + attrs: { + allAnchors: { default: [] as { href: string, title: string, anchorId: string }[] }, + linkDoc: { default: "" }, + location: { default: null }, + title: { default: null }, + }, + inclusive: false, + parseDOM: [{ + tag: "a[href]", getAttrs(dom: any) { + return { + location: dom.getAttribute("location"), + title: dom.getAttribute("title") + }; + } + }], + toDOM(node: any) { + const targethrefs = node.attrs.allAnchors.reduce((p: string, item: { href: string, title: string, anchorId: string }) => p ? p + " " + item.href : item.href, ""); + const anchorids = node.attrs.allAnchors.reduce((p: string, item: { href: string, title: string, anchorId: string }) => p ? p + " " + item.anchorId : item.anchorId, ""); + return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0]; + } + }, // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each link has an href URL and a title for use in menus and hover (Dash links have linkIDs & targetIDs). `title` // defaults to the empty string. Rendered and parsed as an `` // element. -- cgit v1.2.3-70-g09d2 From dea5f9c74b62706b17958d95b154005af271cf59 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 31 Mar 2022 12:57:21 -0400 Subject: fixed initialization of myPublishedDocs to be a simple list. --- src/client/util/CurrentUserUtils.ts | 10 +--------- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 +++--- 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e51abf63b..f2094407d 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1142,15 +1142,7 @@ export class CurrentUserUtils { // Sharing sidebar is where shared documents are contained static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.myPublishedDocs === undefined) { - let pubDocs = Docs.newAccount ? undefined : Cast((await doc.myPublishedDocs), Doc, null); - if (!pubDocs) { - pubDocs = new Doc(linkDatabaseId, true); - (pubDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail; - (pubDocs as Doc).author = Doc.CurrentUserEmail; - (pubDocs as Doc).data = new List([]); - (pubDocs as Doc)["acl-Public"] = SharingPermissions.Augment; - doc.myPublishedDocs = new PrefetchProxy(pubDocs); - } + doc.myPublishedDocs = new List(); } if (doc.myLinkDatabase === undefined) { let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ab2d84893..6c4ca2d90 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -350,11 +350,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp autoLink = () => { if (this._editorView) { const newAutoLinks = new Set(); - const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship = "automatic"); + const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === "automatic"); const f = this._editorView.state.selection.from; const t = this._editorView.state.selection.to; var tr = this._editorView.state.tr as any; - tr = tr.removeMark(0, tr.doc.content.size, this._editorView!.state.schema.marks.autoLinkAnchor); + const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor + tr = tr.removeMark(0, tr.doc.content.size, autoAnch); DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks)); tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); this._editorView?.dispatch(tr); @@ -363,7 +364,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } updateTitle = () => { - const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship = "automatic"); if (!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] && (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) { -- cgit v1.2.3-70-g09d2 From 19366220aabeeb6fcdac6fa056b9b0e9585e8e6b Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 1 Apr 2022 10:34:09 -0400 Subject: fixed up document focus to take a collectkon so that the treeView can focus on the matching alias when multiple aliases are shown in different collections. --- src/client/util/DocumentManager.ts | 13 ++++++++----- src/client/views/PropertiesDocContextSelector.tsx | 3 +++ src/client/views/StyleProvider.tsx | 8 +++++++- src/client/views/collections/TreeView.tsx | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 0a00ab6e0..ad6d90bc3 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -5,6 +5,7 @@ import { Cast } from '../../fields/Types'; import { returnFalse } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; +import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; @@ -252,10 +253,11 @@ export class DocumentManager { } } -ScriptingGlobals.add(function DocFocusOrOpen(doc: any) { - const dv = DocumentManager.Instance.getDocumentView(doc); - if (dv && dv.props.Document === doc) { - dv.props.focus(doc, { willZoom: true }); +export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) { + const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); + const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView); + if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) { + dv.props.focus(dv.props.Document, { willZoom: true }); Doc.linkFollowHighlight(dv?.props.Document, false); } else { @@ -264,4 +266,5 @@ ScriptingGlobals.add(function DocFocusOrOpen(doc: any) { CollectionDockingView.AddSplit(showDoc === Doc.GetProto(showDoc) ? Doc.MakeAlias(showDoc) : showDoc, "right") && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc)); } -}); \ No newline at end of file +} +ScriptingGlobals.add(DocFocusOrOpen); \ No newline at end of file diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 4d803f992..015e0c8ee 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -4,6 +4,7 @@ import * as React from "react"; import { Doc, DocListCast } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { Cast, NumCast, StrCast } from "../../fields/Types"; +import { DocFocusOrOpen } from "../util/DocumentManager"; import { CollectionDockingView } from "./collections/CollectionDockingView"; import { CollectionViewType } from "./collections/CollectionView"; import { DocumentView } from "./nodes/DocumentView"; @@ -32,12 +33,14 @@ export class PropertiesDocContextSelector extends React.Component { + if (!this.props.DocView) return; col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; if (col._viewType === CollectionViewType.Freeform) { col._panX = NumCast(target.x) + NumCast(target._width) / 2; col._panY = NumCast(target.y) + NumCast(target._height) / 2; } this.props.addDocTab(col, "toggle:right"); + setTimeout(() => DocFocusOrOpen(Doc.GetProto(this.props.DocView!.props.Document), col), 100); } render() { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 2782574c5..649ee8394 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -10,6 +10,7 @@ import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; import { DashColor, lightOrDark } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; +import { DocFocusOrOpen } from '../util/DocumentManager'; import { ColorScheme } from '../util/SettingsManager'; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; @@ -212,7 +213,12 @@ export function DashboardStyleProvider(doc: Opt, props: Opt - {DashboardToggleButton(doc, "hidden", "eye-slash", "eye")} + {DashboardToggleButton(doc, "hidden", "eye-slash", "eye", () => { + doc.hidden = doc.hidden ? undefined : true; + if (!doc.hidden) { + DocFocusOrOpen(doc, props?.ContainingCollectionDoc); + } + })} {DashboardToggleButton(doc, "lockedPosition", "lock", "unlock")} ; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index eedb353e3..ff5c4bab1 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -669,7 +669,7 @@ export class TreeView extends React.Component { ContentScaling={returnOne} />; - const buttons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : "")); + const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : "")); return <>
Date: Mon, 4 Apr 2022 18:35:10 -0400 Subject: fixed autolink. fixed the pileup view to animate properly and activate without selection. added a 'dataTransition" prop to DocumentView to allow collections to animate changes in the size of documents. --- src/client/documents/Documents.ts | 18 ++++++++------- src/client/util/CurrentUserUtils.ts | 12 +++++----- .../views/collections/CollectionPileView.tsx | 27 ++++++++++++---------- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 19 +++++++++++---- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +++++++--- src/fields/Doc.ts | 1 - 9 files changed, 58 insertions(+), 36 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fdfd7bd31..bb60586eb 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -823,7 +823,7 @@ export namespace Docs { } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: "visible", _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { @@ -1344,7 +1344,7 @@ export namespace DocUtils { if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); } - export function pileup(docList: Doc[], x?: number, y?: number) { + export function pileup(docList: Doc[], x?: number, y?: number, create: boolean = true) { let w = 0, h = 0; runInAction(() => { docList.forEach(d => { @@ -1359,12 +1359,14 @@ export namespace DocUtils { d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection }); }); - const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - 55, y: (y || 0) - 55, _width: 110, _height: 100, _overflow: "visible" }); - newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; - newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; - newCollection._width = newCollection._height = 110; - newCollection._jitterRotation = 10; - return newCollection; + if (create) { + const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - 55, y: (y || 0) - 55, _width: 110, _height: 100, }); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; + newCollection._width = newCollection._height = 110; + newCollection._jitterRotation = 10; + return newCollection; + } } export function LeavePushpin(doc: Doc, annotationField: string) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f2094407d..af731ce9f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -292,10 +292,10 @@ export class CurrentUserUtils { if (doc["template-icon-view"] === undefined) { 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)"), system: true + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); // Docs.Create.TextDocument("", { - // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }) // }); // 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); @@ -304,7 +304,7 @@ export class CurrentUserUtils { if (doc["template-icon-view-rtf"] === undefined) { const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); @@ -312,20 +312,20 @@ export class CurrentUserUtils { if (doc["template-icon-view-button"] === undefined) { const iconBtnView = Docs.Create.FontIconDocument({ title: "icon_" + DocumentType.BUTTON, _nativeHeight: 30, _nativeWidth: 30, - _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconBtnView.isTemplateDoc = makeTemplate(iconBtnView, true, "icon_" + DocumentType.BUTTON); doc["template-icon-view-button"] = new PrefetchProxy(iconBtnView); } if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); } if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true }); + const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 0a336c544..0ca0a463e 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -35,6 +35,18 @@ export class CollectionPileView extends CollectionSubView() { layoutEngine = () => StrCast(this.Document._pileLayoutEngine); + @undoBatch + addPileDoc = (doc: Doc | Doc[]) => { + (doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d)); + return this.props.addDocument?.(doc) || false; + } + + @undoBatch + removePileDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { + (doc instanceof Doc ? [doc] : doc).map(undoBatch((d) => Doc.deiconifyView(d))); + return this.props.moveDocument?.(doc, targetCollection, addDoc) || false; + } + // returns the contents of the pileup in a CollectionFreeFormView @computed get contents() { const isStarburst = this.layoutEngine() === "starburst"; @@ -47,14 +59,8 @@ export class CollectionPileView extends CollectionSubView() { { - (doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d)); - return this.props.addDocument?.(doc) || false; - })} - moveDocument={undoBatch((doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { - (doc instanceof Doc ? [doc] : doc).map(undoBatch((d) => Doc.deiconifyView(d))); - return this.props.moveDocument?.(doc, targetCollection, addDoc) || false; - })} /> + addDocument={this.addPileDoc} + moveDocument={this.removePileDoc} />
; } @@ -62,19 +68,16 @@ export class CollectionPileView extends CollectionSubView() { toggleStarburst = action(() => { if (this.layoutEngine() === 'starburst') { const defaultSize = 110; - this.layoutDoc._overflow = undefined; - this.childDocs.forEach(d => DocUtils.iconify(d)); this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2; this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2; this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize); this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize); - DocUtils.pileup(this.childDocs); + DocUtils.pileup(this.childDocs, undefined, undefined, false); this.layoutDoc._panX = 0; this.layoutDoc._panY = -10; this.props.Document._pileLayoutEngine = 'pass'; } else { const defaultSize = 25; - this.layoutDoc._overflow = 'visible'; !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500); !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5); if (this.layoutEngine() === 'pass') { diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 42e157396..d8f1287cd 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -453,7 +453,7 @@ export function CollectionSubView(moreProps?: X) { if (completed) completed(set); else { if (isFreeformView && generatedDocuments.length > 1) { - addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)); + addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!); } else { generatedDocuments.forEach(addDocument); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e2ea81392..b2697cf08 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1152,6 +1152,7 @@ export class CollectionFreeFormView extends CollectionSubView this.props.removeDocument?.(d)); - const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2); + const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2)!; this.props.addDocument?.(newCollection); this.props.selectDocuments([newCollection]); MarqueeOptionsMenu.Instance.fadeOut(true); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 979e86738..a9abf066e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -116,6 +116,7 @@ export interface DocumentViewSharedProps { PanelWidth: () => number; PanelHeight: () => number; docViewPath: () => DocumentView[]; + dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean); styleProvider: Opt; focus: DocFocusFunc; @@ -182,6 +183,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps { @observer export class DocumentViewInternal extends DocComponent() { public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered. + _animateScaleTime = 300; // milliseconds; @observable _animateScalingTo = 0; @observable _mediaState = 0; @observable _pendingDoubleClick = false; @@ -1078,9 +1080,8 @@ export class DocumentViewInternal extends DocComponent {this.innards} @@ -1247,8 +1248,8 @@ export class DocumentView extends React.Component { setTimeout(action(() => { this.setCustomView(custom, view); this.docView && (this.docView._animateScalingTo = 1); // expand it - setTimeout(action(() => this.docView && (this.docView._animateScalingTo = 0)), 400); - }), 400); + setTimeout(action(() => this.docView && (this.docView._animateScalingTo = 0)), this.docView!._animateScaleTime - 10); + }), this.docView!._animateScaleTime - 10); }); startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource); @@ -1293,6 +1294,7 @@ export class DocumentView extends React.Component { {!this.props.Document || !this.props.PanelWidth() ? (null) : (
{ } } +export function deiconifyViewFunc(documentView: DocumentView) { + documentView.iconify(); + //StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc); +} +ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) { + documentView.iconify(); +} +); + ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) { if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout"); else dv.switchViews(true, detailLayoutKeySuffix); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 75f0978a8..1f7e557d9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -359,18 +359,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks)); tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); this._editorView?.dispatch(tr); - oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink)).forEach(LinkManager.Instance.deleteLink); + oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink); } } updateTitle = () => { + const title = StrCast(this.dataDoc.title) if (!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing - StrCast(this.dataDoc.title).startsWith("-") && this._editorView && !this.dataDoc["title-custom"] && + (title.startsWith("-") || title.startsWith("@")) && this._editorView && !this.dataDoc["title-custom"] && (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) { let node = this._editorView.state.doc; while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild; const str = node.textContent; - this.dataDoc.title = "-" + str.substr(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : ""); + const prefix = str.startsWith("@") ? "" : "-"; + this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : ""); + if (str.startsWith("@") && str.length > 1) { + Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc); + } } } @@ -909,6 +914,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } if (this._editorView && selected) { RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props); + this.autoLink(); } }), { fireImmediately: true }); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 1edab16be..ebdbae344 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1414,7 +1414,6 @@ ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); -ScriptingGlobals.add(function deiconifyView(doc: any) { Doc.deiconifyView(doc); }); ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); }); ScriptingGlobals.add(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); }); ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; }); -- cgit v1.2.3-70-g09d2 From 51d7ce5f71465f2f578a08a998b2df353242ff4d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 5 Apr 2022 13:54:26 -0400 Subject: fixes to allow dragging golden layout windows to be aborted with 'esc'. some code cleanup as well to avoid rebuilding golden layout when tabs are closed. Fixes for undo and goldenlayout --- src/client/goldenLayout.js | 32 ++++-- src/client/util/DragManager.ts | 25 ++--- src/client/views/DocumentDecorations.tsx | 7 +- src/client/views/GlobalKeyHandler.ts | 1 + .../views/collections/CollectionDockingView.tsx | 109 +++++++++++++-------- src/client/views/collections/TabDocView.tsx | 3 +- 6 files changed, 108 insertions(+), 69 deletions(-) (limited to 'src/client/util') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 896237e1d..295875ef6 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -366,6 +366,7 @@ this._nOriginalY = 0; this._bDragging = false; + this._bAborting = false; this._fMove = lm.utils.fnBind(this.onMouseMove, this); this._fUp = lm.utils.fnBind(this.onMouseUp, this); @@ -427,6 +428,24 @@ } }, + AbortDrag: function () { + if (this._timeout != null) { + clearTimeout(this._timeout); + this._eBody.removeClass('lm_dragging'); + this._eElement.removeClass('lm_dragging'); + this._oDocument.find('iframe').css('pointer-events', ''); + this._oDocument.unbind('mousemove touchmove', this._fMove); + this._oDocument.unbind('mouseup touchend', this._fUp); + + if (this._bDragging === true) { + this._bDragging = false; + this._bAborting = true; + this.emit('dragStop', { pageX: 0, pageY: 0 }, this._nOriginalX + this._nX); + this._bAborting = false; + } + } + }, + onMouseUp: function (oEvent) { if (this._timeout != null) { clearTimeout(this._timeout); @@ -2178,18 +2197,18 @@ */ _onDrop: function () { this._layoutManager.dropTargetIndicator.hide(); - + let abortedDrop = this._dragListener._bAborting; /* * Valid drop area found */ - if (this._area !== null) { + if (!abortedDrop && this._area !== null) { this._area.contentItem._$onDrop(this._contentItem, this._area); /** * No valid drop area available at present, but one has been found before. * Use it */ - } else if (this._lastValidArea !== null) { + } else if (!abortedDrop && this._lastValidArea !== null) { this._lastValidArea.contentItem._$onDrop(this._contentItem, this._lastValidArea); /** @@ -2197,7 +2216,7 @@ * content item to its original position if a original parent is provided. * (Which is not the case if the drag had been initiated by createDragSource) */ - } else if (this._originalParent) { + } else if (!abortedDrop && this._originalParent) { this._originalParent.addChild(this._contentItem); /** @@ -2212,7 +2231,7 @@ restoreScrollTops(this._contentItem.element) this.element.remove(); - this._layoutManager.emit('itemDropped', this._contentItem); + !abortedDrop && this._layoutManager.emit('itemDropped', this._contentItem); }, /** @@ -2963,7 +2982,7 @@ if (this.contentItem.parent.isMaximised === true) { this.contentItem.parent.toggleMaximise(); } - new lm.controls.DragProxy( + let proxy = new lm.controls.DragProxy( x, y, this._dragListener, @@ -2971,6 +2990,7 @@ this.contentItem, this.header.parent ); + this._layoutManager.emit('dragStart', proxy); }, /** diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 0d6a77f71..411fc6d11 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,3 +1,4 @@ +import { extend } from "lodash"; import { action } from "mobx"; import { DateField } from "../../fields/DateField"; import { Doc, Field, Opt } from "../../fields/Doc"; @@ -56,12 +57,7 @@ export function SetupDrag( if (e.shiftKey) { e.persist(); const dragDoc = await docFunc(); - dragDoc && DragManager.StartWindowDrag?.({ - pageX: e.pageX, - pageY: e.pageY, - preventDefault: emptyFunction, - button: 0 - }, [dragDoc]); + dragDoc && DragManager.StartWindowDrag?.(e, [dragDoc]); } else { document.addEventListener("pointermove", onRowMove); document.addEventListener("pointerup", onRowUp); @@ -74,7 +70,8 @@ export function SetupDrag( export namespace DragManager { let dragDiv: HTMLDivElement; let dragLabel: HTMLDivElement; - export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined; + export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>; + export let CompleteWindowDrag: Opt<(aborted: boolean) => void>; export function Root() { const root = document.getElementById("root"); @@ -460,17 +457,13 @@ export namespace DragManager { } if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { dragData.dropAction = dragData.userDropAction || "same"; - if (dragData.dropAction === "move" || dragData.dropAction === "same") { - dragData.removeDocument?.(dragData.draggedDocuments[0]); - } AbortDrag(); await finishDrag?.(new DragCompleteEvent(true, dragData)); - DragManager.StartWindowDrag?.({ - pageX: e.pageX, - pageY: e.pageY, - preventDefault: emptyFunction, - button: 0 - }, dragData.droppedDocuments); + DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => { + if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) { + dragData.removeDocument?.(dragData.draggedDocuments[0]); + } + }); } const target = document.elementFromPoint(e.x, e.y); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 8cd646935..d7ead713a 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -151,12 +151,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P } onMaximizeDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, () => { - DragManager.StartWindowDrag?.({ - pageX: e.pageX, - pageY: e.pageY, - preventDefault: emptyFunction, - button: 0 - }, [SelectionManager.Views().slice(-1)[0].rootDoc]); + DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]); return true; }, emptyFunction, this.onMaximizeClick, false, false); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 1a4080d81..4a327a842 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -114,6 +114,7 @@ export class KeyManager { DocumentLinksButton.StartLinkView = undefined; InkStrokeProperties.Instance._controlButton = false; CurrentUserUtils.SelectedTool = InkTool.None; + DragManager.CompleteWindowDrag?.(true); var doDeselect = true; if (SnappingManager.GetIsDragging()) { DragManager.AbortDrag(); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9e8374605..6931d9896 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -11,6 +11,7 @@ import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { inheritParentAcls } from '../../../fields/util'; +import { emptyFunction } from '../../../Utils'; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -46,7 +47,7 @@ export class CollectionDockingView extends CollectionSubView() { private _reactionDisposer?: IReactionDisposer; private _lightboxReactionDisposer?: IReactionDisposer; private _containerRef = React.createRef(); - private _flush: UndoManager.Batch | undefined; + public _flush: UndoManager.Batch | undefined; private _ignoreStateChange = ""; public tabMap: Set = new Set(); public get initialized() { return this._goldenLayout !== null; } @@ -62,15 +63,37 @@ export class CollectionDockingView extends CollectionSubView() { DragManager.StartWindowDrag = this.StartOtherDrag; } - public StartOtherDrag = (e: any, dragDocs: Doc[]) => { - !this._flush && (this._flush = UndoManager.StartBatch("golden layout drag")); + /** + * Switches from dragging a document around a freeform canvas to dragging it as a tab to be docked. + * + * @param e fake mouse down event position data containing pageX and pageY coordinates + * @param dragDocs the documents to be dragged + * @param batch optionally an undo batch that has been started to use instead of starting a new batch + */ + public StartOtherDrag = (e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => { + this._flush = this._flush ?? UndoManager.StartBatch("golden layout drag"); const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : - { type: 'row', content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) }; + { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) }; const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config); - //dragSource._dragListener.on("dragStop", dragSource.destroy); - dragSource._dragListener.onMouseDown(e); + this.tabDragStart(dragSource, finishDrag); + dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }); } + tabItemDropped = () => DragManager.CompleteWindowDrag?.(false); + tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => { + DragManager.CompleteWindowDrag = (aborted: boolean) => { + if (aborted) { + proxy._dragListener.AbortDrag(); + if (this._flush) { + this._flush.cancel(); // cancel the undo change being logged + this._flush = undefined; + this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout) + } + DragManager.CompleteWindowDrag = undefined; + } + finishDrag?.(aborted); + } + } @undoBatch public CloseFullScreen = () => { this._goldenLayout._maximisedItem?.toggleMaximise(); @@ -106,7 +129,6 @@ export class CollectionDockingView extends CollectionSubView() { docconfig.callDownwards('_$init'); instance._goldenLayout._$maximiseItem(docconfig); instance._goldenLayout.emit('stateChanged'); - instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig()); instance.stateChanged(); return true; } @@ -255,7 +277,6 @@ export class CollectionDockingView extends CollectionSubView() { layoutChanged() { this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); this._goldenLayout.emit('stateChanged'); - this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); this.stateChanged(); return true; } @@ -294,6 +315,9 @@ export class CollectionDockingView extends CollectionSubView() { } } this._goldenLayout.init(); + this._goldenLayout.root.layoutManager.on('itemDropped', this.tabItemDropped); + this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart) + this._goldenLayout.root.layoutManager.on('activeContentItemChanged', this.stateChanged); } } @@ -335,13 +359,12 @@ export class CollectionDockingView extends CollectionSubView() { @action onPointerUp = (e: MouseEvent): void => { window.removeEventListener("pointerup", this.onPointerUp); - if (this._flush) { - setTimeout(() => { - CollectionDockingView.Instance._ignoreStateChange = JSON.stringify(CollectionDockingView.Instance._goldenLayout.toConfig()); - this.stateChanged(); - this._flush?.end(); - this._flush = undefined; - }, 10); + const flush = this._flush; + this._flush = undefined; + if (flush) { + DragManager.CompleteWindowDrag = undefined; + if (!this.stateChanged()) flush.cancel(); + else flush.end(); } } @@ -352,9 +375,12 @@ export class CollectionDockingView extends CollectionSubView() { hitFlyout = (par.className === "dockingViewButtonSelector"); } if (!hitFlyout) { + const htmlTarget = e.target as HTMLElement; window.addEventListener("mouseup", this.onPointerUp); - if (!(e.target as HTMLElement).closest("*.lm_content") && ((e.target as HTMLElement).closest("*.lm_tab") || (e.target as HTMLElement).closest("*.lm_stack"))) { - this._flush = UndoManager.StartBatch("golden layout edit"); + if (!htmlTarget.closest("*.lm_content") && (htmlTarget.closest("*.lm_tab") || htmlTarget.closest("*.lm_stack"))) { + if (htmlTarget.className !== "lm_close_tab") { + this._flush = UndoManager.StartBatch("golden layout edit"); + } } } if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && @@ -388,38 +414,43 @@ export class CollectionDockingView extends CollectionSubView() { @action stateChanged = () => { + this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); const json = JSON.stringify(this._goldenLayout.toConfig()); const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")); const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc); - - this.props.Document.dockingConfig = json; - setTimeout(async () => { - const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]); - const tabs = sublists && Cast(sublists[0], Doc, null); - // const other = sublists && Cast(sublists[1], Doc, null); - const tabdocs = await DocListCastAsync(tabs?.data); - // const otherdocs = await DocListCastAsync(other?.data); - if (tabs) { - tabs.data = new List(docs); - // DocListCast(tabs.aliases).forEach(tab => tab !== tabs && (tab.data = new List(docs))); - } - // const otherSet = new Set(); - // otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); - // tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc)); - // const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP); - // this.props.Document[DataSym][this.props.fieldKey + "-all"] = new List([...docs, ...vals]); - // if (other) { - // other.data = new List(vals); - // // DocListCast(other.aliases).forEach(tab => tab !== other && (tab.data = new List(vals))); - // } - }, 0); + const changesMade = this.props.Document.dockingConfig !== json; + if (changesMade && !this._flush) { + this.props.Document.dockingConfig = json; + setTimeout(async () => { + const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]); + const tabs = sublists && Cast(sublists[0], Doc, null); + // const other = sublists && Cast(sublists[1], Doc, null); + const tabdocs = await DocListCastAsync(tabs?.data); + // const otherdocs = await DocListCastAsync(other?.data); + if (tabs) { + tabs.data = new List(docs); + // DocListCast(tabs.aliases).forEach(tab => tab !== tabs && (tab.data = new List(docs))); + } + // const otherSet = new Set(); + // otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); + // tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc)); + // const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d).filter(d => d.type !== DocumentType.KVP); + // this.props.Document[DataSym][this.props.fieldKey + "-all"] = new List([...docs, ...vals]); + // if (other) { + // other.data = new List(vals); + // // DocListCast(other.aliases).forEach(tab => tab !== other && (tab.data = new List(vals))); + // } + }, 0); + } + return changesMade; } tabDestroyed = (tab: any) => { this.tabMap.delete(tab); tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); + this.stateChanged(); } tabCreated = (tab: any) => { tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content) diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 8b5022593..ad5c0efb3 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -190,7 +190,7 @@ export class TabDocView extends React.Component { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true); SelectionManager.DeselectAll(); - tab.contentItem.remove(); + UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab"); }); } } @@ -277,7 +277,6 @@ export class TabDocView extends React.Component { private onActiveContentItemChanged(contentItem: any) { if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) { this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab; - (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged(); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } -- cgit v1.2.3-70-g09d2 From 4823e1c7ceb0e58cdb515e2bb013632d81767ae3 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Tue, 5 Apr 2022 20:03:36 -0400 Subject: added basic grouping when exiting pen mode --- src/Utils.ts | 1 + src/client/util/CurrentUserUtils.ts | 1 + src/client/views/GestureOverlay.tsx | 23 +++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 67 +++++++++++++++-- .../collections/collectionFreeForm/MarqueeView.tsx | 3 + src/client/views/nodes/button/FontIconBox.tsx | 83 +++++++++++++++++++++- 6 files changed, 161 insertions(+), 17 deletions(-) (limited to 'src/client/util') diff --git a/src/Utils.ts b/src/Utils.ts index d0d891f77..6519b5d13 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -407,6 +407,7 @@ export function formatTime(time: number) { return (hours ? hours.toString() + ":" : "") + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0'); } +// x is furthest left, y is furthest top, r is furthest right, b is furthest bottom export function aggregateBounds(boundsList: { x: number, y: number, width?: number, height?: number }[], xpad: number, ypad: number) { const bounds = boundsList.map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) })).reduce((bounds, b) => ({ x: Math.min(b.x, bounds.x), y: Math.min(b.y, bounds.y), diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c7f293f2c..6cab8c100 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1011,6 +1011,7 @@ export class CurrentUserUtils { static inkTools(doc: Doc) { const tools: Button[] = [ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' }, + { title: "Mode", toolTip: "Mode (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' }, { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' }, // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' }, { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' }, diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 05e5b7d5f..87b006a93 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -617,22 +617,23 @@ export class GestureOverlay extends Touchable { newPoints.pop(); const controlPoints: { X: number, Y: number }[] = []; - // const bezierCurves = fitCurve(newPoints, 10); - // for (const curve of bezierCurves) { + const bezierCurves = fitCurve(newPoints, 10); + for (const curve of bezierCurves) { - // controlPoints.push({ X: curve[0][0], Y: curve[0][1] }); - // controlPoints.push({ X: curve[1][0], Y: curve[1][1] }); - // controlPoints.push({ X: curve[2][0], Y: curve[2][1] }); - // controlPoints.push({ X: curve[3][0], Y: curve[3][1] }); + controlPoints.push({ X: curve[0][0], Y: curve[0][1] }); + controlPoints.push({ X: curve[1][0], Y: curve[1][1] }); + controlPoints.push({ X: curve[2][0], Y: curve[2][1] }); + controlPoints.push({ X: curve[3][0], Y: curve[3][1] }); - // } - // const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) + - // (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y)); - // if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0]; - // this._points = controlPoints; + } + const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) + + (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y)); + if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0]; + this._points = controlPoints; this.dispatchGesture(GestureUtils.Gestures.Stroke); + } this._points = []; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e2ea81392..a694ca2b3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -3,7 +3,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { DateField } from "../../../../fields/DateField"; -import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; @@ -17,7 +17,7 @@ import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; -import { Docs, DocUtils } from "../../../documents/Documents"; +import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { DocumentManager } from "../../../util/DocumentManager"; @@ -97,6 +97,9 @@ export class CollectionFreeFormView extends CollectionSubView = new Map(); private _lastTap = 0; private _batch: UndoManager.Batch | undefined = undefined; + + // private isWritingMode: boolean = true; + // private writingModeDocs: Doc[] = []; private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; } @@ -114,6 +117,7 @@ export class CollectionFreeFormView extends CollectionSubView(); @observable _marqueeRef = React.createRef(); + @observable _marqueeViewRef = React.createRef(); @observable _keyframeEditing = false; @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. @@ -431,6 +435,26 @@ export class CollectionFreeFormView extends CollectionSubView { + if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { + document.removeEventListener("pointerup", this.onPenUp); + const currentCol = DocListCast(this.rootDoc.currentInkDoc) + const rootDocList = DocListCast(this.rootDoc.data); + console.log("rootDocList", rootDocList[rootDocList.length - 1]); + console.log("currentCol", currentCol); + // if (!currentCol[0].data) { + // currentCol[0].data = []; + // } + // let docList = DocListCast(currentCol[0]); + + currentCol.push(rootDocList[rootDocList.length - 1]); + console.log(currentCol); + + this._batch?.end(); + } + } + @action onPointerDown = (e: React.PointerEvent): void => { this._downX = this._lastX = e.pageX; @@ -442,7 +466,32 @@ export class CollectionFreeFormView extends CollectionSubView(); @undoBatch onGesture = (e: Event, ge: GestureUtils.GestureEvent) => { switch (ge.gesture) { @@ -494,7 +545,14 @@ export class CollectionFreeFormView extends CollectionSubView 0 ? undefined : this.nudge} addDocTab={this.addDocTab} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b10b0912f..eeb2b653b 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -60,7 +60,9 @@ export class MarqueeView extends React.Component { + const selected = ffView.unprocessedDocs; + ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true); + // loop through selected an get the bound + const bounds: { x: number, y: number, width?: number, height?: number }[] = [] + + selected.map(action(d => { + const x = NumCast(d.x); + const y = NumCast(d.y); + const width = d[WidthSym](); + const height = d[HeightSym](); + bounds.push({x, y, width, height}); + })) + + const aggregBounds = aggregateBounds(bounds, 0, 0); + const marqViewRef = ffView._marqueeViewRef.current; + + // set the vals for bounds in marqueeView + if (marqViewRef) { + marqViewRef._downX = aggregBounds.x; + marqViewRef._downY = aggregBounds.y; + marqViewRef._lastX = aggregBounds.r; + marqViewRef._lastY = aggregBounds.b; + } + + selected.map(action(d => { + const dx = NumCast(d.x); + const dy = NumCast(d.y); + delete d.x; + delete d.y; + delete d.activeFrame; + delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + // TODO: nda - actually calc the bounds + // get the bounds + // d.x = dx - aggregBounds.x; + // d.y = dy - aggregBounds.y; + + // d.x = dx - aggregBounds.x ; + // d.y = dy; + if (marqViewRef?.Bounds) { + d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds?.width / 2; + d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; + } + console.log(d[DataSym], d.x, d.y) + return d; + })); + ffView.props.removeDocument?.(selected); + // TODO: nda - this is the code to actually get a new grouped collection + // const newCollection = ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true); + const newCollection = marqViewRef?.getCollection(selected, undefined, [], true); + console.log("newcoll:", newCollection?.[DataSym]); + + // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs + newCollection && ffView.props.addDocument?.(newCollection); + ffView.unprocessedDocs = []; + }); + + CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); + if (checkResult) { return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? Colors.MEDIUM_BLUE : "transparent"; @@ -716,6 +792,9 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole } else if (tool) { // pen or eraser if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) { Doc.UserDoc().activeInkTool = InkTool.None; + } else if (tool == "write") { + console.log("write mode selected - create groupDoc here!") + // } else { Doc.UserDoc().activeInkTool = tool; GestureOverlay.Instance.InkShape = ""; -- cgit v1.2.3-70-g09d2 From ef63a072a586f51e8fa51b4684987491287540b2 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Thu, 7 Apr 2022 17:21:01 -0400 Subject: added grouping and write mode vs pen mode --- src/client/util/CurrentUserUtils.ts | 4 +- src/client/views/GestureOverlay.tsx | 5 +- .../views/collections/CollectionDockingView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 12 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/nodes/button/FontIconBox.tsx | 112 ++++++++++----------- src/client/views/pdf/PDFViewer.tsx | 2 +- src/fields/InkField.ts | 3 +- webpack.config.js | 2 +- 14 files changed, 77 insertions(+), 79 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6cab8c100..f08f13bbc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1010,8 +1010,8 @@ export class CurrentUserUtils { static inkTools(doc: Doc) { const tools: Button[] = [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' }, - { title: "Mode", toolTip: "Mode (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' }, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", click: 'setActiveInkTool("pen", _readOnly_)' }, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' }, { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' }, // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' }, { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' }, diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 87b006a93..53b360e60 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -491,7 +491,10 @@ export class GestureOverlay extends Touchable { @action onPointerDown = (e: React.PointerEvent) => { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { + CurrentUserUtils.SelectedTool = InkTool.Write; + } this._points.push({ X: e.clientX, Y: e.clientY }); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)"); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 9e8374605..c9f55a7bd 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -358,7 +358,7 @@ export class CollectionDockingView extends CollectionSubView() { } } if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && - ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { e.stopPropagation(); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a694ca2b3..40164313c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -468,6 +468,8 @@ export class CollectionFreeFormView extends CollectionSubView e.preventDefault()} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7092b335c..2e822bb6a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -517,7 +517,7 @@ export class DocumentViewInternal extends DocComponent { // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document) - if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) { + if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) { if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { e.stopPropagation(); if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it @@ -550,7 +550,7 @@ export class DocumentViewInternal extends DocComponent { if (e.cancelBubble) return; - if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return; + if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return; if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0a4168698..a8da22b61 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -322,7 +322,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent; } marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index aa2130af5..9bf7c2e8c 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 33fdc4935..a5b8967c4 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -549,7 +549,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 7ff47107e..54c49ecad 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -529,7 +529,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index fc0cc87ca..7f3a667a9 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -13,6 +13,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields import { WebField } from '../../../../fields/URLField'; import { aggregateBounds, Utils } from '../../../../Utils'; import { DocumentType } from '../../../documents/DocumentTypes'; +import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DocumentManager } from '../../../util/DocumentManager'; import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; import { SelectionManager } from '../../../util/SelectionManager'; @@ -717,64 +718,56 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole // easy way without backing up to the server // - CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { - const selected = ffView.unprocessedDocs; - ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true); - // loop through selected an get the bound - const bounds: { x: number, y: number, width?: number, height?: number }[] = [] - - selected.map(action(d => { - const x = NumCast(d.x); - const y = NumCast(d.y); - const width = d[WidthSym](); - const height = d[HeightSym](); - bounds.push({x, y, width, height}); - })) - - const aggregBounds = aggregateBounds(bounds, 0, 0); - const marqViewRef = ffView._marqueeViewRef.current; - - // set the vals for bounds in marqueeView - if (marqViewRef) { - marqViewRef._downX = aggregBounds.x; - marqViewRef._downY = aggregBounds.y; - marqViewRef._lastX = aggregBounds.r; - marqViewRef._lastY = aggregBounds.b; - } - - selected.map(action(d => { - const dx = NumCast(d.x); - const dy = NumCast(d.y); - delete d.x; - delete d.y; - delete d.activeFrame; - delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - // TODO: nda - actually calc the bounds - // get the bounds - // d.x = dx - aggregBounds.x; - // d.y = dy - aggregBounds.y; + if (CurrentUserUtils.SelectedTool === InkTool.Write) { + CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { + const selected = ffView.unprocessedDocs; + // loop through selected an get the bound + const bounds: { x: number, y: number, width?: number, height?: number }[] = [] - // d.x = dx - aggregBounds.x ; - // d.y = dy; - if (marqViewRef?.Bounds) { - d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds?.width / 2; - d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; - } - console.log(d[DataSym], d.x, d.y) - return d; - })); - ffView.props.removeDocument?.(selected); - // TODO: nda - this is the code to actually get a new grouped collection - // const newCollection = ffView._marqueeViewRef.current?.getCollection(ffView.unprocessedDocs, undefined, [], true); - const newCollection = marqViewRef?.getCollection(selected, undefined, [], true); - console.log("newcoll:", newCollection?.[DataSym]); - - // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs - newCollection && ffView.props.addDocument?.(newCollection); - ffView.unprocessedDocs = []; - }); - + selected.map(action(d => { + const x = NumCast(d.x); + const y = NumCast(d.y); + const width = d[WidthSym](); + const height = d[HeightSym](); + bounds.push({x, y, width, height}); + })) + + const aggregBounds = aggregateBounds(bounds, 0, 0); + const marqViewRef = ffView._marqueeViewRef.current; + + // set the vals for bounds in marqueeView + if (marqViewRef) { + marqViewRef._downX = aggregBounds.x; + marqViewRef._downY = aggregBounds.y; + marqViewRef._lastX = aggregBounds.r; + marqViewRef._lastY = aggregBounds.b; + } + + selected.map(action(d => { + const dx = NumCast(d.x); + const dy = NumCast(d.y); + delete d.x; + delete d.y; + delete d.activeFrame; + delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + // TODO: nda - actually calc the bounds + // calculate pos based on bounds + if (marqViewRef?.Bounds) { + d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; + d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; + } + return d; + })); + ffView.props.removeDocument?.(selected); + // TODO: nda - this is the code to actually get a new grouped collection + const newCollection = marqViewRef?.getCollection(selected, undefined, [], true); + + // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs + newCollection && ffView.props.addDocument?.(newCollection); + ffView.unprocessedDocs = []; + }); + } CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); if (checkResult) { @@ -793,8 +786,9 @@ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boole if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) { Doc.UserDoc().activeInkTool = InkTool.None; } else if (tool == "write") { - console.log("write mode selected - create groupDoc here!") - // + console.log("write mode selected - create groupDoc here!", tool) + Doc.UserDoc().activeInkTool = tool; + GestureOverlay.Instance.InkShape = ""; } else { Doc.UserDoc().activeInkTool = tool; GestureOverlay.Instance.InkShape = ""; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 1856c5353..37fbd7a1d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -371,7 +371,7 @@ export class PDFViewer extends React.Component { if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true, false); } - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { this.props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 31024e805..c34e8f93d 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -11,7 +11,8 @@ export enum InkTool { Pen = "pen", Highlighter = "highlighter", Eraser = "eraser", - Stamp = "stamp" + Stamp = "stamp", + Write = "write" } diff --git a/webpack.config.js b/webpack.config.js index 3fd00bcf3..5a954db19 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -49,7 +49,7 @@ module.exports = { repl: ["./src/debug/Repl.tsx", 'webpack-hot-middleware/client?reload=true'], test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'], inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'], - mobileInterface: ["./src/mobile/MobileMain.tsx", 'webpack-hot-middleware/client?reload=true'], + mobileInterface: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'], }, devtool: "source-map", output: { -- cgit v1.2.3-70-g09d2 From b3d6eaa3a0b126712eae25c1b91925d030a2d900 Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 7 Apr 2022 18:06:40 -0400 Subject: added RecordingView --- package-lock.json | 13 + package.json | 2 + src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 16 +- src/client/util/CurrentUserUtils.ts | 4 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/FieldView.tsx | 1 + .../views/nodes/RecordingBox/ProgressBar.scss | 26 ++ .../views/nodes/RecordingBox/ProgressBar.tsx | 46 +++ .../views/nodes/RecordingBox/RecordingBox.tsx | 24 ++ .../views/nodes/RecordingBox/RecordingView.scss | 207 +++++++++++++ .../views/nodes/RecordingBox/RecordingView.tsx | 326 +++++++++++++++++++++ src/client/views/nodes/RecordingBox/index.ts | 2 + src/fields/URLField.ts | 1 + 14 files changed, 667 insertions(+), 5 deletions(-) create mode 100644 src/client/views/nodes/RecordingBox/ProgressBar.scss create mode 100644 src/client/views/nodes/RecordingBox/ProgressBar.tsx create mode 100644 src/client/views/nodes/RecordingBox/RecordingBox.tsx create mode 100644 src/client/views/nodes/RecordingBox/RecordingView.scss create mode 100644 src/client/views/nodes/RecordingBox/RecordingView.tsx create mode 100644 src/client/views/nodes/RecordingBox/index.ts (limited to 'src/client/util') diff --git a/package-lock.json b/package-lock.json index b1431e55b..d84e8a572 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2001,6 +2001,14 @@ "@types/react": "*" } }, + "@types/react-icons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-3.0.0.tgz", + "integrity": "sha512-Vefs6LkLqF61vfV7AiAqls+vpR94q67gunhMueDznG+msAkrYgRxl7gYjNem/kZ+as2l2mNChmF1jRZzzQQtMg==", + "requires": { + "react-icons": "*" + } + }, "@types/react-measure": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.8.tgz", @@ -15764,6 +15772,11 @@ "react-resizable": "^1.9.0" } }, + "react-icons": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==" + }, "react-image-lightbox-with-rotate": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/react-image-lightbox-with-rotate/-/react-image-lightbox-with-rotate-5.1.1.tgz", diff --git a/package.json b/package.json index 2c3e641dd..35aa2ef92 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "@types/dom-speech-recognition": "0.0.1", "@types/formidable": "1.0.31", "@types/google-maps": "^3.2.3", + "@types/react-icons": "^3.0.0", "@types/react-reconciler": "^0.26.4", "@types/reveal": "^3.3.33", "@types/supercluster": "^7.1.0", @@ -253,6 +254,7 @@ "react-datepicker": "^3.8.0", "react-dom": "^16.14.0", "react-grid-layout": "^0.18.3", + "react-icons": "^4.3.1", "react-image-lightbox-with-rotate": "^5.1.1", "react-jsx-parser": "^1.29.0", "react-loading": "^2.0.3", diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 161dff6e0..ca942a38a 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -9,6 +9,7 @@ export enum DocumentType { KVP = "kvp", VID = "video", AUDIO = "audio", + REC = "recording", PDF = "pdf", INK = "inks", SCREENSHOT = "screenshot", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bb60586eb..e50172af5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -11,7 +11,7 @@ import { RichTextField } from "../../fields/RichTextField"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; -import { AudioField, ImageField, MapField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; +import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Upload } from "../../server/SharedMediaTypes"; import { OmitKeys, Utils } from "../../Utils"; @@ -61,6 +61,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { MapBox } from "../views/nodes/MapBox/MapBox"; +import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox"; const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); class EmptyBox { @@ -401,6 +402,10 @@ export namespace Docs { layout: { view: AudioBox, dataField: defaultDataKey }, options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" } }], + [DocumentType.REC, { + layout: { view: VideoBox, dataField: defaultDataKey }, + options: { _height: 100, backgroundColor: "pink", links: "@links(self)" } + }], [DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" } @@ -468,7 +473,7 @@ export namespace Docs { options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" }, }], [DocumentType.WEBCAM, { - layout: { view: DashWebRTCVideo, dataField: defaultDataKey }, + layout: { view: RecordingBox, dataField: defaultDataKey }, options: { links: "@links(self)" } }], [DocumentType.PRESELEMENT, { @@ -699,6 +704,10 @@ export namespace Docs { { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }); } + export function RecordingDocument(url: string, options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.REC), "", options); + } + export function SearchDocument(options: DocumentOptions = {}) { return InstanceFromProto(Prototypes.get(DocumentType.SEARCH), new List([]), options); } @@ -1160,6 +1169,9 @@ export namespace DocUtils { } else if (field instanceof AudioField) { created = Docs.Create.AudioDocument((field).url.href, resolved); layout = AudioBox.LayoutString; + } else if (field instanceof RecordingField) { + created = Docs.Create.RecordingDocument((field).url.href, resolved); + layout = RecordingBox.LayoutString; } else if (field instanceof InkField) { created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); layout = InkingStroke.LayoutString; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index af731ce9f..edb362a37 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -424,8 +424,8 @@ export class CurrentUserUtils { doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, title: "empty screenshot", _width: 400, _height: 200, system: true, cloneFieldFilter: new List(["system"]) }); } if (doc.emptyWall === undefined) { - doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List(["system"]) }); - (doc.emptyWall as Doc).videoWall = true; + doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List(["system"]) }); + (doc.emptyWall as Doc).recording = true; } if (doc.emptyAudio === undefined) { doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "audio recording", system: true, cloneFieldFilter: new List(["system"]) }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 005133eb0..da9b448ec 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -41,6 +41,7 @@ import { WebBox } from "./WebBox"; import React = require("react"); import XRegExp = require("xregexp"); import { MapBox } from "./MapBox/MapBox"; +import { RecordingBox } from "./RecordingBox"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -225,7 +226,7 @@ export class DocumentContentsView extends React.Component void +} + +export function ProgressBar(props: ProgressBarProps) { + + const handleClick = (e: React.MouseEvent) => { + let progressbar = document.getElementById('progressbar')! + let bounds = progressbar!.getBoundingClientRect(); + let x = e.clientX - bounds.left; + let percent = x / progressbar.clientWidth * 100 + + for (let i = 0; i < props.marks.length; i++) { + let start = i == 0 ? 0 : props.marks[i-1]; + if (percent > start && percent < props.marks[i]) { + props.playSegment(i) + // console.log(i) + // console.log(percent) + // console.log(props.marks[i]) + break + } + } + } + + return( +
+
+ {props.marks.map((mark) => { + return
+ })} +
+ ) +} \ No newline at end of file diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx new file mode 100644 index 000000000..6d444d324 --- /dev/null +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -0,0 +1,24 @@ +import { observer } from "mobx-react"; +import * as React from "react"; +import { ViewBoxBaseComponent } from "../../DocComponent"; +import { FieldView } from "../FieldView"; +import { RecordingView } from './RecordingView'; + + +@observer +export class RecordingBox extends ViewBoxBaseComponent(){ + + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(RecordingBox, fieldKey); } + + private _ref: React.RefObject = React.createRef(); + + constructor(props: any) { + super(props); + } + + render() { + return
+ +
; + } +} diff --git a/src/client/views/nodes/RecordingBox/RecordingView.scss b/src/client/views/nodes/RecordingBox/RecordingView.scss new file mode 100644 index 000000000..2eaf5468d --- /dev/null +++ b/src/client/views/nodes/RecordingBox/RecordingView.scss @@ -0,0 +1,207 @@ +video { + flex: 100%; + width: 100%; + min-height: 400px; + height: auto; + display: block; + background-color: black; +} + +button { margin: 0 .5rem } + +.recording-container { + height: 100%; + width: 100%; + display: flex; +} + +.video-wrapper { + max-width: 600px; + max-width: 700px; + position: relative; + display: flex; + justify-content: center; + overflow: hidden; + border-radius: 10px; +} + +.video-wrapper:hover .controls { + bottom: 30px; + transform: translateY(0%); + opacity: 100%; +} + +.controls { + display: flex; + align-items: center; + justify-content: space-evenly; + position: absolute; + padding: 14px; + width: 100%; + max-width: 500px; + flex-wrap: wrap; + background: rgba(255, 255, 255, 0.25); + box-shadow: 0 8px 32px 0 rgba(255, 255, 255, 0.1); + backdrop-filter: blur(4px); + border-radius: 10px; + border: 1px solid rgba(255, 255, 255, 0.18); + // transform: translateY(150%); + transition: all 0.3s ease-in-out; + // opacity: 0%; + bottom: 30px; + // bottom: -150px; +} + +.actions button { + background: none; + border: none; + outline: none; + cursor: pointer; +} + +.actions button i { + background-color: none; + color: white; + font-size: 30px; +} + +// input[type="range"] { +// -webkit-appearance: none !important; +// background: rgba(255, 255, 255, 0.2); +// border-radius: 20px; +// height: 4px; +// width: 350px; +// } + +// input[type="range"]::-webkit-slider-thumb { +// -webkit-appearance: none !important; +// cursor: pointer; +// height: 6px; +// } + +// input[type="range"]::-moz-range-progress { +// background: white; +// } + +.velocity { + appearance: none; + background: none; + color: white; + outline: none; + border: none; + text-align: center; + font-size: 16px; +} + +.mute-btn { + background: none; + border: none; + outline: none; + cursor: pointer; +} + +.mute-btn i { + background-color: none; + color: white; + font-size: 20px; +} + +.recording-sign { + height: 20px; + width: auto; + display: flex; + flex-direction: row; + position: absolute; + top: 10px; + right: 15px; + align-items: center; + justify-content: center; + + .timer { + font-size: 15px; + color: white; + margin: 0; + } + + .dot { + height: 15px; + width: 15px; + margin: 5px; + background-color: red; + border-radius: 50%; + display: inline-block; + } +} + +.controls-inner-container { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + +} + +.record-button-wrapper { + width: 35px; + height: 35px; + font-size: 0; + background-color: grey; + border: 0px; + border-radius: 35px; + margin: 10px; + + .record-button { + position: relative; + background-color: red; + border: 0px; + border-radius: 50%; + height: 28px; + width: 28px; + top: 50%; + left: 50%; + margin: -14px 0px 0px -14px; + + &:hover { + width: 30px; + height: 30px; + margin: -15px 0px 0px -15px; + } + } + + .stop-button{ + position: relative; + background-color: red; + border: 0px; + border-radius: 10%; + height: 18px; + width: 18px; + top: 50%; + left: 50%; + margin: -9px 0px 0px -9px; + + // &:hover { + // width: 40px; + // height: 40px + // } + } + +} + +.video-edit-wrapper { + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + position: absolute; + top: 0; + bottom: 0; + right: 50% - 15; + + .video-edit-buttons { + margin: 0 5px; + } + +} + + + diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx new file mode 100644 index 000000000..15f8c8626 --- /dev/null +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -0,0 +1,326 @@ +import * as React from 'react'; +import "./RecordingView.scss"; +import { ReactElement, useCallback, useEffect, useRef, useState } from "react"; +import { ProgressBar } from "./ProgressBar" +import { MdBackspace } from 'react-icons/md'; +import { FaCheckCircle } from 'react-icons/fa'; +import { IconContext } from "react-icons"; + + +enum RecordingStatus { + Recording, + Stopped, + Paused +} + +interface VideoSegment { + chunks: any[], + endTime: number +} + +const MAXTIME = 1000; + +export function RecordingView() { + + const [recording, setRecording] = useState(false); + const recordingTimerRef = useRef(0); + const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second + const [playing, setPlaying] = useState(false); + const [progress, setProgress] = useState(0); + const [speed, setSpeed] = useState(1); + const [muted, setMuted] = useState(false); + + const [videos, setVideos] = useState([]); + // const [videos, setVideos] = useState([]); + const [currentVid, setCurrentVid] = useState(0); + const recorder = useRef(null); + const videoElementRef = useRef(null); + + const [finished, setFinished] = useState(false) + + + + const DEFAULT_MEDIA_CONSTRAINTS = { + video: { + width: 1280, + height: 720, + }, + audio: { + echoCancellation: true, + noiseSuppression: true, + sampleRate: 44100 + } + } + + useEffect(() => { + + if (finished) { + let allVideoChunks : any = [] + console.log(videos) + videos.forEach((vid) => { + console.log(vid.chunks) + allVideoChunks = allVideoChunks.concat(vid.chunks) + }) + + console.log(allVideoChunks) + + const blob = new Blob(allVideoChunks, { + type: 'video/webm' + }) + const blobUrl = URL.createObjectURL(blob) + + videoElementRef.current!.srcObject = null + videoElementRef.current!.src = blobUrl + videoElementRef.current!.muted = false + } + + + }, [finished]) + + useEffect(() => { + // check if the browser supports media devices on first load + if (!navigator.mediaDevices) { + console.log('This browser does not support getUserMedia.') + } + console.log('This device has the correct media devices.') + }, []) + + useEffect(() => { + // get access to the video element on every render + // videoElement = document.getElementById('video') as HTMLVideoElement; + videoElementRef.current = document.getElementById('video') as HTMLVideoElement; + }) + + // useEffect(() => { + // if (playing) { + // videoElement!.srcObject = null + // // videoElement!.src = videos[currentVid].url + // videoElement!.muted = false + // videoElement!.play() + // } else { + // videoElement!.pause(); + // } + // }, [playing, videoElement]); + + useEffect(() => { + let interval: any = null; + if (recording) { + interval = setInterval(() => { + setRecordingTimer(unit => unit + 1); + }, 10); + } else if (!recording && recordingTimer !== 0) { + clearInterval(interval); + } + return () => clearInterval(interval); + }, [recording]) + + useEffect(() => { + setVideoProgressHelper(recordingTimer) + recordingTimerRef.current = recordingTimer; + }, [recordingTimer]) + + const setVideoProgressHelper = (progress: number) => { + const newProgress = (progress / MAXTIME) * 100; + setProgress(newProgress) + } + const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => { + const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints) + + videoElementRef.current!.src = "" + videoElementRef.current!.srcObject = stream + videoElementRef.current!.muted = true + + return stream + } + + const record = async () => { + const stream = await startShowingStream(); + recorder.current = new MediaRecorder(stream) + + // temporary chunks of video + let chunks: any = [] + recorder.current.ondataavailable = (event: any) => { + // store the video chunks as it is recording + console.log("data available") + if (event.data.size > 0) { + chunks.push(event.data) + } + } + + recorder.current.onstart = (event: any) => { + console.log("on start") + setRecording(true); + } + + recorder.current.onstop = () => { + // if we have a last portion + if (chunks.length > 1) { + // append the current portion to the video pieces + setVideos(videos => [...videos, {chunks: chunks, endTime: recordingTimerRef.current}]) + } + + // reset the temporary chunks + chunks = [] + setRecording(false); + setFinished(true); + } + + // recording paused + recorder.current.onpause = (event: any) => { + // append the current portion to the video pieces + console.log(chunks) + setVideos(videos => [...videos, {chunks: chunks, endTime: recordingTimerRef.current}]) + + // reset the temporary chunks + chunks = [] + setRecording(false); + } + + recorder.current.onresume = async (event: any) => { + console.log(event) + await startShowingStream(); + setRecording(true); + } + + recorder.current.start(200) + } + + + const stop = () => { + if (recorder.current) { + if (recorder.current.state !== "inactive") { + recorder.current.stop(); + // recorder.current.stream.getTracks().forEach((track: any) => track.stop()) + } + } + } + + const pause = () => { + if (recorder.current) { + if (recorder.current.state === "recording") { + recorder.current.pause(); + } + } + } + + const startOrResume = () => { + console.log('[RecordingView.tsx] startOrResume') + if (!recorder.current || recorder.current.state === "inactive") { + record(); + } else if (recorder.current.state === "paused") { + recorder.current.resume(); + } + } + + const playSegment = (idx: number) => { + console.log(idx) + let currentChunks = videos[idx].chunks + console.log(currentChunks) + + const blob = new Blob(currentChunks, { + type: 'video/webm' + }) + const blobUrl = URL.createObjectURL(blob) + console.log(blobUrl) + + videoElementRef.current!.srcObject = null + videoElementRef.current!.src = blobUrl + videoElementRef.current!.muted = false + } + + const clearPrevious = () => { + const numVideos = videos.length + setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime) + setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime) + setVideos(videos.filter((_, idx) => idx !== numVideos - 1)); + } + + // const handleVideoProgress = (event: any) => { + // const manualChange = Number(event.target.value); + // videoElement!.currentTime = (videoElement!.duration / 100) * manualChange; + // setProgress(manualChange) + // }; + + // const handleVideoSpeed = (event: any) => { + // const newSpeed = Number(event.target.value); + // videoElement!.playbackRate = speed; + // setSpeed(newSpeed) + // }; + + const handleOnTimeUpdate = () => { + if (playing) { + setVideoProgressHelper(videoElementRef.current!.currentTime) + } + }; + + const millisecondToMinuteSecond = (milliseconds: number) => { + const toTwoDigit = (digit: number) => { + return String(digit).length == 1 ? "0" + digit : digit + } + const minutes = Math.floor(( milliseconds % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000); + return toTwoDigit(minutes) + " : " + toTwoDigit(seconds); + } + + + + + useEffect(() => { + console.log(videos.map((elt) => elt.endTime / MAXTIME * 100)) + console.log(videos) + }, [videos]) + + return ( +
+
+
+
) +} \ No newline at end of file diff --git a/src/client/views/nodes/RecordingBox/index.ts b/src/client/views/nodes/RecordingBox/index.ts new file mode 100644 index 000000000..ff21eaed6 --- /dev/null +++ b/src/client/views/nodes/RecordingBox/index.ts @@ -0,0 +1,2 @@ +export * from './RecordingView' +export * from './RecordingBox' \ No newline at end of file diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts index 1d4bbaed0..3e7542e46 100644 --- a/src/fields/URLField.ts +++ b/src/fields/URLField.ts @@ -53,6 +53,7 @@ export abstract class URLField extends ObjectField { export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg"; @scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { } +@scriptingGlobal @Deserializable("recording") export class RecordingField extends URLField { } @scriptingGlobal @Deserializable("image") export class ImageField extends URLField { } @scriptingGlobal @Deserializable("video") export class VideoField extends URLField { } @scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { } -- cgit v1.2.3-70-g09d2 From 6c7101d4f69dd79a83a48d04356748213c38a435 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 11 Apr 2022 13:31:33 -0400 Subject: making layout icons work better. --- src/client/documents/Documents.ts | 4 ++- src/client/util/CurrentUserUtils.ts | 15 +++++--- src/client/util/DragManager.ts | 6 ++-- src/client/views/DocumentDecorations.tsx | 20 ++++++++--- src/client/views/InkingStroke.tsx | 6 +++- src/client/views/StyleProvider.tsx | 7 ++-- .../views/collections/CollectionCarouselView.tsx | 4 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 40 ++++++++++++++++++++++ .../collectionFreeForm/MarqueeView.scss | 1 + src/client/views/nodes/DocumentView.tsx | 7 ++-- src/client/views/nodes/ImageBox.tsx | 6 ++-- src/client/views/nodes/LabelBigText.js | 33 +++++++++++++----- src/client/views/nodes/LabelBox.tsx | 5 +-- src/client/views/nodes/WebBox.tsx | 4 +-- src/fields/Doc.ts | 2 +- 15 files changed, 123 insertions(+), 37 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index bb60586eb..652f8e7e0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -139,6 +139,8 @@ export class DocumentOptions { _itemIndex?: number; // which item index the carousel viewer is showing _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document) + _minFontSize?: number; // minimum font size for labelBoxes + _maxFontSize?: number; // maximum font size for labelBoxes _columnWidth?: number; _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden _fontSize?: string; @@ -441,7 +443,7 @@ export namespace Docs { }], [DocumentType.LABEL, { layout: { view: LabelBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } + options: { links: "@links(self)", _singleLine: true } }], [DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index af731ce9f..bc4dbcb2e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -9,7 +9,7 @@ import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; -import { nullAudio } from "../../fields/URLField"; +import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; @@ -292,7 +292,7 @@ export class CurrentUserUtils { if (doc["template-icon-view"] === undefined) { 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(documentView)", { documentView: "any" }), system: true + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); // Docs.Create.TextDocument("", { // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }) @@ -304,7 +304,8 @@ export class CurrentUserUtils { if (doc["template-icon-view-rtf"] === undefined) { const iconRtfView = Docs.Create.LabelDocument({ title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + _singleLine: false, _minFontSize: 18, _maxFontSize: 24, + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); @@ -319,14 +320,18 @@ export class CurrentUserUtils { } if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); } if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 180 / 4, _height: 135 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); + const proto = iconColView.proto as Doc; + proto["icon-nativeWidth"] = 180 / 4; + proto["icon-nativeHeight"] = 135 / 4; + proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } if (doc["template-icons"] === undefined) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 411fc6d11..d6d04db9a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -380,8 +380,8 @@ export namespace DragManager { } } const rect = ele.getBoundingClientRect(); - const scaleX = rect.width / ele.offsetWidth; - const scaleY = ele.offsetHeight ? rect.height / ele.offsetHeight : scaleX; + const scaleX = rect.width / (ele.offsetWidth || rect.width); + const scaleY = ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX; elesCont.left = Math.min(rect.left, elesCont.left); elesCont.top = Math.min(rect.top, elesCont.top); @@ -424,7 +424,7 @@ export namespace DragManager { const hideDragShowOriginalElements = (hide: boolean) => { dragLabel.style.display = hide ? "" : "none"; - !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); + //!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); eles.forEach(ele => ele.hidden = hide); }; options?.hideSource && hideDragShowOriginalElements(true); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d7ead713a..1487d5bb0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -144,10 +144,17 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P return true; } + _iconifyTimeout: NodeJS.Timeout | undefined; onCloseClick = () => { - const selected = SelectionManager.Views().slice(); - SelectionManager.DeselectAll(); - selected.map(dv => dv.props.removeDocument?.(dv.props.Document)); + const views = SelectionManager.Views().slice().filter(v => v); + const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon"); + const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon"); + icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document)); + others.forEach(dv => dv.iconify()); + others.length && (this._iconifyTimeout = setTimeout(() => { + views.forEach(view => SelectionManager.DeselectView(view)); + this._iconifyTimeout = undefined; + }, 1000)); } onMaximizeDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, () => { @@ -171,7 +178,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P } else if (e.altKey) { // open same document in new tab CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); } else { - LightboxView.SetLightboxDoc(selectedDocs[0].props.Document, undefined, selectedDocs.slice(1).map(view => view.props.Document)); + var openDoc = selectedDocs[0].props.Document; + if (openDoc.layoutKey === "layout_icon") { + openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc); + Doc.deiconifyView(openDoc); + } + LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document)); } } SelectionManager.DeselectAll(); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 06671961d..5e589fdea 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -70,7 +70,11 @@ export class InkingStroke extends ViewBoxBaseComponent() { // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc) - screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1); + screenToLocal = () => { + console.log("Scaling = " + this.props.scaling?.()) + console.log("Stolocal = " + this.props.ScreenToLocalTransform().Scale) + return this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1); + } getAnchor = () => { console.log(document.activeElement); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 649ee8394..739670ecf 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -117,7 +117,10 @@ export function DefaultStyleProvider(doc: Opt, props: Opt = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : "")); + let docColor: Opt = + StrCast(doc?.[fieldKey + "backgroundColor"], + StrCast(doc?._backgroundColor, + StrCast(props?.Document.backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""))); switch (doc?.type) { case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break; case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; @@ -127,7 +130,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt
- { + if (this.clicks) { + console.log("UPDATE ICON"); + } + this.clicks++; + this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString(); + this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString(); + var htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!); + this.props.docViewPath().lastElement().ContentDiv!.style.width = ""; + this.props.docViewPath().lastElement().ContentDiv!.style.height = ""; + const nativeWidth = this.layoutDoc[WidthSym](); + const nativeHeight = this.layoutDoc[HeightSym](); + + CreateImage( + "", + document.styleSheets, + htmlString?.replace(/"marqueeView"/g, '"marqueeView marqueeView2"'), + nativeWidth, + nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), + NumCast(this.layoutDoc._scrollTop) + ).then + ((data_url: any) => { + VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true).then( + returnedfilename => setTimeout(action(() => { + + this.dataDoc.icon = new ImageField(returnedfilename); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; + }), 500)); + }) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); + } + componentWillUnmount() { Object.values(this._disposers).forEach(disposer => disposer?.()); this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); @@ -1476,6 +1515,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); + appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); this.props.ContainingCollectionView && appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" }); !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss index 62510ce9d..41e4d6b6a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss @@ -10,6 +10,7 @@ user-select: none; } + .marqueeView:focus-within { overflow: hidden; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 103c3624d..185eafa4a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -80,6 +80,7 @@ export type DocAfterFocusFunc = (notFocused: boolean) => Promise export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { + updateIcon?: () => void; // updates the icon representation of the document getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) scrollFocus?: (doc: Doc, smooth: boolean) => Opt; // returns the duration of the focus setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document @@ -1118,6 +1119,7 @@ export class DocumentViewInternal extends DocComponent !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)} onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)} style={{ + display: "inline", borderRadius: this.borderRounding, pointerEvents: this.pointerEvents, outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px", @@ -1233,6 +1235,7 @@ export class DocumentView extends React.Component { } public iconify() { + this.ComponentView?.updateIcon?.(); const layoutKey = Cast(this.Document.layoutKey, "string", null); if (layoutKey !== "layout_icon") { this.switchViews(true, "icon"); @@ -1332,8 +1335,8 @@ export function deiconifyViewFunc(documentView: DocumentView) { } ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) { documentView.iconify(); -} -); + documentView.select(false); +}); ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) { if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout"); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 17f95c1cc..fb9e86487 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -73,7 +73,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent this._curSuffix = forceFull ? "_o" : scrSize < 100 ? "_s" : scrSize < 400 ? "_m" : scrSize < 800 || !selected ? "_l" : "_o", + ({ forceFull, scrSize, selected }) => this._curSuffix = this.fieldKey === "icon" ? "_m" : forceFull ? "_o" : scrSize < 100 ? "_s" : scrSize < 400 ? "_m" : scrSize < 800 || !selected ? "_l" : "_o", { fireImmediately: true, delay: 1000 }); this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }), ({ nativeSize, width }) => { @@ -245,8 +245,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent options.maximumFontSize) { + if (fontSize < options.minimumFontSize) { + parentStyle.display = "flex"; + parentStyle.alignItems = "center"; + style.whiteSpace = "pre-wrap"; + style.textAlign = "center"; + style.visibility = ""; + style.fontSize = "18px"; + style.lineHeight = "20px"; + style.top = ""; + style.left = ""; + style.margin = ""; + return element; + } + if (options.maximumFontSize && fontSize > options.maximumFontSize) { fontSize = options.maximumFontSize; lineHeight = fontSize / options.fontSizeFactor; } diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 0015f0b71..c689d9f40 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { Doc, DocListCast } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { Cast, StrCast } from '../../../fields/Types'; +import { Cast, StrCast, NumCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; @@ -100,7 +100,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro BigText(r, { rotateText: null, fontSizeFactor: 1, - maximumFontSize: null, + minimumFontSize: NumCast(this.layoutDoc._minFontSize), + maximumFontSize: NumCast(this.layoutDoc._maxFontSize), limitingDimension: "both", horizontalAlign: "center", verticalAlign: "center", diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index c12059943..0fd193977 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -130,8 +130,8 @@ export class WebBox extends ViewBoxAnnotatableComponent Date: Mon, 11 Apr 2022 16:06:15 -0400 Subject: lots of layout fixes to groups, labels, ink to support iconification better. simpliifed documentdecorations. fixed display artifacts related to things not showing up when dragging, or otherwise not getting a halo of nested freeform colletions. --- src/client/util/CurrentUserUtils.ts | 8 +- src/client/util/DragManager.ts | 2 +- src/client/views/DocumentDecorations.scss | 496 ++++++++++----------- src/client/views/DocumentDecorations.tsx | 10 +- src/client/views/InkingStroke.tsx | 6 +- src/client/views/StyleProvider.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 11 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 5 +- src/client/views/nodes/DocumentIcon.tsx | 1 + src/client/views/nodes/DocumentView.scss | 3 + src/client/views/nodes/DocumentView.tsx | 8 +- src/client/views/nodes/LabelBigText.js | 1 + src/client/views/nodes/LabelBox.scss | 2 +- src/client/views/nodes/LabelBox.tsx | 31 +- 14 files changed, 275 insertions(+), 315 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index bc4dbcb2e..6d8e2d30c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -320,17 +320,17 @@ export class CurrentUserUtils { } if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 50, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + title: "data", _width: 150, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); } if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 180 / 4, _height: 135 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); const proto = iconColView.proto as Doc; - proto["icon-nativeWidth"] = 180 / 4; - proto["icon-nativeHeight"] = 135 / 4; + proto["icon-nativeWidth"] = 360 / 4; + proto["icon-nativeHeight"] = 270 / 4; proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index d6d04db9a..94a09eac4 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -424,7 +424,7 @@ export namespace DragManager { const hideDragShowOriginalElements = (hide: boolean) => { dragLabel.style.display = hide ? "" : "none"; - //!hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); + !hide && dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); eles.forEach(ele => ele.hidden = hide); }; options?.hideSource && hideDragShowOriginalElements(true); diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 82dca1287..35e37a2cd 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -4,8 +4,8 @@ $linkGap: 3px; .documentDecorations-Dark, .documentDecorations { - position: absolute; - z-index: 2000; + position: absolute; + z-index: 2000; } .documentDecorations-Dark { background: dimgray; @@ -17,11 +17,11 @@ $linkGap: 3px; left: 0; display: grid; grid-template-rows: 20px 8px 1fr 8px; - grid-template-columns: 8px 16px 1fr 8px 8px; + grid-template-columns: 8px 1fr 8px; pointer-events: none; .documentDecorations-centerCont { - grid-column: 3; + grid-column: 2; background: none; } @@ -41,6 +41,7 @@ $linkGap: 3px; opacity: 1; transform: translate(10px, 10px); grid-row: 4; + grid-column: 3 } .documentDecorations-topLeftResizer, @@ -73,20 +74,18 @@ $linkGap: 3px; .documentDecorations-topResizer, .documentDecorations-bottomResizer { - grid-column-start: 2; - grid-column-end: 5; + grid-column: 2; } .documentDecorations-bottomRightResizer, .documentDecorations-topRightResizer, .documentDecorations-rightResizer { - grid-column-start: 5; - grid-column-end: 7; + grid-column: 3; } .documentDecorations-rotation, .documentDecorations-borderRadius { - grid-column: 5; + grid-column: 3; grid-row: 4; border-radius: 100%; background: black; @@ -132,114 +131,113 @@ $linkGap: 3px; opacity: 1; } - .documentDecorations-topLeftResizer, - .documentDecorations-leftResizer, - .documentDecorations-bottomLeftResizer { - grid-column: 1; - } - - .documentDecorations-topResizer, - .documentDecorations-bottomResizer { - grid-column-start: 2; - grid-column-end: 5; - } - - .documentDecorations-bottomRightResizer, - .documentDecorations-topRightResizer, - .documentDecorations-rightResizer { - grid-column-start: 5; - grid-column-end: 7; - } - - .documentDecorations-rotation, - .documentDecorations-borderRadius { - grid-column: 5; - grid-row: 4; - border-radius: 100%; - background: black; - height: 8; - right: -12; - top: 12; - position: relative; - pointer-events: all; - cursor: nwse-resize; - - .borderRadiusTooltip { - width: 10px; - height: 10px; - position: absolute; - } - } - .documentDecorations-rotation { - background: transparent; - right: -15; - } - - .documentDecorations-topLeftResizer, - .documentDecorations-bottomRightResizer { - cursor: nwse-resize; - background: unset; - opacity: 1; - } + .documentDecorations-topLeftResizer, + .documentDecorations-leftResizer, + .documentDecorations-bottomLeftResizer { + grid-column: 1; + } - .documentDecorations-topLeftResizer { - border-left: 2px solid; - border-top: solid 2px; - } + .documentDecorations-topResizer, + .documentDecorations-bottomResizer { + grid-column: 2; + } - .documentDecorations-bottomRightResizer { - border-right: 2px solid; - border-bottom: solid 2px; - } + .documentDecorations-bottomRightResizer, + .documentDecorations-topRightResizer, + .documentDecorations-rightResizer { + grid-column: 3 + } - .documentDecorations-topLeftResizer:hover, - .documentDecorations-bottomRightResizer:hover { - opacity: 1; - } + .documentDecorations-rotation, + .documentDecorations-borderRadius { + grid-column: 3; + grid-row: 4; + border-radius: 100%; + background: black; + height: 8; + right: -12; + top: 12; + position: relative; + pointer-events: all; + cursor: nwse-resize; - .documentDecorations-bottomRightResizer { - grid-row: 4; - } + .borderRadiusTooltip { + width: 10px; + height: 10px; + position: absolute; + } + } + .documentDecorations-rotation { + background: transparent; + right: -15; + } - .documentDecorations-topRightResizer, - .documentDecorations-bottomLeftResizer { - cursor: nesw-resize; - background: unset; - opacity: 1; - } + .documentDecorations-topLeftResizer, + .documentDecorations-bottomRightResizer { + cursor: nwse-resize; + background: unset; + opacity: 1; + } + + .documentDecorations-topLeftResizer { + border-left: 2px solid; + border-top: solid 2px; + } + + .documentDecorations-bottomRightResizer { + border-right: 2px solid; + border-bottom: solid 2px; + } + + .documentDecorations-topLeftResizer:hover, + .documentDecorations-bottomRightResizer:hover { + opacity: 1; + } + + .documentDecorations-bottomRightResizer { + grid-row: 4; + } + + .documentDecorations-topRightResizer, + .documentDecorations-bottomLeftResizer { + cursor: nesw-resize; + background: unset; + opacity: 1; + } - .documentDecorations-topRightResizer { - border-right: 2px solid; - border-top: 2px solid; - } + .documentDecorations-topRightResizer { + border-right: 2px solid; + border-top: 2px solid; + } - .documentDecorations-bottomLeftResizer { - border-left: 2px solid; - border-bottom: 2px solid; - } + .documentDecorations-bottomLeftResizer { + border-left: 2px solid; + border-bottom: 2px solid; + } - .documentDecorations-topRightResizer:hover, - .documentDecorations-bottomLeftResizer:hover { +.documentDecorations-topRightResizer:hover, +.documentDecorations-bottomLeftResizer:hover { cursor: nesw-resize; background: black; opacity: 1; - } +} - .documentDecorations-topResizer, - .documentDecorations-bottomResizer { +.documentDecorations-topResizer, +.documentDecorations-bottomResizer { cursor: ns-resize; - } +} - .documentDecorations-title-Dark, - .documentDecorations-title { +.documentDecorations-title-Dark, +.documentDecorations-title { opacity: 1; - grid-column-start: 2; - grid-column-end: 4; + width: 100%; + grid-column: 2; pointer-events: auto; overflow: hidden; text-align: center; display: flex; - margin-left: 5px; + padding-left: 5px; + padding-right: 12px; height: 20px; position: absolute; border-radius: 8px; @@ -247,7 +245,7 @@ $linkGap: 3px; .documentDecorations-titleSpan, .documentDecorations-titleSpan-Dark { - width: 100%; + width: calc(100% - 17px); // = padding-left + padding-right border-radius: 8px; background: #ffffffa0; position: absolute; @@ -263,105 +261,61 @@ $linkGap: 3px; background: black; } - .documentDecorations-contextMenu { - width: 25px; - height: calc(100% + 8px); // 8px for the height of the top resizer bar - grid-column-start: 2; - grid-column-end: 2; - pointer-events: all; - padding-left: 5px; - cursor: pointer; - } - - .documentDecorations-titleBackground { - background: #ffffffcf; - border-radius: 8px; - width: 100%; - height: 100%; - position: absolute; - } - - .documentDecorations-title { - opacity: 1; - grid-column-start: 2; - grid-column-end: 4; - pointer-events: auto; - overflow: hidden; - text-align: center; - display: flex; - margin-left: 5px; - height: 20px; - position: absolute; - .documentDecorations-titleSpan { - width: 100%; - border-radius: 8px; - background: #ffffffcf; - position: absolute; - display: inline-block; - cursor: move; - } - } - - .focus-visible { - margin-left: 0px; - } -} + .documentDecorations-titleBackground { + background: #ffffffcf; + border-radius: 8px; + width: 100%; + height: 100%; + position: absolute; + } -.documentDecorations-iconifyButton { - opacity: 1; - grid-column-start: 4; - grid-column-end: 4; - pointer-events: all; - right: 0; - cursor: pointer; - position: absolute; - width: 20px; + .focus-visible { + margin-left: 0px; + } } .documentDecorations-openButton { - display: flex; - align-items: center; - opacity: 1; - grid-column-start: 5; - grid-column-end: 5; - pointer-events: all; - cursor: pointer; + display: flex; + align-items: center; + opacity: 1; + grid-column-start: 3; + pointer-events: all; + cursor: pointer; } .documentDecorations-closeButton { - display: flex; - align-items: center; - opacity: 1; - grid-column-start: 1; - grid-column-end: 3; - pointer-events: all; - cursor: pointer; - - > svg { - margin: 0; - } + display: flex; + align-items: center; + opacity: 1; + grid-column: 1; + pointer-events: all; + cursor: pointer; + + > svg { + margin: 0; + } } .documentDecorations-background { - background: lightblue; - position: absolute; - opacity: 0.1; + background: lightblue; + position: absolute; + opacity: 0.1; } .linkFlyout { - grid-column: 2/4; + grid-column: 2/4; } .linkButton-empty:hover { - background: $medium-gray; - transform: scale(1.05); - cursor: pointer; + background: $medium-gray; + transform: scale(1.05); + cursor: pointer; } .linkButton-nonempty:hover { - background: $medium-gray; - transform: scale(1.05); - cursor: pointer; + background: $medium-gray; + transform: scale(1.05); + cursor: pointer; } .link-button-container { @@ -379,132 +333,132 @@ $linkGap: 3px; } .linkButtonWrapper { - pointer-events: auto; - padding-right: 5px; - width: 25px; + pointer-events: auto; + padding-right: 5px; + width: 25px; } .linkButton-linker { - height: 20px; - width: 20px; - text-align: center; - border-radius: 50%; - pointer-events: auto; - color: $dark-gray; - border: $dark-gray 1px solid; + height: 20px; + width: 20px; + text-align: center; + border-radius: 50%; + pointer-events: auto; + color: $dark-gray; + border: $dark-gray 1px solid; } .linkButton-linker:hover { - cursor: pointer; - transform: scale(1.05); + cursor: pointer; + transform: scale(1.05); } .linkButton-empty, .linkButton-nonempty { - height: 20px; - width: 20px; - border-radius: 50%; - opacity: 0.9; - pointer-events: auto; - background-color: $dark-gray; - color: $white; - text-transform: uppercase; - letter-spacing: 2px; - font-size: 75%; - transition: transform 0.2s; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - - &:hover { - background: $medium-gray; - transform: scale(1.05); - cursor: pointer; - } + height: 20px; + width: 20px; + border-radius: 50%; + opacity: 0.9; + pointer-events: auto; + background-color: $dark-gray; + color: $white; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + transition: transform 0.2s; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + + &:hover { + background: $medium-gray; + transform: scale(1.05); + cursor: pointer; + } } .templating-menu { - position: absolute; - pointer-events: auto; - text-transform: uppercase; - letter-spacing: 2px; - font-size: 75%; - transition: transform 0.2s; - text-align: center; - display: flex; - justify-content: center; - align-items: center; + position: absolute; + pointer-events: auto; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + transition: transform 0.2s; + text-align: center; + display: flex; + justify-content: center; + align-items: center; } .documentdecorations-icon { - margin: 0px; + margin: 0px; } .templating-button, .docDecs-tagButton { - width: 20px; - height: 20px; - border-radius: 50%; - opacity: 0.9; - font-size: 14; - background-color: $dark-gray; - color: $white; - text-align: center; - cursor: pointer; - - &:hover { - background: $medium-gray; - transform: scale(1.05); - } + width: 20px; + height: 20px; + border-radius: 50%; + opacity: 0.9; + font-size: 14; + background-color: $dark-gray; + color: $white; + text-align: center; + cursor: pointer; + + &:hover { + background: $medium-gray; + transform: scale(1.05); + } } #template-list { - position: absolute; - top: 25px; - left: 0px; - width: max-content; - font-family: $sans-serif; - font-size: 12px; - background-color: $light-gray; - padding: 2px 12px; - list-style: none; - - .templateToggle, - .chromeToggle { - text-align: left; - } - - input { - margin-right: 10px; - } + position: absolute; + top: 25px; + left: 0px; + width: max-content; + font-family: $sans-serif; + font-size: 12px; + background-color: $light-gray; + padding: 2px 12px; + list-style: none; + + .templateToggle, + .chromeToggle { + text-align: left; + } + + input { + margin-right: 10px; + } } @-moz-keyframes spin { - 100% { - -moz-transform: rotate(360deg); - } + 100% { + -moz-transform: rotate(360deg); + } } @-webkit-keyframes spin { - 100% { - -webkit-transform: rotate(360deg); - } + 100% { + -webkit-transform: rotate(360deg); + } } @keyframes spin { - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } } @keyframes shadow-pulse { - 0% { - box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8); - } + 0% { + box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.8); + } - 100% { - box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); - } + 100% { + box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); + } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 1487d5bb0..353843b8d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -449,7 +449,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) { return (null); } - const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles; + const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc.isGroup; const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle; const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton); const canDelete = SelectionManager.Views().some(docView => { @@ -466,16 +466,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P ); const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); - const titleArea = hideTitle ?
: + const titleArea = hideTitle ?
: this._edtingTitle ? this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : -
+
{`${this.selectionTitle}`}
; @@ -515,8 +515,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")} {hideResizers ? (null) : <> - {SelectionManager.Views().length !== 1 || hideTitle ? (null) : - topBtn("iconify", `window-${seldoc.finalLayoutKey.includes("icon") ? "restore" : "minimize"}`, undefined, this.onIconifyClick, `${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`)}
e.preventDefault()} />
e.preventDefault()} />
e.preventDefault()} /> diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 5e589fdea..06671961d 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -70,11 +70,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke // fit within its panel (e.g., for content fitting views like Lightbox or multicolumn, etc) - screenToLocal = () => { - console.log("Scaling = " + this.props.scaling?.()) - console.log("Stolocal = " + this.props.ScreenToLocalTransform().Scale) - return this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1); - } + screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1); getAnchor = () => { console.log(document.activeElement); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 739670ecf..eb6551fc8 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -98,7 +98,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt (props?.PanelHeight() || 0) ? 5 : 10) : 0; case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || - doc?.type === DocumentType.RTF) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0; + doc?.type === DocumentType.RTF || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0; case StyleProp.BackgroundColor: { if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY; let docColor: Opt = @@ -143,7 +143,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt pair.layout); - docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); + const docs = this.childLayoutPairs.map(pair => pair.layout).slice(); + docs.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); let zlast = docs.length ? Math.max(docs.length, NumCast(docs[docs.length - 1].zIndex)) : 1; if (zlast - docs.length > 100) { for (let i = 0; i < docs.length; i++) doc.zIndex = i + 1; @@ -1414,12 +1414,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.clicks) { - console.log("UPDATE ICON"); - } - this.clicks++; this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString(); this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString(); var htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!); @@ -1431,7 +1426,7 @@ export class CollectionFreeFormView extends CollectionSubView {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints} - {!this.props.isSelected() || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) : + {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) : + Offset={[this.topMost ? 0 : !this.props.isSelected() ? - 15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? - 15 : -30]} /> } {audioView}
; @@ -1111,7 +1111,7 @@ export class DocumentViewInternal extends DocComponent !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)} onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)} style={{ - display: "inline", borderRadius: this.borderRounding, pointerEvents: this.pointerEvents, outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px", @@ -1244,6 +1243,7 @@ export class DocumentView extends React.Component { const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null); this.switchViews(deiconifyLayout ? true : false, deiconifyLayout); this.Document.deiconifyLayout = undefined; + this.props.bringToFront(this.rootDoc); } } @undoBatch diff --git a/src/client/views/nodes/LabelBigText.js b/src/client/views/nodes/LabelBigText.js index 78fe2839e..02c36c4bc 100644 --- a/src/client/views/nodes/LabelBigText.js +++ b/src/client/views/nodes/LabelBigText.js @@ -156,6 +156,7 @@ export default function BigText(element, options) { width: element.offsetWidth, //Note: This is slightly larger than the jQuery version height: element.offsetHeight, }; + if (!box.width || !box.height) return element; if (options.rotateText !== null) { diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss index 6a0d651d2..42e158584 100644 --- a/src/client/views/nodes/LabelBox.scss +++ b/src/client/views/nodes/LabelBox.scss @@ -4,7 +4,7 @@ border-radius: inherit; display: flex; flex-direction: column; - position: absolute; + position: relative; } .labelBox-mainButton { diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index c689d9f40..3405a7635 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -30,6 +30,11 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro componentDidMount() { this.props.setContentView?.(this); + console.log("MOUNTING") + } + + componentDidUpdate() { + console.log("UPDATING") } getTitle() { @@ -73,6 +78,19 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro @observable _mouseOver = false; @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; } + fitTextToBox = (r: any) => { + BigText(r, { + rotateText: null, + fontSizeFactor: 1, + minimumFontSize: NumCast(this.layoutDoc._minFontSize), + maximumFontSize: NumCast(this.layoutDoc._maxFontSize), + limitingDimension: "both", + horizontalAlign: "center", + verticalAlign: "center", + textAlign: "center", + whiteSpace: "nowrap" + }) + } // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []); @@ -97,17 +115,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro }} > { if (r) { - BigText(r, { - rotateText: null, - fontSizeFactor: 1, - minimumFontSize: NumCast(this.layoutDoc._minFontSize), - maximumFontSize: NumCast(this.layoutDoc._maxFontSize), - limitingDimension: "both", - horizontalAlign: "center", - verticalAlign: "center", - textAlign: "center", - whiteSpace: "nowrap" - }); + if (!r.offsetWidth || !r.offsetHeight) setTimeout(() => this.fitTextToBox(r)); + else this.fitTextToBox(r); } }}>{label.startsWith("#") ? (null) : label}
-- cgit v1.2.3-70-g09d2 From ec4cf9ea0439701fde3aa49e462b054998c657f1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 11 Apr 2022 17:57:37 -0400 Subject: got pdf's to generate icons when iconifying. --- src/client/util/CurrentUserUtils.ts | 20 ++++++++++- src/client/views/DocumentDecorations.scss | 5 +-- src/client/views/nodes/PDFBox.tsx | 57 +++++++++++++++++++++++++++++-- src/client/views/nodes/VideoBox.scss | 1 + src/client/views/nodes/VideoBox.tsx | 20 +++++++---- src/client/views/pdf/PDFViewer.tsx | 2 +- 6 files changed, 92 insertions(+), 13 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6d8e2d30c..0e392cc85 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -334,9 +334,27 @@ export class CurrentUserUtils { proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } + if (doc["template-icon-view-video"] === undefined) { + const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID); + const proto = iconVidView.proto as Doc; + proto["icon-nativeWidth"] = 360 / 4; + proto["icon-nativeHeight"] = 270 / 4; + proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); + doc["template-icon-view-video"] = new PrefetchProxy(iconVidView); + } + if (doc["template-icon-view-pdf"] === undefined) { + const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); + const proto = iconPdfView.proto as Doc; + proto["icon-nativeWidth"] = 360 / 4; + proto["icon-nativeHeight"] = 270 / 4; + proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); + doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView); + } if (doc["template-icons"] === undefined) { doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-video"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); } else { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 35e37a2cd..7432d45bf 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -232,12 +232,13 @@ $linkGap: 3px; opacity: 1; width: 100%; grid-column: 2; + grid-column-end: 2; pointer-events: auto; overflow: hidden; text-align: center; display: flex; - padding-left: 5px; - padding-right: 12px; + padding-left: 2px; + padding-right: 2px; height: 20px; position: absolute; border-radius: 8px; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index a2e7d2aa3..3f1771e68 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -2,10 +2,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; +import { CreateImage } from "../nodes/WebBoxRenderer"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types'; -import { PdfField } from "../../../fields/URLField"; +import { ImageField, PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; @@ -21,6 +22,8 @@ import { SidebarAnnos } from '../SidebarAnnos'; import { FieldView, FieldViewProps } from './FieldView'; import "./PDFBox.scss"; import React = require("react"); +import { Id } from '../../../fields/FieldSymbols'; +import { VideoBox } from './VideoBox'; @observer export class PDFBox extends ViewBoxAnnotatableComponent() { @@ -52,6 +55,56 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + if (oldDiv.childNodes) { + for (let i = 0; i < oldDiv.childNodes.length; i++) { + this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement); + } + } + if (oldDiv instanceof HTMLCanvasElement) { + const canvas = oldDiv as HTMLCanvasElement; + var img = document.createElement('img'); // create a Image Element + img.src = canvas.toDataURL(); //image source + img.style.width = canvas.style.width; + img.style.height = canvas.style.height; + const newCan = newDiv as HTMLCanvasElement; + const parEle = newCan.parentElement as HTMLElement; + parEle.removeChild(newCan); + parEle.appendChild(img); + } + } + + updateIcon = () => { + const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; + const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; + newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); + newDiv.style.height = (this.layoutDoc[HeightSym]()).toString(); + this.replaceCanvases(docViewContent, newDiv) + var htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv); + const nativeWidth = this.layoutDoc[WidthSym](); + const nativeHeight = this.layoutDoc[HeightSym](); + + CreateImage( + "", + document.styleSheets, + htmlString?.replace(/docView-hack/g, 'documentView-hack'), + nativeWidth, + nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), + NumCast(this.layoutDoc._scrollTop) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"]) + ).then + ((data_url: any) => { + VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true).then( + returnedfilename => setTimeout(action(() => { + this.dataDoc.icon = new ImageField(returnedfilename); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; + }), 500)); + }) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); + } + componentWillUnmount() { this._selectReactionDisposer?.(); } componentDidMount() { this.props.setContentView?.(this); diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 3cf10a033..f47a71469 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -81,6 +81,7 @@ align-items: center; justify-content: center; display: flex; + width: 100%; visibility: none; opacity: 0; background-color: $dark-gray; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index ca8dc8515..b7b8ce064 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -4,10 +4,10 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import { basename } from "path"; import * as rp from 'request-promise'; -import { Doc, DocListCast } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc"; import { InkTool } from "../../../fields/InkField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { AudioField, VideoField } from "../../../fields/URLField"; +import { AudioField, ImageField, VideoField } from "../../../fields/URLField"; import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; @@ -220,16 +220,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent void) => { const width = NumCast(this.layoutDoc._width); const canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1); const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions if (ctx) { - // ctx.rect(0, 0, canvas.width, canvas.height); - // ctx.fillStyle = "blue"; - // ctx.fill(); this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height); } @@ -259,10 +256,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent - returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY)); + returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); } } + updateIcon = () => { + const makeIcon = (returnedfilename: string) => { + this.dataDoc.icon = new ImageField(returnedfilename); + this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym](); + this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym](); + } + this.Snapshot(undefined, undefined, makeIcon); + } + // creates link for snapshot createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => { const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 651b0401b..9aaa6e90f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -74,7 +74,7 @@ export class PDFViewer extends React.Component { private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); - private _mainCont: React.RefObject = React.createRef(); + _mainCont: React.RefObject = React.createRef(); private _selectionText: string = ""; private _downX: number = 0; private _downY: number = 0; -- cgit v1.2.3-70-g09d2 From 0f8b556ac88632d31db4218bcea2e7a559deb6e4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 11 Apr 2022 20:11:42 -0400 Subject: fixed sendtoback menu and functionality. fixed mixedblendmode for groups. fixed orange highlighting when docs are toggled hidden. fixed placement of linking button. --- src/client/util/DocumentManager.ts | 5 ++++- src/client/views/StyleProvider.tsx | 11 ++++++----- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 10 ++++------ src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 4 ++-- 5 files changed, 17 insertions(+), 15 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index ad6d90bc3..6076de055 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -169,7 +169,10 @@ export class DocumentManager { const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; const docView = getFirstDocView(targetDoc, originatingDoc); const wasHidden = targetDoc.hidden; // - if (wasHidden) runInAction(() => targetDoc.hidden = false); // if the target is hidden, un-hide it here. + if (wasHidden) runInAction(() => { + targetDoc.hidden = false; + docView?.props.bringToFront(targetDoc); + }); // if the target is hidden, un-hide it here. const focusAndFinish = (didFocus: boolean) => { if (originatingDoc?.isPushpin) { if (!didFocus && !wasHidden) { // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index eb6551fc8..37ef96782 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -143,13 +143,14 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? Doc.UserDoc().activeCollectionNestedBackground : - Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE)))); + Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE)) + )); break; //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index ba34d846e..277d6dc53 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -166,11 +166,9 @@ export class CollectionFreeFormDocumentView extends DocComponent {this.props.renderCutoffProvider(this.props.Document) ? diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 6d96a9ab6..46537df7e 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -278,7 +278,7 @@ export class DocumentLinksButton extends React.Component +
{ (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 423a5edbf..9ec7b9a58 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -752,7 +752,7 @@ export class DocumentViewInternal extends DocComponent SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" }); zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" }); } - !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" }); + !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" }); !Doc.UserDoc().noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" }); @@ -1119,6 +1119,7 @@ export class DocumentViewInternal extends DocComponent !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)} onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)} style={{ + display: this.hidden ? "inline" : undefined, borderRadius: this.borderRounding, pointerEvents: this.pointerEvents, outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px", @@ -1322,7 +1323,6 @@ export class DocumentView extends React.Component { ContentScaling={this.ContentScale} ScreenToLocalTransform={this.screenToLocalTransform} focus={this.props.focus || emptyFunction} - bringToFront={emptyFunction} ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} />
)}
); -- cgit v1.2.3-70-g09d2 From 7cd890392f79f1783af2bdb0fe86fa6a49db849a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 11 Apr 2022 23:17:07 -0400 Subject: fixes to header to be transparent for icons. layout fixes for doc decoratoins title row. groups are not semi transnparent anymore to avoid mixBlendMode confusion --- src/client/util/CurrentUserUtils.ts | 13 +++++-------- src/client/views/DocumentDecorations.scss | 9 +++++---- src/client/views/DocumentDecorations.tsx | 3 +-- src/client/views/StyleProvider.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 5 +++-- src/client/views/nodes/DocumentView.tsx | 6 ++++-- 6 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 0e392cc85..bf722157a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -303,7 +303,7 @@ export class CurrentUserUtils { } if (doc["template-icon-view-rtf"] === undefined) { const iconRtfView = Docs.Create.LabelDocument({ - title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), + title: "icon_" + DocumentType.RTF, _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), _singleLine: false, _minFontSize: 18, _maxFontSize: 24, _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); @@ -320,13 +320,13 @@ export class CurrentUserUtils { } if (doc["template-icon-view-img"] === undefined) { const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 150, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + title: "data", _width: 150, isTemplateDoc: true, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); } if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + const iconColView = Docs.Create.ImageDocument("", { title: "icon", _showTitle: "title", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); const proto = iconColView.proto as Doc; proto["icon-nativeWidth"] = 360 / 4; @@ -335,7 +335,7 @@ export class CurrentUserUtils { doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } if (doc["template-icon-view-video"] === undefined) { - const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID); const proto = iconVidView.proto as Doc; proto["icon-nativeWidth"] = 360 / 4; @@ -344,7 +344,7 @@ export class CurrentUserUtils { doc["template-icon-view-video"] = new PrefetchProxy(iconVidView); } if (doc["template-icon-view-pdf"] === undefined) { - const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); const proto = iconPdfView.proto as Doc; proto["icon-nativeWidth"] = 360 / 4; @@ -1344,9 +1344,6 @@ export class CurrentUserUtils { // 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; } diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 7432d45bf..481b90249 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -230,15 +230,15 @@ $linkGap: 3px; .documentDecorations-title-Dark, .documentDecorations-title { opacity: 1; - width: 100%; + width: calc(100% - 8px); // = margin-left + margin-right grid-column: 2; grid-column-end: 2; pointer-events: auto; overflow: hidden; text-align: center; display: flex; - padding-left: 2px; - padding-right: 2px; + margin-left: 6px; // closeButton width (14) - leftColumn width (8) + margin-right: 2px; height: 20px; position: absolute; border-radius: 8px; @@ -246,7 +246,7 @@ $linkGap: 3px; .documentDecorations-titleSpan, .documentDecorations-titleSpan-Dark { - width: calc(100% - 17px); // = padding-left + padding-right + width: 100% ; border-radius: 8px; background: #ffffffa0; position: absolute; @@ -290,6 +290,7 @@ $linkGap: 3px; opacity: 1; grid-column: 1; pointer-events: all; + width: 14px; cursor: pointer; > svg { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 353843b8d..9d9505c08 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -469,13 +469,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P const titleArea = hideTitle ?
: this._edtingTitle ? this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} /> : -
+
{`${this.selectionTitle}`}
; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 37ef96782..2ce78f64f 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -146,7 +146,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE)) diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 277d6dc53..7fb2c235a 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -18,6 +18,8 @@ import "./CollectionFreeFormDocumentView.scss"; import { DocumentView, DocumentViewProps } from "./DocumentView"; import React = require("react"); import { DocumentType } from "../../documents/DocumentTypes"; +import { collectionSchema } from "../../../fields/documentSchemas"; +import { CollectionViewType } from "../collections/CollectionView"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; @@ -167,8 +169,7 @@ export class CollectionFreeFormDocumentView extends DocComponent
; + return
{!this._retryThumb || !this.thumbShown() ? (null) : @@ -1030,7 +1031,8 @@ export class DocumentViewInternal extends DocComponent
; const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc); - const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"); + const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, + Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)"); const titleView = !showTitle ? (null) :
Date: Tue, 12 Apr 2022 10:54:28 -0400 Subject: cleaned up errors/warnings. fixed dragging to turn off pointer events on whatever's being dragged. --- src/client/util/DocumentManager.ts | 10 ++++++---- src/client/util/DragManager.ts | 2 +- src/client/views/StyleProvider.tsx | 4 +++- src/client/views/collections/CollectionDockingView.tsx | 4 ++-- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 4 ++-- src/client/views/nodes/LabelBox.tsx | 7 +------ src/client/views/nodes/PDFBox.tsx | 6 +++--- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 10 files changed, 21 insertions(+), 22 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 6076de055..1d603e18f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -169,10 +169,12 @@ export class DocumentManager { const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; const docView = getFirstDocView(targetDoc, originatingDoc); const wasHidden = targetDoc.hidden; // - if (wasHidden) runInAction(() => { - targetDoc.hidden = false; - docView?.props.bringToFront(targetDoc); - }); // if the target is hidden, un-hide it here. + if (wasHidden) { + runInAction(() => { + targetDoc.hidden = false; + docView?.props.bringToFront(targetDoc); + }); // if the target is hidden, un-hide it here. + } const focusAndFinish = (didFocus: boolean) => { if (originatingDoc?.isPushpin) { if (!didFocus && !wasHidden) { // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 94a09eac4..13e5940d6 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -405,7 +405,7 @@ export namespace DragManager { Array.from(pdfBox).filter(pb => pb.width && pb.height).map((pb, i) => pb.getContext('2d')!.drawImage(pdfBoxSrc[i], 0, 0)); } [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => - ele.hasAttribute("style") && ((ele as any).style.pointerEvents = "none")); + (ele as any).style && ((ele as any).style.pointerEvents = "none")); dragDiv.appendChild(dragElement); if (dragElement !== ele) { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 2ce78f64f..9803f9782 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -149,7 +149,9 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? Doc.UserDoc().activeCollectionNestedBackground : - Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? Colors.BLACK : Colors.WHITE)) + Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? + Colors.BLACK : + "linear-gradient(#065fff, #85c1f9)")) )); break; //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6931d9896..e95371aaa 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -92,7 +92,7 @@ export class CollectionDockingView extends CollectionSubView() { DragManager.CompleteWindowDrag = undefined; } finishDrag?.(aborted); - } + }; } @undoBatch public CloseFullScreen = () => { @@ -316,7 +316,7 @@ export class CollectionDockingView extends CollectionSubView() { } this._goldenLayout.init(); this._goldenLayout.root.layoutManager.on('itemDropped', this.tabItemDropped); - this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart) + this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart); this._goldenLayout.root.layoutManager.on('activeContentItemChanged', this.stateChanged); } } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 04beed541..9b0acffdf 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1417,7 +1417,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString(); this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString(); - var htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!); + const htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!); this.props.docViewPath().lastElement().ContentDiv!.style.width = ""; this.props.docViewPath().lastElement().ContentDiv!.style.height = ""; const nativeWidth = this.layoutDoc[WidthSym](); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7fb2c235a..3e35039e3 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -168,8 +168,8 @@ export class CollectionFreeFormDocumentView extends DocComponent m + ":").join(" ") + ")") render() { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3f1771e68..c73823a2b 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -63,7 +63,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - const title = StrCast(this.dataDoc.title) + const title = StrCast(this.dataDoc.title); if (!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing (title.startsWith("-") || title.startsWith("@")) && this._editorView && !this.dataDoc["title-custom"] && (Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === "text")) { -- cgit v1.2.3-70-g09d2 From 594f585b74bf6ecec3795a4b780da619c66f341d Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 13 Apr 2022 10:57:08 -0400 Subject: added images to treeView bullets for docs with icons. fixed following pushpin links to prototypes to toggle delegates when shown. gave groups a default padding. fixed creating icons to not create unnecessary template docs. fixed toggling isPushpin. made summaries simple links instead of templates. --- src/client/documents/Documents.ts | 38 ++++++++++++---------- src/client/util/DocumentManager.ts | 5 +-- src/client/views/DocumentDecorations.tsx | 5 ++- src/client/views/StyleProvider.tsx | 13 ++++++-- src/client/views/collections/TreeView.scss | 2 +- src/client/views/collections/TreeView.tsx | 5 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 9 ++--- src/client/views/nodes/DocumentView.tsx | 8 +++-- src/client/views/nodes/LinkDocPreview.tsx | 9 ++++- 9 files changed, 59 insertions(+), 35 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 652f8e7e0..465cdf3ec 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -223,6 +223,7 @@ export class DocumentOptions { dockingConfig?: string; annotationOn?: Doc; isPushpin?: boolean; + isGroup?: boolean; // whether a collection should use a grouping UI behavior _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document // BACKGROUND GRID @@ -807,7 +808,7 @@ export namespace Docs { } export function FreeformDocument(documents: Array, options: DocumentOptions, id?: string) { - const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Freeform }, id); + const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id); documents.map(d => d.context = inst); return inst; } @@ -1317,22 +1318,25 @@ export namespace DocUtils { const _height = NumCast(doc._height); const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; - let fieldTemplate: Opt; - if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { - fieldTemplate = Docs.Create.TextDocument("", options); - } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); - } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); - } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); - } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); - } - const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); - - fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); - docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + if (docLayoutTemplate) { + Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + } else { + let fieldTemplate: Opt; + if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { + fieldTemplate = Docs.Create.TextDocument("", options); + } else if (doc.data instanceof PdfField) { + fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); + } else if (doc.data instanceof VideoField) { + fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); + } else if (doc.data instanceof AudioField) { + fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); + } else if (doc.data instanceof ImageField) { + fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); + } + const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); + docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + } } export function makeCustomView(doc: Doc, custom: boolean, layout: string) { Doc.setNativeView(doc); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 1d603e18f..fb50ecb4a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -176,12 +176,13 @@ export class DocumentManager { }); // if the target is hidden, un-hide it here. } const focusAndFinish = (didFocus: boolean) => { + const finalTargetDoc = docView?.Document ?? targetDoc; if (originatingDoc?.isPushpin) { if (!didFocus && !wasHidden) { // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal - targetDoc.hidden = !targetDoc.hidden; + finalTargetDoc.hidden = !finalTargetDoc.hidden; } } else { - targetDoc.hidden && (targetDoc.hidden = undefined); + finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined); !noSelect && docView?.select(false); } finished?.(); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 062ca1181..a8d91e0a2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -453,6 +453,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin); }); } + @computed get hasIcons() { + return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon"); + } render() { const bounds = this.Bounds; @@ -516,7 +519,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> - {!canDelete ?
: topBtn("close", "times", undefined, this.onCloseClick, "Close")} + {!canDelete ?
: topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, this.onCloseClick, "Close")} {titleArea} {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")} {hideResizers ? (null) : diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 9803f9782..dba290b46 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -3,10 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import { action, runInAction } from 'mobx'; +import { extname } from 'path'; import { Doc, Opt, StrListCast } from "../../fields/Doc"; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; -import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; +import { BoolCast, Cast, ImageCast, NumCast, StrCast } from "../../fields/Types"; import { DashColor, lightOrDark } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; @@ -88,7 +89,15 @@ export function DefaultStyleProvider(doc: Opt, props: Opt props?.styleProvider?.(doc, props, StyleProp.ShowTitle); const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + ((Math.abs(x * y) * 9301 + 49297) % 233280 / 233280) * (max - min); switch (property.split(":")[0]) { - case StyleProp.TreeViewIcon: return Doc.toIcon(doc, isOpen); + case StyleProp.TreeViewIcon: + const icon = Cast(doc?.layout_icon, Doc, null) ? Doc.expandTemplateLayout(Cast(doc?.layout_icon, Doc, null), doc) : undefined; + if (icon && ImageCast(doc?.icon, ImageCast(doc?.data))) { + const img = ImageCast(doc?.icon, ImageCast(doc?.data)); + const ext = extname(img.url.href); + const url = doc?.icon ? img.url.href : img.url.href.replace(ext, "_s" + ext); + return ; + } + return Doc.toIcon(doc, isOpen); case StyleProp.DocContents: return undefined; case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? "lightgrey" : "dimgrey"; case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null)); diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index 2e33d3564..7c75810d3 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -47,7 +47,7 @@ width: $TREE_BULLET_WIDTH; color: $medium-gray; margin-top: 3px; - transform: scale(1.3, 1.3); + // transform: scale(1.3, 1.3); // bcz: why was this here? It makes displaying images in the treeView for documents that have icons harder. border: #80808030 1px solid; border-radius: 4px; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index ff5c4bab1..c2d0983da 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -31,6 +31,7 @@ import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; import React = require("react"); +import { IconProp } from '@fortawesome/fontawesome-svg-core'; export interface TreeViewProps { treeView: CollectionTreeView; @@ -471,7 +472,7 @@ export class TreeView extends React.Component { onClick={this.bulletClick} style={this.props.treeView.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : { color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), - opacity: checked === "unchecked" ? undefined : 0.4 + opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4 }}> {this.props.treeView.outlineMode ? !(this.doc.text as RichTextField)?.Text ? (null) : @@ -484,7 +485,7 @@ export class TreeView extends React.Component { checked === "unchecked" ? "square" : !this.treeViewOpen ? "caret-right" : "caret-down"} />
- {this.onCheckedClick ? (null) : } + {this.onCheckedClick ? (null) : typeof iconType === "string" ? : iconType}
}
; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b8f687489..5405ab500 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -519,13 +519,8 @@ export class MarqueeView extends React.Component(selected); - Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations"); - summary._backgroundColor = "#e2ad32"; - portal.layoutKey = "layout_portal"; - portal.title = "document collection"; + const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" }); + const portal = Docs.Create.FreeformDocument(selected, { isGroup: true, backgroundColor: "transparent" }); DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summarizing", ""); this.props.addLiveTextDocument(summary); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 195f6c9e5..8bf9348c7 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -594,8 +594,12 @@ export class DocumentViewInternal extends DocComponent, zoom: boolean, setPushpin: boolean): void => { this.Document.ignoreClick = false; - this.Document._isLinkButton = !this.Document._isLinkButton; - setPushpin && (this.Document.isPushpin = this.Document._isLinkButton); + if (setPushpin) { + this.Document.isPushpin = !this.Document.isPushpin; + this.Document._isLinkButton = this.Document.isPushpin || this.Document._isLinkButton; + } else { + this.Document._isLinkButton = !this.Document._isLinkButton; + } if (this.Document._isLinkButton && !this.onClickHandler) { this.Document.followLinkZoom = zoom; this.Document.followLinkLocation = location; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index d5f3f3b4e..eca2e3cfd 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -15,6 +15,7 @@ import { DocumentView, DocumentViewSharedProps } from "./DocumentView"; import './LinkDocPreview.scss'; import React = require("react"); import { DocumentType } from '../../documents/DocumentTypes'; +import { DragManager } from '../../util/DragManager'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -137,7 +138,13 @@ export class LinkDocPreview extends React.Component { @computed get previewHeader() { return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
-
+
{ + DragManager.StartDocumentDrag([this._infoRef.current!], + new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY); + e.stopPropagation(); + LinkDocPreview.Clear(); + }}> {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title}

{StrCast(this._linkDoc.description)}

-- cgit v1.2.3-70-g09d2 From f4446ed5f08a483abaeab3551b6e62d0a41915c8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 14 Apr 2022 10:11:43 -0400 Subject: fixed pushpin behavior when there are multiple links to the pushpin (they all toggle). changed lightboxview to allow stepping into links when doc has no contents. --- src/client/util/LinkManager.ts | 12 +++++++----- src/client/views/LightboxView.tsx | 6 ++++-- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index df2c02a8d..0f3da0548 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -221,7 +221,9 @@ export class LinkManager { const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView; const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs); - const followLinks = linkDocList.length ? (sourceDoc.isPushpin ? linkDocList : [linkDocList[0]]) : []; + const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1); + var count = 0; + const allFinished = () => ++count === followLinks.length && finished?.(); followLinks.forEach(async linkDoc => { if (linkDoc) { const target = (sourceDoc === linkDoc.anchor1 ? linkDoc.anchor2 : sourceDoc === linkDoc.anchor2 ? linkDoc.anchor1 : @@ -232,20 +234,20 @@ export class LinkManager { const tour = DocListCast(target[fieldKey]).reverse(); LightboxView.SetLightboxDoc(currentContext, undefined, tour); setTimeout(LightboxView.Next); - finished?.(); + allFinished(); } else { 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); + DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, allFinished); } } else { - finished?.(); + allFinished(); } } else { - finished?.(); + allFinished(); } }); } diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index ec3bf6c18..b39b5020c 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -36,7 +36,7 @@ export class LightboxView extends React.Component { @observable private static _tourMap: Opt = []; // list of all tours available from the current target private static _savedState: Opt<{ panX: Opt, panY: Opt, scale: Opt, scrollTop: Opt }>; private static _history: Opt<{ doc: Doc, target?: Doc }[]> = []; - private static _future: Opt = []; + @observable private static _future: Opt = []; private static _docView: Opt; static path: { doc: Opt, target: Opt, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt, saved: Opt<{ panX: Opt, panY: Opt, scale: Opt, scrollTop: Opt }> }[] = []; @action public static SetLightboxDoc(doc: Opt, target?: Doc, future?: Doc[], layoutTemplate?: Doc) { @@ -195,7 +195,9 @@ export class LightboxView extends React.Component { const coll = LightboxView._docTarget; if (coll) { const fieldKey = Doc.LayoutFieldKey(coll); - LightboxView.SetLightboxDoc(coll, undefined, [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + "-annotations"])]); + const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + "-annotations"])]; + const links = DocListCast(coll.links).map(link => LinkManager.getOppositeAnchor(link, coll)).filter(doc => doc).map(doc => doc!); + LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links); TabDocView.PinDoc(coll, { hidePresBox: true }); } } -- cgit v1.2.3-70-g09d2 From 74ef5f7a8c60dc68a1262077f7502d0535377b88 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 14 Apr 2022 11:49:28 -0400 Subject: fixed following links to documents in iconified collections/text to deiconify. fixed ctrl-x to cut (not iconify). changed lightboxview to add to future docs when stepping into. added placeholder for indicating how many documents are left in lightbox future --- src/client/util/DocumentManager.ts | 14 +++++++++++++- src/client/views/DocumentDecorations.tsx | 8 ++++---- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/LightboxView.tsx | 11 ++++------- src/client/views/nodes/DocumentView.tsx | 15 +++++++++------ src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 ++-- 6 files changed, 33 insertions(+), 21 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fb50ecb4a..607a3d6bf 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -197,7 +197,14 @@ export class DocumentManager { (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; if (!docView && annoContainerView) { - annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below + if (annoContainerView.props.Document.layoutKey === "layout_icon") { + annoContainerView.iconify(() => this.jumpToDocument( + targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, + finished, originalTarget, noSelect, presZoom)); + return; + } else { + annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below + } } if (focusView) { !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox @@ -217,6 +224,11 @@ export class DocumentManager { targetDocContextView.props.focus(targetDocContextView.rootDoc, { willZoom, afterFocus: async () => { targetDocContext._viewTransition = undefined; + if (targetDocContext.layoutKey === "layout_icon") { + targetDocContextView.iconify(() => this.jumpToDocument( + targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, + finished, originalTarget, noSelect, presZoom)); + } return ViewAdjustment.doNothing; } }); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index a8d91e0a2..78a8a4c7e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -145,11 +145,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P } _iconifyTimeout: NodeJS.Timeout | undefined; - onCloseClick = () => { + onCloseClick = (force = false) => { if (this.canDelete) { const views = SelectionManager.Views().slice().filter(v => v); - const icons = this._iconifyTimeout ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon"); - const others = this._iconifyTimeout ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon"); + const icons = this._iconifyTimeout || force ? views : views.filter(view => view.rootDoc.layoutKey === "layout_icon"); + const others = this._iconifyTimeout || force ? [] : views.filter(view => view.rootDoc.layoutKey !== "layout_icon"); icons.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document)); others.forEach(dv => dv.iconify()); others.length && (this._iconifyTimeout = setTimeout(() => { @@ -519,7 +519,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P left: bounds.x - this._resizeBorderWidth / 2, top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> - {!canDelete ?
: topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, this.onCloseClick, "Close")} + {!canDelete ?
: topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(), "Close")} {titleArea} {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")} {hideResizers ? (null) : diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 91e0503b3..e3031d2c5 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -275,7 +275,7 @@ export class KeyManager { const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":"); SelectionManager.Views().length && navigator.clipboard.writeText(text); - DocumentDecorations.Instance.onCloseClick(); + DocumentDecorations.Instance.onCloseClick(true); stopPropagation = false; preventDefault = false; } diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index b39b5020c..59ed0dc92 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -63,7 +63,7 @@ export class LightboxView extends React.Component { } } if (future) { - this._future = future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length); + this._future = [...(this._future ?? []), ...(this.LightboxDoc ? [this.LightboxDoc] : []), ...future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),]; } this._doc = doc; this._layoutTemplate = layoutTemplate; @@ -118,7 +118,7 @@ export class LightboxView extends React.Component { addDocTab = LightboxView.AddDocTab; @action public static Next() { const doc = LightboxView._doc!; - const target = LightboxView._docTarget = LightboxView._future?.pop(); + const target = LightboxView._docTarget = this._future?.pop(); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement(); @@ -163,11 +163,8 @@ export class LightboxView extends React.Component { const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); if (docView) { LightboxView._docTarget = target; - const focusSpeed = 1000; - doc._viewTransition = `transform ${focusSpeed}ms`; if (!target) docView.ComponentView?.shrinkWrap?.(); else docView.focus(target, { willZoom: true, scale: 0.9 }); - setTimeout(() => doc._viewTransition = undefined, focusSpeed); } else { LightboxView.SetLightboxDoc(doc, target); @@ -232,7 +229,7 @@ export class LightboxView extends React.Component { const doc = LightboxView._doc; const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.(); - else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true }); + //else target && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button })); })} Document={LightboxView.LightboxDoc} @@ -272,7 +269,7 @@ export class LightboxView extends React.Component { () => LightboxView.LightboxDoc && LightboxView._future?.length ? "" : "none", e => { e.stopPropagation(); LightboxView.Next(); - })} + }, this.future()?.length.toString())}
{ diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0e22b1b3f..4380cb0bc 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -452,7 +452,7 @@ export class DocumentViewInternal extends DocComponent this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false)); - const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here + const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing; this.props.focus(options?.docTransform ? anchor : this.rootDoc, { ...options, afterFocus: (didFocus: boolean) => @@ -1243,15 +1243,15 @@ export class DocumentView extends React.Component { return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) }; } - public iconify() { + public iconify(finished?: () => void) { this.ComponentView?.updateIcon?.(); const layoutKey = Cast(this.Document.layoutKey, "string", null); if (layoutKey !== "layout_icon") { - this.switchViews(true, "icon"); + this.switchViews(true, "icon", finished); if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.Document.deiconifyLayout = layoutKey.replace("layout_", ""); } else { const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null); - this.switchViews(deiconifyLayout ? true : false, deiconifyLayout); + this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished); this.Document.deiconifyLayout = undefined; this.props.bringToFront(this.rootDoc); } @@ -1262,12 +1262,15 @@ export class DocumentView extends React.Component { Doc.setNativeView(this.props.Document); custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined); } - switchViews = action((custom: boolean, view: string) => { + switchViews = action((custom: boolean, view: string, finished?: () => void) => { this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc setTimeout(action(() => { this.setCustomView(custom, view); this.docView && (this.docView._animateScalingTo = 1); // expand it - setTimeout(action(() => this.docView && (this.docView._animateScalingTo = 0)), this.docView!._animateScaleTime - 10); + setTimeout(action(() => { + this.docView && (this.docView._animateScalingTo = 0); + finished?.(); + }), this.docView!._animateScaleTime - 10); }), this.docView!._animateScaleTime - 10); }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index b6713d878..ac291801b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -758,7 +758,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() }); let tr = state.tr.addMark(sel.from, sel.to, splitter); if (sel.from !== sel.to) { - const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), unrendered: true }); + const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: "#" + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true }); const href = targetHref ?? Doc.localServerPath(anchor); if (anchor !== anchorDoc) this.addDocument(anchor); tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { @@ -859,7 +859,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => { autoHeight && this.props.setHeight?.(marginsHeight + Math.max(sidebarHeight, textHeight)); }, { fireImmediately: true }); - this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks + this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l)); this._cachedLinks = newLinks; -- cgit v1.2.3-70-g09d2 From 605137223d8afa0eaf8818de5b556601fca8441f Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Thu, 14 Apr 2022 17:23:49 -0400 Subject: Create smooth record and playback of tracking. --- src/client/util/DragManager.ts | 2 +- src/client/views/MainView.scss | 5 ++++ src/client/views/MainView.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 32 ++++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 8d4b98d88..db9986a41 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -216,7 +216,7 @@ export namespace DragManager { ) { // stop an 'accidental' on-click drag that may have occured if the user is in presenting mode // note: dragData.dropAction is only undefined when the element itself being dragged without being selected - if (Doc.UserDoc()?.isPresenting && dragData.dropAction === undefined) return false; + if (Doc.UserDoc()?.presentationMode === 'recording' && dragData.dropAction === undefined) return false; const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 15cd2c144..3799b6b13 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -29,6 +29,11 @@ left: calc(100% + 5px); z-index: 1; pointer-events: none; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 10px; } .mainView-snapLines { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8c0795881..b98c80068 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -528,6 +528,7 @@ export class MainView extends React.Component { searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> + {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ?
{this.userDoc?.presentationMode}
: <>}
; } @computed get snapLines() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e2ea81392..75855f5d9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -50,6 +50,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); +import e = require("connect-flash"); export const panZoomSchema = createSchema({ _panX: "number", @@ -117,6 +118,8 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z).map(ele => ele.ele); } @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); } @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); } @@ -956,8 +959,37 @@ export class CollectionFreeFormView extends CollectionSubView { + // need the first for subtraction + let first = null; + + this.storedMovements.forEach(movement => { + if (first === null) first = movement.time; + + // set the pan to what was stored + setTimeout(() => { + this.Document._panX = movement.panX; + this.Document._panY = movement.panY; + }, movement.time - first) + }) + + // for now, clear the movements + this.storedMovements = [] + } + @action setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) { + // if not presenting, just retrace the movements + if (Doc.UserDoc()?.presentationMode === 'watching') { + this.followMovements() + return; + } + + if (Doc.UserDoc()?.presentationMode === 'recording') { + // store as many movments as possible + this.storedMovements.push({time: new Date().getTime(), panX, panY}) + } + if (!this.isAnnotationOverlay && clamp) { // this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc); -- cgit v1.2.3-70-g09d2 From 605ab32150c5b2b9d84eb817d3086796b72964df Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Thu, 14 Apr 2022 17:46:15 -0400 Subject: fix: fixed panning with finger interaction --- src/client/util/InteractionUtils.tsx | 2 ++ src/client/views/GestureOverlay.tsx | 13 +++++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 2 ++ 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 3e051dec8..675dff00b 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -286,6 +286,8 @@ export namespace InteractionUtils { // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2 case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); + case TOUCHTYPE: + return e.pointerType === TOUCHTYPE; default: return e.pointerType === type; } } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index f034e06ad..75a757280 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -85,7 +85,6 @@ export class GestureOverlay extends Touchable { const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches); const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches); const nt: (React.Touch | Touch)[] = Array.from(e.touches); - console.log("getNewTouches"); this._hands.forEach((hand) => { for (let i = 0; i < e.targetTouches.length; i++) { const pt = e.targetTouches.item(i); @@ -112,7 +111,6 @@ export class GestureOverlay extends Touchable { } onReactTouchStart = (te: React.TouchEvent) => { - console.log("react touch start"); document.removeEventListener("touchmove", this.onReactHoldTouchMove); document.removeEventListener("touchend", this.onReactHoldTouchEnd); if (RadialMenu.Instance?._display === true) { @@ -131,6 +129,7 @@ export class GestureOverlay extends Touchable { // pen is also a touch, but with a radius of 0.5 (at least with the surface pens) // and this seems to be the only way of differentiating pen and touch on touch events if (pt.radiusX > 1 && pt.radiusY > 1) { + Doc.UserDoc().activeInkTool = InkTool.None; this.prevPoints.set(pt.identifier, pt); } } @@ -209,7 +208,6 @@ export class GestureOverlay extends Touchable { } onReactTouchMove = (e: TouchEvent) => { - console.log("react touch move"); const nts: any = this.getNewTouches(e); this._holdTimer && clearTimeout(this._holdTimer); this._holdTimer = undefined; @@ -500,6 +498,10 @@ export class GestureOverlay extends Touchable { @action onPointerDown = (e: React.PointerEvent) => { + if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { + CurrentUserUtils.SelectedTool = InkTool.None; + return; + } if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { CurrentUserUtils.SelectedTool = InkTool.Write; @@ -631,7 +633,8 @@ export class GestureOverlay extends Touchable { } // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document - if (!actionPerformed) { + console.log("dispatch gesture", CurrentUserUtils.SelectedTool); + if (!actionPerformed && CurrentUserUtils.SelectedTool !== InkTool.None) { const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]); newPoints.pop(); console.log("getting to bezier math"); @@ -650,11 +653,9 @@ export class GestureOverlay extends Touchable { (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y)); if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0]; this._points = controlPoints; - this.dispatchGesture(GestureUtils.Gestures.Stroke); // TODO: nda - check inks to group here checkInksToGroup(); - } this._points = []; this._timeStamps = []; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 908f08f88..c9465e7a4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -704,6 +704,8 @@ export class CollectionFreeFormView extends CollectionSubView { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { + console.log("is touch"); + Doc.UserDoc().activeInkTool = InkTool.None; if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (this.tryDragCluster(e, this._hitCluster)) { -- cgit v1.2.3-70-g09d2 From 969ab00c44859b932884bb32fef1b1e8b11d5448 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 24 Apr 2022 15:01:00 -0400 Subject: from last --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 33 +++++++++++----------- src/client/views/collections/TreeView.tsx | 12 ++++---- 3 files changed, 22 insertions(+), 25 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index bf722157a..c7309d15e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -389,7 +389,7 @@ export class CurrentUserUtils { const textDoc = Docs.Create.TreeDocument([], { title: "Slide", _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, - backgroundColor: "transparent", system: true, cloneFieldFilter: new List(["system"]) + backgroundColor: "white", system: true, cloneFieldFilter: new List(["system"]) }); Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text'); FormattedTextBox.SelectOnLoad = textDoc[Id]; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 0eb94c394..5bdd0c0ac 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,32 +1,23 @@ -import { action, computed, observable } from "mobx"; -import ReactLoading from 'react-loading'; -import * as rp from 'request-promise'; +import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx"; import CursorField from "../../../fields/CursorField"; -import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc"; +import { Doc, Opt, Field, DocListCast, AclPrivate, StrListCast } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { WebField } from "../../../fields/URLField"; -import { GetEffectiveAcl, TraceMobx } from "../../../fields/util"; +import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; -import { returnFalse, Utils } from "../../../Utils"; +import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils"; import { DocServer } from "../../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { Networking } from "../../Network"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { DragManager, dropActionType } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { InteractionUtils } from "../../util/InteractionUtils"; -import { SelectionManager } from "../../util/SelectionManager"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; -import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; -import { OverlayView } from "../OverlayView"; -import { CollectionView, CollectionViewProps, CollectionViewType } from "./CollectionView"; import React = require("react"); +import ReactLoading from 'react-loading'; +import * as rp from 'request-promise'; +import { Networking } from "../../Network"; export interface SubCollectionViewProps extends CollectionViewProps { @@ -481,4 +472,12 @@ export function CollectionSubView(moreProps?: X) { return CollectionSubView; } - +import { DragManager, dropActionType } from "../../util/DragManager"; +import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox"; +import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView"; +import { SelectionManager } from "../../util/SelectionManager"; +import { OverlayView } from "../OverlayView"; +import { GetEffectiveAcl, TraceMobx } from "../../../fields/util"; \ No newline at end of file diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 61c7ff78e..6213850d8 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,7 +1,8 @@ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; @@ -9,7 +10,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils, returnOne } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -21,17 +22,14 @@ import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from "../EditableView"; import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss'; -import { DocumentView, DocumentViewProps, StyleProviderFunc, DocumentViewInternal } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; -import { KeyValueBox } from '../nodes/KeyValueBox'; -import { SliderBox } from '../nodes/SliderBox'; -import { StyleProp, testDocProps } from '../StyleProvider'; +import { StyleProp } from '../StyleProvider'; import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; import React = require("react"); -import { IconProp } from '@fortawesome/fontawesome-svg-core'; export interface TreeViewProps { treeView: CollectionTreeView; -- cgit v1.2.3-70-g09d2 From 40ccb702d1dddf0ce9f15e047feb321353f01580 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 27 Apr 2022 14:55:04 -0400 Subject: fixed following pushpin links to text selections to toggle the text box (since markers don't have a hidden flag for toggling). fixed pdf next/prev page and # pages. fixed pdf selections to not include
's which apparently have the wrong bounds. --- src/client/util/DocumentManager.ts | 11 ++++++----- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 10 ++++++++-- src/client/views/pdf/PDFViewer.tsx | 8 +++----- 4 files changed, 18 insertions(+), 13 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 607a3d6bf..cc3196baa 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -168,15 +168,17 @@ export class DocumentManager { originalTarget = originalTarget ?? targetDoc; const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; const docView = getFirstDocView(targetDoc, originatingDoc); - const wasHidden = targetDoc.hidden; // + const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); + const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field + const wasHidden = resolvedTarget.hidden; if (wasHidden) { runInAction(() => { - targetDoc.hidden = false; - docView?.props.bringToFront(targetDoc); + resolvedTarget.hidden = false; + docView?.props.bringToFront(resolvedTarget); }); // if the target is hidden, un-hide it here. } const focusAndFinish = (didFocus: boolean) => { - const finalTargetDoc = docView?.Document ?? targetDoc; + const finalTargetDoc = docView?.Document ?? resolvedTarget; if (originatingDoc?.isPushpin) { if (!didFocus && !wasHidden) { // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal finalTargetDoc.hidden = !finalTargetDoc.hidden; @@ -188,7 +190,6 @@ export class DocumentManager { finished?.(); return false; }; - const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); const annoContainerView = annotatedDoc && getFirstDocView(annotatedDoc); const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9d738180b..88000ecbd 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -460,7 +460,7 @@ export class DocumentViewInternal extends DocComponent this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false)); const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here - const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing; + const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing; this.props.focus(options?.docTransform ? anchor : this.rootDoc, { ...options, afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus) : ViewAdjustment.doNothing), focusSpeed ?? 0)) diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 23749d479..b556b0ef1 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -157,8 +157,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.prevAnnotation(); public nextAnnotation = () => this._pdfViewer?.nextAnnotation(); - public backPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) - 1; return true; }; - public forwardPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) + 1; return true; }; + public backPage = () => { + this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1); + return true; + }; + public forwardPage = () => { + this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1); + return true; + }; public gotoPage = (p: number) => this.Document._curPage = p; @undoBatch diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 92a69c02b..b58966173 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -167,7 +167,7 @@ export class PDFViewer extends React.Component { }); if (i === this.props.pdf.numPages - 1) { this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], - page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], i); + page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages); } })))); this.props.Document.scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72; @@ -299,9 +299,7 @@ export class PDFViewer extends React.Component { @action gotoPage = (p: number) => { - if (this._pdfViewer?._setDocumentViewerElement?.offsetParent) { - this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); - } + this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); } @action @@ -419,7 +417,7 @@ export class PDFViewer extends React.Component { const clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { const rect = clientRects.item(i); - if (rect && rect.width !== this._mainCont.current.clientWidth) { + if (rect && rect.width !== this._mainCont.current.clientWidth && rect.width) { const scaleX = this._mainCont.current.offsetWidth / boundingRect.width; const annoBox = document.createElement("div"); annoBox.className = "marqueeAnnotator-annotationBox"; -- cgit v1.2.3-70-g09d2 From 2192b6cd994e71812d335a9902ef4e4ae296314b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 27 Apr 2022 23:33:19 -0400 Subject: fixed following links (jumpToDocument) to work properly with markers and hidden flags in nested arrangements. pushpin behaviors had been broken for scrolling to a location within a text document and hiding the document if no scroll was needed. unhiding hidden images/pdfs wasn't working when following a pushpin to an annotation on them. --- src/client/util/DocumentManager.ts | 26 +++++++++++----------- src/client/views/nodes/DocumentView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 7 ++++-- 3 files changed, 19 insertions(+), 16 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index cc3196baa..9febd4302 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -167,15 +167,15 @@ export class DocumentManager { ): Promise => { originalTarget = originalTarget ?? targetDoc; const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; - const docView = getFirstDocView(targetDoc, originatingDoc); + var docView = getFirstDocView(targetDoc, originatingDoc); const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field - const wasHidden = resolvedTarget.hidden; + var wasHidden = resolvedTarget.hidden; if (wasHidden) { runInAction(() => { - resolvedTarget.hidden = false; + resolvedTarget.hidden = false; // if the target is hidden, un-hide it here. docView?.props.bringToFront(resolvedTarget); - }); // if the target is hidden, un-hide it here. + }); } const focusAndFinish = (didFocus: boolean) => { const finalTargetDoc = docView?.Document ?? resolvedTarget; @@ -188,16 +188,15 @@ export class DocumentManager { !noSelect && docView?.select(false); } finished?.(); - return false; }; - const annoContainerView = annotatedDoc && getFirstDocView(annotatedDoc); + const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc); const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined; const targetDocContext = contextDoc || annotatedDoc; const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; - if (!docView && annoContainerView) { + if ((!docView && targetDoc.type !== DocumentType.MARKER) && annoContainerView) { if (annoContainerView.props.Document.layoutKey === "layout_icon") { annoContainerView.iconify(() => this.jumpToDocument( targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, @@ -221,6 +220,8 @@ export class DocumentManager { createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); } else { // otherwise try to get a view of the context of the target if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. + wasHidden = wasHidden || targetDocContextView.rootDoc.hidden; + targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden targetDocContext._viewTransition = "transform 500ms"; targetDocContextView.props.focus(targetDocContextView.rootDoc, { willZoom, afterFocus: async () => { @@ -240,24 +241,24 @@ export class DocumentManager { finished?.(); } else { // no timecode means we need to find the context view and focus on our target const findView = (delay: number) => { - const retryDocView = getFirstDocView(targetDoc); // test again for the target view snce we presumably created the context above by focusing on it - if (retryDocView) { // we found the target in the context + const retryDocView = getFirstDocView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it + if (retryDocView) { // we found the target in the context. Doc.linkFollowHighlight(retryDocView.rootDoc); - retryDocView.props.focus(targetDoc, { + retryDocView.focus(targetDoc, { willZoom, afterFocus: (didFocus: boolean) => new Promise(res => { !noSelect && focusAndFinish(didFocus); res(ViewAdjustment.doNothing); }) }); // focus on the target in the context - } else if (delay > 1500) { + } else if (delay > 1000) { // we didn't find the target, so it must have moved out of the context. Go back to just creating it. if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc); if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target } } else { - setTimeout(() => findView(delay + 250), 250); + setTimeout(() => findView(delay + 200), 200); } }; findView(0); @@ -270,7 +271,6 @@ export class DocumentManager { } } } - } export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) { const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 88000ecbd..9d738180b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -460,7 +460,7 @@ export class DocumentViewInternal extends DocComponent this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false)); const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here - const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing; + const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing; this.props.focus(options?.docTransform ? anchor : this.rootDoc, { ...options, afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus) : ViewAdjustment.doNothing), focusSpeed ?? 0)) diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 41e2fc266..756d6c2d6 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -813,6 +813,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }; let start = 0; + this._didScroll = false; // assume we don't need to scroll. if we do, this will get set to true in handleScrollToSelextion when we dispatch the setSelection below if (this._editorView && textAnchorId) { const editor = this._editorView; const ret = findAnchorFrag(editor.state.doc.content, editor); @@ -832,7 +833,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } - return this._focusSpeed; + return this._didScroll ? this._focusSpeed : undefined; // if we actually scrolled, then return some focusSpeed } // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc. @@ -1128,6 +1129,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } }); } + _didScroll = false; setupEditor(config: any, fieldKey: string) { const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.props.fieldKey]); const rtfField = Cast((!curText && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField); @@ -1142,7 +1144,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const scrollRef = self._scrollRef.current; const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined; const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined; - if ((topOff || botOff) && scrollRef) { + if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) { const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE); const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale; if (this._focusSpeed !== undefined) { @@ -1150,6 +1152,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } else { scrollRef.scrollTo({ top: scrollPos }); } + this._didScroll = true; } return true; }, -- cgit v1.2.3-70-g09d2 From cf0f4d9fe144d05e8604d5224066b0e86879a3de Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 28 Apr 2022 00:08:36 -0400 Subject: fixes for pushpin links to anchors on pdfviews --- src/client/util/DocumentManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 9febd4302..412d5a169 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -178,7 +178,7 @@ export class DocumentManager { }); } const focusAndFinish = (didFocus: boolean) => { - const finalTargetDoc = docView?.Document ?? resolvedTarget; + const finalTargetDoc = resolvedTarget; if (originatingDoc?.isPushpin) { if (!didFocus && !wasHidden) { // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal finalTargetDoc.hidden = !finalTargetDoc.hidden; @@ -261,7 +261,7 @@ export class DocumentManager { setTimeout(() => findView(delay + 200), 200); } }; - findView(0); + setTimeout(() => findView(0), 0); } } else { // there's no context view so we need to create one first and try again when that finishes const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget); -- cgit v1.2.3-70-g09d2 From 778432158e5f0bcdcb2e68ff6872a5c9335ef3af Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 28 Apr 2022 10:49:32 -0400 Subject: made sharing menu a developer feature. made link relationships support non-symmetry --- src/client/util/LinkManager.ts | 9 ++++++--- src/client/views/PropertiesView.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 11 +++++++++-- src/client/views/nodes/LinkDocPreview.tsx | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 10 ++++++---- 5 files changed, 23 insertions(+), 11 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 0f3da0548..7ba7a1d4c 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -29,6 +29,7 @@ export class LinkManager { public static currentLink: Opt; public static get Instance() { return LinkManager._instance; } public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb); + public static AutoKeywords = "keywords:Usages"; static links: Doc[] = []; constructor() { LinkManager._instance = this; @@ -149,9 +150,11 @@ export class LinkManager { public getRelatedGroupedLinks(anchor: Doc): Map> { const anchorGroups = new Map>(); this.relatedLinker(anchor).forEach(link => { - if (!link.linkRelationship || link?.linkRelationship !== "-ungrouped-") { - const group = anchorGroups.get(StrCast(link.linkRelationship)); - anchorGroups.set(StrCast(link.linkRelationship), group ? [...group, link] : [link]); + if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") { + const relation = StrCast(link.linkRelationship); + const anchorRelation = relation.indexOf(":") ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation; + const group = anchorGroups.get(anchorRelation); + anchorGroups.set(anchorRelation, group ? [...group, link] : [link]); } else { // if link is in no groups then put it in default group const group = anchorGroups.get("*"); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index b63395c76..21c688421 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1362,7 +1362,7 @@ export class PropertiesView extends React.Component { {this.fieldsSubMenu} - {this.sharingSubMenu} + {isNovice ? null : this.sharingSubMenu} {isNovice ? null : this.filtersSubMenu} diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 9b79db319..d2e6e8579 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -17,6 +17,7 @@ import { DocumentView } from "./DocumentView"; import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; import { TaskCompletionBox } from "./TaskCompletedBox"; import React = require("react"); +import { DocumentType } from "../../documents/DocumentTypes"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -158,7 +159,9 @@ export class DocumentLinksButton extends React.Component +
{ (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 28915674d..375434933 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -85,7 +85,7 @@ export class LinkDocPreview extends React.Component { anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; - const automaticLink = this._linkDoc.linkRelationship === "automatic"; + const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords; if (automaticLink) { // automatic links specify the target in the link info, not the source const linkTarget = anchor; this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 756d6c2d6..29117794e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -349,7 +349,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp autoLink = () => { if (this._editorView) { const newAutoLinks = new Set(); - const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === "automatic"); + const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords); const f = this._editorView.state.selection.from; const t = this._editorView.state.selection.to; var tr = this._editorView.state.tr as any; @@ -382,14 +382,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set) => { const editorView = this._editorView; if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) { - const flattened1 = this.findInNode(editorView, editorView.state.doc, StrCast(target.title).replace(/^@/, "")); + const autoLinkTerm = StrCast(target.title).replace(/^@/, ""); + const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm); var alink: Doc | undefined; flattened1.forEach((flat, i) => { - const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, StrCast(target.title).replace(/^@/, "")); + const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm); this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; alink = alink ?? (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && - Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, "automatic")!); + Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, + LinkManager.AutoKeywords)!); newAutoLinks.add(alink); const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() }); const sel = flattened[i]; -- cgit v1.2.3-70-g09d2 From 59a22f3a007b201e68ac2f3e1d62e0ca5e66488a Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 28 Apr 2022 12:13:44 -0400 Subject: fixed feedback dragging link button. fixed warnings. added some assymetric linkRelationships. --- src/client/documents/Documents.ts | 3 ++- src/client/util/DocumentManager.ts | 2 +- src/client/util/LinkManager.ts | 2 +- src/client/views/SidebarAnnos.tsx | 2 +- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/linking/LinkMenu.tsx | 4 +--- src/client/views/nodes/DocumentLinksButton.scss | 2 ++ src/client/views/nodes/DocumentLinksButton.tsx | 10 +++++----- src/client/views/nodes/DocumentView.tsx | 4 ++-- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 ++-- src/client/views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/search/SearchBox.tsx | 2 +- src/server/ApiManagers/UploadManager.ts | 4 ++-- 15 files changed, 24 insertions(+), 23 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0bb873667..087d32a60 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1094,10 +1094,11 @@ export namespace DocUtils { export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) { broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1); return DocUtils.ActiveRecordings.map(audio => - DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording link", "recording timeline")); + DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline")); } export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) { + if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? "Commentary:Comments On" : "link"; const sv = DocumentManager.Instance.getDocumentView(source.doc); if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return; if (target.doc === Doc.UserDoc()) return undefined; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 412d5a169..b6c28d2fe 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -167,7 +167,7 @@ export class DocumentManager { ): Promise => { originalTarget = originalTarget ?? targetDoc; const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; - var docView = getFirstDocView(targetDoc, originatingDoc); + const docView = getFirstDocView(targetDoc, originatingDoc); const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field var wasHidden = resolvedTarget.hidden; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 7ba7a1d4c..9445533dc 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -152,7 +152,7 @@ export class LinkManager { this.relatedLinker(anchor).forEach(link => { if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") { const relation = StrCast(link.linkRelationship); - const anchorRelation = relation.indexOf(":") ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation; + const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation; const group = anchorGroups.get(anchorRelation); anchorGroups.set(anchorRelation, group ? [...group, link] : [link]); } else { diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 43a02d029..04c0565ea 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -61,7 +61,7 @@ export class SidebarAnnos extends React.Component { FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; this.allMetadata.map(tag => target[tag] = tag); - DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup"); + DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline comment:comment on"); this.addDocument(target); this._stackRef.current?.focusDocument(target); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f0b3d70a0..3f72052ae 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -278,7 +278,7 @@ export class CollectionFreeFormView extends CollectionSubView
: -
+
{this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node -
DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}> @@ -256,7 +255,8 @@ export class DocumentLinksButton extends React.Component +
: diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9d738180b..8570f6fff 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent d.anchor1 === this.props.Document); if (!portalLink) { const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" }); - DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to"); + DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to:portal from"); } this.Document.followLinkLocation = "inPlace"; this.Document.followLinkZoom = true; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index aadad5ffa..5982d4d66 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -152,7 +152,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1); return true; - }; + } public forwardPage = () => { this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1); return true; - }; + } public gotoPage = (p: number) => this.Document._curPage = p; @undoBatch diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 00c03875b..427e05edb 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -275,7 +275,7 @@ export class RichTextRules { this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3)))); } const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: rawdocid.replace(/^:/, ""), _width: 500, _height: 500, }, docid); - DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, "portal to", undefined); + DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, "portal to:portal from", undefined); const fstate = this.TextBox.EditorView?.state; if (fstate && selection) { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 5fe2a5ab1..9257cb75e 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -112,7 +112,7 @@ export class SearchBox extends ViewBoxBaseComponent() { if (this.props.linkFrom) { const linkFrom = this.props.linkFrom(); if (linkFrom) { - DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }, "Link"); + DocUtils.MakeLink({ doc: linkFrom }, { doc: linkTo }); } } }); diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 5e0cd67cc..83a2bc79b 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -14,7 +14,7 @@ import { normalize } from "path"; import RouteSubscriber from "../RouteSubscriber"; const imageDataUri = require('image-data-uri'); import { SolrManager } from "./SearchManager"; -let fs = require('fs'); +const fs = require('fs'); export enum Directory { parsed_files = "parsed_files", @@ -265,7 +265,7 @@ export default class UploadManager extends ApiManager { } if (deleteFiles) { const path = serverPathToFile(Directory.images, ""); - let regex = new RegExp(`${deleteFiles}.*`); + const regex = new RegExp(`${deleteFiles}.*`); fs.readdirSync(path).filter((f: any) => regex.test(f)).map((f: any) => fs.unlinkSync(path + f)); } return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => { -- cgit v1.2.3-70-g09d2 From 4f6b6306803437851959e9ad214fd61a14207a20 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Thu, 28 Apr 2022 16:34:31 -0400 Subject: added basic code for subgrouping transcribed text --- src/client/util/InteractionUtils.tsx | 1 + src/client/views/GestureOverlay.tsx | 2 +- src/client/views/InkTranscription.tsx | 71 +++++++++++++++++++++- src/client/views/LightboxView.tsx | 3 +- src/client/views/Touchable.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DocumentLinksButton.tsx | 2 + src/client/views/nodes/button/FontIconBox.tsx | 56 ++--------------- 8 files changed, 86 insertions(+), 55 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 675dff00b..e2c5fab64 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -357,6 +357,7 @@ export namespace InteractionUtils { } export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean { + console.log("getting here"); for (const touch of newTouches) { if (touch) { const oldTouch = oldTouches.get(touch.identifier); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 5f0311d38..e689cd43c 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -212,7 +212,7 @@ export class GestureOverlay extends Touchable { const nts: any = this.getNewTouches(e); this._holdTimer && clearTimeout(this._holdTimer); this._holdTimer = undefined; - + document.dispatchEvent( new CustomEvent>("dashOnTouchMove", { diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index ea5d5a1ec..3b464db10 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -5,6 +5,7 @@ import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { InkData, InkField } from "../../fields/InkField"; import { Cast, NumCast, StrCast } from '../../fields/Types'; +import { aggregateBounds } from '../../Utils'; import { DocumentType } from "../documents/DocumentTypes"; import './InkTranscription.scss'; @@ -15,12 +16,18 @@ export class InkTranscription extends React.Component { @observable _mathRef: any; @observable _textRegister: any; @observable _textRef: any; + private lastJiix: any; private currGroup?: Doc; + private wordsBoundMapping: any; + private inkDocs: Doc[]; + private ffView: any; constructor(props: Readonly<{}>) { super(props); InkTranscription.Instance = this; + this.wordsBoundMapping = new Map(); + this.inkDocs = new Array(); } componentWillUnmount() { @@ -98,8 +105,10 @@ export class InkTranscription extends React.Component { return this._textRef = r; } - transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => { + transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean, ffView?: any) => { if (!groupDoc) return; + this.inkDocs = inkDocs; + this.ffView = ffView; const validInks = inkDocs.filter(s => s.type === DocumentType.INK); @@ -137,8 +146,20 @@ export class InkTranscription extends React.Component { }; } + mmToPixel = (mm: number) => { + return ((96 * mm) / 25.4); + } + + calcBounds = (coords: any) => { + // find max and min x values and subtract + const max = Math.max(...coords); + const min = Math.min(...coords); + return max - min; + } + exportInk = (e: any, ref: any) => { const exports = e.detail.exports; + console.log(e); if (exports) { if (exports['application/x-latex']) { const latex = exports['application/x-latex']; @@ -152,12 +173,60 @@ export class InkTranscription extends React.Component { ref.editor.clear(); } else if (exports['text/plain']) { + if (exports['application/vnd.myscript.jiix']) { + this.lastJiix = JSON.parse(exports['application/vnd.myscript.jiix']); + // console.log("lastJiix:", this.lastJiix); + // TODO: nda - go through all the words and look at x,y values and figure out the selection + this.lastJiix.words.forEach((word: any) => { + // map each word to a new map + this.wordsBoundMapping[word] = new Map(); + if (word.items) { + word.items.map((item: any) => { + this.wordsBoundMapping.set(word, {xList: item.X, yList: item.Y, width: this.calcBounds(item.X), height: this.calcBounds(item.Y)}); + }) + } + }) + // make groups for each of the words + // loop through this.wordsBoundMapping + + this.wordsBoundMapping.forEach((value: any, key: any) => { + const xList = value.xList; + const yList = value.yList; + const width = value.width; + const height = value.height; + const bounds: { x: number, y: number, width?: number, height?: number }[] = [] + // loop through xList + for (let i = 0; i < xList.length; i++) { + const x = xList[i]; + const y = yList[i]; + const newBounds = { x: x, y: y }; + bounds.push(newBounds); + } + const aggregBounds = aggregateBounds(bounds, 0, 0); + + const marqViewRef = this.ffView?._marqueeViewRef.current; + + // set the vals for bounds in marqueeView + if (marqViewRef) { + marqViewRef._downX = aggregBounds.x; + marqViewRef._downY = aggregBounds.y; + marqViewRef._lastX = aggregBounds.r; + marqViewRef._lastY = aggregBounds.b; + } + + }); + + // console.log(this.wordsBoundMapping); + } const text = exports['text/plain']; console.log(text); if (this.currGroup) { + console.log("curr grouping"); this.currGroup.text = text; this.currGroup.title = text; + console.log(this.lastJiix); + console.log(this.currGroup); } ref.editor.clear(); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index ec3bf6c18..c8f6b5f0b 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -5,7 +5,7 @@ import "normalize.css"; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; @@ -17,6 +17,7 @@ import "./LightboxView.scss"; import { DocumentView } from './nodes/DocumentView'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; import { CollectionMenu } from './collections/CollectionMenu'; +import { utils } from 'mocha'; interface LightboxViewProps { PanelWidth: number; diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index 789756a78..c712e7ed3 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -127,11 +127,13 @@ export abstract class Touchable extends React.Component { } handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { + console.log("getting to handle1PointersMove"); e.stopPropagation(); e.preventDefault(); } handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { + console.log("getting to handle2PointersMove"); e.stopPropagation(); e.preventDefault(); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 023953658..7c11c252a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -702,9 +702,9 @@ export class CollectionFreeFormView extends CollectionSubView { + console.log("this is onPointerMove"); if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - console.log("is touch"); Doc.UserDoc().activeInkTool = InkTool.None; if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { @@ -825,6 +825,7 @@ export class CollectionFreeFormView extends CollectionSubView) => { + console.log("getting here yeet"); if (!e.cancelBubble) { const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); if (myTouches[0]) { @@ -836,6 +837,7 @@ export class CollectionFreeFormView extends CollectionSubView only want to do this when collection is selected' this.pan(myTouches[0]); } } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 7e6ca4248..f9d2b9917 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -21,6 +21,8 @@ import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; import { TaskCompletionBox } from "./TaskCompletedBox"; import React = require("react"); import { Transform } from "../../util/Transform"; +import { InkTool } from "../../../fields/InkField"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index fc358f106..85130e69e 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -757,13 +757,17 @@ export function createInkGroup(inksToGroup?: Doc[]) { ffView.props.removeDocument?.(selected); // TODO: nda - this is the code to actually get a new grouped collection const newCollection = marqViewRef?.getCollection(selected, undefined, [], true); + if (newCollection) { + newCollection.height = newCollection[HeightSym](); + newCollection.width = newCollection[WidthSym](); + } // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs newCollection && ffView.props.addDocument?.(newCollection); // TODO: nda - will probably need to go through and only remove the unprocessed selected docs ffView.unprocessedDocs = []; - InkTranscription.Instance.transcribeInk(newCollection, selected, false); + InkTranscription.Instance.transcribeInk(newCollection, selected, false, ffView); }); } CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); @@ -778,56 +782,6 @@ export function createInkGroup(inksToGroup?: Doc[]) { ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) { createInkGroup(); - // if (CurrentUserUtils.SelectedTool === InkTool.Write) { - // CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { - // const selected = ffView.unprocessedDocs; - // // loop through selected an get the bound - // const bounds: { x: number, y: number, width?: number, height?: number }[] = [] - - // selected.map(action(d => { - // const x = NumCast(d.x); - // const y = NumCast(d.y); - // const width = d[WidthSym](); - // const height = d[HeightSym](); - // bounds.push({x, y, width, height}); - // })) - - // const aggregBounds = aggregateBounds(bounds, 0, 0); - // const marqViewRef = ffView._marqueeViewRef.current; - - // // set the vals for bounds in marqueeView - // if (marqViewRef) { - // marqViewRef._downX = aggregBounds.x; - // marqViewRef._downY = aggregBounds.y; - // marqViewRef._lastX = aggregBounds.r; - // marqViewRef._lastY = aggregBounds.b; - // } - - // selected.map(action(d => { - // const dx = NumCast(d.x); - // const dy = NumCast(d.y); - // delete d.x; - // delete d.y; - // delete d.activeFrame; - // delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - // delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - // // calculate pos based on bounds - // if (marqViewRef?.Bounds) { - // d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; - // d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; - // } - // return d; - // })); - // ffView.props.removeDocument?.(selected); - // // TODO: nda - this is the code to actually get a new grouped collection - // const newCollection = marqViewRef?.getCollection(selected, undefined, [], true); - - // // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs - // newCollection && ffView.props.addDocument?.(newCollection); - // ffView.unprocessedDocs = []; - // }); - // } - // CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); if (checkResult) { return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? -- cgit v1.2.3-70-g09d2 From 7b08cb4fd86a7e7048e14c05ab980f5b008d18d8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 29 Apr 2022 13:57:42 -0400 Subject: cleaned up creation of icon templates. fixed labelBox to support padding and to work with multiple lines and min font sizes. --- src/client/util/CurrentUserUtils.ts | 141 +++++++++++++++------------------ src/client/util/DropConverter.ts | 5 ++ src/client/views/nodes/LabelBigText.js | 7 +- src/client/views/nodes/LabelBox.tsx | 46 ++++++++--- 4 files changed, 105 insertions(+), 94 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c7309d15e..7948aa70b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -9,7 +9,7 @@ import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; -import { ImageField, nullAudio } from "../../fields/URLField"; +import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; @@ -22,11 +22,10 @@ import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; -import { LabelBox } from "../views/nodes/LabelBox"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; import { DragManager } from "./DragManager"; -import { makeTemplate } from "./DropConverter"; +import { makeTemplate, MakeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; @@ -289,82 +288,68 @@ 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.LabelDocument({ - title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimgray", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - }); - // Docs.Create.TextDocument("", { - // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }) - // }); - // 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-rtf"] === undefined) { - const iconRtfView = Docs.Create.LabelDocument({ - title: "icon_" + DocumentType.RTF, _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), - _singleLine: false, _minFontSize: 18, _maxFontSize: 24, - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - }); - iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); - doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); - } - if (doc["template-icon-view-button"] === undefined) { - const iconBtnView = Docs.Create.FontIconDocument({ - title: "icon_" + DocumentType.BUTTON, _nativeHeight: 30, _nativeWidth: 30, - _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - }); - iconBtnView.isTemplateDoc = makeTemplate(iconBtnView, true, "icon_" + DocumentType.BUTTON); - doc["template-icon-view-button"] = new PrefetchProxy(iconBtnView); - } - if (doc["template-icon-view-img"] === undefined) { - const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 150, isTemplateDoc: true, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - }); - iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); - doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); - } - if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.ImageDocument("", { title: "icon", _showTitle: "title", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); - iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); - const proto = iconColView.proto as Doc; - proto["icon-nativeWidth"] = 360 / 4; - proto["icon-nativeHeight"] = 270 / 4; - proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); - doc["template-icon-view-col"] = new PrefetchProxy(iconColView); - } - if (doc["template-icon-view-video"] === undefined) { - const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); - iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID); - const proto = iconVidView.proto as Doc; - proto["icon-nativeWidth"] = 360 / 4; - proto["icon-nativeHeight"] = 270 / 4; - proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); - doc["template-icon-view-video"] = new PrefetchProxy(iconVidView); - } - if (doc["template-icon-view-pdf"] === undefined) { - const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); - iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); - const proto = iconPdfView.proto as Doc; - proto["icon-nativeWidth"] = 360 / 4; - proto["icon-nativeHeight"] = 270 / 4; - proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); - doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView); - } - if (doc["template-icons"] === undefined) { - doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-video"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); - } else { - const templateIconsDoc = Cast(doc["template-icons"], Doc, null); - const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; - DocListCastAsync(templateIconsDoc.data).then(async curIcons => { - curIcons && await Promise.all(curIcons); - requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); - }); + const templateIconsDoc = Cast(doc["template-icons"], Doc, null); + + const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc, finalize: (icon: Doc) => Doc = (doc: Doc) => doc) => { + const iconFieldName = type ? "icon_" + type : "icon"; + if (templateIconsDoc?.[iconFieldName] === undefined) { + const template = finalize(MakeTemplate(iconTemplate(), true, iconFieldName, templateField)); + if (templateIconsDoc) { + templateIconsDoc[iconFieldName] = new PrefetchProxy(template); + Doc.AddDocToList(templateIconsDoc, "data", template); + } + return template; + } + return templateIconsDoc[iconFieldName] as Doc; + }; + const finalizeIconTemplate = (icon: Doc) => { + const iconProto = Doc.GetProto(icon); + iconProto["icon-nativeWidth"] = iconProto.width; + iconProto["icon-nativeHeight"] = iconProto.height; + return icon; + }; + const iconList = [ + makeIconTemplate(undefined, "title", () => Docs.Create.LabelDocument({ + textTransform: "unset", letterSpacing: "unset", _backgroundColor: "dimgray", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, + onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + })), + makeIconTemplate(DocumentType.AUDIO, "title", () => Docs.Create.LabelDocument({ + textTransform: "unset", letterSpacing: "unset", _backgroundColor: "lightgreen", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, + onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + })), + makeIconTemplate(DocumentType.RTF, "text", () => Docs.Create.LabelDocument({ + _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, + onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + })), + makeIconTemplate(DocumentType.PDF, "title", () => Docs.Create.LabelDocument({ + textTransform: "unset", letterSpacing: "unset", _backgroundColor: "pink", _singleLine: false, _minFontSize: 10, _maxFontSize: 24, borderRounding: "5px", + _width: 75, _height: 125, _xPadding: 10, _yPadding: 10, + onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + })), + makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ + _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, + onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + })), + makeIconTemplate(DocumentType.IMG, "data", () => Docs.Create.ImageDocument("", { + _width: 150, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + })), + makeIconTemplate(DocumentType.COL, "icon", () => Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/noImage.png", { + _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + }), finalizeIconTemplate), + makeIconTemplate(DocumentType.VID, "icon", () => Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/noImage.png", { + _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + }), finalizeIconTemplate), + // makeIconTemplate(DocumentType.PDF, "data", () => Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/noImage.png", { + // _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true + // }), setIconProtoFields) + ]; + if (!templateIconsDoc) { + doc["template-icons"] = Docs.Create.TreeDocument(iconList, { title: "icon templates", _height: 75, system: true }); + iconList.map(ntype => (doc["template-icons"] as Doc)[StrCast(ntype.title)] = new PrefetchProxy(ntype)); } - return doc["template-icons"] as Doc; } static creatorBtnDescriptors(doc: Doc): { diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 082b6d8bd..076afd3a0 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -10,6 +10,11 @@ import { ImageField } from "../../fields/URLField"; import { ScriptingGlobals } from "./ScriptingGlobals"; import { listSpec } from "../../fields/Schema"; +export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined, templateField: string = "") { + if (templateField) Doc.GetProto(doc).title = templateField; /// the title determines which field is being templated + doc.isTemplateDoc = makeTemplate(doc, first, rename); + return doc; +} // // converts 'doc' into a template that can be used to render other documents. // the title of doc is used to determine which field is being templated, so diff --git a/src/client/views/nodes/LabelBigText.js b/src/client/views/nodes/LabelBigText.js index 02c36c4bc..6a212d9ea 100644 --- a/src/client/views/nodes/LabelBigText.js +++ b/src/client/views/nodes/LabelBigText.js @@ -193,11 +193,12 @@ export default function BigText(element, options) { if (fontSize < options.minimumFontSize) { parentStyle.display = "flex"; parentStyle.alignItems = "center"; - style.whiteSpace = "pre-wrap"; style.textAlign = "center"; style.visibility = ""; - style.fontSize = "18px"; - style.lineHeight = "20px"; + style.fontSize = options.minimumFontSize + "px"; + style.lineHeight = ""; + style.overflow = "hidden"; + style.textOverflow = "ellipsis"; style.top = ""; style.left = ""; style.margin = ""; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index d539ca9b8..dfff59005 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -27,14 +27,19 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro return `<${fieldType.name} fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`; //e.g., "" } private dropDisposer?: DragManager.DragDropDisposer; - + private _timeout: any; componentDidMount() { this.props.setContentView?.(this); } + componentWillUnMount() { + this._timeout && clearTimeout(this._timeout); + } getTitle() { - return this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : - typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); + return this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) : + this.props.label ? this.props.label : + typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : + StrCast(this.rootDoc.title); } protected createDropTarget = (ele: HTMLDivElement) => { @@ -73,8 +78,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro @observable _mouseOver = false; @computed get hoverColor() { return this._mouseOver ? StrCast(this.layoutDoc._hoverBackgroundColor) : "unset"; } - fitTextToBox = (r: any) => { - BigText(r, { + fitTextToBox = (r: any): any => { + const params = { rotateText: null, fontSizeFactor: 1, minimumFontSize: NumCast(this.layoutDoc._minFontSize), @@ -83,11 +88,25 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro horizontalAlign: "center", verticalAlign: "center", textAlign: "center", - whiteSpace: "nowrap" - }); + whiteSpace: this.layoutDoc._singleLine ? "nowrap" : "pre-wrap" + }; + this._timeout = undefined; + if (!r) return params; + if (!r.offsetHeight || !r.offsetWidth) return this._timeout = setTimeout(() => this.fitTextToBox(r)); + const parent = r.parentNode; + const parentStyle = parent.style; + parentStyle.display = ""; + parentStyle.alignItems = ""; + r.setAttribute("style", ""); + r.style.width = this.layoutDoc._singleLine ? "" : "100%"; + + r.style.textOverflow = "ellipsis"; + r.style.overflow = "hidden"; + BigText(r, params); } // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { + this.fitTextToBox(null);// this causes mobx to trigger re-render when data changes const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []); const missingParams = params?.filter(p => !this.paramsDoc[p]); params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ... @@ -104,16 +123,17 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit", letterSpacing: StrCast(this.layoutDoc.letterSpacing), textTransform: StrCast(this.layoutDoc.textTransform) as any, + paddingLeft: NumCast(this.rootDoc._xPadding), + paddingRight: NumCast(this.rootDoc._xPadding), + paddingTop: NumCast(this.rootDoc._yPadding), + paddingBottom: NumCast(this.rootDoc._yPadding), width: this.props.PanelWidth(), height: this.props.PanelHeight(), whiteSpace: this.layoutDoc._singleLine ? "pre" : "pre-wrap" }} > - { - if (r) { - if (!r.offsetWidth || !r.offsetHeight) setTimeout(() => this.fitTextToBox(r)); - else this.fitTextToBox(r); - } - }}>{label.startsWith("#") ? (null) : label} + this.fitTextToBox(r))}> + {label.startsWith("#") ? (null) : label} +
{!missingParams?.length ? (null) : missingParams.map(m =>
{m}
)} -- cgit v1.2.3-70-g09d2 From 32a7dfc6eae2def8d1b792edcb550316948f6ab0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 29 Apr 2022 16:22:46 -0400 Subject: more cleanup --- src/client/documents/Documents.ts | 2 + src/client/util/CurrentUserUtils.ts | 80 +++++++++++++------------------------ 2 files changed, 30 insertions(+), 52 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 741868a99..dd3e13cfd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -156,6 +156,8 @@ export class DocumentOptions { _timelineLabel?: boolean; // whether the document exists on a timeline "_carousel-caption-xMargin"?: number; "_carousel-caption-yMargin"?: number; + "icon-nativeWidth"?: number; + "icon-nativeHeight"?: number; x?: number; y?: number; z?: number; // whether document is in overlay (1) or not (0 or undefined) diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7948aa70b..57313cecc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -290,65 +290,41 @@ export class CurrentUserUtils { static setupDefaultIconTemplates(doc: Doc) { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); - const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc, finalize: (icon: Doc) => Doc = (doc: Doc) => doc) => { - const iconFieldName = type ? "icon_" + type : "icon"; - if (templateIconsDoc?.[iconFieldName] === undefined) { - const template = finalize(MakeTemplate(iconTemplate(), true, iconFieldName, templateField)); + const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => { + const iconFieldName = "icon" + (type ? "_" + type : ""); + if (!templateIconsDoc?.[iconFieldName]) { + const template = MakeTemplate(iconTemplate(), true, iconFieldName, templateField); if (templateIconsDoc) { templateIconsDoc[iconFieldName] = new PrefetchProxy(template); Doc.AddDocToList(templateIconsDoc, "data", template); + } else { + return template; } - return template; } - return templateIconsDoc[iconFieldName] as Doc; - }; - const finalizeIconTemplate = (icon: Doc) => { - const iconProto = Doc.GetProto(icon); - iconProto["icon-nativeWidth"] = iconProto.width; - iconProto["icon-nativeHeight"] = iconProto.height; - return icon; }; - const iconList = [ - makeIconTemplate(undefined, "title", () => Docs.Create.LabelDocument({ - textTransform: "unset", letterSpacing: "unset", _backgroundColor: "dimgray", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, - onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - })), - makeIconTemplate(DocumentType.AUDIO, "title", () => Docs.Create.LabelDocument({ - textTransform: "unset", letterSpacing: "unset", _backgroundColor: "lightgreen", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, - onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - })), - makeIconTemplate(DocumentType.RTF, "text", () => Docs.Create.LabelDocument({ - _showTitle: "creationDate", textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, - onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - })), - makeIconTemplate(DocumentType.PDF, "title", () => Docs.Create.LabelDocument({ - textTransform: "unset", letterSpacing: "unset", _backgroundColor: "pink", _singleLine: false, _minFontSize: 10, _maxFontSize: 24, borderRounding: "5px", - _width: 75, _height: 125, _xPadding: 10, _yPadding: 10, - onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - })), - makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ - _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, - onDoubleClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - })), - makeIconTemplate(DocumentType.IMG, "data", () => Docs.Create.ImageDocument("", { - _width: 150, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - })), - makeIconTemplate(DocumentType.COL, "icon", () => Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/noImage.png", { - _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - }), finalizeIconTemplate), - makeIconTemplate(DocumentType.VID, "icon", () => Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/noImage.png", { - _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - }), finalizeIconTemplate), - // makeIconTemplate(DocumentType.PDF, "data", () => Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/noImage.png", { - // _width: 360 / 4, _height: 270 / 4, _showTitle: "title", onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true - // }), setIconProtoFields) - ]; + const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); + const labelBox = (extra: object) => Docs.Create.LabelDocument({ + textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, system: true, onClick: deiconifyScript(), ...extra, + }); + const imageBox = (url: string, extra: object) => Docs.Create.ImageDocument(url, { + "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, + _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra + }); + const newIconsList = [ + makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), + makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), + makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), + makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), + makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), + makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), + makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), + makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })), + // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}), finalizeImageTemplate) + ].filter(d => d).map(d => d!); if (!templateIconsDoc) { - doc["template-icons"] = Docs.Create.TreeDocument(iconList, { title: "icon templates", _height: 75, system: true }); - iconList.map(ntype => (doc["template-icons"] as Doc)[StrCast(ntype.title)] = new PrefetchProxy(ntype)); + doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true }); + newIconsList.map(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d)); } } -- cgit v1.2.3-70-g09d2 From 45062251a0ee91c5ece36141730a75dde03814dd Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 29 Apr 2022 16:35:05 -0400 Subject: fixed creating collection icons to capture pfs (and other things with canvases) --- src/client/util/CurrentUserUtils.ts | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 30 ++++++++++++++++++---- src/client/views/nodes/PDFBox.tsx | 1 + 3 files changed, 27 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 57313cecc..03c63e737 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -320,7 +320,7 @@ export class CurrentUserUtils { makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })), - // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}), finalizeImageTemplate) + // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) ].filter(d => d).map(d => d!); if (!templateIconsDoc) { doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3f72052ae..452a6bfcf 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1422,12 +1422,32 @@ export class CollectionFreeFormView extends CollectionSubView { + if (oldDiv.childNodes) { + for (let i = 0; i < oldDiv.childNodes.length; i++) { + this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement); + } + } + if (oldDiv instanceof HTMLCanvasElement) { + const canvas = oldDiv; + const img = document.createElement('img'); // create a Image Element + img.src = canvas.toDataURL(); //image source + img.style.width = canvas.style.width; + img.style.height = canvas.style.height; + const newCan = newDiv as HTMLCanvasElement; + const parEle = newCan.parentElement as HTMLElement; + parEle.removeChild(newCan); + parEle.appendChild(img); + } + } + updateIcon = () => { - this.props.docViewPath().lastElement().ContentDiv!.style.width = (this.layoutDoc[WidthSym]()).toString(); - this.props.docViewPath().lastElement().ContentDiv!.style.height = (this.layoutDoc[HeightSym]()).toString(); - const htmlString = this._mainCont && new XMLSerializer().serializeToString(this.props.docViewPath().lastElement().ContentDiv!); - this.props.docViewPath().lastElement().ContentDiv!.style.width = ""; - this.props.docViewPath().lastElement().ContentDiv!.style.height = ""; + const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; + const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; + newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); + newDiv.style.height = (this.layoutDoc[HeightSym]()).toString(); + this.replaceCanvases(docViewContent, newDiv); + const htmlString = this._mainCont && new XMLSerializer().serializeToString(newDiv); const nativeWidth = this.layoutDoc[WidthSym](); const nativeHeight = this.layoutDoc[HeightSym](); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 668c6c8fc..f386c7bbd 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -290,6 +290,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { const funcs: ContextMenuProps[] = []; funcs.push({ description: "Copy path", event: () => this.pdfUrl && Utils.CopyText(Utils.prepend("") + this.pdfUrl.url.pathname), icon: "expand-arrows-alt" }); + funcs.push({ description: "update icon", event: () => this.pdfUrl && this.updateIcon(), icon: "expand-arrows-alt" }); //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } -- cgit v1.2.3-70-g09d2 From a6aec8759298fba41b486a973796662056f47da0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 29 Apr 2022 17:05:10 -0400 Subject: changed the way labelBox splits words to allow .&- added icon for webBox --- src/client/util/CurrentUserUtils.ts | 1 + .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/DocumentView.scss | 6 ++++-- src/client/views/nodes/LabelBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/WebBoxRenderer.js | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 03c63e737..9720343e1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -315,6 +315,7 @@ export class CurrentUserUtils { makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), + makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })), makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 452a6bfcf..5d1e1892c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1454,7 +1454,7 @@ export class CollectionFreeFormView extends CollectionSubView this.fitTextToBox(r))}> - {label.startsWith("#") ? (null) : label} + {label.startsWith("#") ? (null) : label.replace(/([\.\-&])/g, "$1 ")}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index f386c7bbd..69662d53a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -87,7 +87,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent/g, ""), width, height, scroll); + const val = (new ForeignHtmlRenderer(styleSheets)).renderToBase64Png(webUrl, html.replace(/docView-hack/g, 'documentView-hack').replace(/\n/g, "").replace(//g, ""), width, height, scroll); return val; } -- cgit v1.2.3-70-g09d2 From 1eb2c362b020b3cbe446bbc1585108129fda6977 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 01:11:54 -0400 Subject: Get storing pres data working, but is choppy due to mobx usage. --- src/client/apis/recording/recordingApi.ts | 300 -------------------- src/client/util/RecordingApi.ts | 303 +++++++++++++++++++++ src/client/views/Main.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 14 +- .../views/nodes/RecordingBox/RecordingView.tsx | 26 +- src/client/views/nodes/VideoBox.tsx | 14 + 7 files changed, 348 insertions(+), 316 deletions(-) delete mode 100644 src/client/apis/recording/recordingApi.ts create mode 100644 src/client/util/RecordingApi.ts (limited to 'src/client/util') diff --git a/src/client/apis/recording/recordingApi.ts b/src/client/apis/recording/recordingApi.ts deleted file mode 100644 index b57e675b7..000000000 --- a/src/client/apis/recording/recordingApi.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { CollectionFreeFormView } from "../../views/collections/collectionFreeForm"; -import React, { useState } from "react"; -import { IReactionDisposer, observable, reaction } from "mobx"; -import { NumCast } from "../../../fields/Types"; - -type Movement = { - time: number, - panX: number, - panY: number, -} - -type Presentation = { - movements: Array - meta: Object, - startDate: Date | null, -} - -export class RecordingApi { - - private static NULL_PRESENTATION: Presentation = { - movements: [], - meta: {}, - startDate: null, - } - - // instance variables - private currentPresentation: Presentation; - private isRecording: boolean; - private absoluteStart: number; - - - // create static instance and getter for global use - @observable static _instance: RecordingApi; - public static get instance(): RecordingApi { return RecordingApi._instance } - public constructor() { - // init the global instance - RecordingApi._instance = this; - - // init the instance variables - this.currentPresentation = RecordingApi.NULL_PRESENTATION - this.isRecording = false; - this.absoluteStart = -1; - - // used for tracking movements in the view frame - this.disposeFunc = null; - - // for now, set playFFView - this.playFFView = null; - this.timers = null; - } - - // little helper :) - private get isInitPresenation(): boolean { - return this.currentPresentation.startDate === null - } - - public start = (view: CollectionFreeFormView, meta?: Object): Error | undefined => { - // check if already init a presentation - if (!this.isInitPresenation) { - console.error('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.') - return new Error('[recordingApi.ts] start()') - } - - // (1a) get start date for presenation - const startDate = new Date() - // (1b) set start timestamp to absolute timestamp - this.absoluteStart = startDate.getTime() - - // (2) assign meta content if it exists - this.currentPresentation.meta = meta || {} - // (3) assign start date to currentPresenation - this.currentPresentation.startDate = startDate - // (4) set isRecording true to allow trackMovements - this.isRecording = true - } - - public clear = (): Error | undefined => { - // TODO: maybe archive the data? - if (this.isRecording) { - console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() or finish() first') - return new Error('[recordingApi.ts] clear()') - } - - // clear presenation data - this.currentPresentation = RecordingApi.NULL_PRESENTATION - // clear isRecording - this.isRecording = false - // clear absoluteStart - this.absoluteStart = -1 - - // clear the disposeFunc - this.disposeFunc = null - } - - public pause = (): Error | undefined => { - if (this.currentPresentation.startDate === null) { - console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first') - return new Error('[recordingApi.ts] pause(): no presenation') - } - // don't allow track movments - this.isRecording = false - - // set relativeStart to the pausedTimestamp - const timestamp = new Date().getTime() - this.absoluteStart = timestamp - } - - public resume = () => { - const timestamp = new Date().getTime() - const startTimestamp = this.currentPresentation.startDate?.getTime() - if (!startTimestamp) { - console.error('[recordingApi.ts] resume() failed: no presentation data. try calling start() first') - return new Error('[recordingApi.ts] pause()') - } - - // update absoluteStart to bridge the paused time - const absoluteTimePaused = timestamp - this.absoluteStart - this.absoluteStart = absoluteTimePaused - } - - public finish = (): Error | Presentation => { - if (this.isInitPresenation) { - console.error('[recordingApi.ts] finish() failed: no presentation data. try calling start() first') - return new Error('[recordingApi.ts] finish()') - } - - // return a copy of the the presentation data - return { ...this.currentPresentation } - } - - private trackMovements = (panX: number, panY: number): Error | undefined => { - // ensure we are recording - if (!this.isRecording) { - console.error('[recordingApi.ts] pause() failed: recording is paused()') - return new Error('[recordingApi.ts] pause()') - } - - // get the relative time - const timestamp = new Date().getTime() - const relativeTime = timestamp - this.absoluteStart - - // make new movement struct - const movement: Movement = { time: relativeTime, panX, panY } - - // add that movement to the current presentation data's movement array - this.currentPresentation.movements.push(movement) - } - - // instance variable for the FFView - private disposeFunc: IReactionDisposer | null; - - // set the FFView that will be used in a reaction to track the movements - public setRecordingFFView = (view: CollectionFreeFormView): void => { - // set the view to the current view - if (view === null) return; - - //this.recordingFFView = view; - // set the reaction to track the movements - this.disposeFunc = reaction( - () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1) }), - (res) => (res.x !== -1 && res.y !== -1) && this.trackMovements(res.x, res.y) - ) - - // for now, set the most recent recordingFFView to the playFFView - this.playFFView = view; - } - - // call on dispose function to stop tracking movements - public removeRecordingFFView = (): void => { - this.disposeFunc?.(); - this.disposeFunc = null; - } - - // TODO: extract this into different class with pause and resume recording - private playFFView: CollectionFreeFormView | null; - private timers: Timer[] | null; - - public followMovements = (presentation: Presentation): undefined | Error => { - if (presentation.startDate === null || this.playFFView === null) { - return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') - } - - const document = this.playFFView.Document - const { movements } = presentation - this.timers = movements.map(movement => { - const { panX, panY, time } = movement - return new Timer(() => { - document._panX = panX; - document._panY = panY; - // TODO: consider cleaning this array to null or some state - }, time) - }) - } - - public pauseMovements = (): undefined | Error => { - if (this.playFFView === null) { - return new Error('[recordingApi.ts] pauseMovements() failed: no view') - } - // TODO: set userdoc presentMode to browsing - this.timers?.forEach(timer => timer.pause()) - } - - public resumeMovements = (): undefined | Error => { - if (this.playFFView === null) { - return new Error('[recordingApi.ts] resumeMovements() failed: no view') - } - this.timers?.forEach(timer => timer.resume()) - } - - // public followMovements = (presentation: Presentation): undefined | Error => { - // if (presentation.startDate === null || this.playFFView === null) { - // return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') - // } - - // const document = this.playFFView.Document - // const { movements } = presentation - // movements.forEach(movement => { - // const { panX, panY, time } = movement - // // set the pan to what was stored - // setTimeout(() => { - // document._panX = panX; - // document._panY = panY; - // }, time) - // }) - // } - // Unfinished code for tracing multiple free form views - // export let pres: Map = new Map() - - // export function AddRecordingFFView(ffView: CollectionFreeFormView): void { - // pres.set(ffView, - // reaction(() => ({ x: ffView.panX, y: ffView.panY }), - // (pt) => RecordingApi.trackMovements(ffView, pt.x, pt.y))) - // ) - // } - - // export function RemoveRecordingFFView(ffView: CollectionFreeFormView): void { - // const disposer = pres.get(ffView); - // disposer?.(); - // pres.delete(ffView) - // } -} - -/** Represents the `setTimeout` with an ability to perform pause/resume actions - * citation: https://stackoverflow.com/questions/3969475/javascript-pause-settimeout - */ -export class Timer { - private _start: Date; - private _remaining: number; - private _durationTimeoutId?: NodeJS.Timeout; - private _callback: (...args: any[]) => void; - private _done = false; - get done () { - return this._done; - } - - public constructor(callback: (...args: any[]) => void, ms = 0) { - this._callback = () => { - callback(); - this._done = true; - }; - this._remaining = ms; - this.resume(); - } - - /** pauses the timer */ - public pause(): Timer { - if (this._durationTimeoutId && !this._done) { - this._clearTimeoutRef(); - this._remaining -= new Date().getTime() - this._start.getTime(); - } - return this; - } - - /** resumes the timer */ - public resume(): Timer { - if (!this._durationTimeoutId && !this._done) { - this._start = new Date; - this._durationTimeoutId = setTimeout(this._callback, this._remaining); - } - return this; - } - - /** - * clears the timeout and marks it as done. - * - * After called, the timeout will not resume - */ - public clearTimeout() { - this._clearTimeoutRef(); - this._done = true; - } - - private _clearTimeoutRef() { - if (this._durationTimeoutId) { - clearTimeout(this._durationTimeoutId); - this._durationTimeoutId = undefined; - } - } - -} \ No newline at end of file diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts new file mode 100644 index 000000000..c4f76282c --- /dev/null +++ b/src/client/util/RecordingApi.ts @@ -0,0 +1,303 @@ +import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; +import { IReactionDisposer, observable, reaction } from "mobx"; +import { NumCast } from "../../fields/Types"; + +type Movement = { + time: number, + panX: number, + panY: number, +} + +type Presentation = { + movements: Array + meta: Object, + startDate: Date | null, +} + +export class RecordingApi { + + private static NULL_PRESENTATION: Presentation = { + movements: [], + meta: {}, + startDate: null, + } + + // instance variables + private currentPresentation: Presentation; + private isRecording: boolean; + private absoluteStart: number; + + + // create static instance and getter for global use + @observable static _instance: RecordingApi; + public static get Instance(): RecordingApi { return RecordingApi._instance } + public constructor() { + // init the global instance + RecordingApi._instance = this; + + // init the instance variables + this.currentPresentation = RecordingApi.NULL_PRESENTATION + this.isRecording = false; + this.absoluteStart = -1; + + // used for tracking movements in the view frame + this.disposeFunc = null; + + // for now, set playFFView + this.playFFView = null; + this.timers = null; + } + + // little helper :) + private get isInitPresenation(): boolean { + return this.currentPresentation.startDate === null + } + + public start = (meta?: Object): Error | undefined => { + // check if already init a presentation + if (!this.isInitPresenation) { + console.error('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.') + return new Error('[recordingApi.ts] start()') + } + + // (1a) get start date for presenation + const startDate = new Date() + // (1b) set start timestamp to absolute timestamp + this.absoluteStart = startDate.getTime() + + // (2) assign meta content if it exists + this.currentPresentation.meta = meta || {} + // (3) assign start date to currentPresenation + this.currentPresentation.startDate = startDate + // (4) set isRecording true to allow trackMovements + this.isRecording = true + } + + public clear = (): Error | Presentation => { + // TODO: maybe archive the data? + if (this.isRecording) { + console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() first') + return new Error('[recordingApi.ts] clear()') + } + + const presCopy = { ...this.currentPresentation } + + // clear presenation data + this.currentPresentation = RecordingApi.NULL_PRESENTATION + // clear isRecording + this.isRecording = false + // clear absoluteStart + this.absoluteStart = -1 + // clear the disposeFunc + this.disposeFunc = null + + return presCopy; + } + + public pause = (): Error | undefined => { + if (this.currentPresentation.startDate === null) { + console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first') + return new Error('[recordingApi.ts] pause(): no presenation') + } + // don't allow track movments + this.isRecording = false + + // set relativeStart to the pausedTimestamp + const timestamp = new Date().getTime() + this.absoluteStart = timestamp + } + + public resume = () => { + const timestamp = new Date().getTime() + const startTimestamp = this.currentPresentation.startDate?.getTime() + if (!startTimestamp) { + console.error('[recordingApi.ts] resume() failed: no presentation data. try calling start() first') + return new Error('[recordingApi.ts] pause()') + } + + // update absoluteStart to bridge the paused time + const absoluteTimePaused = timestamp - this.absoluteStart + this.absoluteStart = absoluteTimePaused + } + + // public finish = (): Error | Presentation => { + // if (this.isInitPresenation) { + // console.error('[recordingApi.ts] finish() failed: no presentation data. try calling start() first') + // return new Error('[recordingApi.ts] finish()') + // } + + // // return a copy of the the presentation data + // return { ...this.currentPresentation } + // } + + private trackMovements = (panX: number, panY: number): Error | undefined => { + // ensure we are recording + if (!this.isRecording) { + console.error('[recordingApi.ts] pause() failed: recording is paused()') + return new Error('[recordingApi.ts] pause()') + } + + // get the relative time + const timestamp = new Date().getTime() + const relativeTime = timestamp - this.absoluteStart + + // make new movement struct + const movement: Movement = { time: relativeTime, panX, panY } + + // add that movement to the current presentation data's movement array + this.currentPresentation.movements.push(movement) + } + + // instance variable for the FFView + private disposeFunc: IReactionDisposer | null; + + // set the FFView that will be used in a reaction to track the movements + public setRecordingFFView = (view: CollectionFreeFormView): void => { + // set the view to the current view + if (view === null || view === this.playFFView) return; + + //this.recordingFFView = view; + // set the reaction to track the movements + this.disposeFunc = reaction( + () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1) }), + (res) => (res.x !== -1 && res.y !== -1) && this.trackMovements(res.x, res.y) + ) + + // for now, set the most recent recordingFFView to the playFFView + this.playFFView = view; + } + + // call on dispose function to stop tracking movements + public removeRecordingFFView = (): void => { + this.disposeFunc?.(); + this.disposeFunc = null; + } + + // TODO: extract this into different class with pause and resume recording + private playFFView: CollectionFreeFormView | null; + private timers: Timer[] | null; + + // public followMovements = (presentation: Presentation): undefined | Error => { + // console.log(presentation) + // if (presentation.startDate === null || this.playFFView === null) { + // return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') + // } + + // const document = this.playFFView.Document + // const { movements } = presentation + // this.timers = movements.map(movement => { + // const { panX, panY, time } = movement + // return new Timer(() => { + // document._panX = panX; + // document._panY = panY; + // // TODO: consider cleaning this array to null or some state + // }, time) + // }) + // } + + public pauseMovements = (): undefined | Error => { + if (this.playFFView === null) { + return new Error('[recordingApi.ts] pauseMovements() failed: no view') + } + // TODO: set userdoc presentMode to browsing + this.timers?.forEach(timer => timer.pause()) + } + + public resumeMovements = (): undefined | Error => { + if (this.playFFView === null) { + return new Error('[recordingApi.ts] resumeMovements() failed: no view') + } + this.timers?.forEach(timer => timer.resume()) + } + + public followMovements = (presentation: Presentation): undefined | Error => { + if (presentation.startDate === null || this.playFFView === null) { + return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') + } + + const document = this.playFFView.Document + const { movements } = presentation + movements.forEach(movement => { + const { panX, panY, time } = movement + // set the pan to what was stored + setTimeout(() => { + document._panX = panX; + document._panY = panY; + }, time) + }) + } + // Unfinished code for tracing multiple free form views + // export let pres: Map = new Map() + + // export function AddRecordingFFView(ffView: CollectionFreeFormView): void { + // pres.set(ffView, + // reaction(() => ({ x: ffView.panX, y: ffView.panY }), + // (pt) => RecordingApi.trackMovements(ffView, pt.x, pt.y))) + // ) + // } + + // export function RemoveRecordingFFView(ffView: CollectionFreeFormView): void { + // const disposer = pres.get(ffView); + // disposer?.(); + // pres.delete(ffView) + // } +} + +/** Represents the `setTimeout` with an ability to perform pause/resume actions + * citation: https://stackoverflow.com/questions/3969475/javascript-pause-settimeout + */ +export class Timer { + private _start: Date; + private _remaining: number; + private _durationTimeoutId?: NodeJS.Timeout; + private _callback: (...args: any[]) => void; + private _done = false; + get done () { + return this._done; + } + + public constructor(callback: (...args: any[]) => void, ms = 0) { + this._callback = () => { + callback(); + this._done = true; + }; + this._remaining = ms; + this.resume(); + } + + /** pauses the timer */ + public pause(): Timer { + if (this._durationTimeoutId && !this._done) { + this._clearTimeoutRef(); + this._remaining -= new Date().getTime() - this._start.getTime(); + } + return this; + } + + /** resumes the timer */ + public resume(): Timer { + if (!this._durationTimeoutId && !this._done) { + this._start = new Date; + this._durationTimeoutId = setTimeout(this._callback, this._remaining); + } + return this; + } + + /** + * clears the timeout and marks it as done. + * + * After called, the timeout will not resume + */ + public clearTimeout() { + this._clearTimeoutRef(); + this._done = true; + } + + private _clearTimeoutRef() { + if (this._durationTimeoutId) { + clearTimeout(this._durationTimeoutId); + this._durationTimeoutId = undefined; + } + } + +} \ No newline at end of file diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 8560ccb29..517fe097c 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -8,6 +8,7 @@ import { AssignAllExtensions } from "../../extensions/General/Extensions"; import { Docs } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { LinkManager } from "../util/LinkManager"; +import { RecordingApi } from "../util/RecordingApi"; import { CollectionView } from "./collections/CollectionView"; import { MainView } from "./MainView"; @@ -36,5 +37,6 @@ AssignAllExtensions(); const expires = "expires=" + d.toUTCString(); document.cookie = `loadtime=${loading};${expires};path=/`; new LinkManager(); + new RecordingApi; ReactDOM.render(, document.getElementById('root')); })(); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 35bd9cf79..f74e526b6 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -54,7 +54,7 @@ import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import e = require("connect-flash"); -import { RecordingApi } from "../../../apis/recording/RecordingApi"; +import { RecordingApi } from "../../../util/RecordingApi"; export const panZoomSchema = createSchema({ _panX: "number", @@ -965,6 +965,9 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout).filter(doc => doc instanceof Doc); diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 86358e838..1b17476f7 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -1,7 +1,7 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { AudioField, VideoField } from "../../../../fields/URLField"; +import { VideoField } from "../../../../fields/URLField"; import { Upload } from "../../../../server/SharedMediaTypes"; import { ViewBoxBaseComponent } from "../../DocComponent"; import { FieldView } from "../FieldView"; @@ -9,6 +9,8 @@ import { VideoBox } from "../VideoBox"; import { RecordingView } from './RecordingView'; import { DocumentType } from "../../../documents/DocumentTypes"; +import { RecordingApi } from "../../../util/RecordingApi"; + @observer export class RecordingBox extends ViewBoxBaseComponent() { @@ -31,17 +33,19 @@ export class RecordingBox extends ViewBoxBaseComponent() { @action setResult = (info: Upload.FileInformation) => { - console.log("Setting result to " + info) + // console.log("Setting result to " + info) this.result = info - console.log(this.result.accessPaths.agnostic.client) + // console.log(this.result.accessPaths.agnostic.client) this.dataDoc.type = DocumentType.VID; - console.log(this.videoDuration) + // console.log(this.videoDuration) this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration; this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey); // this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined; // this.layoutDoc._fitWidth = undefined; - this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client); + this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client); + // stringify the presenation and store it + this.dataDoc[this.fieldKey + "-presentation"] = JSON.stringify(RecordingApi.Instance.clear()); } render() { diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index d99492095..870ef87d7 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -8,7 +8,7 @@ import { IconContext } from "react-icons"; import { Networking } from '../../../Network'; import { Upload } from '../../../../server/SharedMediaTypes'; -import { RecordingApi } from '../../../apis/recording/RecordingApi'; +import { RecordingApi } from '../../../util/RecordingApi'; enum RecordingStatus { @@ -57,11 +57,11 @@ export function RecordingView(props: IRecordingViewProps) { height: 720, }, // audio: true, - // audio: { - // echoCancellation: true, - // noiseSuppression: true, - // sampleRate: 44100 - // } + audio: { + echoCancellation: true, + noiseSuppression: true, + sampleRate: 44100 + } } useEffect(() => { @@ -159,7 +159,10 @@ export function RecordingView(props: IRecordingViewProps) { // } videoRecorder.current.onstart = (event: any) => { - setRecording(true); + setRecording(true); + // TODO: update names + // RecordingApi.Instance.clear(); + RecordingApi.Instance.start(); } videoRecorder.current.onstop = () => { @@ -172,7 +175,8 @@ export function RecordingView(props: IRecordingViewProps) { // reset the temporary chunks videoChunks = [] setRecording(false); - setFinished(true); + setFinished(true); + RecordingApi.Instance.pause(); } // recording paused @@ -182,12 +186,14 @@ export function RecordingView(props: IRecordingViewProps) { // reset the temporary chunks videoChunks = [] - setRecording(false); + setRecording(false); + RecordingApi.Instance.pause(); } videoRecorder.current.onresume = async (event: any) => { await startShowingStream(); - setRecording(true); + setRecording(true); + RecordingApi.Instance.resume(); } videoRecorder.current.start(200) diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index e57cb1abe..7364a64d9 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -28,6 +28,7 @@ import { AnchorMenu } from "../pdf/AnchorMenu"; import { StyleProp } from "../StyleProvider"; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; +import { RecordingApi } from "../../util/RecordingApi"; const path = require('path'); @@ -117,6 +118,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + // if presentation isn't null, call followmovements on the recording api + if (this.presentation) { + const err = RecordingApi.Instance.followMovements(this.presentation); + if (err) console.log(err); + } + + this._playing = true; const eleTime = this.player?.currentTime || 0; if (this.timeline) { -- cgit v1.2.3-70-g09d2 From 91746b4e9c570a7579e3396e2e31c73d8fa9915e Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 01:32:27 -0400 Subject: updated to subtracting timestamps for better smoothness --- src/client/util/RecordingApi.ts | 27 +++++++++++----------- .../views/nodes/RecordingBox/RecordingView.tsx | 2 ++ 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index c4f76282c..ab7642a90 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -11,7 +11,7 @@ type Movement = { type Presentation = { movements: Array meta: Object, - startDate: Date | null, + startTime: number | null, } export class RecordingApi { @@ -19,7 +19,7 @@ export class RecordingApi { private static NULL_PRESENTATION: Presentation = { movements: [], meta: {}, - startDate: null, + startTime: null, } // instance variables @@ -50,7 +50,7 @@ export class RecordingApi { // little helper :) private get isInitPresenation(): boolean { - return this.currentPresentation.startDate === null + return this.currentPresentation.startTime === null } public start = (meta?: Object): Error | undefined => { @@ -63,12 +63,12 @@ export class RecordingApi { // (1a) get start date for presenation const startDate = new Date() // (1b) set start timestamp to absolute timestamp - this.absoluteStart = startDate.getTime() + // this.absoluteStart = startDate.getTime() // (2) assign meta content if it exists this.currentPresentation.meta = meta || {} // (3) assign start date to currentPresenation - this.currentPresentation.startDate = startDate + this.currentPresentation.startTime = startDate.getTime() // (4) set isRecording true to allow trackMovements this.isRecording = true } @@ -89,15 +89,15 @@ export class RecordingApi { // clear absoluteStart this.absoluteStart = -1 // clear the disposeFunc - this.disposeFunc = null + this.removeRecordingFFView() return presCopy; } public pause = (): Error | undefined => { - if (this.currentPresentation.startDate === null) { + if (this.isInitPresenation) { console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first') - return new Error('[recordingApi.ts] pause(): no presenation') + return new Error('[recordingApi.ts] pause(): no presentation') } // don't allow track movments this.isRecording = false @@ -109,7 +109,7 @@ export class RecordingApi { public resume = () => { const timestamp = new Date().getTime() - const startTimestamp = this.currentPresentation.startDate?.getTime() + const startTimestamp = this.currentPresentation.startTime if (!startTimestamp) { console.error('[recordingApi.ts] resume() failed: no presentation data. try calling start() first') return new Error('[recordingApi.ts] pause()') @@ -137,12 +137,11 @@ export class RecordingApi { return new Error('[recordingApi.ts] pause()') } - // get the relative time + // get the time const timestamp = new Date().getTime() - const relativeTime = timestamp - this.absoluteStart // make new movement struct - const movement: Movement = { time: relativeTime, panX, panY } + const movement: Movement = { time: timestamp, panX, panY } // add that movement to the current presentation data's movement array this.currentPresentation.movements.push(movement) @@ -211,7 +210,7 @@ export class RecordingApi { } public followMovements = (presentation: Presentation): undefined | Error => { - if (presentation.startDate === null || this.playFFView === null) { + if (presentation.startTime === null || this.playFFView === null) { return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') } @@ -223,7 +222,7 @@ export class RecordingApi { setTimeout(() => { document._panX = panX; document._panY = panY; - }, time) + }, time - presentation.startTime) }) } // Unfinished code for tracing multiple free form views diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 870ef87d7..51cc44941 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -176,6 +176,7 @@ export function RecordingView(props: IRecordingViewProps) { videoChunks = [] setRecording(false); setFinished(true); + console.log("finished recording") RecordingApi.Instance.pause(); } @@ -187,6 +188,7 @@ export function RecordingView(props: IRecordingViewProps) { // reset the temporary chunks videoChunks = [] setRecording(false); + console.log("paused recording") RecordingApi.Instance.pause(); } -- cgit v1.2.3-70-g09d2 From 4dc4b0939d4e4afbc9f6db999ff80d434ef4ccc6 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 02:00:55 -0400 Subject: Added the presentation mode back in. However, pres isn't being recreated smoothly after being stored in the db. --- src/client/util/RecordingApi.ts | 21 +++++++++++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 ++++-- .../views/nodes/RecordingBox/RecordingView.tsx | 3 --- 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index ab7642a90..b53cba79d 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -1,6 +1,7 @@ import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; import { IReactionDisposer, observable, reaction } from "mobx"; import { NumCast } from "../../fields/Types"; +import { Doc } from "../../fields/Doc"; type Movement = { time: number, @@ -42,6 +43,7 @@ export class RecordingApi { // used for tracking movements in the view frame this.disposeFunc = null; + this.recordingFFView = null; // for now, set playFFView this.playFFView = null; @@ -60,6 +62,9 @@ export class RecordingApi { return new Error('[recordingApi.ts] start()') } + // update the presentation mode + Doc.UserDoc().presentationMode = 'recording' + // (1a) get start date for presenation const startDate = new Date() // (1b) set start timestamp to absolute timestamp @@ -80,6 +85,9 @@ export class RecordingApi { return new Error('[recordingApi.ts] clear()') } + // update the presentation mode + Doc.UserDoc().presentationMode = 'none' + const presCopy = { ...this.currentPresentation } // clear presenation data @@ -133,8 +141,8 @@ export class RecordingApi { private trackMovements = (panX: number, panY: number): Error | undefined => { // ensure we are recording if (!this.isRecording) { - console.error('[recordingApi.ts] pause() failed: recording is paused()') - return new Error('[recordingApi.ts] pause()') + console.error('[recordingApi.ts] trackMovements() failed: recording is paused()') + return new Error('[recordingApi.ts] trackMovements()') } // get the time @@ -149,11 +157,12 @@ export class RecordingApi { // instance variable for the FFView private disposeFunc: IReactionDisposer | null; + private recordingFFView: CollectionFreeFormView | null; // set the FFView that will be used in a reaction to track the movements public setRecordingFFView = (view: CollectionFreeFormView): void => { // set the view to the current view - if (view === null || view === this.playFFView) return; + if (view === this.recordingFFView || view === null) return; //this.recordingFFView = view; // set the reaction to track the movements @@ -163,7 +172,7 @@ export class RecordingApi { ) // for now, set the most recent recordingFFView to the playFFView - this.playFFView = view; + this.recordingFFView = view; } // call on dispose function to stop tracking movements @@ -194,6 +203,10 @@ export class RecordingApi { // }) // } + public setPlayFFView = (view: CollectionFreeFormView): void => { + this.playFFView = view + } + public pauseMovements = (): undefined | Error => { if (this.playFFView === null) { return new Error('[recordingApi.ts] pauseMovements() failed: no view') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f74e526b6..8bcf6f46f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -965,8 +965,10 @@ export class CollectionFreeFormView extends CollectionSubView { setRecording(true); - // TODO: update names // RecordingApi.Instance.clear(); RecordingApi.Instance.start(); } @@ -176,7 +175,6 @@ export function RecordingView(props: IRecordingViewProps) { videoChunks = [] setRecording(false); setFinished(true); - console.log("finished recording") RecordingApi.Instance.pause(); } @@ -188,7 +186,6 @@ export function RecordingView(props: IRecordingViewProps) { // reset the temporary chunks videoChunks = [] setRecording(false); - console.log("paused recording") RecordingApi.Instance.pause(); } -- cgit v1.2.3-70-g09d2 From a2173243c7a447527e2e86ec0a00998ed8b9cc40 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 03:30:07 -0400 Subject: Get play and pause to work. There is a bug with getting double called that was causing choppiness. --- src/client/util/RecordingApi.ts | 207 ++++++++++++++------- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 10 +- 3 files changed, 148 insertions(+), 71 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index b53cba79d..dfe8e98ca 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -183,11 +183,10 @@ export class RecordingApi { // TODO: extract this into different class with pause and resume recording private playFFView: CollectionFreeFormView | null; - private timers: Timer[] | null; + private timers: NodeJS.Timeout[] | null; - // public followMovements = (presentation: Presentation): undefined | Error => { - // console.log(presentation) - // if (presentation.startDate === null || this.playFFView === null) { + // public loadPresentation = (presentation: Presentation): undefined | Error => { + // if (presentation.startTime === null || this.playFFView === null) { // return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') // } @@ -195,48 +194,85 @@ export class RecordingApi { // const { movements } = presentation // this.timers = movements.map(movement => { // const { panX, panY, time } = movement - // return new Timer(() => { + + // const t = new Timer(() => { // document._panX = panX; // document._panY = panY; // // TODO: consider cleaning this array to null or some state - // }, time) + // }, time - presentation.startTime) + // t.pause() + // return t // }) + // console.log(this.timers) // } public setPlayFFView = (view: CollectionFreeFormView): void => { this.playFFView = view } + // public pauseMovements = (): undefined | Error => { + // console.log('[recordingApi.ts] pauseMovements()') + // if (this.playFFView === null) { + // return new Error('[recordingApi.ts] pauseMovements() failed: no view') + // } + // // TODO: set userdoc presentMode to browsing + // this.timers?.forEach(timer => timer.pause()) + // } + public pauseMovements = (): undefined | Error => { if (this.playFFView === null) { return new Error('[recordingApi.ts] pauseMovements() failed: no view') } - // TODO: set userdoc presentMode to browsing - this.timers?.forEach(timer => timer.pause()) - } - public resumeMovements = (): undefined | Error => { - if (this.playFFView === null) { - return new Error('[recordingApi.ts] resumeMovements() failed: no view') + if (!this._isPlaying) { + return new Error('[recordingApi.ts] pauseMovements() failed: not playing') } - this.timers?.forEach(timer => timer.resume()) + // TODO: set userdoc presentMode to browsing + //console.log('cleared timers', this.timers) + console.log(this.timers?.map(timer => clearTimeout(timer))) + console.log('[recordingApi.ts] pauseMovements()') + + this._isPlaying = false } - public followMovements = (presentation: Presentation): undefined | Error => { + // public resumeMovements = (): undefined | Error => { + // if (this.playFFView === null) { + // return new Error('[recordingApi.ts] resumeMovements() failed: no view') + // } + // console.log('resume') + // this.timers?.forEach(timer => timer.resume()) + // } + + private _isPlaying = false; + + public playMovements = (presentation: Presentation, timeViewed: number = 0): undefined | Error => { if (presentation.startTime === null || this.playFFView === null) { return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') } + if (this._isPlaying) { + return new Error('[recordingApi.ts] playMovements() failed: already playing') + } + this._isPlaying = true; + + console.log(timeViewed) + const document = this.playFFView.Document const { movements } = presentation - movements.forEach(movement => { + this.timers = movements.reduce((arr: NodeJS.Timeout[], movement) => { const { panX, panY, time } = movement + const absoluteTime = time - presentation.startTime - timeViewed*1000 + if (absoluteTime < 0) return arr; + // set the pan to what was stored - setTimeout(() => { + arr.push(setTimeout(() => { document._panX = panX; document._panY = panY; - }, time - presentation.startTime) - }) + }, absoluteTime)) + return arr; + }, []) + + console.log(this.timers.length) } // Unfinished code for tracing multiple free form views // export let pres: Map = new Map() @@ -258,58 +294,95 @@ export class RecordingApi { /** Represents the `setTimeout` with an ability to perform pause/resume actions * citation: https://stackoverflow.com/questions/3969475/javascript-pause-settimeout */ -export class Timer { - private _start: Date; - private _remaining: number; - private _durationTimeoutId?: NodeJS.Timeout; - private _callback: (...args: any[]) => void; - private _done = false; - get done () { - return this._done; +class Timer { + private timerId: NodeJS.Timeout | null; + private callback: (...args: any[]) => void + private start: number + private remaining: number; + + constructor(callback: (...args: any[]) => void, delay: number) { + this.callback = callback; + this.remaining = delay; + + this.start = Date.now(); + this.timerId = setTimeout(this.callback, this.remaining); } - public constructor(callback: (...args: any[]) => void, ms = 0) { - this._callback = () => { - callback(); - this._done = true; - }; - this._remaining = ms; - this.resume(); - } - - /** pauses the timer */ - public pause(): Timer { - if (this._durationTimeoutId && !this._done) { - this._clearTimeoutRef(); - this._remaining -= new Date().getTime() - this._start.getTime(); - } - return this; - } + public pause = () => { + console.log('[timer.ts] pause()') + this.timerId !== null && clearTimeout(this.timerId); + this.timerId = null; + this.remaining -= (Date.now() - this.start); + }; - /** resumes the timer */ - public resume(): Timer { - if (!this._durationTimeoutId && !this._done) { - this._start = new Date; - this._durationTimeoutId = setTimeout(this._callback, this._remaining); + public resume = () => { + if (this.timerId) { + return; } - return this; - } - /** - * clears the timeout and marks it as done. - * - * After called, the timeout will not resume - */ - public clearTimeout() { - this._clearTimeoutRef(); - this._done = true; - } + this.start = Date.now(); + this.timerId = setTimeout(this.callback, this.remaining); + }; - private _clearTimeoutRef() { - if (this._durationTimeoutId) { - clearTimeout(this._durationTimeoutId); - this._durationTimeoutId = undefined; - } - } - -} \ No newline at end of file + public clear = () => { + console.log('[timer.ts] clear()') + this.timerId !== null && clearTimeout(this.timerId); + // this.timerId = null; + // this.remaining -= (Date.now() - this.start); + }; +} +// class Timer { +// private _start: Date; +// private _remaining: number; +// private _durationTimeoutId?: NodeJS.Timeout; +// private _callback: (...args: any[]) => void; +// private _done = false; +// get done () { +// return this._done; +// } + +// public constructor(callback: (...args: any[]) => void, ms = 0) { +// this._callback = () => { +// callback(); +// this._done = true; +// }; +// this._remaining = ms; +// this.resume(); +// } + +// /** pauses the timer */ +// public pause(): Timer { +// if (this._durationTimeoutId && !this._done) { +// this._clearTimeoutRef(); +// this._remaining -= new Date().getTime() - this._start.getTime(); +// } +// return this; +// } + +// /** resumes the timer */ +// public resume(): Timer { +// if (!this._durationTimeoutId && !this._done) { +// this._start = new Date; +// this._durationTimeoutId = setTimeout(this._callback, this._remaining); +// } +// return this; +// } + +// /** +// * clears the timeout and marks it as done. +// * +// * After called, the timeout will not resume +// */ +// public clearTimeout() { +// this._clearTimeoutRef(); +// this._done = true; +// } + +// private _clearTimeoutRef() { +// if (this._durationTimeoutId) { +// clearTimeout(this._durationTimeoutId); +// this._durationTimeoutId = undefined; +// } +// } + +// } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 8bcf6f46f..214d4bbdc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -966,7 +966,7 @@ export class CollectionFreeFormView extends CollectionSubView { // if presentation isn't null, call followmovements on the recording api if (this.presentation) { - const err = RecordingApi.Instance.followMovements(this.presentation); - if (err) console.log(err); + const err = RecordingApi.Instance.playMovements(this.presentation, this.player?.currentTime || 0); + err && console.log(err) } - this._playing = true; const eleTime = this.player?.currentTime || 0; if (this.timeline) { @@ -198,6 +197,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + if (this.presentation) { + const err = RecordingApi.Instance.pauseMovements(); + err && console.log(err); + } + this._playing = false; this.removeCurrentlyPlaying(); try { -- cgit v1.2.3-70-g09d2 From 4ee1c1f87b311f54e1b928b417017c3a0429d1a4 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 03:31:46 -0400 Subject: Condense code. --- src/client/util/RecordingApi.ts | 147 +--------------------------------------- 1 file changed, 1 insertion(+), 146 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index dfe8e98ca..e779a4ab1 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -128,16 +128,6 @@ export class RecordingApi { this.absoluteStart = absoluteTimePaused } - // public finish = (): Error | Presentation => { - // if (this.isInitPresenation) { - // console.error('[recordingApi.ts] finish() failed: no presentation data. try calling start() first') - // return new Error('[recordingApi.ts] finish()') - // } - - // // return a copy of the the presentation data - // return { ...this.currentPresentation } - // } - private trackMovements = (panX: number, panY: number): Error | undefined => { // ensure we are recording if (!this.isRecording) { @@ -185,40 +175,10 @@ export class RecordingApi { private playFFView: CollectionFreeFormView | null; private timers: NodeJS.Timeout[] | null; - // public loadPresentation = (presentation: Presentation): undefined | Error => { - // if (presentation.startTime === null || this.playFFView === null) { - // return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') - // } - - // const document = this.playFFView.Document - // const { movements } = presentation - // this.timers = movements.map(movement => { - // const { panX, panY, time } = movement - - // const t = new Timer(() => { - // document._panX = panX; - // document._panY = panY; - // // TODO: consider cleaning this array to null or some state - // }, time - presentation.startTime) - // t.pause() - // return t - // }) - // console.log(this.timers) - // } - public setPlayFFView = (view: CollectionFreeFormView): void => { this.playFFView = view } - // public pauseMovements = (): undefined | Error => { - // console.log('[recordingApi.ts] pauseMovements()') - // if (this.playFFView === null) { - // return new Error('[recordingApi.ts] pauseMovements() failed: no view') - // } - // // TODO: set userdoc presentMode to browsing - // this.timers?.forEach(timer => timer.pause()) - // } - public pauseMovements = (): undefined | Error => { if (this.playFFView === null) { return new Error('[recordingApi.ts] pauseMovements() failed: no view') @@ -235,14 +195,6 @@ export class RecordingApi { this._isPlaying = false } - // public resumeMovements = (): undefined | Error => { - // if (this.playFFView === null) { - // return new Error('[recordingApi.ts] resumeMovements() failed: no view') - // } - // console.log('resume') - // this.timers?.forEach(timer => timer.resume()) - // } - private _isPlaying = false; public playMovements = (presentation: Presentation, timeViewed: number = 0): undefined | Error => { @@ -271,9 +223,8 @@ export class RecordingApi { }, absoluteTime)) return arr; }, []) - - console.log(this.timers.length) } + // Unfinished code for tracing multiple free form views // export let pres: Map = new Map() @@ -290,99 +241,3 @@ export class RecordingApi { // pres.delete(ffView) // } } - -/** Represents the `setTimeout` with an ability to perform pause/resume actions - * citation: https://stackoverflow.com/questions/3969475/javascript-pause-settimeout - */ -class Timer { - private timerId: NodeJS.Timeout | null; - private callback: (...args: any[]) => void - private start: number - private remaining: number; - - constructor(callback: (...args: any[]) => void, delay: number) { - this.callback = callback; - this.remaining = delay; - - this.start = Date.now(); - this.timerId = setTimeout(this.callback, this.remaining); - } - - public pause = () => { - console.log('[timer.ts] pause()') - this.timerId !== null && clearTimeout(this.timerId); - this.timerId = null; - this.remaining -= (Date.now() - this.start); - }; - - public resume = () => { - if (this.timerId) { - return; - } - - this.start = Date.now(); - this.timerId = setTimeout(this.callback, this.remaining); - }; - - public clear = () => { - console.log('[timer.ts] clear()') - this.timerId !== null && clearTimeout(this.timerId); - // this.timerId = null; - // this.remaining -= (Date.now() - this.start); - }; -} -// class Timer { -// private _start: Date; -// private _remaining: number; -// private _durationTimeoutId?: NodeJS.Timeout; -// private _callback: (...args: any[]) => void; -// private _done = false; -// get done () { -// return this._done; -// } - -// public constructor(callback: (...args: any[]) => void, ms = 0) { -// this._callback = () => { -// callback(); -// this._done = true; -// }; -// this._remaining = ms; -// this.resume(); -// } - -// /** pauses the timer */ -// public pause(): Timer { -// if (this._durationTimeoutId && !this._done) { -// this._clearTimeoutRef(); -// this._remaining -= new Date().getTime() - this._start.getTime(); -// } -// return this; -// } - -// /** resumes the timer */ -// public resume(): Timer { -// if (!this._durationTimeoutId && !this._done) { -// this._start = new Date; -// this._durationTimeoutId = setTimeout(this._callback, this._remaining); -// } -// return this; -// } - -// /** -// * clears the timeout and marks it as done. -// * -// * After called, the timeout will not resume -// */ -// public clearTimeout() { -// this._clearTimeoutRef(); -// this._done = true; -// } - -// private _clearTimeoutRef() { -// if (this._durationTimeoutId) { -// clearTimeout(this._durationTimeoutId); -// this._durationTimeoutId = undefined; -// } -// } - -// } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 32f1fb8df81b5dfc12539090c3fe589099bf165b Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 04:43:22 -0400 Subject: Pause works with presentation now. --- src/client/util/RecordingApi.ts | 86 ++++++++++++---------- .../collectionFreeForm/CollectionFreeFormView.tsx | 7 +- src/client/views/nodes/VideoBox.tsx | 2 +- 3 files changed, 56 insertions(+), 39 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index e779a4ab1..ee427e5df 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -2,6 +2,7 @@ import { CollectionFreeFormView } from "../views/collections/collectionFreeForm" import { IReactionDisposer, observable, reaction } from "mobx"; import { NumCast } from "../../fields/Types"; import { Doc } from "../../fields/Doc"; +import { VideoBox } from "../views/nodes/VideoBox"; type Movement = { time: number, @@ -10,17 +11,15 @@ type Movement = { } type Presentation = { - movements: Array + movements: Array | null meta: Object, - startTime: number | null, } export class RecordingApi { private static NULL_PRESENTATION: Presentation = { - movements: [], + movements: null, meta: {}, - startTime: null, } // instance variables @@ -52,7 +51,7 @@ export class RecordingApi { // little helper :) private get isInitPresenation(): boolean { - return this.currentPresentation.startTime === null + return this.currentPresentation.movements === null } public start = (meta?: Object): Error | undefined => { @@ -68,12 +67,12 @@ export class RecordingApi { // (1a) get start date for presenation const startDate = new Date() // (1b) set start timestamp to absolute timestamp - // this.absoluteStart = startDate.getTime() + this.absoluteStart = startDate.getTime() // (2) assign meta content if it exists this.currentPresentation.meta = meta || {} // (3) assign start date to currentPresenation - this.currentPresentation.startTime = startDate.getTime() + this.currentPresentation.movements = [] // (4) set isRecording true to allow trackMovements this.isRecording = true } @@ -110,39 +109,35 @@ export class RecordingApi { // don't allow track movments this.isRecording = false - // set relativeStart to the pausedTimestamp + // set adjust absoluteStart to add the time difference const timestamp = new Date().getTime() - this.absoluteStart = timestamp + this.absoluteStart = timestamp - this.absoluteStart } public resume = () => { - const timestamp = new Date().getTime() - const startTimestamp = this.currentPresentation.startTime - if (!startTimestamp) { - console.error('[recordingApi.ts] resume() failed: no presentation data. try calling start() first') - return new Error('[recordingApi.ts] pause()') - } - - // update absoluteStart to bridge the paused time - const absoluteTimePaused = timestamp - this.absoluteStart - this.absoluteStart = absoluteTimePaused + this.isRecording = true + // set absoluteStart to the pausedTimestamp + this.absoluteStart = new Date().getTime() - this.absoluteStart } private trackMovements = (panX: number, panY: number): Error | undefined => { // ensure we are recording if (!this.isRecording) { - console.error('[recordingApi.ts] trackMovements() failed: recording is paused()') return new Error('[recordingApi.ts] trackMovements()') } + // check to see if the presetation is init + if (this.isInitPresenation) { + return new Error('[recordingApi.ts] trackMovements(): no presentation') + } // get the time - const timestamp = new Date().getTime() - - // make new movement struct - const movement: Movement = { time: timestamp, panX, panY } + const time = new Date().getTime() - this.absoluteStart + // make new movement object + console.log(time) + const movement: Movement = { time, panX, panY } // add that movement to the current presentation data's movement array - this.currentPresentation.movements.push(movement) + this.currentPresentation.movements && this.currentPresentation.movements.push(movement) } // instance variable for the FFView @@ -172,6 +167,7 @@ export class RecordingApi { } // TODO: extract this into different class with pause and resume recording + // TODO: store the FFview with the movements private playFFView: CollectionFreeFormView | null; private timers: NodeJS.Timeout[] | null; @@ -185,42 +181,58 @@ export class RecordingApi { } if (!this._isPlaying) { - return new Error('[recordingApi.ts] pauseMovements() failed: not playing') + //return new Error('[recordingApi.ts] pauseMovements() failed: not playing') + return } + this._isPlaying = false // TODO: set userdoc presentMode to browsing - //console.log('cleared timers', this.timers) - console.log(this.timers?.map(timer => clearTimeout(timer))) - console.log('[recordingApi.ts] pauseMovements()') + this.timers?.map(timer => clearTimeout(timer)) - this._isPlaying = false + this.videoBox = null; + } + + private videoBox: VideoBox | null = null; + + // by calling pause on the VideoBox, the pauseMovements will be called + public pauseVideoAndMovements = (): boolean => { + this.videoBox?.Pause() + return this.videoBox === null } private _isPlaying = false; - public playMovements = (presentation: Presentation, timeViewed: number = 0): undefined | Error => { - if (presentation.startTime === null || this.playFFView === null) { + public playMovements = (presentation: Presentation, timeViewed: number = 0, videoBox?: VideoBox): undefined | Error => { + if (presentation.movements === null || this.playFFView === null) { return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') } if (this._isPlaying) { - return new Error('[recordingApi.ts] playMovements() failed: already playing') + //return new Error('[recordingApi.ts] playMovements() failed: already playing') + return } this._isPlaying = true; + // Doc.UserDoc().presentationMode = 'watching'; - console.log(timeViewed) + // TODO: consider this bug at the end of the clip on seek + console.log(timeViewed, videoBox?.player?.currentTime) + this.videoBox = videoBox || null; const document = this.playFFView.Document const { movements } = presentation this.timers = movements.reduce((arr: NodeJS.Timeout[], movement) => { const { panX, panY, time } = movement - const absoluteTime = time - presentation.startTime - timeViewed*1000 - if (absoluteTime < 0) return arr; + + const timeDiff = time - timeViewed*1000 + if (timeDiff < 0) return arr; // set the pan to what was stored arr.push(setTimeout(() => { document._panX = panX; document._panY = panY; - }, absoluteTime)) + // if (movement === movements[movements.length - 1]) { + // this._isPlaying = false; + // } + }, timeDiff)) return arr; }, []) } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 214d4bbdc..9e57bca4f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -968,7 +968,12 @@ export class CollectionFreeFormView extends CollectionSubView { // if presentation isn't null, call followmovements on the recording api if (this.presentation) { - const err = RecordingApi.Instance.playMovements(this.presentation, this.player?.currentTime || 0); + const err = RecordingApi.Instance.playMovements(this.presentation, this.player?.currentTime || 0, this); err && console.log(err) } -- cgit v1.2.3-70-g09d2 From a20320fc54f2cddffeabce79b22451a6da551aa5 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 04:48:35 -0400 Subject: Stopping on interaction will pause the presenation and the video. --- src/client/util/RecordingApi.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index ee427e5df..8fb503b00 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -211,10 +211,10 @@ export class RecordingApi { return } this._isPlaying = true; - // Doc.UserDoc().presentationMode = 'watching'; + Doc.UserDoc().presentationMode = 'watching'; // TODO: consider this bug at the end of the clip on seek - console.log(timeViewed, videoBox?.player?.currentTime) + // console.log(timeViewed) this.videoBox = videoBox || null; const document = this.playFFView.Document @@ -229,9 +229,10 @@ export class RecordingApi { arr.push(setTimeout(() => { document._panX = panX; document._panY = panY; - // if (movement === movements[movements.length - 1]) { - // this._isPlaying = false; - // } + if (movement === movements[movements.length - 1]) { + this._isPlaying = false; + Doc.UserDoc().presentationMode = 'none'; + } }, timeDiff)) return arr; }, []) -- cgit v1.2.3-70-g09d2 From a9ff0c90656de71e37edafba68e946807d41403f Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 4 May 2022 13:36:13 -0400 Subject: fix infinite pause/play bug with a stopper, but still very buggy --- src/client/util/RecordingApi.ts | 3 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- .../views/nodes/RecordingBox/RecordingView.scss | 20 ++++++++++++-------- .../views/nodes/RecordingBox/RecordingView.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 5 +++++ 5 files changed, 22 insertions(+), 12 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index 8fb503b00..fec579486 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -188,7 +188,7 @@ export class RecordingApi { // TODO: set userdoc presentMode to browsing this.timers?.map(timer => clearTimeout(timer)) - this.videoBox = null; + // this.videoBox = null; } private videoBox: VideoBox | null = null; @@ -236,6 +236,7 @@ export class RecordingApi { }, timeDiff)) return arr; }, []) + } // Unfinished code for tracing multiple free form views diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9e57bca4f..aa2e0c417 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -970,9 +970,9 @@ export class CollectionFreeFormView extends CollectionSubView
); @@ -572,7 +572,7 @@ export class SharingManager extends React.Component<{}> {
Owner -
+
) : null, @@ -622,10 +622,10 @@ export class SharingManager extends React.Component<{}> { {this.sharingOptions(uniform, group.title === "Override")} ) : ( -
- {permissions} -
- )} +
+ {permissions} +
+ )}
); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index eb2bcc18c..936946df0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -113,7 +113,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P } } - titleEntered = (e: React.KeyboardEvent) => e.key === "Enter" && (e.target as any).blur(); + titleEntered = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + e.stopPropagation(); + (e.target as any).blur(); + } + } @action onTitleDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => { @@ -288,7 +293,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles const project = (p: number[], a: number[], b: number[]) => { const atob = [b[0] - a[0], b[1] - a[1]]; - const atop = [p[0] - a[0], p[1] - a[1]]; first + const atop = [p[0] - a[0], p[1] - a[1]]; const len = atob[0] * atob[0] + atob[1] * atob[1]; let dot = atop[0] * atob[0] + atop[1] * atob[1]; const t = dot / len; @@ -501,7 +506,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P value={this._accumulatedTitle} onBlur={e => this.titleBlur()} onChange={action(e => this._accumulatedTitle = e.target.value)} - onKeyPress={this.titleEntered} /> : + onKeyDown={this.titleEntered} /> :
{`${this.selectionTitle}`}
; @@ -521,7 +526,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox; const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : ""; - const rotation = NumCast(seldoc.rootDoc._jitterRotation) + const rotation = NumCast(seldoc.rootDoc._jitterRotation); const rdeg = rotation / 180 * Math.PI; return (
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c794c73db..db51c54f8 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -133,7 +133,7 @@ export class MainView extends React.Component { window.addEventListener("paste", KeyManager.Instance.paste as any); document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = FormattedTextBox.GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null)); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : (null)); }); document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener); this.initEventListeners(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 78b125e07..8dfc7edc7 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -256,7 +256,7 @@ export class TabDocView extends React.Component { } PresBox.Instance?._selectedArray.clear(); pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array - DocumentManager.Instance.jumpToDocument(doc, false, undefined); + DocumentManager.Instance.jumpToDocument(doc, false, undefined, []); batch.end(); }); } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 72480b094..164021358 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -72,7 +72,7 @@ export enum TreeSort { Down = "down", Zindex = "z", None = "none" -}; +} /** * Renders a treeView of a collection of documents * diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5d1e1892c..5b3a7db41 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1423,7 +1423,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (oldDiv.childNodes) { + if (oldDiv.childNodes && newDiv) { for (let i = 0; i < oldDiv.childNodes.length; i++) { this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement); } diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index e3d4f86c1..c615bfb8e 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -231,7 +231,7 @@ export class CollectionLinearView extends CollectionSubView() { Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => - DocumentManager.Instance.jumpToDocument(clip, true)}> + DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}> {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")} )} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index c2bb3b3ac..adcd9e1e3 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -199,7 +199,7 @@ export class CollectionSchemaCell extends React.Component { const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); // Jump to the this document - DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext, + DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext ? [targetContext] : [], undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc)); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8570f6fff..02ea87103 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb && + !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) && !this._componentView?.isAnyChildContentActive?.() ? true : false @computed get contents() { TraceMobx(); diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index a9d33f161..ccac66996 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -54,6 +54,7 @@ export class LinkDescriptionPopup extends React.Component<{}> { top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350, }}> e.stopPropagation()} onKeyPress={e => e.key === "Enter" && this.onDismiss(true)} placeholder={"(Optional) Enter link description..."} onChange={(e) => this.descriptionChanged(e)}> diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 69662d53a..48465976a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -75,6 +75,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + return; // currently we render pdf icons as text labels const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index b037e7220..d8dd074a5 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -156,6 +156,9 @@ position: absolute; top: 0; left: 0; + cursor: text; + padding: 15px; + height: 100% } .webBox-cont { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 80961c92f..f441e5d36 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -207,8 +207,8 @@ export class WebBox extends ViewBoxAnnotatableComponent { - if (this._mainCont.current) { + createTextAnnotation = (sel: Selection, selRange: Range | undefined) => { + if (this._mainCont.current && selRange) { const clientRects = selRange.getClientRects(); for (let i = 0; i < clientRects.length; i++) { const rect = clientRects.item(i); @@ -235,7 +235,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.urlEditor; // controls to be added to the top bar when a document of this type is selected scrollFocus = (doc: Doc, smooth: boolean) => { - this.props.select(true); + this.props.select(false); // hack to preven the webBox from being replaced with a thumbnail that currently doesn't show the the anchors if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth); if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) { this.toggleSidebar(!smooth); @@ -252,7 +252,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { @@ -279,7 +279,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.createTextAnnotation(sel, sel.getRangeAt(0)); + this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined); AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale); } @@ -650,6 +650,7 @@ export class WebBox extends ViewBoxAnnotatableComponent e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}> {this.urlContent}
; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 30ad43562..820d7ef96 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -389,11 +389,11 @@ export class PresBox extends ViewBoxBaseComponent() { LightboxView.SetLightboxDoc(targetDoc); } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) { LightboxView.SetLightboxDoc(undefined); - await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right + await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) { LightboxView.SetLightboxDoc(undefined); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right + await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right } // After navigating to the document, if it is added as a presPinView then it will // adjust the pan and scale to that of the pinView when it was added. diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 9257cb75e..3d36efbc4 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -365,7 +365,7 @@ export class SearchBox extends ViewBoxBaseComponent() { * or opening it in a new tab. */ selectElement = async (doc: Doc, finishFunc: () => void) => { - await DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, undefined, undefined, finishFunc); + await DocumentManager.Instance.jumpToDocument(doc, true, undefined, [], undefined, undefined, undefined, finishFunc); } /** -- cgit v1.2.3-70-g09d2 From 3847b2bbba9a64b846ecd6af481fd04df55bd798 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 13 May 2022 16:26:33 -0400 Subject: fixed bug with following links in lightbox. --- src/client/util/LinkManager.ts | 2 +- src/server/server_Initialization.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index b28662a57..2100b1195 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -246,7 +246,7 @@ export class LinkManager { while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) { containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext]; } - const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]] : containerDocContext; + const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext; DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished); } } else { diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 8bf84622d..bdea57cb2 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -228,9 +228,9 @@ function registerEmbeddedBrowseRelativePathHandler(server: express.Express) { try { const proxiedRefererUrl = decodeURIComponent(req.headers.referer); // (e.g., http://localhost:/corsProxy/https://en.wikipedia.org/wiki/Engelbart) const dashServerUrl = proxiedRefererUrl.match(/.*corsProxy\//)![0]; // the dash server url (e.g.: http://localhost:/corsProxy/ ) - const actualReferUrl = proxiedRefererUrl.replace(dashServerUrl, ""); // the url of the referer without the proxy (e.g., : http:s//en.wikipedia.org/wiki/Engelbart) + const actualReferUrl = proxiedRefererUrl.replace(dashServerUrl, ""); // the url of the referer without the proxy (e.g., : https://en.wikipedia.org/wiki/Engelbart) const absoluteTargetBaseUrl = actualReferUrl.match(/https?:\/\/[^\/]*/)![0]; // the base of the original url (e.g., https://en.wikipedia.org) - const redirectedProxiedUrl = dashServerUrl + encodeURIComponent(absoluteTargetBaseUrl + relativeUrl); // the new proxied full url (e..g, http://localhost:/corsProxy/https://en.wikipedia.org/) + const redirectedProxiedUrl = dashServerUrl + encodeURIComponent(absoluteTargetBaseUrl + relativeUrl); // the new proxied full url (e.g., http://localhost:/corsProxy/https://en.wikipedia.org/) if (relativeUrl.startsWith("//")) res.redirect("http:" + relativeUrl); else res.redirect(redirectedProxiedUrl); } catch (e) { -- cgit v1.2.3-70-g09d2 From 21ea42b425946d54eb8a811e10734e714c0164a0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 13 May 2022 18:46:05 -0400 Subject: dragging docs already within a locked document can now still be dropped onto the locked document. --- src/client/util/DragManager.ts | 1 - .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 13e5940d6..12c0fe6c0 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,4 +1,3 @@ -import { extend } from "lodash"; import { action } from "mobx"; import { DateField } from "../../fields/DateField"; import { Doc, Field, Opt } from "../../fields/Doc"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 322944d92..a60253914 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -221,7 +221,7 @@ export class CollectionFreeFormView extends CollectionSubView { const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); - const pointerEvents = this.props.isContentActive() === false ? "none" : this.props.childPointerEvents ? "all" : - (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.(); + const pointerEvents = this.props.isContentActive() === false ? "none" : + this.props.childPointerEvents ? "all" : + (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.(); return pointerEvents; } getChildDocView(entry: PoolData) { @@ -1735,7 +1736,7 @@ export class CollectionFreeFormView extends CollectionSubView Date: Sun, 15 May 2022 11:32:35 -0400 Subject: fixed enterting titles in tab's to not trigger inconify. added drop placement for mulitirow/col. added time delay for switching to tab dragging. all this helps support a worktop-style topBar. --- src/client/util/DragManager.ts | 27 ++++++++++----- src/client/views/collections/TabDocView.tsx | 3 ++ .../CollectionMulticolumnView.tsx | 39 +++++++++++++++++++--- .../CollectionMultirowView.tsx | 38 ++++++++++++++++++--- 4 files changed, 89 insertions(+), 18 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 12c0fe6c0..037da132b 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -449,20 +449,27 @@ export namespace DragManager { SnappingManager.clearSnapLines(); batch.end(); }); - const moveHandler = async (e: PointerEvent) => { + var startWindowDragTimer: any; + const moveHandler = (e: PointerEvent) => { e.preventDefault(); // required or dragging text menu link item ends up dragging the link button as native drag/drop if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction; } if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { - dragData.dropAction = dragData.userDropAction || "same"; - AbortDrag(); - await finishDrag?.(new DragCompleteEvent(true, dragData)); - DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => { - if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) { - dragData.removeDocument?.(dragData.draggedDocuments[0]); - } - }); + if (!startWindowDragTimer) startWindowDragTimer = setTimeout(async () => { + startWindowDragTimer = undefined; + dragData.dropAction = dragData.userDropAction || "same"; + AbortDrag(); + await finishDrag?.(new DragCompleteEvent(true, dragData)); + DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => { + if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) { + dragData.removeDocument?.(dragData.draggedDocuments[0]); + } + }); + }, 500); + } else { + clearTimeout(startWindowDragTimer); + startWindowDragTimer = undefined; } const target = document.elementFromPoint(e.x, e.y); @@ -528,6 +535,8 @@ export namespace DragManager { ); }; const upHandler = (e: PointerEvent) => { + clearTimeout(startWindowDragTimer); + startWindowDragTimer = undefined; dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, cleanupDrag); }; document.addEventListener("pointermove", moveHandler, true); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 73e5a9178..1310a582c 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -83,6 +83,9 @@ export class TabDocView extends React.Component { titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; + titleEle.onkeydown = (e: KeyboardEvent) => { + e.stopPropagation(); + } titleEle.onchange = undoBatch(action((e: any) => { titleEle.size = e.currentTarget.value.length + 3; Doc.GetProto(doc).title = e.currentTarget.value; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 92f9b2f49..3582718a3 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,10 +1,10 @@ import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from "react"; -import { Doc } from '../../../../fields/Doc'; +import { Doc, DocListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse } from '../../../../Utils'; +import { emptyFunction, returnFalse, returnTrue } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -194,11 +194,38 @@ export class CollectionMulticolumnView extends CollectionSubView() { @undoBatch @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { - if (super.onInternalDrop(e, de)) { + let dropInd = -1; + if (de.complete.docDragData && this._mainCont) { + let curInd = -1; de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { - d._dimUnit = "*"; - d._dimMagnitude = 1; + curInd = this.childDocs.indexOf(d); })); + Array.from(this._mainCont.children).forEach((child, index) => { + const brect = child.getBoundingClientRect(); + if (brect.x < de.x && brect.x + brect.width > de.x) { + if (curInd !== -1 && curInd === Math.floor(index / 2)) { + dropInd = curInd; + } + else if (child.className === "multiColumnResizer") { + dropInd = Math.floor(index / 2); + } else { + dropInd = Math.ceil(index / 2 + (de.x - brect.x > brect.width / 2 ? 0 : -1)); + } + } + }) + if (super.onInternalDrop(e, de)) { + de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { + d._dimUnit = "*"; + d._dimMagnitude = 1; + if (dropInd !== curInd || dropInd === -1) { + if (this.childDocs.includes(d)) { + if (dropInd > this.childDocs.indexOf(d)) dropInd--; + } + Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d); + Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1); + } + })); + } } return false; } @@ -214,6 +241,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { } return this.props.addDocTab(doc, where); } + isContentActive = () => this.props.isSelected() || this.props.isContentActive(); getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return { - if (super.onInternalDrop(e, de)) { + let dropInd = -1; + if (de.complete.docDragData && this._mainCont) { + let curInd = -1; de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { - d._dimUnit = "*"; - d._dimMagnitude = 1; + curInd = this.childDocs.indexOf(d); })); + Array.from(this._mainCont.children).forEach((child, index) => { + const brect = child.getBoundingClientRect(); + if (brect.y < de.y && brect.y + brect.height > de.y) { + if (curInd !== -1 && curInd === Math.floor(index / 2)) { + dropInd = curInd; + } + else if (child.className === "multiColumnResizer") { + dropInd = Math.floor(index / 2); + } else { + dropInd = Math.ceil(index / 2 + (de.y - brect.y > brect.height / 2 ? 0 : -1)); + } + } + }) + if (super.onInternalDrop(e, de)) { + de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { + d._dimUnit = "*"; + d._dimMagnitude = 1; + if (dropInd !== curInd || dropInd === -1) { + if (this.childDocs.includes(d)) { + if (dropInd > this.childDocs.indexOf(d)) dropInd--; + } + Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d); + Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1); + } + })); + } } return false; } @@ -213,6 +241,7 @@ export class CollectionMultirowView extends CollectionSubView() { } return this.props.addDocTab(doc, where); } + isContentActive = () => this.props.isSelected() || this.props.isContentActive(); getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return Date: Mon, 16 May 2022 08:45:46 -0400 Subject: re-added fwd/back animation buttons --- src/client/util/CurrentUserUtils.ts | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9720343e1..78bf531c6 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -16,11 +16,13 @@ import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; +import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; 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"; +import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; @@ -991,6 +993,14 @@ export class CurrentUserUtils { CollectionViewType.Grid], script: 'setView(value, _readOnly_)', }, // Always show + { + title: "back", toolTip: "prev", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left", + hidden: 'IsNoviceMode()' + }, + { + title: "Forward", toolTip: "next", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right", + hidden: 'IsNoviceMode()' + }, { title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' @@ -1588,4 +1598,29 @@ ScriptingGlobals.add(function makeTopLevelFolder() { }); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; -}); \ No newline at end of file +}); +ScriptingGlobals.add(function nextKeyFrame() { + const sel = SelectionManager.Views()[0]; + const col = (sel.ComponentView as CollectionFreeFormView); + const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); + if (currentFrame === undefined) { + sel.props.Document._currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); + } + CollectionFreeFormDocumentView.updateKeyframe(col.childDocs, currentFrame || 0); + sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1); + sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame)); +} +); +ScriptingGlobals.add(function prevKeyFrame() { + const sel = SelectionManager.Views()[0]; + const col = (sel.ComponentView as CollectionFreeFormView); + const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); + if (currentFrame === undefined) { + sel.props.Document._currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); + } + CollectionFreeFormDocumentView.gotoKeyframe(col.childDocs.slice()); + sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1); +} +); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ee1fa0ad22f1643888e4c467c6f8df837cc69178 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 17 May 2022 10:18:45 -0400 Subject: fixed pointerevents on treview open icon. added browse mode for freeformcollections --- src/client/util/DragManager.ts | 24 ++++++++++++---------- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 13 ++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 23 ++++++++++++++++----- .../CollectionMulticolumnView.tsx | 2 +- .../CollectionMultirowView.tsx | 2 +- 6 files changed, 43 insertions(+), 23 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 037da132b..5a00e0c04 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -456,17 +456,19 @@ export namespace DragManager { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction; } if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { - if (!startWindowDragTimer) startWindowDragTimer = setTimeout(async () => { - startWindowDragTimer = undefined; - dragData.dropAction = dragData.userDropAction || "same"; - AbortDrag(); - await finishDrag?.(new DragCompleteEvent(true, dragData)); - DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => { - if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) { - dragData.removeDocument?.(dragData.draggedDocuments[0]); - } - }); - }, 500); + if (!startWindowDragTimer) { + startWindowDragTimer = setTimeout(async () => { + startWindowDragTimer = undefined; + dragData.dropAction = dragData.userDropAction || "same"; + AbortDrag(); + await finishDrag?.(new DragCompleteEvent(true, dragData)); + DragManager.StartWindowDrag?.(e, dragData.droppedDocuments, (aborted) => { + if (!aborted && (dragData.dropAction === "move" || dragData.dropAction === "same")) { + dragData.removeDocument?.(dragData.draggedDocuments[0]); + } + }); + }, 500); + } } else { clearTimeout(startWindowDragTimer); startWindowDragTimer = undefined; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 1310a582c..cebc3f779 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -85,7 +85,7 @@ export class TabDocView extends React.Component { titleEle.value = doc.title; titleEle.onkeydown = (e: KeyboardEvent) => { e.stopPropagation(); - } + }; titleEle.onchange = undoBatch(action((e: any) => { titleEle.size = e.currentTarget.value.length + 3; Doc.GetProto(doc).title = e.currentTarget.value; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index d608f800c..c004b4d49 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -494,10 +494,15 @@ export class TreeView extends React.Component { return
+ style={this.props.treeView.outlineMode ? + { + opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) + } : + { + pointerEvents: this.props.isContentActive() ? "all" : undefined, + opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4, + color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), + }}> {this.props.treeView.outlineMode ? !(this.doc.text as RichTextField)?.Text ? (null) : : diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a60253914..68a51ef69 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -53,6 +53,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); +import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; export const panZoomSchema = createSchema({ _panX: "number", @@ -154,7 +155,7 @@ export class CollectionFreeFormView extends CollectionSubView this._keyframeEditing = set; getKeyFrameEditing = () => this._keyframeEditing; - onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); + onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick, ScriptCast(this.layoutDoc.onBrowseClick)); onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); elementFunc = () => this._layoutElements; shrinkWrap = () => { @@ -621,7 +622,13 @@ export class CollectionFreeFormView extends CollectionSubView { - if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { + + if (this.layoutDoc.onBrowseClick) { + this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); + e.stopPropagation(); + e.preventDefault(); + } + else if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { if (e.shiftKey) { if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); @@ -1127,7 +1134,7 @@ export class CollectionFreeFormView extends CollectionSubView this.layoutDoc.onBrowseClick = this.layoutDoc.onBrowseClick ? undefined : ScriptField.MakeScript("CollectionBrowseClick(documentView)", { documentView: "any" }), icon: "compress-arrows-alt" }); !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); @@ -1973,4 +1980,10 @@ class CollectionFreeFormBackgroundGrid extends React.Component; } -} \ No newline at end of file +} + +export function CollectionBrowseClick(dv: DocumentView) { + dv.props.focus(dv.props.Document, { willZoom: true }); + Doc.linkFollowHighlight(dv?.props.Document, false); +} +ScriptingGlobals.add(CollectionBrowseClick); \ No newline at end of file diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index c18b4cec7..b6b59a2d6 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -212,7 +212,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { dropInd = Math.ceil(index / 2 + (de.x - brect.x > brect.width / 2 ? 0 : -1)); } } - }) + }); if (super.onInternalDrop(e, de)) { de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { d._dimUnit = "*"; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 95456cf46..4074f8d69 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -212,7 +212,7 @@ export class CollectionMultirowView extends CollectionSubView() { dropInd = Math.ceil(index / 2 + (de.y - brect.y > brect.height / 2 ? 0 : -1)); } } - }) + }); if (super.onInternalDrop(e, de)) { de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { d._dimUnit = "*"; -- cgit v1.2.3-70-g09d2 From 99558fd67dcdb028692f1d219331ab86e07898f1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 20 May 2022 08:42:48 -0400 Subject: fixed script repl to show doc id for tabdocs. fixed hitting 'enter' in several dialogs to stop propagation to prevent iconifying --- src/client/util/SharingManager.tsx | 1 + src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/ScriptingRepl.tsx | 2 +- src/client/views/linking/LinkEditor.tsx | 18 +++++++----------- src/client/views/nodes/DocumentView.tsx | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 11450309a..f439d4488 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -658,6 +658,7 @@ export class SharingManager extends React.Component<{}> { isSearchable closeMenuOnSelect={false} options={options} + onKeyDown={e => e.stopPropagation()} onChange={this.handleUsersChange} value={this.selectedUsers} styles={{ diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 19119bb56..ae3a91c4d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -64,7 +64,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P @computed get Bounds() { const views = SelectionManager.Views(); - return views.map(dv => dv.getBounds()).reduce((bounds, rect) => + return views.filter(dv => dv.props.renderDepth > 0).map(dv => dv.getBounds()).reduce((bounds, rect) => !rect ? bounds : { x: Math.min(rect.left, bounds.x), diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index 4ed6da24a..2b045aa6c 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -212,7 +212,7 @@ export class ScriptingRepl extends React.Component { } onBlur = () => { - this.overlayDisposer && this.overlayDisposer(); + this.overlayDisposer?.(); } render() { diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index c3e0aff11..1414bfdf7 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -22,7 +22,7 @@ export class LinkEditor extends React.Component { @observable description = Field.toString(LinkManager.currentLink?.description as any as Field); @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); - @observable zoomFollow1 = StrCast(this.props.sourceDoc.followLinkZoom); + @observable zoomFollow = StrCast(this.props.sourceDoc.followLinkZoom); @observable openDropdown: boolean = false; @observable showInfo: boolean = false; @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; } @@ -114,6 +114,7 @@ export class LinkEditor extends React.Component { this.setDescripValue(this.description); document.getElementById('input')?.blur(); } + e.stopPropagation(); } onRelationshipKey = (e: React.KeyboardEvent) => { @@ -121,6 +122,7 @@ export class LinkEditor extends React.Component { this.setRelationshipValue(this.relationship); document.getElementById('input')?.blur(); } + e.stopPropagation(); } onDescriptionDown = () => this.setDescripValue(this.description); @@ -193,16 +195,11 @@ export class LinkEditor extends React.Component { + value={this.zoomFollow} + onChange={this.handleZoomFollowChange} />
-
; +
; } @computed @@ -300,9 +297,8 @@ export class LinkEditor extends React.Component { render() { const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); - return !destination ? (null) : ( -
+
e.stopPropagation()}>
Return to link menu
} placement="top">
} placement="bottom"> +
MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}> + {"Explore"} +
+
@@ -64,7 +75,6 @@ export class TopBar extends React.Component {
SettingsManager.Instance.open()}> {"Settings"}
-
-- cgit v1.2.3-70-g09d2 From 6df80c76b4046ef1ac9e98e52f766dfaf540ead2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 20 May 2022 12:51:51 -0400 Subject: removed enumerables field stuff which was hacky and never used. cleaned up some of currentUserUtils initialiazation. fixed treeView sorting for 'none" which fixes problem with slide template. --- src/client/documents/Documents.ts | 24 -- src/client/util/CurrentUserUtils.ts | 242 +++++---------------- .../views/collections/CollectionStackingView.tsx | 1 - src/client/views/collections/TreeView.tsx | 2 +- src/client/views/nodes/ComparisonBox.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 14 -- 6 files changed, 57 insertions(+), 228 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 2dd8de089..4be9674f7 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1406,30 +1406,6 @@ export namespace DocUtils { return undefined; } - export async function addFieldEnumerations(doc: Opt, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) { - let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey); - if (!(optionsCollection instanceof Doc)) { - optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set`, system: true }, enumeratedFieldKey); - Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc); - } - const options = optionsCollection as Doc; - const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc); - const docFind = `options.data?.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`; - targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options })); - targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options })); - targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options })); - enumerations.map(enumeration => { - const found = DocListCast(options.data).find(d => d.title === enumeration.title); - if (found) { - found._backgroundColor = enumeration._backgroundColor || found._backgroundColor; - found._color = enumeration.color || found._color; - } else { - Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, { ...enumeration, system: true })); - } - }); - return optionsCollection; - } - // /** // * // * @param dms Degree Minute Second format exif gps data diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8fed6e940..e0580d3c4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -75,201 +75,68 @@ export class CurrentUserUtils { @observable public static searchPanelWidth: number = 0; // sets up the default User Templates - slideView, headerView - static setupUserTemplateButtons(doc: Doc) { - // Prototype for mobile button (not sure if 'Advanced Item Prototypes' is ideal location) - if (doc["template-mobile-button"] === undefined) { - const queryTemplate = this.mobileButton({ - title: "NEW MOBILE BUTTON", - onClick: undefined, - }, - [this.createToolButton({ - ignoreClick: true, - icon: "mobile", - btnType: ButtonType.ToolButton, - backgroundColor: "transparent" - }), - this.mobileTextContainer({}, - [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]); - doc["template-mobile-button"] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile", btnType: ButtonType.ToolButton, - }); - } - - if (doc["template-button-slides"] === undefined) { - const slideTemplate = Docs.Create.MultirowDocument( - [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) - ], - { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } - ); - slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); - doc["template-button-slides"] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", icon: "address-card", - btnType: ButtonType.ToolButton - }); - } - - if (doc["template-button-link"] === undefined) { // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to. - const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _autoHeight: true, system: true }, "header")); // text needs to be a space to allow templateText to be created - linkTemplate.system = true; - Doc.GetProto(linkTemplate).layout = - "
" + - " " + - " " + - "
"; - (linkTemplate.proto as Doc).isTemplateDoc = makeTemplate(linkTemplate.proto as Doc, true, "linkView"); - - const rtf2 = { - doc: { - type: "doc", content: [ - { - type: "paragraph", - content: [{ - type: "dashField", - attrs: { - fieldKey: "src", - hideKey: false - } - }] - }, - { type: "paragraph" }, - { - type: "paragraph", - content: [{ - type: "dashField", - attrs: { - fieldKey: "dst", - hideKey: false - } - }] - }] + static setupExperimentalTemplateButtons(doc: Doc) { + if (doc["template-experimental-buttons"] === undefined) { + const requiredTypeNameFields = [ + { + type: "slide", icon: "address-card", template: Docs.Create.MultirowDocument( + [ + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), + Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) + ], + { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } + ) }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - linkTemplate.header = new RichTextField(JSON.stringify(rtf2), ""); - - doc["template-button-link"] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", icon: "window-maximize", system: true, - btnType: ButtonType.ToolButton + { + type: "mobile", icon: "mobile", template: this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, }, + [this.createToolButton({ ignoreClick: true, icon: "mobile", btnType: ButtonType.ToolButton, backgroundColor: "transparent" }), + this.mobileTextContainer({}, + [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")]) + ] + ) + }, + ]; + const requiredTypes = requiredTypeNameFields.map(({ type, icon, template }) => { + if (doc[`template-button-${type}`] === undefined) { + template.isTemplateDoc = makeTemplate(template); + doc[`template-button-${type}`] = CurrentUserUtils.createToolButton({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(template) as any as Doc, + title: type, + icon, + btnType: ButtonType.ToolButton, + }); + } + return doc[`template-button-${type}`] as Doc; }); - } - // if (doc["template-button-switch"] === undefined) { - // const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; - - // const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); - // const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); - // const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); - // const labelTemplate = { - // doc: { - // type: "doc", content: [{ - // type: "paragraph", - // content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] - // }] - // }, - // selection: { type: "text", anchor: 1, head: 1 }, - // storedMarks: [] - // }; - // Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); - // Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); - // // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); - // // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); - // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); - // // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); - // const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); - // box.isTemplateDoc = makeTemplate(box, true, "switch"); - - // doc["template-button-switch"] = CurrentUserUtils.createToolButton({ - // onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - // dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, - // btnType: ButtonType.ToolButton - // }); - // } - - const requiredTypes = [ - doc["template-button-slides"] as Doc, - doc["template-mobile-button"] as Doc, - doc["template-button-link"] as Doc, - //doc["template-button-switch"] as Doc] - ]; - if (doc["template-buttons"] === undefined) { - doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, { - title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title", _chromeHidden: true, + doc["template-experimental-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, { + title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true, hidden: ComputedField.MakeFunction("IsNoviceMode()") as any, _stayInCollection: true, _hideContextMenu: true, _forceActive: true, _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true })); - } else { - const curButnTypes = Cast(doc["template-buttons"], Doc, null); - DocListCastAsync(curButnTypes.data).then(async curBtns => { - curBtns && await Promise.all(curBtns); - requiredTypes.map(btype => Doc.AddDocToList(curButnTypes, "data", btype)); - }); } - return doc["template-buttons"] as Doc; + return doc["template-experimental-buttons"] as Doc; } // setup the different note type skins static setupNoteTemplates(doc: Doc) { - if (doc["template-note-Note"] === undefined) { - const noteView = Docs.Create.TextDocument("", { - title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true, icon: "sticky-note", - _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), - }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Note"); - doc["template-note-Note"] = new PrefetchProxy(noteView); - } - if (doc["template-note-Idea"] === undefined) { - const noteView = Docs.Create.TextDocument("", { - title: "text", backgroundColor: "pink", system: true, icon: "lightbulb", - _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), - }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea"); - doc["template-note-Idea"] = new PrefetchProxy(noteView); - } - if (doc["template-note-Topic"] === undefined) { - const noteView = Docs.Create.TextDocument("", { - title: "text", backgroundColor: "lightblue", system: true, icon: "book-open", - _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), - }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic"); - doc["template-note-Topic"] = new PrefetchProxy(noteView); - } - // if (doc["template-note-Todo"] === undefined) { - // const noteView = Docs.Create.TextDocument("", { - // title: "text", backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption", - // layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus"), system: true, - // _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), - // }); - // noteView.isTemplateDoc = makeTemplate(noteView, true, "Todo"); - // doc["template-note-Todo"] = new PrefetchProxy(noteView); - // } - // const taskStatusValues = [ - // { title: "todo", _backgroundColor: "blue", color: "white", system: true }, - // { title: "in progress", _backgroundColor: "yellow", color: "black", system: true }, - // { title: "completed", _backgroundColor: "green", color: "white", system: true } - // ]; - // if (doc.fieldTypes === undefined) { - // doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations", system: true }); - // DocUtils.addFieldEnumerations(Doc.GetProto(doc["template-note-Todo"] as any as Doc), "taskStatus", taskStatusValues); - // } - if (doc["template-notes"] === undefined) { - doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc], // doc["template-note-Todo"] as any as Doc], - { title: "Note Layouts", _height: 75, system: true })); - } else { - const curNoteTypes = Cast(doc["template-notes"], Doc, null); - const requiredTypes = [doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc];//, doc["template-note-Todo"] as any as Doc]; - DocListCastAsync(curNoteTypes.data).then(async curNotes => { - curNotes && await Promise.all(curNotes); - requiredTypes.map(ntype => Doc.AddDocToList(curNoteTypes, "data", ntype)); - }); + const requiredTypeNameFields = [ + { type: "Note", backgroundColor: "yellow", icon: "sticky-note" }, + { type: "Idea", backgroundColor: "pink", icon: "lightbulb" }, + { type: "Topic", backgroundColor: "lightblue", icon: "book-open" }]; + const requiredTypes = requiredTypeNameFields.map(({ type, backgroundColor, icon }) => + doc[`template-note-${type}`] as Doc ?? + (() => { + const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor, system: true, icon }); + noteView.isTemplateDoc = makeTemplate(noteView, true, type); + return doc[`template-note-${type}`] = new PrefetchProxy(noteView); + })()); + + doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument(requiredTypes, { title: "Note Layouts", _height: 75, system: true })); } return doc["template-notes"] as Doc; @@ -277,11 +144,12 @@ export class CurrentUserUtils { // creates Note templates, and initial "user" templates static setupDocTemplates(doc: Doc) { - const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc); - const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); - const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc); if (doc.templateDocs === undefined) { - doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], { + doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([ + CurrentUserUtils.setupNoteTemplates(doc), + CurrentUserUtils.setupExperimentalTemplateButtons(doc), + CurrentUserUtils.setupClickEditorTemplates(doc) + ], { title: "template layouts", _xMargin: 0, system: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) })); @@ -717,7 +585,7 @@ export class CurrentUserUtils { static async setupToolsBtnPanel(doc: Doc) { // setup a masonry view of all he creators const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); - const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); + const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc); doc["tabs-button-tools"] = undefined; @@ -1260,7 +1128,7 @@ export class CurrentUserUtils { doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; doc._raiseWhenDragged = true; - doc._showLabel = false; + doc._showLabel = true; doc._showMenuLabel = true; doc.textAlign = StrCast(doc.textAlign, "left"); doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4338dedcc..4630b3bf2 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -482,7 +482,6 @@ export class CollectionStackingView extends CollectionSubView { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); } - const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion)); + const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None)); const rowWidth = () => panelWidth() - treeBulletWidth(); const treeViewRefs = new Map(); return docs.filter(child => child instanceof Doc).map((child, i) => { diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 49cd39f43..5ea6d567a 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -76,7 +76,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { return
e.stopPropagation()} // prevent triggering slider movement in registerSliding - onClick={e => this.clearDoc(e, `compareBox-${which}`)}> + onClick={e => this.clearDoc(e, which)}>
; }; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 34908e54b..1a8352b72 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -58,7 +58,6 @@ export class DashFieldViewInternal extends React.Component(); - @observable _showEnumerables: boolean = false; @observable _dashDoc: Doc | undefined; constructor(props: IDashFieldViewInternal) { @@ -110,7 +109,6 @@ export class DashFieldViewInternal extends React.Component this.fieldSpanKeyDown(e, r)); r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false)); r?.addEventListener("pointerdown", action((e) => { - this._showEnumerables = true; e.stopPropagation(); })); }} > @@ -124,7 +122,6 @@ export class DashFieldViewInternal extends React.Component { if (e.key === "Enter") { // handle the enter key by "submitting" the current text to Dash's database. - e.ctrlKey && DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: span.textContent! }]); this.updateText(span.textContent!, true); e.preventDefault();// prevent default to avoid a newline from being generated and wiping out this field view } @@ -142,7 +139,6 @@ export class DashFieldViewInternal extends React.Component { - this._showEnumerables = false; if (nodeText) { const newText = nodeText.startsWith(":=") || nodeText.startsWith("=:=") ? ":=-computed-" : nodeText; @@ -154,7 +150,6 @@ export class DashFieldViewInternal extends React.Component (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title))); if (modText) { // elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText; - DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []); Doc.SetInPlace(this._dashDoc!, this._fieldKey, modText, true); } // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key else if (nodeText.startsWith(":=")) { @@ -178,13 +173,6 @@ export class DashFieldViewInternal extends React.Component { - e.stopPropagation(); - const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]); - collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "add:right"); - } - // clicking on the label creates a pivot view collection of all documents // in the same collection. The pivot field is the fieldKey of this label @@ -220,8 +208,6 @@ export class DashFieldViewInternal extends React.Component} -
; } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 5dd7905530dbfbac3318e55fc625fe46427e23c4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 20 May 2022 13:45:21 -0400 Subject: adjusted placement of fonticonbox labels --- src/client/util/CurrentUserUtils.ts | 64 ++++++++++++-------------- src/client/views/MainView.tsx | 2 +- src/client/views/nodes/button/FontIconBox.scss | 8 ++-- src/client/views/nodes/button/FontIconBox.tsx | 23 +++++---- 4 files changed, 51 insertions(+), 46 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e0580d3c4..b9d2d63ba 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -181,21 +181,22 @@ export class CurrentUserUtils { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra }); - const newIconsList = [ - makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), - makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), - makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), - makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })), - makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), - makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), - makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })), - // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) - ].filter(d => d).map(d => d!); if (!templateIconsDoc) { + const newIconsList = [ + makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), + makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), + makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), + makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })), + makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), + makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), + makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), + makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), + makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })), + // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) + ].filter(d => d).map(d => d!); + doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true }); - newIconsList.map(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d)); + newIconsList.forEach(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d)); } } @@ -224,7 +225,6 @@ export class CurrentUserUtils { backgroundColor: "white", system: true, cloneFieldFilter: new List(["system"]) }); Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text'); - FormattedTextBox.SelectOnLoad = textDoc[Id]; doc.emptySlide = textDoc; } if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) { @@ -287,11 +287,7 @@ export class CurrentUserUtils { ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptyNote === undefined) { - doc.emptyNote = Docs.Create.TextDocument("", { - _width: 200, title: "text note", _autoHeight: true, system: true, - _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), - cloneFieldFilter: new List(["system"]) - }); + doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }); ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptyImage === undefined) { @@ -790,20 +786,20 @@ export class CurrentUserUtils { "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], script: 'setFont(value, _readOnly_)' }, - { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' }, - { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' }, + { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' }, + { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' }, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' }, - { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' }, - { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' }, - { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' }, + { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' }, + { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' }, + { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' }, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'}, // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'}, // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'}, - { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' }, - { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' }, - { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' }, + { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' }, + { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' }, + { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' }, ]; return tools; } @@ -816,9 +812,9 @@ export class CurrentUserUtils { { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' }, // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' }, - { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" }, - { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' }, - { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' }, + { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" }, + { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' }, + { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' }, ]; return tools; } @@ -862,19 +858,19 @@ export class CurrentUserUtils { script: 'setView(value, _readOnly_)', }, // Always show { - title: "back", toolTip: "prev", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left", + title: "Back", toolTip: "Prev AnimationFrame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left", hidden: 'IsNoviceMode()' }, { - title: "Forward", toolTip: "next", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right", + title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right", hidden: 'IsNoviceMode()' }, { - title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", + title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected { - title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", + title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', }, // Only when a document is selected { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9be00fa9e..45dd84e31 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -173,7 +173,7 @@ export class MainView extends React.Component { fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, - fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, + fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag, fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index e3e2be515..df9046f12 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -64,7 +64,7 @@ justify-items: center; &:hover { - filter:brightness(0.85) !important; + filter: brightness(0.85) !important; } } @@ -166,8 +166,8 @@ width: 45% !important; height: 45%; } - - &:hover{ + + &:hover { filter: brightness(0.85); } } @@ -278,10 +278,12 @@ background-color: #e3e3e3; box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3); border-radius: $standard-border-radius; + input[type=range]::-webkit-slider-runnable-track { background: gray; height: 3px; } + input[type=range]::-webkit-slider-thumb { box-shadow: 1px 1px 1px #000000; border: 1px solid #000000; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index d3ff2586b..20b2be216 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -107,6 +107,11 @@ export class FontIconBox extends DocComponent() { // Script for checking the outcome of the toggle const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; + const label = !Doc.UserDoc()._showLabel ? (null) : +
+ {this.label} +
; + if (numBtnType === NumButtonType.Slider) { const dropdown =
() { onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} > {checkResult} + {label} {this.rootDoc.dropDownOpen ? dropdown : null}
); @@ -206,7 +212,7 @@ export class FontIconBox extends DocComponent() { style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> - {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} + {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
}
@@ -277,7 +283,7 @@ export class FontIconBox extends DocComponent() { }); const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; @@ -331,7 +337,7 @@ export class FontIconBox extends DocComponent() { const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; @@ -375,7 +381,7 @@ export class FontIconBox extends DocComponent() { // Button label const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; @@ -387,7 +393,7 @@ export class FontIconBox extends DocComponent() { - +
); @@ -416,7 +422,8 @@ export class FontIconBox extends DocComponent() { style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
- {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} + {!this.label || !Doc.UserDoc()._showLabel ? (null) : +
{this.label}
}
); @@ -443,12 +450,12 @@ export class FontIconBox extends DocComponent() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : -
+
{this.label}
; -- cgit v1.2.3-70-g09d2 From 366cd5f3799a78c297c9a0dc31eb0bd8c0e4c4bd Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 20 May 2022 16:35:24 -0400 Subject: some cleanup of currentUserUtils. css fixes for fonticon boxes with labels --- src/client/util/CurrentUserUtils.ts | 288 +++++++-------------- .../collectionLinear/CollectionLinearView.tsx | 2 +- src/client/views/nodes/button/FontIconBox.scss | 44 +++- src/client/views/nodes/button/FontIconBox.tsx | 44 ++-- 4 files changed, 146 insertions(+), 232 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b9d2d63ba..b9789ab7f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -6,7 +6,6 @@ import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; -import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; @@ -23,7 +22,6 @@ import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; -import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; import { DragManager } from "./DragManager"; @@ -53,6 +51,7 @@ interface Button { list?: string[]; ignoreClick?: boolean; buttonText?: string; + hidden?: string; } export let resolvedPorts: { server: number, socket: number }; @@ -62,7 +61,6 @@ export class CurrentUserUtils { //TODO tfs: these should be temporary... private static mainDocId: string | undefined; - public static searchBtn: Doc; public static get id() { return this.curr_id; } public static get MainDocId() { return this.mainDocId; } public static set MainDocId(id: string | undefined) { this.mainDocId = id; } @@ -196,7 +194,6 @@ export class CurrentUserUtils { ].filter(d => d).map(d => d!); doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true }); - newIconsList.forEach(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d)); } } @@ -329,15 +326,11 @@ export class CurrentUserUtils { } // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools - static async setupCreatorButtons(doc: Doc) { + static setupCreatorButtons(doc: Doc) { let alreadyCreatedButtons: string[] = []; const dragCreatorSet = Cast(doc.myItemCreators, Doc, null); if (dragCreatorSet) { - const dragCreators = Cast(dragCreatorSet.data, listSpec(Doc)); - if (dragCreators) { - const dragDocs = await Promise.all(Array.from(dragCreators)); - alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title)); - } + alreadyCreatedButtons = DocListCast(dragCreatorSet.data).map(d => StrCast(d.title)); } const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)); const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({ @@ -373,7 +366,7 @@ export class CurrentUserUtils { return doc.myItemCreators as Doc; } - static async menuBtnDescriptions(doc: Doc) { + static menuBtnDescriptions(doc: Doc) { return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, @@ -390,7 +383,7 @@ export class CurrentUserUtils { static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments, hidden }) => + const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) => Docs.Create.FontIconDocument({ icon, btnType: ButtonType.MenuButton, @@ -411,17 +404,7 @@ export class CurrentUserUtils { }) ); - menuBtns.forEach(menuBtn => { - if (menuBtn.title === "Search") { - this.searchBtn = menuBtn; - } - }); - - menuBtns.forEach(menuBtn => { - if (menuBtn.title === "Search") { - doc.searchBtn = menuBtn; - } - }); + doc.searchBtn = menuBtns.find(btn => btn.title === "Search"); doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { title: "menuItemPanel", @@ -436,19 +419,6 @@ export class CurrentUserUtils { _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true })); } - // this resets all sidebar buttons to being deactivated - PromiseValue(Cast(doc.menuStack, Doc)).then(stack => { - stack && PromiseValue(stack.data).then(btns => { - DocListCastAsync(btns).then(bts => bts?.forEach(btn => { - btn.dontUndo = true; - btn.system = true; - if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files - btn.target = Doc.UserDoc().myFilesystem; - btn.title = "My Files"; - } - })); - }); - }); return doc.menuStack as Doc; } @@ -578,35 +548,26 @@ export class CurrentUserUtils { // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) - static async setupToolsBtnPanel(doc: Doc) { + static setupToolsBtnPanel(doc: Doc) { // setup a masonry view of all he creators - const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); + const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc); const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc); - doc["tabs-button-tools"] = undefined; - - if (doc.myCreators === undefined) { - doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { - title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true, - _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true, - })); - } - // setup a color picker - if (doc.myColorPicker === undefined) { - const color = Docs.Create.ColorDocument({ - title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true - }); - doc.myColorPicker = new PrefetchProxy(color); - } + doc.myCreators = doc.myCreators ?? new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { + title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true, + _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true, + })); + if (!DocListCast(doc.myCreators).includes(creatorBtns as Doc) || !DocListCast(doc.myCreators).includes(templateBtns as Doc)) Doc.GetProto(doc.myCreators as Doc).data = new List([creatorBtns, templateBtns]); - if (doc.myTools === undefined) { - const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], { - title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, - system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", - })) as any as Doc; + doc.myColorPicker = doc.myColorPicker ?? new PrefetchProxy(Docs.Create.ColorDocument({ + title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true + })); - doc.myTools = toolsStack; - } + doc.myTools = doc.myTools ?? new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], { + title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, + system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", + })) as any as Doc; + if (!DocListCast(doc.myTools).includes(doc.myCreators as Doc)) Doc.GetProto(doc.myTools as Doc).data = new List([doc.myCreators as Doc]); } static async setupDashboards(doc: Doc) { @@ -639,8 +600,7 @@ export class CurrentUserUtils { return doc.myDashboards as any as Doc; } - static async setupPresentations(doc: Doc) { - await doc.myTrails; + static setupPresentations(doc: Doc) { if (doc.myTrails === undefined) { const newTrail = ScriptField.MakeScript(`createNewPresentation()`); const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true }); @@ -743,7 +703,7 @@ export class CurrentUserUtils { // setup the list of sidebar mode buttons which determine what is displayed in the sidebar static async setupSidebarButtons(doc: Doc) { CurrentUserUtils.setupSidebarContainer(doc); - await CurrentUserUtils.setupToolsBtnPanel(doc); + CurrentUserUtils.setupToolsBtnPanel(doc); CurrentUserUtils.setupImportSidebar(doc); CurrentUserUtils.setupDashboards(doc); CurrentUserUtils.setupPresentations(doc); @@ -764,17 +724,26 @@ export class CurrentUserUtils { /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc) { - if (doc["dockedBtn-undo"] === undefined) { - doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("undo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true }); - } - if (doc["dockedBtn-redo"] === undefined) { - doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({ onClick: ScriptField.MakeScript("redo()"), _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, btnType: ButtonType.ToolButton, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true }); - } if (doc.dockedBtns === undefined) { - doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); + if (doc["dockedBtn-undo"] === undefined) { + doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({ + title: "undo", icon: "undo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true, + onClick: ScriptField.MakeScript("undo()"), btnType: ButtonType.ToolButton, _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo" + }); + } + if (doc["dockedBtn-redo"] === undefined) { + doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({ + title: "redo", icon: "redo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true, + onClick: ScriptField.MakeScript("redo()"), btnType: ButtonType.ToolButton, _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", + }); + } + doc.dockedBtns = CurrentUserUtils.linearButtonList({ + title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, + linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true + }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); } - (doc["dockedBtn-undo"] as Doc).dontUndo = true; - (doc["dockedBtn-redo"] as Doc).dontUndo = true; } static textTools(doc: Doc) { @@ -821,16 +790,7 @@ export class CurrentUserUtils { static schemaTools(doc: Doc) { const tools: Button[] = - [ - { - title: "Show preview", - toolTip: "Show preview of selected document", - btnType: ButtonType.ToggleButton, - buttonText: "Show Preview", - icon: "eye", - click: 'toggleSchemaPreview(_readOnly_)', - }, - ]; + [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview(_readOnly_)', }]; return tools; } @@ -846,7 +806,7 @@ export class CurrentUserUtils { return tools; } - static async contextMenuTools(doc: Doc) { + static contextMenuTools(doc: Doc) { return [ { title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, @@ -857,128 +817,52 @@ export class CurrentUserUtils { CollectionViewType.Grid], script: 'setView(value, _readOnly_)', }, // Always show - { - title: "Back", toolTip: "Prev AnimationFrame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left", - hidden: 'IsNoviceMode()' - }, - { - title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right", - hidden: 'IsNoviceMode()' - }, - { - title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", - script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' - }, // Only when a document is selected - { - title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", - script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', - }, // Only when a document is selected - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform + { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame(_readOnly_)', icon: "chevron-left", hidden: 'IsNoviceMode()' }, + { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame(_readOnly_)', icon: "chevron-right", hidden: 'IsNoviceMode()' }, + { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected + { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', }, + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected - { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available - { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available - { title: "Web", type: "webTools", subMenu: true, hidden: 'selectedDocumentType("web")' }, // Only when Web is selected - { title: "Schema", type: "schemaTools", subMenu: true, hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected + { title: "Text", type: "textTools", subMenu: CurrentUserUtils.textTools(doc), expanded: 'selectedDocumentType("rtf")' }, // Always available + { title: "Ink", type: "inkTools", subMenu: CurrentUserUtils.inkTools(doc), expanded: 'selectedDocumentType("ink")' }, // Always available + { title: "Web", type: "webTools", subMenu: CurrentUserUtils.webTools(doc), hidden: 'selectedDocumentType("web")' }, // Only when Web is selected + { title: "Schema", type: "schemaTools", subMenu: CurrentUserUtils.schemaTools(doc), hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected ]; } // Sets up the default context menu buttons - static async setupContextMenuButtons(doc: Doc) { + static setupContextMenuButtons(doc: Doc) { + const btnFunc = (params: Button) => Docs.Create.FontIconDocument({ + title: params.title, icon: params.icon, toolTip: params.toolTip, color: Colors.WHITE, system: true, dontUndo: true, ignoreClick: params.ignoreClick, + _nativeWidth: params.width ? params.width : 30, + _nativeHeight: 30, + _width: params.width ? params.width : 30, + _height: 30, + btnType: params.btnType, + numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax, + btnList: new List(params.list), + _stayInCollection: true, + _hideContextMenu: true, + _lockedPosition: true, + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + script: params.script ? ScriptField.MakeScript(params.script, { value: "any" }) : undefined, + backgroundColor: params.click ? ComputedField.MakeFunction(params.click) as any : "transparent", + onClick: params.click ? ScriptField.MakeScript(params.click, { scriptContext: "any" }, { _readOnly_: false }) : undefined, + hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, + }); if (doc.contextMenuBtns === undefined) { - const docList: Doc[] = []; - - (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, backgroundColor, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => { - const menuDocList: Doc[] = []; - if (subMenu) { - // default is textTools - let tools: Button[]; - switch (type) { - case "inkTools": - tools = CurrentUserUtils.inkTools(doc); - break; - case "schemaTools": - tools = CurrentUserUtils.schemaTools(doc); - break; - case "webTools": - tools = CurrentUserUtils.webTools(doc); - break; - case "textTools": - tools = CurrentUserUtils.textTools(doc); - break; - default: - tools = CurrentUserUtils.textTools(doc); - break; - } - tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => { - const backgroundColor = click ? ComputedField.MakeFunction(click) as any : "transparent"; - menuDocList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 25, - _nativeHeight: 25, - _width: width ? width : 25, - _height: 25, - icon, - toolTip, - numBtnType, - numBtnMin, - numBtnMax, - script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined, - btnType: btnType, - btnList: new List(list), - ignoreClick: ignoreClick, - _stayInCollection: true, - _hideContextMenu: true, - _lockedPosition: true, - system: true, - dontUndo: true, - title, - switchToggle, - color: Colors.WHITE, - backgroundColor, - _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click) : undefined - })); - }); - docList.push(CurrentUserUtils.linearButtonList({ - linearViewSubMenu: true, - flexGap: 0, - ignoreClick: true, - linearViewExpandable: true, - icon: title, - _height: 30, - // backgroundColor: hidden ? ComputedField.MakeFunction(hidden, { }, { _readOnly_: true }) as any : "transparent", - linearViewIsExpanded: expanded ? !(ComputedField.MakeFunction(expanded) as any) : undefined, - hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, - }, menuDocList)); - } else { - docList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 25, - _nativeHeight: 25, - _width: width ? width : 25, - _height: 25, - icon, - toolTip, - script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined, - btnType, - btnList: new List(list), - ignoreClick, - _stayInCollection: true, - _hideContextMenu: true, - _lockedPosition: true, - system: true, - dontUndo: true, - title, - color: Colors.WHITE, - backgroundColor: backgroundColor ? ComputedField.MakeFunction(backgroundColor) as any : "transparent", - _dropAction: "alias", - hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }, { _readOnly_: false }) : undefined - })); - } - }); - - doc.contextMenuBtns = CurrentUserUtils.linearButtonList({ title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }, docList); + doc.contextMenuBtns = CurrentUserUtils.linearButtonList( + { title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }, + CurrentUserUtils.contextMenuTools(doc).map(params => + !params.subMenu ? + btnFunc(params) : + CurrentUserUtils.linearButtonList({ + linearViewSubMenu: true, flexGap: 0, ignoreClick: true, + linearViewExpandable: true, icon: params.title, _height: 30, + linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, + hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, + }, params.subMenu.map(subParams => btnFunc(subParams))))); } } @@ -1463,7 +1347,8 @@ ScriptingGlobals.add(function makeTopLevelFolder() { ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }); -ScriptingGlobals.add(function nextKeyFrame() { +ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { + if (readOnly) return; const sel = SelectionManager.Views()[0]; const col = (sel.ComponentView as CollectionFreeFormView); const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); @@ -1476,7 +1361,8 @@ ScriptingGlobals.add(function nextKeyFrame() { sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame)); } ); -ScriptingGlobals.add(function prevKeyFrame() { +ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { + if (readOnly) return; const sel = SelectionManager.Views()[0]; const col = (sel.ComponentView as CollectionFreeFormView); const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index bec582dcd..c0a33a5e0 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -151,7 +151,7 @@ export class CollectionLinearView extends CollectionSubView() { removeDocument={this.props.removeDocument} ScreenToLocalTransform={docXf} PanelWidth={nested ? doc[WidthSym] : this.dimension} - PanelHeight={nested ? doc[HeightSym] : this.dimension} + PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension} renderDepth={this.props.renderDepth + 1} focus={emptyFunction} styleProvider={this.props.styleProvider} diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index df9046f12..6cd56f84e 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -18,12 +18,12 @@ .fontIconBox-label { color: $white; - position: relative; + bottom: 0; + position: absolute; text-align: center; font-size: 7px; letter-spacing: normal; background-color: inherit; - margin-top: 5px; border-radius: 8px; padding: 0; width: 100%; @@ -40,8 +40,10 @@ height: 80%; } - &.clickBtn { + &.clickBtn, + &.clickBtnLabel { cursor: pointer; + flex-direction: column; &:hover { background-color: rgba(0, 0, 0, 0.3) !important; @@ -53,6 +55,12 @@ } } + &.clickBtnLabel { + svg { + margin-top: -4px; + } + } + &.textBtn { display: grid; /* grid-row: auto; */ @@ -68,8 +76,10 @@ } } - &.tglBtn { + &.tglBtn, + &.tglBtnLabel { cursor: pointer; + flex-direction: column; &.switch { //TOGGLE @@ -146,10 +156,19 @@ } } - &.toolBtn { + &.tglBtnLabel { + svg { + margin-top: -4px; + } + } + + &.toolBtn, + &.toolBtnLabel { cursor: pointer; width: 100%; border-radius: 100%; + flex-direction: column; + margin-top: -4px; svg { width: 60% !important; @@ -157,6 +176,12 @@ } } + &.toolBtnLabel { + svg { + margin-top: -4px; + } + } + &.menuBtn { cursor: pointer !important; border-radius: 0px; @@ -174,7 +199,8 @@ - &.colorBtn { + &.colorBtn, + &.colorBtnLabel { color: black; cursor: pointer; flex-direction: column; @@ -204,6 +230,12 @@ } } + &.colorBtnLabel { + svg { + margin-top: -4px; + } + } + &.drpdownList { width: 100%; display: grid; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 20b2be216..e6d8fe88d 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -108,23 +108,19 @@ export class FontIconBox extends DocComponent() { const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; const label = !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; if (numBtnType === NumButtonType.Slider) { - const dropdown = -
e.stopPropagation()} - > - this._batch = UndoManager.StartBatch("presDuration")} - onPointerUp={() => this._batch?.end()} - onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }} - /> -
; + const dropdown =
e.stopPropagation()} > + this._batch = UndoManager.StartBatch("presDuration")} + onPointerUp={() => this._batch?.end()} + onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }} + /> +
; return (
() { style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> - {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} + {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
}
@@ -283,7 +279,7 @@ export class FontIconBox extends DocComponent() { }); const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; @@ -337,7 +333,7 @@ export class FontIconBox extends DocComponent() { const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; @@ -348,7 +344,7 @@ export class FontIconBox extends DocComponent() {
; setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView return ( -
this.colorPickerClosed = !this.colorPickerClosed)} onPointerDown={e => e.stopPropagation()}> @@ -381,7 +377,7 @@ export class FontIconBox extends DocComponent() { // Button label const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; @@ -399,7 +395,7 @@ export class FontIconBox extends DocComponent() { ); } else { return ( -
{label} @@ -423,7 +419,7 @@ export class FontIconBox extends DocComponent() {
{!this.label || !Doc.UserDoc()._showLabel ? (null) : -
{this.label}
} +
{this.label}
}
); @@ -450,12 +446,12 @@ export class FontIconBox extends DocComponent() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : -
+
{this.label}
; @@ -496,7 +492,7 @@ export class FontIconBox extends DocComponent() { break; case ButtonType.ToolButton: button = ( -
+
{label}
@@ -508,7 +504,7 @@ export class FontIconBox extends DocComponent() { break; case ButtonType.ClickButton: button = ( -
+
{label}
-- cgit v1.2.3-70-g09d2 From 0cddc2350124f7ae870e362a4774de6f4d9a0545 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 23 May 2022 10:12:19 -0400 Subject: more code cleanup in currentuserutils. some fixes to dragFactory-count titles. --- src/Utils.ts | 1 + src/client/documents/Documents.ts | 9 +- src/client/util/CurrentUserUtils.ts | 156 ++++++--------------- src/client/util/DragManager.ts | 6 +- src/client/views/MainView.tsx | 3 +- src/client/views/collections/CollectionMenu.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 13 +- 7 files changed, 66 insertions(+), 124 deletions(-) (limited to 'src/client/util') diff --git a/src/Utils.ts b/src/Utils.ts index c3c13a555..dbae0aa8c 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -8,6 +8,7 @@ import Color = require('color'); export namespace Utils { export let DRAG_THRESHOLD = 4; + export let SNAP_THRESHOLD = 10; export function readUploadedFileAsText(inputFile: File) { const temporaryFileReader = new FileReader(); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4be9674f7..6fa467368 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -156,6 +156,7 @@ export class DocumentOptions { "_carousel-caption-yMargin"?: number; "icon-nativeWidth"?: number; "icon-nativeHeight"?: number; + "dragFactory-count"?: number; // number of items created from a drag button (used for setting title with incrementing index) x?: number; y?: number; z?: number; // whether document is in overlay (1) or not (0 or undefined) @@ -672,8 +673,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options }); } - export function PresDocument(initial: List = new List(), options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.PRES), initial, options); + export function PresDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List(), options); } export function ScriptingDocument(script: Opt, options: DocumentOptions = {}, fieldKey?: string) { @@ -693,8 +694,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options); } - export function ScreenshotDocument(title: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", { ...options, title }); + export function ScreenshotDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options); } export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b9789ab7f..85769d915 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -24,7 +24,7 @@ import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; -import { DragManager } from "./DragManager"; +import { DragManager, dropActionType } from "./DragManager"; import { makeTemplate, MakeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; import { LinkManager } from "./LinkManager"; @@ -55,7 +55,6 @@ interface Button { } export let resolvedPorts: { server: number, socket: number }; -const headerViewVersion = "0.1"; export class CurrentUserUtils { private static curr_id: string; //TODO tfs: these should be temporary... @@ -87,7 +86,7 @@ export class CurrentUserUtils { }, { type: "mobile", icon: "mobile", template: this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, }, - [this.createToolButton({ ignoreClick: true, icon: "mobile", btnType: ButtonType.ToolButton, backgroundColor: "transparent" }), + [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")]) ] @@ -102,7 +101,6 @@ export class CurrentUserUtils { dragFactory: new PrefetchProxy(template) as any as Doc, title: type, icon, - btnType: ButtonType.ToolButton, }); } return doc[`template-button-${type}`] as Doc; @@ -179,6 +177,9 @@ export class CurrentUserUtils { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra }); + const fontBox = () => Docs.Create.FontIconDocument({ + _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() + }); if (!templateIconsDoc) { const newIconsList = [ makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), @@ -189,7 +190,7 @@ export class CurrentUserUtils { makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })), + makeIconTemplate(DocumentType.BUTTON, "data", () => fontBox()), // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) ].filter(d => d).map(d => d!); @@ -201,30 +202,23 @@ export class CurrentUserUtils { title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc }[] { + const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List(["system"]) }); if (doc.emptyPresentation === undefined) { - doc.emptyPresentation = Docs.Create.PresDocument(new List(), - { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyPresentation = Docs.Create.PresDocument({ ...standardOps(), title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); } if (doc.emptyCollection === undefined) { - doc.emptyCollection = Docs.Create.FreeformDocument([], - { _nativeWidth: undefined, _nativeHeight: undefined, _fitWidth: true, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyCollection = Docs.Create.FreeformDocument([], { ...standardOps(), title: "freeform", _width: 150, _height: 100 }); } if (doc.emptyPane === undefined) { - doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _backgroundGridShow: true, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyPane = Docs.Create.FreeformDocument([], { ...standardOps(), title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }); } if (doc.emptySlide === undefined) { - const textDoc = Docs.Create.TreeDocument([], { - title: "Slide", _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, - allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, - backgroundColor: "white", system: true, cloneFieldFilter: new List(["system"]) + doc.emptySlide = Docs.Create.TreeDocument([], { + ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined, + allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" }); - Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text'); - doc.emptySlide = textDoc; } - if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) { + if (doc.emptyHeader === undefined) { const json = { doc: { type: "doc", @@ -245,9 +239,8 @@ export class CurrentUserUtils { storedMarks: [] }; const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { - title: "text", version: headerViewVersion, _height: 70, _headerPointerEvents: "all", - _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, - cloneFieldFilter: new List(["system"]) + ...standardOps(), title: "text", _height: 70, + _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, }, "header"); const headerBtnHgt = 10; headerTemplate[DataSym].layout = @@ -263,43 +256,34 @@ export class CurrentUserUtils { // "
"; (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); doc.emptyHeader = headerTemplate; - ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptyComparison === undefined) { - doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "comparison box", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "Comparer", _width: 300, _height: 300 }); } if (doc.emptyScript === undefined) { - doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { ...standardOps(), title: "script", _width: 200, _height: 250, }); } if (doc.emptyScreenshot === undefined) { - doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, title: "empty screenshot", _width: 400, _height: 200, system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 }); } if (doc.emptyWall === undefined) { - doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyWall = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "screen snapshot", _width: 400, _height: 200, }); (doc.emptyWall as Doc).videoWall = true; } if (doc.emptyAudio === undefined) { - doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "audio recording", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", _width: 200, _height: 100, }); } if (doc.emptyNote === undefined) { - doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyImage === undefined) { - doc.emptyImage = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth: 250, title: "an image of a cat", system: true }); + doc.emptyNote = Docs.Create.TextDocument("", { ...standardOps(), title: "text note", _width: 200, _autoHeight: true }); } if (doc.emptyButton === undefined) { - doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyButton as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyButton = Docs.Create.ButtonDocument({ ...standardOps(), title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }); } if (doc.emptyWebpage === undefined) { - doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { ...standardOps(), title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }); } if (doc.emptyMap === undefined) { - doc.emptyMap = Docs.Create.MapDocument([], { title: "map", _showSidebar: true, _width: 800, _height: 600, system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyMap as Doc).proto as Doc)["dragFactory-count"] = 0; + doc.emptyMap = Docs.Create.MapDocument([], { ...standardOps(), title: "map", _showSidebar: true, _width: 800, _height: 600, }); } if (doc.activeMobileMenu === undefined) { this.setupActiveMobileMenu(doc); @@ -309,7 +293,6 @@ export class CurrentUserUtils { { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, }, { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true }, { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc }, - { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc }, { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true }, { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc }, { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, @@ -719,30 +702,21 @@ export class CurrentUserUtils { })) as any as Doc static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ - ...opts, btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _removeDropProperties: new List(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true + btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["_dropAction", "_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts, })) as any as Doc /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc) { if (doc.dockedBtns === undefined) { - if (doc["dockedBtn-undo"] === undefined) { - doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({ - title: "undo", icon: "undo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true, - onClick: ScriptField.MakeScript("undo()"), btnType: ButtonType.ToolButton, _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo" - }); - } - if (doc["dockedBtn-redo"] === undefined) { - doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({ - title: "redo", icon: "redo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true, - onClick: ScriptField.MakeScript("redo()"), btnType: ButtonType.ToolButton, _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", - }); - } + const dockBtn = (opts: DocumentOptions) => CurrentUserUtils.createToolButton({ _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...opts }); + const btnDescs = [ + { title: "undo", opts: () => ({ icon: "undo-alt", onClick: ScriptField.MakeScript("undo()"), toolTip: "Click to undo" }) }, + { title: "redo", opts: () => ({ icon: "redo-alt", onClick: ScriptField.MakeScript("redo()"), toolTip: "Click to redo" }) } + ]; doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true - }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); + }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() })))); } } @@ -996,7 +970,7 @@ export class CurrentUserUtils { static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); - const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + await DocListCastAsync((doc.globalGroupDatabase as Doc).data); reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]), async () => { const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); @@ -1025,9 +999,6 @@ export class CurrentUserUtils { doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); - doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // - doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); // - Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]); doc.savedFilters = new List(); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; @@ -1049,15 +1020,6 @@ export class CurrentUserUtils { doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); - // uncomment this to setup a default note style that uses the custom header layout - // PromiseValue(doc.emptyHeader).then(factory => { - // if (Cast(doc.defaultTextLayout, Doc, null)?.version !== headerViewVersion) { - // const deleg = Doc.delegateDragFactory(factory as Doc); - // deleg.title = "header"; - // doc.defaultTextLayout = new PrefetchProxy(deleg); - // Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg); - // } - // }); setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); if (doc.activeDashboard instanceof Doc) { @@ -1264,26 +1226,16 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { view && SelectionManager.SelectView(view, false); } }); -ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, - "document containing all shared Docs"); -ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, - "is Dash in novice mode"); -ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, - "creates a snapshot copy of a dashboard"); -ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, - "creates a new dashboard when called"); -ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, - "creates a new presentation when called"); -ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, - "creates a new folder in myFiles when called"); -ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, - "returns all the links to the document or its annotations", "(doc: any)"); -ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, - "imports files from device directly into the import sidebar"); -ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { - SharingManager.Instance.open(undefined, dashboard); -}, - "opens sharing dialog for Dashboard"); +ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, "document containing all shared Docs"); +ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode"); +ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); +ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard"); +ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called"); +ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); +ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); +ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); +ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); +ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); if (dashboards && dashboards.length > 1) { @@ -1292,21 +1244,10 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { } }, "Remove Dashboard from Dashboards"); -ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) { +ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); - - const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]); - - // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly - // dashboard["data-all"] = new List(allDocs); - // dashboardAlias["data-all"] = new List((allDocs || []).map(doc => Doc.MakeAlias(doc))); - - // const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); - // dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); - dashboardAlias.data = new List(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias); - //new List(); DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); @@ -1344,9 +1285,6 @@ ScriptingGlobals.add(function makeTopLevelFolder() { TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder); }); -ScriptingGlobals.add(function toggleComicMode() { - Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; -}); ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { if (readOnly) return; const sel = SelectionManager.Views()[0]; @@ -1359,8 +1297,7 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { CollectionFreeFormDocumentView.updateKeyframe(col.childDocs, currentFrame || 0); sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1); sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame)); -} -); +}); ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { if (readOnly) return; const sel = SelectionManager.Views()[0]; @@ -1372,5 +1309,4 @@ ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { } CollectionFreeFormDocumentView.gotoKeyframe(col.childDocs.slice()); sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1); -} -); \ No newline at end of file +}); \ No newline at end of file diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 5a00e0c04..9f8c49081 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -7,7 +7,7 @@ import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types"; -import { emptyFunction } from "../../Utils"; +import { emptyFunction, Utils } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; import * as globalCssVariables from "../views/global/globalCssVariables.scss"; import { DocumentView } from "../views/nodes/DocumentView"; @@ -277,7 +277,7 @@ export namespace DragManager { SnappingManager.setSnapLines(horizLines, vertLines); } export function snapDragAspect(dragPt: number[], snapAspect: number) { - let closest = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10); + let closest = Utils.SNAP_THRESHOLD; let near = dragPt; const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => { if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0 @@ -312,7 +312,7 @@ export namespace DragManager { } // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) { - const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10); + const snapThreshold = Utils.SNAP_THRESHOLD; const snapVal = (pts: number[], drag: number, snapLines: number[]) => { if (snapLines.length) { const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]]; // offsets from drag pt diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 45dd84e31..6a2df3b51 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -244,8 +244,7 @@ export class MainView extends React.Component { title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true })); } - const pres = Docs.Create.PresDocument(new List(), - { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); + const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); CollectionDockingView.AddSplit(pres, "right"); this.userDoc.activePresentation = pres; Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index b3fbd4ca4..c322d9e16 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -467,7 +467,7 @@ export class CollectionViewBaseChrome extends React.Component { - const doc = Docs.Create.ScreenshotDocument("screen recording", { _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" }); + const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" }); //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); CollectionDockingView.AddSplit(doc, "right"); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d0c8c5826..164bcb9b0 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -17,7 +17,7 @@ import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; -import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; +import { Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; @@ -62,6 +62,7 @@ import { schema } from "./schema_rts"; import { SummaryView } from "./SummaryView"; import applyDevTools = require("prosemirror-dev-tools"); import React = require("react"); +import { ComputedField } from '../../../../fields/ScriptField'; const translateGoogleApi = require("translate-google-api"); export interface FormattedTextBoxProps { @@ -371,9 +372,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild; const str = node.textContent; const prefix = str.startsWith("@") ? "" : "-"; - this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : ""); - if (str.startsWith("@") && str.length > 1) { - Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc); + + const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title)); + if (!(cfield instanceof ComputedField)) { + this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : ""); + if (str.startsWith("@") && str.length > 1) { + Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc); + } } } } -- cgit v1.2.3-70-g09d2 From 3fe0e0e02a6c9bd717b3df9b000c13d1131f6eb4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 23 May 2022 12:28:31 -0400 Subject: cleaning up singleLine text box api and TreeView types --- src/client/util/CurrentUserUtils.ts | 7 +-- src/client/views/MainView.tsx | 5 +- .../views/collections/CollectionTreeView.tsx | 29 ++++++---- src/client/views/collections/TreeView.tsx | 35 ++++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 34 ++++++++++-- .../collections/collectionFreeForm/MarqueeView.tsx | 13 +++-- src/client/views/nodes/DocumentView.tsx | 2 + src/client/views/nodes/FieldView.tsx | 3 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 7 +-- .../formattedText/ProsemirrorExampleTransfer.ts | 62 ++++++---------------- 10 files changed, 101 insertions(+), 96 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 85769d915..1ebb5365a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -16,6 +16,7 @@ import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; +import { TreeViewType } from "../views/collections/CollectionTreeView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { TreeView } from "../views/collections/TreeView"; import { Colors } from "../views/global/globalEnums"; @@ -215,7 +216,7 @@ export class CurrentUserUtils { if (doc.emptySlide === undefined) { doc.emptySlide = Docs.Create.TreeDocument([], { ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined, - allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" + allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" }); } if (doc.emptyHeader === undefined) { @@ -563,7 +564,7 @@ export class CurrentUserUtils { title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, _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, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: 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 toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`); @@ -616,7 +617,7 @@ export class CurrentUserUtils { title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, - isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, + isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." })); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6a2df3b51..09a57843c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,11 +8,9 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; import { PromiseValue, StrCast } from '../../fields/Types'; -import { TraceMobx } from '../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; @@ -34,6 +32,7 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/collectionLinear'; import { CollectionMenu } from './collections/CollectionMenu'; +import { TreeViewType } from './collections/CollectionTreeView'; import { CollectionViewType } from './collections/CollectionView'; import "./collections/TreeView.scss"; import { ComponentDecorations } from './ComponentDecorations'; @@ -263,7 +262,7 @@ export class MainView extends React.Component { title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", treeViewTruncateTitleWidth: 150, ignoreClick: true, - isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, + isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." })); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 5e89016db..99ffb9bb0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -27,6 +27,7 @@ import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import { TreeView } from "./TreeView"; import React = require("react"); +import { FieldViewProps } from "../nodes/FieldView"; const _global = (window /* browser */ || global /* node */) as any; export type collectionTreeViewProps = { @@ -39,6 +40,12 @@ export type collectionTreeViewProps = { onChildClick?: () => ScriptField; }; +export enum TreeViewType { + outline = "outline", + fileSystem = "fileSystem", + default = "default" +} + @observer export class CollectionTreeView extends CollectionSubView>() { private _treedropDisposer?: DragManager.DragDropDisposer; @@ -54,8 +61,8 @@ export class CollectionTreeView extends CollectionSubView StrCast(this.dataDoc.title)} SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => { - if (enter && this.props.Document.treeViewType === "outline") this.makeTextCollection(this.treeChildren); + if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren); this.dataDoc.title = value; return true; })} />; } + onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { + if (this.outlineMode && e.key === "Enter") { + e.stopPropagation(); + this.makeTextCollection(this.treeChildren); + return true; + } + } get documentTitle() { return this._titleRef = r} - onKeyDown={e => { - if (this.outlineMode) { - e.stopPropagation(); - e.key === "Enter" && this.makeTextCollection(this.treeChildren); - } - }}> + ref={r => this._titleRef = r}> {this.outlineMode ? this.documentTitle : this.editableTitle}
; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 65f8fe248..70ad23f41 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -26,11 +26,12 @@ import { DocumentView, DocumentViewInternal, DocumentViewProps, StyleProviderFun import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { StyleProp } from '../StyleProvider'; -import { CollectionTreeView } from './CollectionTreeView'; +import { CollectionTreeView, TreeViewType } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; import React = require("react"); import { KeyValueBox } from '../nodes/KeyValueBox'; +import { FieldViewProps } from '../nodes/FieldView'; export interface TreeViewProps { treeView: CollectionTreeView; @@ -241,7 +242,7 @@ export class TreeView extends React.Component { layout: CollectionView.LayoutString("data"), title: "-title-", treeViewExpandedViewLock: true, treeViewExpandedView: "data", - _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: "outline", + _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: TreeViewType.outline, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _width: 1000, _height: 10 }); Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); @@ -604,24 +605,29 @@ export class TreeView extends React.Component { if (property.startsWith(StyleProp.Decorations)) return (null); return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView } - onKeyDown = (e: React.KeyboardEvent) => { + onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) { switch (e.key) { case "Tab": - e.stopPropagation(); - e.preventDefault(); + e.stopPropagation?.(); + e.preventDefault?.(); setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150); - return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab"); + UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab"); + return true; case "Backspace": - e.stopPropagation(); - e.preventDefault(); - return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc); + if (!(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc)) { + e.stopPropagation?.(); + e.preventDefault?.(); + return true; + } + break; case "Enter": - e.stopPropagation(); - e.preventDefault(); + e.stopPropagation?.(); + e.preventDefault?.(); return UndoManager.RunInBatch(this.makeTextCollection, "bullet"); } } + return false; } titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth())); @@ -758,6 +764,7 @@ export class TreeView extends React.Component { hideResizeHandles={this.props.treeView.outlineMode} focus={this.refocus} ContentScaling={returnOne} + onKey={this.onKeyDown} hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)} dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)} ScreenToLocalTransform={this.docTransform} @@ -800,7 +807,7 @@ export class TreeView extends React.Component { @computed get renderBorder() { const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None); const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string, label: string } }; - return
+ return
{!this.treeViewOpen ? (null) : this.renderContent}
; } @@ -821,9 +828,7 @@ export class TreeView extends React.Component { return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document - onKeyDown={this.onKeyDown}> + onDrop={this.onTreeDrop}>
  • {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader" this.renderEmbeddedDocument(false, returnFalse) : diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index df09569ef..3b32cf57d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,18 +1,18 @@ import { Bezier } from "bezier-js"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { DateField } from "../../../../fields/DateField"; -import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; +import { DataSym, Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { ObjectField } from "../../../../fields/ObjectField"; import { RichTextField } from "../../../../fields/RichTextField"; -import { ImageField } from "../../../../fields/URLField"; import { createSchema, listSpec } from "../../../../fields/Schema"; import { ScriptField } from "../../../../fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; +import { ImageField } from "../../../../fields/URLField"; import { TraceMobx } from "../../../../fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils"; @@ -26,6 +26,7 @@ import { DragManager, dropActionType } from "../../../util/DragManager"; import { HistoryUtil } from "../../../util/History"; import { InteractionUtils } from "../../../util/InteractionUtils"; import { LinkManager } from "../../../util/LinkManager"; +import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; import { SearchUtil } from "../../../util/SearchUtil"; import { SelectionManager } from "../../../util/SelectionManager"; import { ColorScheme } from "../../../util/SettingsManager"; @@ -47,13 +48,14 @@ import { CreateImage } from "../../nodes/WebBoxRenderer"; import { StyleProp } from "../../StyleProvider"; import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; +import { TreeViewType } from "../CollectionTreeView"; import { CollectionViewType } from "../CollectionView"; import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; +import { FieldView, FieldViewProps } from "../../nodes/FieldView"; export const panZoomSchema = createSchema({ _panX: "number", @@ -957,7 +959,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return; + if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -1160,6 +1162,27 @@ export class CollectionFreeFormView extends CollectionSubView this.props.isSelected() || this.props.isContentActive(); + @undoBatch + @action + onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => { + const docView = fieldProps.DocumentView?.(); + if (docView && docView.rootDoc._singleLine && ["Tab", "Enter"].includes(e.key)) { + e.stopPropagation?.(); + const below = !e.altKey && e.key !== "Tab"; + const layoutKey = StrCast(docView.LayoutFieldKey); + const newDoc = Doc.MakeCopy(docView.rootDoc, true); + const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)]; + newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; + if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10; + else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10; + if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) { + newDoc[layoutKey] = docView.rootDoc[layoutKey]; + } + Doc.GetProto(newDoc).text = undefined; + FormattedTextBox.SelectOnLoad = newDoc[Id]; + return this.addDocument?.(newDoc); + } + } pointerEvents = () => { const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); const pointerEvents = this.props.isContentActive() === false ? "none" : @@ -1185,6 +1208,7 @@ export class CollectionFreeFormView extends CollectionSubView Transform; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f5ad47ca7..f7312e59c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -50,6 +50,7 @@ import { ScriptingBox } from "./ScriptingBox"; import { PresBox } from './trails/PresBox'; import React = require("react"); import { DocServer } from "../../DocServer"; +import { FieldViewProps } from "./FieldView"; const { Howl } = require('howler'); interface Window { @@ -178,6 +179,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { onPointerDown?: () => ScriptField; onPointerUp?: () => ScriptField; onBrowseClick?: () => (ScriptField | undefined); + onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined); } // these props are only available in DocumentViewIntenral diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 94937430e..79c1f1c40 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -5,7 +5,7 @@ import { DateField } from "../../../fields/DateField"; import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc"; import { List } from "../../../fields/List"; import { WebField } from "../../../fields/URLField"; -import { DocumentViewSharedProps } from "./DocumentView"; +import { DocumentView, DocumentViewSharedProps } from "./DocumentView"; import { ScriptField } from "../../../fields/ScriptField"; // @@ -25,6 +25,7 @@ export interface FieldViewProps extends DocumentViewSharedProps { scaling?: () => number; setHeight?: (height: number) => void; onBrowseClick?: () => (ScriptField | undefined); + onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined); // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) pointerEvents?: () => Opt; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 164bcb9b0..0e29461a6 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -17,6 +17,7 @@ import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; +import { ComputedField } from '../../../../fields/ScriptField'; import { Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; @@ -62,7 +63,6 @@ import { schema } from "./schema_rts"; import { SummaryView } from "./SummaryView"; import applyDevTools = require("prosemirror-dev-tools"); import React = require("react"); -import { ComputedField } from '../../../../fields/ScriptField'; const translateGoogleApi = require("translate-google-api"); export interface FormattedTextBoxProps { @@ -1471,11 +1471,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, "titler"); } } + onKeyDown = (e: React.KeyboardEvent) => { - // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container) - if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) { - return; - } if (e.altKey) { e.preventDefault(); return; diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index c76eda859..0dd0a8411 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -1,20 +1,15 @@ -import { chainCommands, exitCode, joinDown, joinUp, lift, deleteSelection, joinBackward, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn, newlineInCode } from "prosemirror-commands"; -import { liftTarget } from "prosemirror-transform"; +import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from "prosemirror-commands"; import { redo, undo } from "prosemirror-history"; import { Schema } from "prosemirror-model"; -import { liftListItem, sinkListItem } from "./prosemirrorPatches.js"; -import { splitListItem, wrapInList, } from "prosemirror-schema-list"; -import { EditorState, Transaction, TextSelection } from "prosemirror-state"; -import { SelectionManager } from "../../../util/SelectionManager"; -import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast, AclAugment, AclSelfEdit } from "../../../../fields/Doc"; -import { FormattedTextBox } from "./FormattedTextBox"; -import { Id } from "../../../../fields/FieldSymbols"; -import { Docs } from "../../../documents/Documents"; -import { Utils } from "../../../../Utils"; -import { listSpec } from "../../../../fields/Schema"; -import { List } from "../../../../fields/List"; +import { splitListItem, wrapInList } from "prosemirror-schema-list"; +import { EditorState, TextSelection, Transaction } from "prosemirror-state"; +import { liftTarget } from "prosemirror-transform"; +import { AclAugment, AclSelfEdit, Doc } from "../../../../fields/Doc"; import { GetEffectiveAcl } from "../../../../fields/util"; +import { Utils } from "../../../../Utils"; +import { Docs } from "../../../documents/Documents"; +import { SelectionManager } from "../../../util/SelectionManager"; +import { liftListItem, sinkListItem } from "./prosemirrorPatches.js"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -48,29 +43,6 @@ export function buildKeymap>(schema: S, props: any, mapKey keys[key] = cmd; } - /// bcz; Argh!! replace with an onEnter func that conditionally handles Enter - const addTextBox = (below: boolean, force?: boolean) => { - if (props.Document.treeViewType === "outline") return true; // bcz: Arghh .. need to determine if this is an treeViewOutlineBox in which case Enter's are ignored.. - const layoutDoc = props.Document; - const originalDoc = layoutDoc.rootDocument || layoutDoc; - if (force || props.Document._singleLine) { - const layoutKey = StrCast(originalDoc.layoutKey); - const newDoc = Doc.MakeCopy(originalDoc, true); - const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)]; - newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; - if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10; - else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10; - if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) { - newDoc[layoutKey] = originalDoc[layoutKey]; - } - Doc.GetProto(newDoc).text = undefined; - FormattedTextBox.SelectOnLoad = newDoc[Id]; - props.addDocument(newDoc); - return true; - } - return false; - }; - const canEdit = (state: any) => { switch (GetEffectiveAcl(props.Document)) { case AclAugment: return false; @@ -109,11 +81,7 @@ export function buildKeymap>(schema: S, props: any, mapKey bind("Ctrl-i", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any)); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - /// bcz; Argh!! replace layotuTEmpalteString with a onTab prop conditionally handles Tab); - if (props.Document._singleLine) { - if (!props.LayoutTemplateString) return addTextBox(false, true); - return true; - } + if (props.onKey?.({ key: "Tab" }, props)) return true; if (!canEdit(state)) return true; const ref = state.selection; const range = ref.$from.blockRange(ref.$to); @@ -138,8 +106,7 @@ export function buildKeymap>(schema: S, props: any, mapKey }); bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - /// bcz; Argh!! replace with a onShiftTab prop conditionally handles Tab); - if (props.Document._singleLine) return true; + if (props.onKey?.({ key: "Tab", shiftKey: true })) return true; // single line docs don't process tabs so that their containers can decide what to do. This should be a prop if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); @@ -188,13 +155,14 @@ export function buildKeymap>(schema: S, props: any, mapKey }; //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", () => addTextBox(false, true)); + bind("Alt-Enter", () => true); //Command to create a text document to the bottom of the selected textbox - bind("Ctrl-Enter", () => addTextBox(true, true)); + bind("Ctrl-Enter", () => true); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { + if (props.onKey?.(event, props)) return true; if (!canEdit(state)) return true; if (!deleteSelection(state, (tx: Transaction) => { @@ -216,8 +184,8 @@ export function buildKeymap>(schema: S, props: any, mapKey //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock //command to break line bind("Enter", (state: EditorState, dispatch: (tx: Transaction>) => void) => { - if (addTextBox(true, false)) return true; + if (props.onKey?.(event, props)) return true; if (!canEdit(state)) return true; const trange = state.selection.$from.blockRange(state.selection.$to); -- cgit v1.2.3-70-g09d2 From 414b56260c55f5ea7cac43dad83b41688fb924b5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 23 May 2022 20:34:07 -0400 Subject: added headerBar for closed tabs and other docs. extended multirow/col to support hiding chile doc decorations and for making child content/document active work better. changed background style to work better for sub collections. --- src/client/util/CurrentUserUtils.ts | 3 + src/client/views/DocumentDecorations.tsx | 7 +- src/client/views/MainView.tsx | 115 +++++++++++++++------ src/client/views/StyleProvider.tsx | 6 +- src/client/views/collections/CollectionMenu.tsx | 17 ++- src/client/views/collections/CollectionView.tsx | 6 +- src/client/views/collections/TabDocView.tsx | 2 + .../CollectionMulticolumnView.tsx | 11 +- .../CollectionMultirowView.tsx | 11 +- src/client/views/nodes/DocumentView.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 1 - 11 files changed, 125 insertions(+), 60 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1ebb5365a..1e42b9073 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -70,6 +70,7 @@ export class CurrentUserUtils { @observable public static GuestDashboard: Doc | undefined; @observable public static GuestMobile: Doc | undefined; @observable public static propertiesWidth: number = 0; + @observable public static headerBarHeight: number = 0; @observable public static searchPanelWidth: number = 0; // sets up the default User Templates - slideView, headerView @@ -1003,6 +1004,7 @@ export class CurrentUserUtils { doc.savedFilters = new List(); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; + doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); 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 @@ -1210,6 +1212,7 @@ export class CurrentUserUtils { public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); } public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); } public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } + public static get MyHeaderBarDoc() { return Cast(Doc.UserDoc().myHeaderBarDoc, Doc, null); } public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ae3a91c4d..29e088143 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -511,11 +511,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P {`${this.selectionTitle}`}
  • ; - let inMainMenuPanel = false; - for (let node = seldoc.ContentDiv; node && !inMainMenuPanel; node = node?.parentNode as any) { - if (node.className === "mainView-mainContent") inMainMenuPanel = true; - } - const leftBounds = inMainMenuPanel ? 0 : this.props.boundsLeft; + + const leftBounds = this.props.boundsLeft; const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop; bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2; bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 09a57843c..69e394790 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -33,7 +33,7 @@ import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOpti import { CollectionLinearView } from './collections/collectionLinear'; import { CollectionMenu } from './collections/CollectionMenu'; import { TreeViewType } from './collections/CollectionTreeView'; -import { CollectionViewType } from './collections/CollectionView'; +import { CollectionView, CollectionViewType } from './collections/CollectionView'; import "./collections/TreeView.scss"; import { ComponentDecorations } from './ComponentDecorations'; import { ContextMenu } from './ContextMenu'; @@ -81,14 +81,19 @@ export class MainView extends React.Component { @computed private get dashboardTabHeight() { return 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); } - @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); } + @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); } + @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); } + @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); } @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; } @computed private get userDoc() { return Doc.UserDoc(); } @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); } @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } + @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; } @computed public get mainFreeform(): Opt { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } + headerBarDocWidth = () => this._dashUIWidth; + headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0; topMenuHeight = () => 35; topMenuWidth = returnZero; // value is ignored ... leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", "")); @@ -98,7 +103,7 @@ export class MainView extends React.Component { propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0)); propertiesHeight = () => this._dashUIHeight; mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth(); - mainDocViewHeight = () => this._dashUIHeight; + mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); componentDidMount() { document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!)); @@ -177,8 +182,8 @@ export class MainView extends React.Component { fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any, - fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp, - fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, + fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer as any, fa.faExpand, fa.faUndo, + fa.faSlidersH, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, @@ -276,33 +281,67 @@ export class MainView extends React.Component { return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)", { documentView: "any", clientX: "number", clientY: "number" })! : undefined; } - + headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1); + + headerContentActive = () => SnappingManager.GetIsDragging() ? false : true; + @computed get headerBarDocView() { + console.log(this.headerBarDoc) + return
    +
    ; + } @computed get mainDocView() { - return ; + return <> + {this.headerBarDocView} + ; } @computed get dockingContent() { @@ -331,7 +370,7 @@ export class MainView extends React.Component { this.closeFlyout); } - sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1); + sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); addDocTabFunc = (doc: Doc, location: string): boolean => { const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); @@ -461,6 +500,16 @@ export class MainView extends React.Component { ; } + @computed get headerBar() { + return !this.userDoc ? (null) : +
    + {this.headerBarDocView} +
    ; + } + @computed get mainDashboardArea() { return !this.userDoc ? (null) :
    { @@ -620,7 +669,7 @@ export class MainView extends React.Component { - + {LinkDescriptionPopup.descriptionPopup ? : null} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 7ac40fc8e..c44443264 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -158,12 +158,12 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 0 ? + Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : - Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ? + Doc.UserDoc().activeCollectionBackground, "string") ?? (darkScheme() ? Colors.BLACK : "linear-gradient(#065fff, #85c1f9)")) - )); + ); break; //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index c322d9e16..96029a8e4 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -84,11 +84,11 @@ export class CollectionMenu extends AntimodeMenu{ } @action - toggleProperties = () => { - if (CurrentUserUtils.propertiesWidth > 0) { - CurrentUserUtils.propertiesWidth = 0; + toggleTopBar = () => { + if (CurrentUserUtils.headerBarHeight > 0) { + CurrentUserUtils.headerBarHeight = 0; } else { - CurrentUserUtils.propertiesWidth = 250; + CurrentUserUtils.headerBarHeight = 60; } } @@ -137,14 +137,13 @@ export class CollectionMenu extends AntimodeMenu{ render() { - const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left"; - const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel"; + const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down"; + const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar"; - const prop = {propTitle}
    } key="properties" placement="bottom"> + const prop = {propTitle}
    } key="topar" placement="bottom">
    0 ? Colors.MEDIUM_BLUE : undefined }} - key="properties" - onPointerDown={this.toggleProperties}> + onPointerDown={this.toggleTopBar}>
    ; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 19485a0eb..4f92e305e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -6,7 +6,7 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { ObjectField } from '../../../fields/ObjectField'; import { ScriptField } from '../../../fields/ScriptField'; -import { Cast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { returnEmptyString } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; @@ -237,6 +237,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.props.PanelWidth(); + childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles); + childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle); childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null); @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); } @@ -258,6 +260,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent { closeWrap.id = "lm_closeWrap"; closeWrap.onclick = (e: MouseEvent) => { tab.header.parent.contentItem.remove(); + Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc); Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true); }; const docIcon = ; @@ -179,6 +180,7 @@ export class TabDocView extends React.Component { tab.closeElement.off('click') //unbind the current click handler .click(function () { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); + Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", doc); Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true); SelectionManager.DeselectAll(); UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab"); diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index b6b59a2d6..66cc3613d 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -241,7 +241,9 @@ export class CollectionMulticolumnView extends CollectionSubView() { } return this.props.addDocTab(doc, where); } - isContentActive = () => this.props.isSelected() || this.props.isContentActive(); + isContentActive = () => this.props.isSelected() ? true : undefined; + isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined; + isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive(); getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return this.props.isSelected() || this.props.isContentActive(); + isContentActive = () => this.props.isSelected() ? true : undefined; + isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined; + isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive(); getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { return {!this._retryThumb || !this.thumbShown() ? (null) : diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 0e29461a6..822bc996b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -635,7 +635,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }), icon: icon }); }); - !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" }); const highlighting: ContextMenuProps[] = []; const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others"]; const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"]; -- cgit v1.2.3-70-g09d2 From d50b851d8e92797d214fc35cf243bfb29e970b36 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 23 May 2022 21:58:26 -0400 Subject: simplified dashboards by getting rid of confusing on-screen/off-screen tabs. Now off screen tabs appear in the header bar which is not currently specific to a dashboard. --- src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 33 +-------------- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 48 +++++++++++++++++----- src/client/views/collections/TabDocView.tsx | 3 +- 5 files changed, 43 insertions(+), 49 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6fa467368..c3b6953b5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -905,9 +905,7 @@ export namespace Docs { } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true, isFolder: true }); - const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true, isFolder: true }); - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } export function DirectoryImportDocument(options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1e42b9073..9212d8ed9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1172,22 +1172,8 @@ export class CurrentUserUtils { freeformDoc.context = dashboardDoc; // switching the tabs from the datadoc to the regular doc - const dashboardTabs = dashboardDoc[DataSym].data; - dashboardDoc[DataSym].data = new List(); - dashboardDoc.data = dashboardTabs; - - // collating all docs on the dashboard to make a data-all field - const allDocs = new List(); - const allDocs2 = new List(); // Array.from, spread, splice all cause so stack or acl issues for some reason - DocListCast(dashboardTabs).forEach(doc => { - const tabDocs = DocListCast(doc.data); - allDocs.push(...tabDocs); - allDocs2.push(...tabDocs); - }); - dashboardDoc[DataSym]["data-all"] = allDocs; - dashboardDoc["data-all"] = allDocs2; - DocListCast(dashboardDoc.data).forEach(doc => doc.dashboard = dashboardDoc); - DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; + const dashboardTabs = DocListCast(dashboardDoc[DataSym].data); + dashboardDoc.data = new List(dashboardTabs); userDoc.activePresentation = presentation; @@ -1258,21 +1244,6 @@ ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { }, "adds Dashboard to set of Dashboards"); -/** - * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard. - */ -ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) { - if (dashboard[DataSym] instanceof Doc) { - const allDocs = DocListCast(dashboard["data-all"]); - const onScreenTab = DocListCast(dashboard.data)[0]; - const onScreenDocs = DocListCast(onScreenTab.data); - return new List(allDocs.reduce((result: Doc[], doc) => { - !onScreenDocs.includes(doc) && !onScreenDocs.includes(doc.aliasOf as Doc) && (result.push(doc)); - return result; - }, [])); - } - return []; -}); ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) { let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined; if (selected && checkParent) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 69e394790..f3dc2cf85 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -92,7 +92,7 @@ export class MainView extends React.Component { @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; } @computed public get mainFreeform(): Opt { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - headerBarDocWidth = () => this._dashUIWidth; + headerBarDocWidth = () => this.mainDocViewWidth(); headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0; topMenuHeight = () => 35; topMenuWidth = returnZero; // value is ignored ... @@ -102,7 +102,7 @@ export class MainView extends React.Component { leftMenuFlyoutHeight = () => this._dashUIHeight; propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0)); propertiesHeight = () => this._dashUIHeight; - mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth(); + mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth(); mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); componentDidMount() { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 30f60bba6..1d65256a6 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -81,6 +81,8 @@ export class CollectionDockingView extends CollectionSubView() { tabItemDropped = () => DragManager.CompleteWindowDrag?.(false); tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => { + const dashDoc = proxy?._contentItem?.tab?.DashDoc as Doc; + dashDoc && (DragManager.DocDragData = new DragManager.DocumentDragData([proxy._contentItem.tab.DashDoc])); DragManager.CompleteWindowDrag = (aborted: boolean) => { if (aborted) { proxy._dragListener.AbortDrag(); @@ -90,6 +92,8 @@ export class CollectionDockingView extends CollectionSubView() { this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout) } DragManager.CompleteWindowDrag = undefined; + } else { + DragManager.DocDragData?.droppedDocuments.forEach(doc => this.addToTabDocList(doc)); } finishDrag?.(aborted); }; @@ -100,12 +104,43 @@ export class CollectionDockingView extends CollectionSubView() { this.stateChanged(); } + addToTabDocList = (document: Doc) => { + const instance = CollectionDockingView.Instance; + if (instance) { + const docList = DocListCast(instance.props.Document[DataSym].data); + // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases + !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data", document); + // adds an alias of the doc to the data-all field of the layoutdocs of the aliases + DocListCast(instance.props.Document.aliases).forEach(alias => { + const aliasDocList = DocListCast(alias.data); + // if aliasDocList contains the alias, don't do anything + // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias + !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document); + }); + } + } + removeFromTabDocList = (document: Doc) => { + const instance = CollectionDockingView.Instance; + if (instance) { + // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases + Doc.RemoveDocFromList(instance.props.Document[DataSym], "data", document); + // adds an alias of the doc to the data-all field of the layoutdocs of the aliases + DocListCast(instance.props.Document.aliases).forEach(alias => { + // if aliasDocList contains the alias, don't do anything + // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias + Doc.RemoveDocFromList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document); + }); + document.type !== DocumentType.KVP && Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", document); + } + } + @undoBatch public static CloseSplit(document: Opt, panelName?: string): boolean { const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document); if (tab) { const j = tab.header.parent.contentItems.indexOf(tab.contentItem); if (j !== -1) { + CollectionDockingView.Instance.removeFromTabDocList(tab.dashDoc as Doc); tab.header.parent.contentItems[j].remove(); return CollectionDockingView.Instance.layoutChanged(); } @@ -185,16 +220,7 @@ export class CollectionDockingView extends CollectionSubView() { const instance = CollectionDockingView.Instance; if (!instance) return false; else { - const docList = DocListCast(instance.props.Document[DataSym]["data-all"]); - // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases - !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document); - // adds an alias of the doc to the data-all field of the layoutdocs of the aliases - DocListCast(instance.props.Document[DataSym].aliases).forEach(alias => { - const aliasDocList = DocListCast(alias["data-all"]); - // if aliasDocList contains the alias, don't do anything - // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias - !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document); - }); + instance.addToTabDocList(document); } const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); @@ -477,7 +503,7 @@ export class CollectionDockingView extends CollectionSubView() { //if (confirm('really close this?')) { if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) { stack.remove(); - stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true)); + stack.contentItems.forEach((contentItem: any) => CollectionDockingView.Instance.removeFromTabDocList(contentItem.tab.DashDoc)); } else { alert('cant delete the last stack'); } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 0070d0b9d..88d14fdfe 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -180,8 +180,7 @@ export class TabDocView extends React.Component { tab.closeElement.off('click') //unbind the current click handler .click(function () { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); - Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", doc); - Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true); + CollectionDockingView.Instance?.removeFromTabDocList(doc); SelectionManager.DeselectAll(); UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab"); }); -- cgit v1.2.3-70-g09d2 From 13924d1b0bd674146b940bb6e31ad6054c447b09 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 23 May 2022 22:15:17 -0400 Subject: fixed warnings. --- src/client/util/CurrentUserUtils.ts | 6 +++--- src/client/views/collections/CollectionTreeView.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9212d8ed9..e4dc175bd 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -192,7 +192,7 @@ export class CurrentUserUtils { makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.BUTTON, "data", () => fontBox()), + makeIconTemplate(DocumentType.BUTTON, "data", fontBox), // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) ].filter(d => d).map(d => d!); @@ -542,7 +542,7 @@ export class CurrentUserUtils { title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true, _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true, })); - if (!DocListCast(doc.myCreators).includes(creatorBtns as Doc) || !DocListCast(doc.myCreators).includes(templateBtns as Doc)) Doc.GetProto(doc.myCreators as Doc).data = new List([creatorBtns, templateBtns]); + if (!DocListCast(doc.myCreators).includes(creatorBtns) || !DocListCast(doc.myCreators).includes(templateBtns)) Doc.GetProto(doc.myCreators as Doc).data = new List([creatorBtns, templateBtns]); doc.myColorPicker = doc.myColorPicker ?? new PrefetchProxy(Docs.Create.ColorDocument({ title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true @@ -838,7 +838,7 @@ export class CurrentUserUtils { linearViewExpandable: true, icon: params.title, _height: 30, linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, - }, params.subMenu.map(subParams => btnFunc(subParams))))); + }, params.subMenu.map(btnFunc)))); } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 99ffb9bb0..c49580046 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -61,7 +61,7 @@ export class CollectionTreeView extends CollectionSubView Date: Wed, 25 May 2022 15:18:46 -0400 Subject: variety of small fixes for text / sharing. made typed text default to 35 height so that when shared it doesn't start big and jump to small. changed permissions to be private until an Acl is set - this prevents private docs from flicking on momentarily when shared since fields are not distributed atomically. added Shift/Alt/Ctrl Enter for freeform and stacking views to create neighboring docs. fixed first typed char of text to have a user_mark. made shared text doc header overlap to prevent scrolling. --- src/client/DocServer.ts | 60 ++++++++++------------ src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/MainView.tsx | 4 +- src/client/views/StyleProvider.tsx | 5 +- .../views/collections/CollectionStackingView.tsx | 22 ++++++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 16 +++--- .../formattedText/ProsemirrorExampleTransfer.ts | 14 +++-- src/fields/util.ts | 7 ++- 10 files changed, 77 insertions(+), 57 deletions(-) (limited to 'src/client/util') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index e498a7cca..dbc4783d8 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -364,40 +364,36 @@ export namespace DocServer { const proms: Promise[] = []; runInAction(() => { for (const field of fields) { - if (field !== undefined && field !== null && !_cache[field.id]) { + const cached = _cache[field.id]; + if (!cached) { // deserialize - const cached = _cache[field.id]; - if (!cached) { - const prom = SerializationHelper.Deserialize(field).then(deserialized => { - fieldMap[field.id] = deserialized; - - //overwrite or delete any promises (that we inserted as flags - // to indicate that the field was in the process of being fetched). Now everything - // should be an actual value within or entirely absent from the cache. - if (deserialized !== undefined) { - _cache[field.id] = deserialized; - } else { - delete _cache[field.id]; - } - return deserialized; - }); - // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) - // we set the value at the field's id to a promise that will resolve to the field. - // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). - // The mapping in the .then call ensures that when other callers await these promises, they'll - // get the resolved field - _cache[field.id] = prom; - - // adds to a list of promises that will be awaited asynchronously - proms.push(prom); - } else if (cached instanceof Promise) { - proms.push(cached as any); - } - } else if (_cache[field.id] instanceof Promise) { - proms.push(_cache[field.id] as any); - (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f); + const prom = SerializationHelper.Deserialize(field).then(deserialized => { + fieldMap[field.id] = deserialized; + + //overwrite or delete any promises (that we inserted as flags + // to indicate that the field was in the process of being fetched). Now everything + // should be an actual value within or entirely absent from the cache. + if (deserialized !== undefined) { + _cache[field.id] = deserialized; + } else { + delete _cache[field.id]; + } + return deserialized; + }); + // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache) + // we set the value at the field's id to a promise that will resolve to the field. + // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method). + // The mapping in the .then call ensures that when other callers await these promises, they'll + // get the resolved field + _cache[field.id] = prom; + + // adds to a list of promises that will be awaited asynchronously + proms.push(prom); + } else if (cached instanceof Promise) { + proms.push(cached as any); + cached.then((f: any) => fieldMap[field.id] = f); } else if (field) { - proms.push(_cache[field.id] as any); + proms.push(cached as any); fieldMap[field.id] = DocServer.GetCachedRefField(field.id) || field; } } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c3b6953b5..ed37c48dc 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -369,7 +369,7 @@ export namespace Docs { [DocumentType.RTF, { layout: { view: FormattedTextBox, dataField: "text" }, options: { - _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true, + _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true, links: "@links(self)" } }], diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e4dc175bd..436237d6c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1184,7 +1184,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, title + _width: width || 200, _height: 35, x: x, y: y, _fitWidth: true, _autoHeight: true, title }); const template = Doc.UserDoc().defaultTextLayout; if (template instanceof Doc) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 183efc944..ff2857739 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -120,8 +120,8 @@ export class MainView extends React.Component { } this._sidebarContent.proto = undefined; if (!MainView.Live) { - DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", - "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap", + DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "showSidebar", "sidebarWidthPercent", "viewTransition", + "panX", "panY", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap", "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's } DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index c44443264..553f84a67 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -70,6 +70,7 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab // export function DefaultStyleProvider(doc: Opt, props: Opt, property: string): any { + const remoteDocHeader = "author;creationDate;noMargin"; const docProps = testDocProps(props) ? props : undefined; const selected = property.includes(":selected"); const isCaption = property.includes(":caption"); @@ -111,7 +112,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color)); @@ -126,7 +127,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt (props?.PanelHeight() || 0) ? 5 : 10) : 0; case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || - doc?.type === DocumentType.RTF || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0; + (doc?.type === DocumentType.RTF && !showTitle()?.includes("noMargin")) || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0; case StyleProp.BackgroundColor: { if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY; let docColor: Opt = diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 4630b3bf2..dddae4a34 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -28,6 +28,8 @@ import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionViewType } from "./CollectionView"; +import { FieldViewProps } from "../nodes/FieldView"; +import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; const _global = (window /* browser */ || global /* node */) as any; @@ -207,6 +209,25 @@ export class CollectionStackingView extends CollectionSubView { + const docView = fieldProps.DocumentView?.(); + if (docView && ["Enter"].includes(e.key) && e.ctrlKey) { + e.stopPropagation?.(); + const below = !e.altKey && e.key !== "Tab"; + const layoutKey = StrCast(docView.LayoutFieldKey); + const newDoc = Doc.MakeCopy(docView.rootDoc, true); + const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)]; + newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List([]) : undefined; + if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) { + newDoc[layoutKey] = docView.rootDoc[layoutKey]; + } + Doc.GetProto(newDoc).text = undefined; + FormattedTextBox.SelectOnLoad = newDoc[Id]; + return this.addDocument?.(newDoc); + } + } isContentActive = () => this.props.isSelected() || this.props.isContentActive(); getDisplayDoc(doc: Doc, width: () => number) { const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc; @@ -225,6 +246,7 @@ export class CollectionStackingView extends CollectionSubView { const docView = fieldProps.DocumentView?.(); - if (docView && docView.rootDoc._singleLine && ["Tab", "Enter"].includes(e.key)) { + if (docView && (e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) { e.stopPropagation?.(); const below = !e.altKey && e.key !== "Tab"; const layoutKey = StrCast(docView.LayoutFieldKey); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 822bc996b..d468822c0 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -245,8 +245,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const state = this._editorView.state.apply(tx); this._editorView.updateState(state); - const tsel = this._editorView.state.selection.$from; - //tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000))); const curText = state.doc.textBetween(0, state.doc.content.size, " \n"); const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined; // the actual text in the text box const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype @@ -348,7 +346,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } autoLink = () => { - if (this._editorView) { + if (this._editorView?.state.doc.textContent) { const newAutoLinks = new Set(); const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords); const f = this._editorView.state.selection.from; @@ -962,7 +960,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }, { fireImmediately: true } ); quickScroll = undefined; - setTimeout(this.tryUpdateScrollHeight, 10); + this.tryUpdateScrollHeight(); } pushToGoogleDoc = async () => { @@ -1193,19 +1191,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())); if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) { + const selLoadChar = FormattedTextBox.SelectOnLoadChar; FormattedTextBox.SelectOnLoad = ""; this.props.select(false); - if (FormattedTextBox.SelectOnLoadChar && this._editorView) { + if (selLoadChar && this._editorView) { const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined; const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }); const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? []; const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark]; const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks); this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size)))); - FormattedTextBox.SelectOnLoadChar = ""; - } else if (curText && !FormattedTextBox.DontSelectInitialText) { - selectAll(this._editorView!.state, this._editorView?.dispatch); + } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) { + selectAll(this._editorView.state, this._editorView?.dispatch) this.startUndoTypingBatch(); + } else if (this._editorView) { + this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); } FormattedTextBox.DontSelectInitialText = false; } diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 0dd0a8411..e979ae59e 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -80,8 +80,10 @@ export function buildKeymap>(schema: S, props: any, mapKey //Commands for lists bind("Ctrl-i", (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any)); + bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true); + bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (props.onKey?.({ key: "Tab" }, props)) return true; + if (props.onKey?.(event, props)) return true; if (!canEdit(state)) return true; const ref = state.selection; const range = ref.$from.blockRange(ref.$to); @@ -106,7 +108,7 @@ export function buildKeymap>(schema: S, props: any, mapKey }); bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (props.onKey?.({ key: "Tab", shiftKey: true })) return true; // single line docs don't process tabs so that their containers can decide what to do. This should be a prop + if (props.onKey?.(event, props)) return true; if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); @@ -154,11 +156,9 @@ export function buildKeymap>(schema: S, props: any, mapKey return tx; }; - //Command to create a text document to the right of the selected textbox - bind("Alt-Enter", () => true); - //Command to create a text document to the bottom of the selected textbox - bind("Ctrl-Enter", () => true); + bind("Alt-Enter", () => props.onKey?.(event, props) ? true : true); + bind("Ctrl-Enter", () => props.onKey?.(event, props) ? true : true); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind("Backspace", (state: EditorState, dispatch: (tx: Transaction>) => void) => { @@ -244,8 +244,6 @@ export function buildKeymap>(schema: S, props: any, mapKey return false; }); - // mac && bind("Ctrl-Enter", cmd); - // bind("Mod-Enter", cmd); bind("Shift-Enter", cmd); return keys; diff --git a/src/fields/util.ts b/src/fields/util.ts index c708affe3..ef5ac79b8 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -175,8 +175,11 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) { * Calculates the effective access right to a document for the current user. */ export function GetEffectiveAcl(target: any, user?: string): symbol { - return !target ? AclPrivate : - target[UpdatingFromServer] ? AclAdmin : getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) + if (!target) return AclPrivate; + if (target[UpdatingFromServer]) return AclAdmin; + // authored documents are private until an ACL is set. this also fixes notes that flicker on and off when a remote types to create a private note into a shared collection. + if (!target[AclSym] && target.author && target.author !== Doc.CurrentUserEmail) return AclPrivate; + return getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) } function getPropAcl(target: any, prop: string | symbol | number) { -- cgit v1.2.3-70-g09d2 From 76d737a05093e4132f2ab56630c4003879747ef9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 26 May 2022 15:36:32 -0400 Subject: removed setting 'data' field of docking views on aliases. assigned acl's to delegates to match prototype. changed how documents are created to make sure acl's are set first so that, when sharing, remote clients will get the ACLs first and not render documents transiently when they don't have permissions. turned off inheriting ACL's from outer collection --- src/client/documents/Documents.ts | 15 +++++++++------ src/client/util/CurrentUserUtils.ts | 3 --- src/client/views/collections/CollectionDockingView.tsx | 17 ++--------------- src/client/views/global/globalCssVariables.scss | 2 +- src/fields/Doc.ts | 1 + src/fields/util.ts | 3 ++- 6 files changed, 15 insertions(+), 26 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ed37c48dc..8d29bcebe 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -639,13 +639,14 @@ export namespace Docs { const viewKeys = ["x", "y", "system"]; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_"); + dataProps["acl-Override"] = "None"; + dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + dataProps.system = viewProps.system; dataProps.isPrototype = true; dataProps.author = Doc.CurrentUserEmail; dataProps.creationDate = new DateField; dataProps[`${fieldKey}-lastModified`] = new DateField; - dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps[fieldKey] = data; @@ -654,10 +655,12 @@ export namespace Docs { dataProps[fieldKey + "-annotations"] = new List(); const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); - viewProps.author = Doc.CurrentUserEmail; - viewProps["acl-Override"] = "None"; - viewProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; - const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewProps, true, true); + const viewFirstProps: { [id: string]: any } = {}; + viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + viewFirstProps["acl-Override"] = "None"; + viewFirstProps.author = Doc.CurrentUserEmail; + const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); + Doc.assign(viewDoc, viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) && diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 436237d6c..083530ce7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1236,9 +1236,6 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { "Remove Dashboard from Dashboards"); ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); - dashboardAlias.data = new List(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); - DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias); - DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); }, diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1d65256a6..c60434d95 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -109,27 +109,14 @@ export class CollectionDockingView extends CollectionSubView() { if (instance) { const docList = DocListCast(instance.props.Document[DataSym].data); // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases - !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data", document); - // adds an alias of the doc to the data-all field of the layoutdocs of the aliases - DocListCast(instance.props.Document.aliases).forEach(alias => { - const aliasDocList = DocListCast(alias.data); - // if aliasDocList contains the alias, don't do anything - // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias - !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document); - }); + !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document, "data", document); } } removeFromTabDocList = (document: Doc) => { const instance = CollectionDockingView.Instance; if (instance) { // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases - Doc.RemoveDocFromList(instance.props.Document[DataSym], "data", document); - // adds an alias of the doc to the data-all field of the layoutdocs of the aliases - DocListCast(instance.props.Document.aliases).forEach(alias => { - // if aliasDocList contains the alias, don't do anything - // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias - Doc.RemoveDocFromList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document); - }); + Doc.RemoveDocFromList(instance.props.Document, "data", document); document.type !== DocumentType.KVP && Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", document); } } diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index 520ac9357..a14634bdc 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -10,7 +10,7 @@ $black: #000000; $light-blue: #bdddf5; $light-blue-transparent: #bdddf590; $medium-blue: #4476f7; -$medium-blue-alt: #4476f73d; +$medium-blue-alt: #0047ff54; $pink: #e0217d; $yellow: #f5d747; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 194b3ba27..b0a45091e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -821,6 +821,7 @@ export namespace Doc { delegate[Initializing] = true; delegate.proto = doc; delegate.author = Doc.CurrentUserEmail; + Object.keys(doc).filter(key => key.startsWith("acl")).forEach(key => delegate[key] = doc[key]); if (!Doc.IsSystem(doc)) Doc.AddDocToList(doc[DataSym], "aliases", delegate); title && (delegate.title = title); delegate[Initializing] = false; diff --git a/src/fields/util.ts b/src/fields/util.ts index ef5ac79b8..d1e565774 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -138,6 +138,7 @@ export function denormalizeEmail(email: string) { * Copies parent's acl fields to the child */ export function inheritParentAcls(parent: Doc, child: Doc) { + return; const dataDoc = parent[DataSym]; for (const key of Object.keys(dataDoc)) { // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. @@ -177,7 +178,7 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) { export function GetEffectiveAcl(target: any, user?: string): symbol { if (!target) return AclPrivate; if (target[UpdatingFromServer]) return AclAdmin; - // authored documents are private until an ACL is set. this also fixes notes that flicker on and off when a remote types to create a private note into a shared collection. + // authored documents are private until an ACL is set. if (!target[AclSym] && target.author && target.author !== Doc.CurrentUserEmail) return AclPrivate; return getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin. return this directly so that the acls aren't cached (UpdatingFromServer is not observable) } -- cgit v1.2.3-70-g09d2 From 53cae5e2ab9267295a824ff721c119ada5e5dc20 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 31 May 2022 13:03:19 -0400 Subject: added a pop up menu for viewing dashField view collection. added a menu button for turning off autoLinking to text selection. added ability to add button to top contextMenu buttons without blowing away db. --- src/client/util/CurrentUserUtils.ts | 34 +++++++++ src/client/views/MainView.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/button/FontIconBox.tsx | 8 +++ .../views/nodes/formattedText/DashFieldView.tsx | 81 +++++++++++++++++----- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 ++-- .../formattedText/ProsemirrorExampleTransfer.ts | 2 + .../views/nodes/formattedText/RichTextMenu.tsx | 14 +++- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/nodes/formattedText/marks_rts.ts | 14 ++++ 10 files changed, 144 insertions(+), 27 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 083530ce7..c4e2afd08 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -745,6 +745,7 @@ export class CurrentUserUtils { { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' }, { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' }, { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' }, + { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' }, ]; return tools; } @@ -834,11 +835,44 @@ export class CurrentUserUtils { !params.subMenu ? btnFunc(params) : CurrentUserUtils.linearButtonList({ + title: params.title, linearViewSubMenu: true, flexGap: 0, ignoreClick: true, linearViewExpandable: true, icon: params.title, _height: 30, linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, }, params.subMenu.map(btnFunc)))); + } else { + const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data); + let prev = ""; + CurrentUserUtils.contextMenuTools(doc).forEach(params => { + const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title); + if (!menuBtnDoc) { + const newMenuBtnDoc = !params.subMenu ? + btnFunc(params) : + CurrentUserUtils.linearButtonList({ + title: params.title, + linearViewSubMenu: true, flexGap: 0, ignoreClick: true, + linearViewExpandable: true, icon: params.title, _height: 30, + linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, + hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, + }, params.subMenu.map(btnFunc)); + const after = menuBtnList.find(doc => doc.title === prev); + Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after); + } + const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined; + if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) { + let prevSub = ""; + params.subMenu.forEach(sub => { + if (!subMenuBtnList.find(doc => doc.title === sub.title)) { + const newSubMenuBtnDoc = btnFunc(sub); + const after = subMenuBtnList.find(doc => doc.title === prevSub); + Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub); + } + prevSub = params.title; + }) + } + prev = params.title; + }); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ff2857739..55190001b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -50,6 +50,7 @@ import { AudioBox } from './nodes/AudioBox'; import { ButtonType } from './nodes/button/FontIconBox'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; import { DocumentView } from './nodes/DocumentView'; +import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; @@ -686,6 +687,7 @@ export class MainView extends React.Component { + diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f4cb08f8f..259cc1ee5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1166,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView { const docView = fieldProps.DocumentView?.(); - if (docView && (e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) { + if (docView && (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) { e.stopPropagation?.(); const below = !e.altKey && e.key !== "Tab"; const layoutKey = StrCast(docView.LayoutFieldKey); diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index e6d8fe88d..625f5d14a 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -666,6 +666,14 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b if (editorView) RichTextMenu.Instance.setFontSize(size); else Doc.UserDoc()._fontSize = size; }); +ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent"; + } + if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor(); + else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false; +}); ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 1a8352b72..6a3f9ed00 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -8,35 +8,41 @@ import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; import { ComputedField } from "../../../../fields/ScriptField"; import { Cast, StrCast } from "../../../../fields/Types"; import { DocServer } from "../../../DocServer"; -import { DocUtils } from "../../../documents/Documents"; import { CollectionViewType } from "../../collections/CollectionView"; import "./DashFieldView.scss"; import { FormattedTextBox } from "./FormattedTextBox"; import React = require("react"); +import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils"; +import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu"; +import { Tooltip } from "@material-ui/core"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; export class DashFieldView { _fieldWrapper: HTMLDivElement; // container for label and value constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { + const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey); + this._fieldWrapper = document.createElement("div"); this._fieldWrapper.style.width = node.attrs.width; this._fieldWrapper.style.height = node.attrs.height; this._fieldWrapper.style.fontWeight = "bold"; this._fieldWrapper.style.position = "relative"; this._fieldWrapper.style.display = "inline-block"; + this._fieldWrapper.textContent = node.attrs.fieldKey.startsWith("#") ? node.attrs.fieldKey : node.attrs.fieldKey + " " + strVal; this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); }; this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); }; this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); }; this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); }; - ReactDOM.render( ReactDOM.render(, this._fieldWrapper); + />, this._fieldWrapper)); (this as any).dom = this._fieldWrapper; } destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); } @@ -76,16 +82,17 @@ export class DashFieldViewInternal extends React.Component { r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r)); r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false)); - r?.addEventListener("pointerdown", action((e) => { - e.stopPropagation(); - })); + r?.addEventListener("pointerdown", action(e => e.stopPropagation())); }} > {strVal} ; @@ -161,7 +166,7 @@ export class DashFieldViewInternal extends React.Component 1 ? new List(splits) : newText; if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal; @@ -173,11 +178,7 @@ export class DashFieldViewInternal extends React.Component { - e.stopPropagation(); + createPivotForField = (e: React.MouseEvent) => { let container = this.props.tbox.props.ContainingCollectionView; while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) { container = container.props.ContainingCollectionView; @@ -196,6 +197,16 @@ export class DashFieldViewInternal extends React.Component { + setupMoveUpEvents(this, e, returnFalse, returnFalse, (e) => { + DashFieldViewMenu.createFieldView = this.createPivotForField; + DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16); + }); + } + render() { return
    ; } +} +@observer +export class DashFieldViewMenu extends AntimodeMenu { + static Instance: DashFieldViewMenu; + static createFieldView: (e: React.MouseEvent) => void = emptyFunction; + constructor(props: any) { + super(props); + DashFieldViewMenu.Instance = this; + } + @action + showFields = (e: React.MouseEvent) => { + DashFieldViewMenu.createFieldView(e); + DashFieldViewMenu.Instance.fadeOut(true); + } + + public show = (x: number, y: number) => { + this.jumpTo(x, y, true); + const hideMenu = () => { + this.fadeOut(true); + document.removeEventListener("pointerdown", hideMenu); + } + document.addEventListener("pointerdown", hideMenu) + } + render() { + const buttons = [ + {"Remove Link Anchor"}
    }> + + , + ]; + + return this.getElement(buttons); + } } \ No newline at end of file diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d468822c0..ce82821b6 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -391,16 +391,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp flattened1.forEach((flat, i) => { const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm); this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; - alink = alink ?? (DocListCast(this.Document.links).find(link => - Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && - Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, - LinkManager.AutoKeywords)!); - newAutoLinks.add(alink); const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() }); const sel = flattened[i]; tr = tr.addMark(sel.from, sel.to, splitter); tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { - if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { + if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { + alink = alink ?? (DocListCast(this.Document.links).find(link => + Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && + Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, + LinkManager.AutoKeywords)!); + newAutoLinks.add(alink); const allAnchors = [{ href: Doc.localServerPath(target), title: "a link", anchorId: this.props.Document[Id] }]; allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: "auto term", location: "add:right" }); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index e979ae59e..fb49b0698 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -82,6 +82,8 @@ export function buildKeymap>(schema: S, props: any, mapKey bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true); bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true); + bind("Meta-Tab", () => props.onKey?.(event, props) ? true : true); + bind("Meta-Enter", () => props.onKey?.(event, props) ? true : true); bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { if (props.onKey?.(event, props)) return true; if (!canEdit(state)) return true; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index c5f76cc8d..3df1e45a5 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -35,6 +35,7 @@ export class RichTextMenu extends AntimodeMenu { public _brushMap: Map> = new Map(); @observable private collapsed: boolean = false; + @observable private _noLinkActive: boolean = false; @observable private _boldActive: boolean = false; @observable private _italicsActive: boolean = false; @observable private _underlineActive: boolean = false; @@ -79,6 +80,7 @@ export class RichTextMenu extends AntimodeMenu { this._reaction?.(); } + @computed get noAutoLink() { return this._noLinkActive; } @computed get bold() { return this._boldActive; } @computed get underline() { return this._underlineActive; } @computed get italics() { return this._italicsActive; } @@ -220,7 +222,7 @@ export class RichTextMenu extends AntimodeMenu { let activeMarks: MarkType[] = []; if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks; - const markGroup = [schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript]; + const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript]; if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); //current selection const { empty, ranges, $to } = this.view.state.selection as TextSelection; @@ -264,6 +266,7 @@ export class RichTextMenu extends AntimodeMenu { setActiveMarkButtons(activeMarks: MarkType[] | undefined) { if (!activeMarks) return; + this._noLinkActive = false; this._boldActive = false; this._italicsActive = false; this._underlineActive = false; @@ -273,6 +276,7 @@ export class RichTextMenu extends AntimodeMenu { activeMarks.forEach(mark => { switch (mark.name) { + case "noAutoLinkAnchor": this._noLinkActive = true; break; case "strong": this._boldActive = true; break; case "em": this._italicsActive = true; break; case "underline": this._underlineActive = true; break; @@ -283,6 +287,14 @@ export class RichTextMenu extends AntimodeMenu { }); } + toggleNoAutoLinkAnchor = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.noAutoLinkAnchor); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.TextView.autoLink(); + this.view.focus(); + } + } toggleBold = () => { if (this.view) { const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong); diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 427e05edb..8851d52e4 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -348,7 +348,7 @@ export class RichTextRules { this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`; } const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag }); - return state.tr.deleteRange(start, end).insert(start, fieldView); + return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(" "); }), diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 1f6ce014f..2fde5c7ba 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -48,6 +48,20 @@ export const marks: { [index: string]: MarkSpec } = { return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0]; } }, + noAutoLinkAnchor: { + attrs: {}, + inclusive: false, + parseDOM: [{ + tag: "div", getAttrs(dom: any) { + return { + noAutoLink: dom.getAttribute("data-noAutoLink"), + }; + } + }], + toDOM(node: any) { + return ["span", { "data-noAutoLink": "true" }, 0]; + } + }, // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each linkAnchor specifies an href to the URL of the source selection Marker text, // and a title for use in menus and hover. `title` // defaults to the empty string. Rendered and parsed as an `` -- cgit v1.2.3-70-g09d2 From f792c3573aec834bb5540d1d2ceb4486a92e03ef Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 1 Jun 2022 10:24:24 -0400 Subject: Merge with jenny trackScreen --- src/client/util/RecordingApi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index 7592199eb..7d8df0bfc 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -149,7 +149,7 @@ export class RecordingApi { // set the view to the current view if (view === this.recordingFFView || view === null) return; - //this.recordingFFView = view; + // this.recordingFFView = view; // set the reaction to track the movements this.disposeFunc = reaction( () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1) }), -- cgit v1.2.3-70-g09d2 From 22d39de00ed9a246c520c4c9b1d049a151465d73 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 1 Jun 2022 14:01:06 -0400 Subject: now tracks and replays scaling accurately --- src/client/util/RecordingApi.ts | 83 +++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 ++- src/client/views/nodes/VideoBox.tsx | 5 +- 3 files changed, 49 insertions(+), 49 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index 7d8df0bfc..87f44bec4 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -3,11 +3,14 @@ import { IReactionDisposer, observable, reaction } from "mobx"; import { NumCast } from "../../fields/Types"; import { Doc } from "../../fields/Doc"; import { VideoBox } from "../views/nodes/VideoBox"; +import { scaleDiverging } from "d3-scale"; +import { Transform } from "./Transform"; type Movement = { time: number, panX: number, panY: number, + scale: number, } type Presentation = { @@ -120,7 +123,7 @@ export class RecordingApi { this.absoluteStart = new Date().getTime() - this.absoluteStart } - private trackMovements = (panX: number, panY: number): Error | undefined => { + private trackMovements = (panX: number, panY: number, scale: number = 0): Error | undefined => { // ensure we are recording if (!this.isRecording) { return new Error('[recordingApi.ts] trackMovements()') @@ -133,8 +136,7 @@ export class RecordingApi { // get the time const time = new Date().getTime() - this.absoluteStart // make new movement object - console.log(time) - const movement: Movement = { time, panX, panY } + const movement: Movement = { time, panX, panY, scale } // add that movement to the current presentation data's movement array this.currentPresentation.movements && this.currentPresentation.movements.push(movement) @@ -147,13 +149,16 @@ export class RecordingApi { // set the FFView that will be used in a reaction to track the movements public setRecordingFFView = (view: CollectionFreeFormView): void => { // set the view to the current view - if (view === this.recordingFFView || view === null) return; + if (view === this.recordingFFView || view == null) return; // this.recordingFFView = view; // set the reaction to track the movements this.disposeFunc = reaction( - () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1) }), - (res) => (res.x !== -1 && res.y !== -1) && this.trackMovements(res.x, res.y) + () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1), scale: NumCast(view.Document.viewScale, -1) }), + (res) => { + console.log('scale', res.scale); + (res.x !== -1 && res.y !== -1 && this.isRecording) && this.trackMovements(res.x, res.y, res.scale); + } ) // for now, set the most recent recordingFFView to the playFFView @@ -175,6 +180,8 @@ export class RecordingApi { this.playFFView = view } + // pausing movements will dispose all timers that are planned to replay the movements + // play movemvents will recreate them when the user resumes the presentation public pauseMovements = (): undefined | Error => { if (this.playFFView === null) { return new Error('[recordingApi.ts] pauseMovements() failed: no view') @@ -196,7 +203,9 @@ export class RecordingApi { // by calling pause on the VideoBox, the pauseMovements will be called public pauseVideoAndMovements = (): boolean => { this.videoBox?.Pause() - return this.videoBox === null + + this.pauseMovements() + return this.videoBox == null } public _isPlaying = false; @@ -215,48 +224,36 @@ export class RecordingApi { Doc.UserDoc().presentationMode = 'watching'; // TODO: consider this bug at the end of the clip on seek - // console.log(timeViewed) this.videoBox = videoBox || null; - const document = this.playFFView.Document - const { movements } = presentation + // only get the movements that are remaining in the video time left + const filteredMovements = presentation.movements.filter(movement => movement.time > timeViewed * 1000) - const filteredMovements = movements.filter(movement => movement.time > timeViewed * 1000) - - document._panX = filteredMovements[0].panX; - document._panY = filteredMovements[0].panY; - + // helper to replay a movement + const document = this.playFFView + let preScale = -1; + const zoomAndPan = (movement: Movement) => { + const { panX, panY, scale } = movement; + (scale !== -1 && preScale !== scale) && document.zoomSmoothlyAboutPt([panX, panY], scale, 0); + document.Document._panX = panX; + document.Document._panY = panY; + + preScale = scale; + } + + // set the first frame to be at the start of the pres + zoomAndPan(filteredMovements[0]); + + // make timers that will execute each movement at the correct replay time this.timers = filteredMovements.map(movement => { - const { panX, panY, time } = movement - const timeDiff = time - timeViewed*1000 + const timeDiff = movement.time - timeViewed*1000 return setTimeout(() => { - document._panX = panX; - document._panY = panY; - if (movement === movements[movements.length - 1]) { - // stuff for recording API - RecordingApi.Instance._isPlaying = false; - } + // replay the movement + zoomAndPan(movement) + // if last movement, presentation is done -> set the instance var + if (movement === filteredMovements[filteredMovements.length - 1]) RecordingApi.Instance._isPlaying = false; }, timeDiff) - }, []) - - // this.timers = movements.reduce((arr: NodeJS.Timeout[], movement) => { - // const { panX, panY, time } = movement - - // const timeDiff = time - timeViewed*1000 - // if (timeDiff < 0) return arr; - - // // set the pan to what was stored - // arr.push(setTimeout(() => { - // document._panX = panX; - // document._panY = panY; - // if (movement === movements[movements.length - 1]) { - // // stuff for recording API - // RecordingApi.Instance._isPlaying = false; - // } - // }, timeDiff)) - // return arr; - // }, []) - + }) } // Unfinished code for tracing multiple free form views diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index aa2e0c417..19ae99743 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -942,6 +942,12 @@ export class CollectionFreeFormView extends CollectionSubView= 0.05 || localTransform.Scale > this.zoomScaling()) { const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20); @@ -966,9 +972,9 @@ export class CollectionFreeFormView extends CollectionSubView this._fullScreen = vref.webkitDisplayingFullscreen); this._disposers.reactionDisposer?.(); this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode), - time => { - !this._playing && (vref.currentTime = time); - console.log("vref time = " + vref.currentTime) - }, { fireImmediately: true }); + time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); } } -- cgit v1.2.3-70-g09d2 From a46052f0ec637a4bb777d1964413c20ec53dc08b Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 1 Jun 2022 14:04:25 -0400 Subject: small refactoring --- src/client/util/RecordingApi.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index 87f44bec4..93906faac 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -119,7 +119,7 @@ export class RecordingApi { public resume = () => { this.isRecording = true - // set absoluteStart to the pausedTimestamp + // set absoluteStart to the difference in time this.absoluteStart = new Date().getTime() - this.absoluteStart } @@ -214,13 +214,9 @@ export class RecordingApi { if (presentation.movements === null || this.playFFView === null) { return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view') } - - if (this._isPlaying) { - //return new Error('[recordingApi.ts] playMovements() failed: already playing') - return - } + if(this._isPlaying) return; + this._isPlaying = true; - console.log('setWatching lol') Doc.UserDoc().presentationMode = 'watching'; // TODO: consider this bug at the end of the clip on seek -- cgit v1.2.3-70-g09d2 From 560d7090702c3559724420f3571b11d86c930177 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 1 Jun 2022 15:13:06 -0400 Subject: small playview fix --- src/client/util/RecordingApi.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts index 93906faac..76d03156a 100644 --- a/src/client/util/RecordingApi.ts +++ b/src/client/util/RecordingApi.ts @@ -89,6 +89,8 @@ export class RecordingApi { // update the presentation mode Doc.UserDoc().presentationMode = 'none' + // set the previus recording view to the play view + this.playFFView = this.recordingFFView const presCopy = { ...this.currentPresentation } -- cgit v1.2.3-70-g09d2 From 19705d01102fbb594ea5df18bb980bd2dfec93d4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 3 Jun 2022 08:44:03 -0400 Subject: fixed presentation as list index display --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/nodes/trails/PresBox.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c4e2afd08..2ed6c3cbe 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -930,7 +930,7 @@ export class CurrentUserUtils { sharedDocs.childContextMenuIcons = new List(["user-plus",]); } - doc.mySharedDocs = new PrefetchProxy(sharedDocs); + doc.mySharedDocs = new PrefetchProxy(sharedDocs as Doc); } } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index d85f0b640..f4e792acd 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -102,7 +102,7 @@ export class PresBox extends ViewBoxBaseComponent() { // TODO: [AL] add childDocs() and treeMap fields // @computed get childDocs() { return DocListCast(this.rootDoc.presentationaLinearizedDocuments); } //_treeViewMap:Map - @computed get childDocs() { return DocListCast(this.rootDoc.presentationLinearizedDocuments); } + @computed get childDocs() { return this.layoutDoc._viewType === CollectionViewType.Tree ? DocListCast(this.rootDoc.presentationLinearizedDocuments) : DocListCast(this.rootDoc[this.fieldKey]); } @observable _treeViewMap: Map = new Map(); @computed get tagDocs() { @@ -2533,7 +2533,7 @@ export class PresBox extends ViewBoxBaseComponent() { } // this func communicates with PresBoxElement to send information of the doc ScriptingGlobals.add(function lookupPresBoxField(presBoxDoc: Doc, field: string, presEleDoc: Doc) { - if (field === 'indexInPres') return DocListCast(presBoxDoc.presentationLinearizedDocuments).indexOf(presEleDoc); + if (field === 'indexInPres') return DocListCast(presBoxDoc._viewType === CollectionViewType.Tree ? presBoxDoc.presentationLinearizedDocuments : presBoxDoc[StrCast(presBoxDoc.presentationFieldKey)]).indexOf(presEleDoc); if (field === 'presCollapsedHeight') return [CollectionViewType.Tree || CollectionViewType.Stacking].includes(presBoxDoc._viewType as any) ? 35 : 31; if (field === 'presStatus') return presBoxDoc.presStatus; if (field === '_itemIndex') return presBoxDoc._itemIndex; -- cgit v1.2.3-70-g09d2 From ba7824a91da113cc813c58b678719092c96d79a2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 3 Jun 2022 16:02:24 -0400 Subject: fixed opening closed tabs from Files sidebar to re-use best Alias before making a new alias. --- src/.DS_Store | Bin 8196 -> 8196 bytes src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/DocumentManager.ts | 28 ++++++++++----------- src/client/views/LightboxView.tsx | 5 ++-- src/client/views/collections/TabDocView.tsx | 15 ++++++++++- src/client/views/collections/TreeView.tsx | 4 +-- src/client/views/nodes/DocumentIcon.tsx | 4 +-- src/client/views/nodes/DocumentView.tsx | 4 --- src/client/views/nodes/button/FontIconBox.tsx | 11 ++++---- .../views/nodes/formattedText/DashFieldView.tsx | 6 ++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 14 ++--------- src/client/views/pdf/PDFViewer.tsx | 2 +- 13 files changed, 48 insertions(+), 49 deletions(-) (limited to 'src/client/util') diff --git a/src/.DS_Store b/src/.DS_Store index b0987293b..4bf9cdac7 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2ed6c3cbe..85ad58726 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -869,7 +869,7 @@ export class CurrentUserUtils { Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub); } prevSub = params.title; - }) + }); } prev = params.title; }); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index aeddc3249..a82f99d5a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -16,7 +16,7 @@ import { SelectionManager } from './SelectionManager'; export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) - @observable public DocumentViews: DocumentView[] = []; + @observable public DocumentViews = new Set(); @observable public LinkAnchorBoxViews: DocumentView[] = []; @observable public RecordingEvent = 0; @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = []; @@ -46,7 +46,7 @@ export class DocumentManager { // this.LinkedDocumentViews.forEach(view => console.log(" LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " + // view.b.props.Document.title + "/" + view.b.props.LayoutTemplateString)); } else { - this.DocumentViews.push(view); + this.DocumentViews.add(view); } } public RemoveView = action((view: DocumentView) => { @@ -61,8 +61,7 @@ export class DocumentManager { const index = this.LinkAnchorBoxViews.indexOf(view); this.LinkAnchorBoxViews.splice(index, 1); } else { - const index = this.DocumentViews.indexOf(view); - index !== -1 && this.DocumentViews.splice(index, 1); + this.DocumentViews.delete(view); } SelectionManager.DeselectView(view); }); @@ -70,13 +69,13 @@ export class DocumentManager { //gets all views public getDocumentViewsById(id: string) { const toReturn: DocumentView[] = []; - DocumentManager.Instance.DocumentViews.map(view => { + Array.from(DocumentManager.Instance.DocumentViews).map(view => { if (view.rootDoc[Id] === id) { toReturn.push(view); } }); if (toReturn.length === 0) { - DocumentManager.Instance.DocumentViews.map(view => { + Array.from(DocumentManager.Instance.DocumentViews).map(view => { const doc = view.rootDoc.proto; if (doc && doc[Id] && doc[Id] === id) { toReturn.push(view); @@ -96,14 +95,14 @@ export class DocumentManager { const passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; for (const pass of passes) { - DocumentManager.Instance.DocumentViews.map(view => { + Array.from(DocumentManager.Instance.DocumentViews).map(view => { if (view.rootDoc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; return; } }); if (!toReturn) { - DocumentManager.Instance.DocumentViews.map(view => { + Array.from(DocumentManager.Instance.DocumentViews).map(view => { const doc = view.rootDoc.proto; if (doc && doc[Id] === id && (!pass || view.props.ContainingCollectionView === preferredCollection)) { toReturn = view; @@ -122,9 +121,8 @@ export class DocumentManager { } public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { - const docViews = DocumentManager.Instance.DocumentViews; const views: DocumentView[] = []; - docViews.map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view)); + Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view)); return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined); } public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { @@ -133,8 +131,8 @@ export class DocumentManager { } public getDocumentViews(toFind: Doc): DocumentView[] { const toReturn: DocumentView[] = []; - const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.IsLightboxDocView(view.docViewPath)); - const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.IsLightboxDocView(view.docViewPath)); + const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath)); + const lightViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => LightboxView.IsLightboxDocView(view.docViewPath)); // heuristic to return the "best" documents first: // choose a document in the lightbox first @@ -290,7 +288,7 @@ export class DocumentManager { } } } -export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) { +export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) { const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView); if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) { @@ -300,7 +298,9 @@ export function DocFocusOrOpen(doc: any, collectionDoc?: Doc) { else { const context = doc.context !== Doc.UserDoc().myFilesystem && Cast(doc.context, Doc, null); const showDoc = context || doc; - CollectionDockingView.AddSplit(showDoc === Doc.GetProto(showDoc) ? Doc.MakeAlias(showDoc) : showDoc, "right") && context && + const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc; + + CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), "right") && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc)); } } diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index ddfc8b249..3d2769f66 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -275,9 +275,8 @@ export class LightboxView extends React.Component {
    { e.stopPropagation(); - // CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, ""); - LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace"); - console.log("lightbox to tab triggered") + CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, ""); + //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace"); SelectionManager.DeselectAll(); LightboxView.SetLightboxDoc(undefined); }}> diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 8d84e9a56..d64cb2fd7 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -66,6 +66,8 @@ export class TabDocView extends React.Component { get stack() { return (this.props as any).glContainer.parent.parent; } get tab() { return (this.props as any).glContainer.tab; } get view() { return this._view; } + _lastTab: any; + _lastView: DocumentView | undefined; @action init = (tab: any, doc: Opt) => { @@ -266,6 +268,7 @@ export class TabDocView extends React.Component { componentWillUnmount() { this._tabReaction?.(); + this._view && DocumentManager.Instance.RemoveView(this._view); this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab); this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); @@ -353,7 +356,11 @@ export class TabDocView extends React.Component { @computed get docView() { return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) : - <> this._view = r)} + <> { + this._lastView && DocumentManager.Instance.RemoveView(this._lastView); + this._view = r; + this._lastView = this._view; + })} renderDepth={0} Document={this._document} DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} @@ -409,6 +416,12 @@ export class TabDocView extends React.Component { height: "100%", width: "100%" }} ref={ref => { if (this._mainCont = ref) { + if (this._lastTab) { + console.log("DUP tab") + this._view && DocumentManager.Instance.RemoveView(this._view); + CollectionDockingView.Instance.tabMap.delete(this._lastTab); + } + this._lastTab = this.tab; (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 661db7997..9de44fb00 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -183,7 +183,7 @@ export class TreeView extends React.Component { this.treeViewOpen = !this.treeViewOpen; } else { // choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias - const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail); + const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail); const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail); this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), "lightbox"); } @@ -609,7 +609,7 @@ export class TreeView extends React.Component { // TODO: currently doc focus works, but can't seem to edit title // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildClick = () => { - return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.()) + return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.()); } onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index a9c998757..56de2d1fc 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -52,7 +52,7 @@ export class DocumentIconContainer extends React.Component { }; }, getVars() { - const docs = DocumentManager.Instance.DocumentViews; + const docs = Array.from(DocumentManager.Instance.DocumentViews); const capturedVariables: { [name: string]: Field } = {}; usedDocuments.forEach(index => capturedVariables[`d${index}`] = docs[index].props.Document); return { capturedVariables }; @@ -60,6 +60,6 @@ export class DocumentIconContainer extends React.Component { }; } render() { - return DocumentManager.Instance.DocumentViews.map((dv, i) => ); + return Array.from(DocumentManager.Instance.DocumentViews).map((dv, i) => ); } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 14ededaf8..6e4d6a52e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -242,7 +242,6 @@ export class DocumentViewInternal extends DocComponent() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const script = ScriptCast(this.rootDoc.script); - if (!script) { return null }; + if (!script) { return null; } let noviceList: string[] = []; let text: string | undefined; @@ -459,7 +459,7 @@ export class FontIconBox extends DocComponent() { const buttonText = StrCast(this.rootDoc.buttonText); // TODO:glr Add label of button type - let button = this.defaultButton; + let button: JSX.Element | null = this.defaultButton; switch (this.type) { case ButtonType.TextButton: @@ -527,9 +527,10 @@ export class FontIconBox extends DocComponent() { } return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button : - {StrCast(this.layoutDoc.toolTip)}
    }> - {button} - ; + button !== null ? + {StrCast(this.layoutDoc.toolTip)}
    }> + {button} + : null } } diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 6a3f9ed00..bb3791f1e 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -86,7 +86,7 @@ export class DashFieldViewInternal extends React.Component { const hideMenu = () => { this.fadeOut(true); document.removeEventListener("pointerdown", hideMenu); - } - document.addEventListener("pointerdown", hideMenu) + }; + document.addEventListener("pointerdown", hideMenu); } render() { const buttons = [ diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ce82821b6..434fccf8d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1202,7 +1202,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks); this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size)))); } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) { - selectAll(this._editorView.state, this._editorView?.dispatch) + selectAll(this._editorView.state, this._editorView?.dispatch); this.startUndoTypingBatch(); } else if (this._editorView) { this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index f4e792acd..0af7715fe 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -2435,9 +2435,7 @@ export class PresBox extends ViewBoxBaseComponent() { indexNum += (index[i] * (10 ** (-i))); } this._treeViewMap.set(indexNum, treeViewDoc); - console.log(String(index), treeViewDoc) this.props.Document.presentationLinearizedDocuments = new List(this.sort(this._treeViewMap)); // this is a flat array of Docs - console.log(this.props.Document.presentationLinearizedDocuments) return this.childDocs; } @@ -2447,21 +2445,13 @@ export class PresBox extends ViewBoxBaseComponent() { indexNum += (index[i] * (10 ** (-i))); } console.log(String(index), treeViewDoc) - this._treeViewMap.delete(indexNum) + this._treeViewMap.delete(indexNum); this.props.Document.presentationLinearizedDocuments = new List(this.sort(this._treeViewMap)); return this.childDocs; } // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1]) - sort = (treeViewMap: Map): Doc[] => { - // TODO - const sortedMap = [...treeViewMap.entries()].sort(); - var sortedDocs = []; - for (var kv of sortedMap) { - sortedDocs.push(kv[1]); - } - return sortedDocs; - } + sort = (treeViewMap: Map) => [...treeViewMap.entries()].sort().map(kv => kv[1]); render() { // calling this method for keyEvents diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 6db36f36a..d5fd425ad 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -154,7 +154,7 @@ export class PDFViewer extends React.Component { if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight, NumCast(this.props.Document.scrollHeight)); - if (scrollTo !== undefined && scrollTo != this.props.layoutDoc._scrollTop) { + if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { focusSpeed = 500; if (!this._pdfViewer) this._initialScroll = scrollTo; -- cgit v1.2.3-70-g09d2 From b51b78c641c3e64f04cf878f02b5d7b1a620769e Mon Sep 17 00:00:00 2001 From: mehekj Date: Sun, 5 Jun 2022 11:38:46 -0400 Subject: cleanup --- src/Utils.ts | 6 ---- src/client/util/InteractionUtils.tsx | 3 +- src/client/views/GestureOverlay.tsx | 10 ++----- src/client/views/InkTranscription.tsx | 22 +++----------- src/client/views/InkingStroke.tsx | 1 - src/client/views/LightboxView.tsx | 4 +-- src/client/views/Touchable.tsx | 2 -- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +--- .../collections/collectionFreeForm/MarqueeView.tsx | 1 - src/client/views/nodes/DocumentLinksButton.tsx | 3 -- src/client/views/nodes/EquationBox.scss | 34 ---------------------- src/client/views/nodes/button/FontIconBox.tsx | 7 ++--- 12 files changed, 13 insertions(+), 85 deletions(-) (limited to 'src/client/util') diff --git a/src/Utils.ts b/src/Utils.ts index 8c0e8c7c0..523f4eb4f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -5,12 +5,6 @@ import { Socket } from 'socket.io'; import { Colors } from './client/views/global/globalEnums'; import { Message } from './server/Message'; import Color = require('color'); -import { InkTool } from './fields/InkField'; -import { action } from 'mobx'; -import { CurrentUserUtils } from './client/util/CurrentUserUtils'; -import { CollectionFreeFormView } from './client/views/collections/collectionFreeForm'; -import { WidthSym, HeightSym } from './fields/Doc'; -import { NumCast } from './fields/Types'; export namespace Utils { export let DRAG_THRESHOLD = 4; diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index e2c5fab64..289c5bc51 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -286,7 +286,7 @@ export namespace InteractionUtils { // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2 case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); - case TOUCHTYPE: + case TOUCHTYPE: return e.pointerType === TOUCHTYPE; default: return e.pointerType === type; } @@ -357,7 +357,6 @@ export namespace InteractionUtils { } export function IsDragging(oldTouches: Map, newTouches: React.Touch[], leniency: number): boolean { - console.log("getting here"); for (const touch of newTouches) { if (touch) { const oldTouch = oldTouches.get(touch.identifier); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 566b7b65a..21b5a8d01 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -18,13 +18,13 @@ import { SelectionManager } from "../util/SelectionManager"; import { Transform } from "../util/Transform"; import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu"; import "./GestureOverlay.scss"; -import { ActiveArrowEnd, ActiveArrowStart, ActiveArrowScale, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke"; +import { ActiveArrowEnd, ActiveArrowScale, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke"; +import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox"; import { DocumentView } from "./nodes/DocumentView"; import { RadialMenu } from "./nodes/RadialMenu"; import HorizontalPalette from "./Palette"; import { Touchable } from "./Touchable"; import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu"; -import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox"; @observer export class GestureOverlay extends Touchable { @@ -210,7 +210,7 @@ export class GestureOverlay extends Touchable { const nts: any = this.getNewTouches(e); this._holdTimer && clearTimeout(this._holdTimer); this._holdTimer = undefined; - + document.dispatchEvent( new CustomEvent>("dashOnTouchMove", { @@ -296,9 +296,6 @@ export class GestureOverlay extends Touchable { else if (thumb.clientX === leftMost) { pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v); } - else { - // console.log("not hand"); - } this.pointerIdentifier = pointer?.identifier; runInAction(() => { @@ -632,7 +629,6 @@ export class GestureOverlay extends Touchable { if (!actionPerformed) { const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]); newPoints.pop(); - // console.log("getting to bezier math"); const controlPoints: { X: number, Y: number }[] = []; const bezierCurves = fitCurve(newPoints, 10); diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 5e0667bed..6babb3915 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -4,12 +4,12 @@ import * as React from 'react'; import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc'; import { InkData, InkField } from "../../fields/InkField"; import { Cast, DateCast, NumCast } from '../../fields/Types'; -import { DocumentType } from "../documents/DocumentTypes"; -import './InkTranscription.scss'; import { aggregateBounds } from '../../Utils'; -import { CollectionFreeFormView } from './collections/collectionFreeForm'; +import { DocumentType } from "../documents/DocumentTypes"; import { DocumentManager } from "../util/DocumentManager"; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { InkingStroke } from './InkingStroke'; +import './InkTranscription.scss'; export class InkTranscription extends React.Component { @@ -21,7 +21,6 @@ export class InkTranscription extends React.Component { @observable _textRef: any; private lastJiix: any; private currGroup?: Doc; - private containingLayout?: Doc; constructor(props: Readonly<{}>) { super(props); @@ -104,29 +103,22 @@ export class InkTranscription extends React.Component { return this._textRef = r; } - transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => { + transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => { if (!groupDoc) return; const validInks = inkDocs.filter(s => s.type === DocumentType.INK); const strokes: InkData[] = []; const times: number[] = []; - // console.log(validInks); validInks.filter(i => Cast(i.data, InkField)).forEach(i => { const d = Cast(i.data, InkField, null); - // const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]); - // const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]); - // strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top }))); const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke; strokes.push(d.inkData.map(pd => (inkStroke.ptToScreen({ X: pd.X, Y: pd.Y })))); times.push(DateCast(i.creationDate).getDate().getTime()); }); this.currGroup = groupDoc; - this.containingLayout = containingLayout; const pointerData = { "events": strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) }; - // console.log(JSON.stringify(pointerData)); - // console.log(pointerData); const processGestures = false; if (math) { @@ -225,19 +217,15 @@ export class InkTranscription extends React.Component { newCollection.title = word; } // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs - // console.log(newCollection); newCollection && docView.props.addDocument?.(newCollection); }); } exportInk = (e: any, ref: any) => { const exports = e.detail.exports; - // console.log(e); if (exports) { if (exports['application/x-latex']) { const latex = exports['application/x-latex']; - // console.log(latex); - if (this.currGroup) { this.currGroup.text = latex; this.currGroup.title = latex; @@ -282,10 +270,8 @@ export class InkTranscription extends React.Component { } } const text = exports['text/plain']; - // console.log(text); if (this.currGroup) { - // console.log("curr grouping"); this.currGroup.transcription = text; this.currGroup.title = text.split("\n")[0]; } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 6c95f0f77..40fe6aa68 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -253,7 +253,6 @@ export class InkingStroke extends ViewBoxBaseComponent() { (point.X - inkLeft - inkStrokeWidth / 2) * inkScaleX + inkStrokeWidth / 2, (point.Y - inkTop - inkStrokeWidth / 2) * inkScaleY + inkStrokeWidth / 2)).map(p => ({ X: p[0], Y: p[1] })); const screenHdlPts = screenPts; - console.log(screenPts); const startMarker = StrCast(this.layoutDoc.strokeStartMarker); const endMarker = StrCast(this.layoutDoc.strokeEndMarker); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index c8f6b5f0b..40c68878d 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -5,7 +5,7 @@ import "normalize.css"; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, Utils } from '../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; @@ -16,8 +16,6 @@ import { TabDocView } from './collections/TabDocView'; import "./LightboxView.scss"; import { DocumentView } from './nodes/DocumentView'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; -import { CollectionMenu } from './collections/CollectionMenu'; -import { utils } from 'mocha'; interface LightboxViewProps { PanelWidth: number; diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx index c712e7ed3..789756a78 100644 --- a/src/client/views/Touchable.tsx +++ b/src/client/views/Touchable.tsx @@ -127,13 +127,11 @@ export abstract class Touchable extends React.Component { } handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { - console.log("getting to handle1PointersMove"); e.stopPropagation(); e.preventDefault(); } handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent): any => { - console.log("getting to handle2PointersMove"); e.stopPropagation(); e.preventDefault(); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ca8073dac..f292bd96e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -17,7 +17,7 @@ import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents"; +import { Docs, DocUtils } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { DocumentManager } from "../../../util/DocumentManager"; @@ -50,7 +50,6 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { InkTranscription } from "../../InkTranscription"; export const panZoomSchema = createSchema({ _panX: "number", @@ -700,7 +699,6 @@ export class CollectionFreeFormView extends CollectionSubView { - console.log("this is onPointerMove"); if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { Doc.UserDoc().activeInkTool = InkTool.None; @@ -823,7 +821,6 @@ export class CollectionFreeFormView extends CollectionSubView) => { - console.log("getting here yeet"); if (!e.cancelBubble) { const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); if (myTouches[0]) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index f762c6619..b60742eba 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -267,7 +267,6 @@ export class MarqueeView extends React.Component() { "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; } } catch (e) { - // console.log(e); + console.log(e); } // Get items to place into the list @@ -767,7 +766,7 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { // TODO: nda - will probably need to go through and only remove the unprocessed selected docs ffView.unprocessedDocs = []; - InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false, ffView); + InkTranscription.Instance.transcribeInk(newCollection, selected, false, ffView); }); } CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); -- cgit v1.2.3-70-g09d2 From b0408fa264fc8a017c87a17325600d8e458fe711 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 6 Jun 2022 01:32:36 -0400 Subject: fixed up overlayDocs a bit so that they can be removed. made audioBox's appear in overlay when clicked from toolbar. add links to audio when navigating to doc in lightbox. --- src/client/util/CurrentUserUtils.ts | 4 ++-- src/client/views/LightboxView.tsx | 2 ++ src/client/views/OverlayView.tsx | 10 +++++++--- src/client/views/collections/CollectionDockingView.tsx | 2 ++ src/client/views/nodes/AudioBox.tsx | 9 ++++++++- 5 files changed, 21 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cad86c0a6..51bfdbbd2 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -276,7 +276,7 @@ export class CurrentUserUtils { (doc.emptyWall as Doc).videoWall = true; } if (doc.emptyAudio === undefined) { - doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", _width: 200, _height: 100, }); + doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }); } if (doc.emptyNote === undefined) { doc.emptyNote = Docs.Create.TextDocument("", { ...standardOps(), title: "text note", _width: 200, _autoHeight: true }); @@ -301,7 +301,7 @@ export class CurrentUserUtils { { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true }, { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc }, { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, - { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, + { toolTip: "Tap to create an audio recorder in a overlay pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openInOverlay(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc }, diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 2543111b0..f67f37bfb 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -126,6 +126,8 @@ export class LightboxView extends React.Component { const target = LightboxView._docTarget = this._future?.pop(); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { + const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement(); + l && (Cast(l.anchor2, Doc, null).backgroundColor = "lightgreen"); targetDocView.focus(target, { originalTarget: target, willZoom: true, scale: 0.9 }); if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target }); } else { diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 18b87e3d5..95e146df4 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -146,6 +146,10 @@ export class OverlayView extends React.Component { return remove; } + removeOverlayDoc = (doc: Doc | Doc[]) => { + (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc)); + return true; + } @computed get overlayDocs() { return CurrentUserUtils.OverlayDocs?.map(d => { @@ -179,16 +183,16 @@ export class OverlayView extends React.Component { offsetx = NumCast(d.x) - e.clientX; offsety = NumCast(d.y) - e.clientY; }; - return
    + return
    new Transform(-NumCast(d.x), -NumCast(d.y), 1)} renderDepth={1} isDocumentActive={returnFalse} isContentActive={returnTrue} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 5c0d6b1c4..de2106e4a 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -475,4 +475,6 @@ ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab( "opens up document in a lightbox", "(doc: any)"); ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, "opens up document in tab on right side of the screen", "(doc: any)"); +ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); }, + "opens up document in screen overlay layer", "(doc: any)"); ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 9b92762a2..787ad939d 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -350,7 +350,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent Date: Wed, 8 Jun 2022 01:17:24 -0400 Subject: added ability to make thumbnails of dashboards. started to cleanup dockingView/goldenlayout undo event handling. cleaned up tab doc list in docking view. made titles editable again in treeview. fixed overlay view to work with image docs etc by setting top/left in css --- src/client/documents/Documents.ts | 6 +-- src/client/goldenLayout.js | 2 +- src/client/util/CurrentUserUtils.ts | 26 ++++++++--- src/client/views/DocumentButtonBar.tsx | 53 ++++++++++------------ src/client/views/MainView.tsx | 12 +---- src/client/views/OverlayView.scss | 2 + src/client/views/OverlayView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 21 ++++++--- src/client/views/collections/TabDocView.tsx | 28 ++++-------- src/client/views/collections/TreeView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 45 ++++++++++-------- src/client/views/nodes/WebBoxRenderer.js | 4 +- src/client/views/topbar/TopBar.tsx | 6 +-- 13 files changed, 108 insertions(+), 103 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 817478b2f..acc1e341a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -671,9 +671,9 @@ export namespace Docs { return viewDoc; } - export function ImageDocument(url: string, options: DocumentOptions = {}) { - const imgField = new ImageField(url); - return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options }); + export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) { + const imgField = url instanceof ImageField ? url : new ImageField(url); + return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }); } export function PresDocument(options: DocumentOptions = {}) { diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index 82b10608d..4735f216f 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -1431,7 +1431,7 @@ this.root.callDownwards('_$init'); if (config.maximisedItemId === '__glMaximised') { - this.root.getItemsById(config.maximisedItemId)[0].toggleMaximise(); + this.root.getItemsById(config.maximisedItemId)[0]?.toggleMaximise(); } }, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 51bfdbbd2..885679b64 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -8,7 +8,7 @@ import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; -import { nullAudio } from "../../fields/URLField"; +import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; @@ -22,11 +22,10 @@ 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"; -import { LabelBox } from "../views/nodes/LabelBox"; import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; -import { DragManager, dropActionType } from "./DragManager"; +import { DragManager } from "./DragManager"; import { makeTemplate, MakeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; import { LinkManager } from "./LinkManager"; @@ -1185,9 +1184,24 @@ export class CurrentUserUtils { } public static async snapshotDashboard(userDoc: Doc) { - const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); - CurrentUserUtils.openDashboard(userDoc, copy); + const docView = CollectionDockingView.Instance.props.DocumentView?.(); + const content = docView?.ContentDiv; + if (docView && content) { + const _width = Number(getComputedStyle(content).width.replace("px","")); + const _height = Number(getComputedStyle(content).height.replace("px","")); + CollectionFreeFormView.UpdateIcon( + docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(), + content, + _width, _height, + _width, _height, 0, + (iconFile, _nativeWidth, _nativeHeight) => { + const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); + const proto = Cast(img.proto, Doc, null)!; + proto["data-nativeWidth"] = _width; + proto["data-nativeHeight"] = _height; + docView.dataDoc.thumb = img; + }); + } } public static createNewDashboard = async (userDoc: Doc, id?: string) => { diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index c0645f0ab..103734b9b 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -213,34 +213,31 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV @undoBatch @action pinWithView = (targetDoc: Doc) => { - if (targetDoc) { - TabDocView.PinDoc(targetDoc); - setTimeout(() => { - const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1]; - const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking; - const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG); - if (scrollable) { - const scroll = targetDoc._scrollTop; - activeDoc.presPinView = true; - activeDoc.presPinViewScroll = scroll; - } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) { - activeDoc.presPinView = true; - activeDoc.presStartTime = targetDoc._currentTimecode; - activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1; - } else if (pannable) { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeDoc.presPinView = true; - activeDoc.presPinViewX = x; - activeDoc.presPinViewY = y; - activeDoc.presPinViewScale = scale; - } else if (targetDoc.type === DocumentType.COMPARISON) { - const width = targetDoc._clipWidth; - activeDoc.presPinClipWidth = width; - activeDoc.presPinView = true; - } - }); + const activeDoc = targetDoc && TabDocView.PinDoc(targetDoc); + if (activeDoc) { + const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking; + const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG); + if (scrollable) { + const scroll = targetDoc._scrollTop; + activeDoc.presPinView = true; + activeDoc.presPinViewScroll = scroll; + } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) { + activeDoc.presPinView = true; + activeDoc.presStartTime = targetDoc._currentTimecode; + activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1; + } else if (pannable) { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + activeDoc.presPinView = true; + activeDoc.presPinViewX = x; + activeDoc.presPinViewY = y; + activeDoc.presPinViewScale = scale; + } else if (targetDoc.type === DocumentType.COMPARISON) { + const width = targetDoc._clipWidth; + activeDoc.presPinClipWidth = width; + activeDoc.presPinView = true; + } } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cdf85c905..a30db66e4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -226,17 +226,7 @@ export class MainView extends React.Component { if (received && !this.userDoc) { reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true }); } else { - if (received && CurrentUserUtils._urlState.sharing) { - reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, - initialized => initialized && received && DocServer.GetRefField(received).then(docField => { - if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { - CollectionDockingView.AddSplit(docField, "right"); - } - }), - ); - } - const activeDash = PromiseValue(this.userDoc.activeDashboard); - activeDash.then(dash => { + PromiseValue(this.userDoc.activeDashboard).then(dash => { if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash); else CurrentUserUtils.createNewDashboard(this.userDoc); }); diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss index 555f4298d..f22e68ff5 100644 --- a/src/client/views/OverlayView.scss +++ b/src/client/views/OverlayView.scss @@ -46,4 +46,6 @@ .overlayView-doc { z-index: 9002; //so that it appears above chroma position: absolute; + top: 0; + left: 0; } \ No newline at end of file diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index c3c103b50..f5e686473 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -200,7 +200,7 @@ export class OverlayView extends React.Component { ScreenToLocalTransform={this.docScreenToLocalXf(d)} renderDepth={1} isDocumentActive={returnTrue} - isContentActive={retu} + isContentActive={returnTrue} whenChildContentsActiveChanged={emptyFunction} focus={DocUtils.DefaultFocus} styleProvider={DefaultStyleProvider} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index de2106e4a..5af72b7d1 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -26,6 +26,7 @@ import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; import { TabDocView } from './TabDocView'; import React = require("react"); +import { SelectionManager } from '../../util/SelectionManager'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -50,9 +51,8 @@ export class CollectionDockingView extends CollectionSubView() { public _flush: UndoManager.Batch | undefined; private _ignoreStateChange = ""; public tabMap: Set = new Set(); - public get initialized() { return this._goldenLayout !== null; } public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; } - @observable private _goldenLayout: any = null; + private _goldenLayout: any = null; constructor(props: SubCollectionViewProps) { super(props); @@ -118,6 +118,7 @@ export class CollectionDockingView extends CollectionSubView() { @undoBatch public static OpenFullScreen(doc: Doc) { + SelectionManager.DeselectAll(); const instance = CollectionDockingView.Instance; if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); @@ -271,7 +272,7 @@ export class CollectionDockingView extends CollectionSubView() { return true; } - async setupGoldenLayout() { + setupGoldenLayout = async () => { const config = StrCast(this.props.Document.dockingConfig); if (config) { const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g); @@ -291,7 +292,7 @@ export class CollectionDockingView extends CollectionSubView() { } this.tabMap.clear(); this._goldenLayout?.destroy(); - runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config))); + this._goldenLayout = new GoldenLayout(JSON.parse(config)); this._goldenLayout.on('tabCreated', this.tabCreated); this._goldenLayout.on('tabDestroyed', this.tabDestroyed); this._goldenLayout.on('stackCreated', this.stackCreated); @@ -322,7 +323,7 @@ export class CollectionDockingView extends CollectionSubView() { } this._ignoreStateChange = ""; }); - setTimeout(() => this.setupGoldenLayout(), 0); + setTimeout(this.setupGoldenLayout); //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window } } @@ -413,19 +414,25 @@ export class CollectionDockingView extends CollectionSubView() { const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc); const changesMade = this.props.Document.dockcingConfig !== json; if (changesMade && !this._flush) { - this.props.Document.dockingConfig = json; - this.props.Document.data = new List(docs); + UndoManager.RunInBatch(() => { + this.props.Document.dockingConfig = json; + this.props.Document.data = new List(docs); + }, "state changed"); } return changesMade; } tabDestroyed = (tab: any) => { + const dview = CollectionDockingView.Instance.props.Document; + const fieldKey = CollectionDockingView.Instance.props.fieldKey; + Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc); this.tabMap.delete(tab); tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); this.stateChanged(); } tabCreated = (tab: any) => { + this.tabMap.add(tab); tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content) } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 0141724ea..75a8207dd 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -82,7 +82,6 @@ export class TabDocView extends React.Component { const iconWrap = document.createElement("div"); const closeWrap = document.createElement("div"); - titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; titleEle.onkeydown = (e: KeyboardEvent) => { @@ -185,14 +184,13 @@ export class TabDocView extends React.Component { UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab"); }); } - CollectionDockingView.Instance.tabMap.add(tab); } /** * Adds a document to the presentation view **/ @action - public static async PinDoc(doc: Doc, pinProps?: PinProps) { + public static PinDoc(doc: Doc, pinProps?: PinProps) { //add this new doc to props.Document // all docs will be added to the ActivePresentation as stored on CurrentUserUtils @@ -246,19 +244,14 @@ export class TabDocView extends React.Component { pinDoc.presMovement = PresMovement.None; } if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; - const dview = CollectionDockingView.Instance.props.Document; - const fieldKey = CollectionDockingView.Instance.props.fieldKey; - const tabdocs = await DocListCastAsync(dview[fieldKey]); - runInAction(() => { - if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) { - tabdocs?.push(curPres); // bcz: Argh! this is annoying. if multiple documents are pinned, this will get called multiple times before the presentation view is drawn. Thus it won't be in the tabdocs list and it will get created multple times. so need to explicilty add the presbox to the list of open tabs - CollectionDockingView.AddSplit(curPres, "right"); - } - PresBox.Instance?._selectedArray.clear(); - pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array - DocumentManager.Instance.jumpToDocument(doc, false, undefined, []); - batch.end(); - }); + if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) { + CollectionDockingView.AddSplit(curPres, "right"); + setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things + } + PresBox.Instance?._selectedArray.clear(); + pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array + setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs + return pinDoc; } } @@ -282,7 +275,6 @@ export class TabDocView extends React.Component { componentWillUnmount() { this._tabReaction?.(); this._view && DocumentManager.Instance.RemoveView(this._view); - this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab); this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); } @@ -430,9 +422,7 @@ export class TabDocView extends React.Component { }} ref={ref => { if (this._mainCont = ref) { if (this._lastTab) { - console.log("DUP tab") this._view && DocumentManager.Instance.RemoveView(this._view); - CollectionDockingView.Instance.tabMap.delete(this._lastTab); } this._lastTab = this.tab; (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index befd2020c..09f05f69a 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -609,10 +609,8 @@ export class TreeView extends React.Component { return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label })); } - // TODO: currently doc focus works, but can't seem to edit title - // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildClick = () => { - return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.()); + return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); } onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 77cf0fc84..5f927a2a9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1489,7 +1489,7 @@ export class CollectionFreeFormView extends CollectionSubView { + static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) { if (oldDiv.childNodes && newDiv) { for (let i = 0; i < oldDiv.childNodes.length; i++) { this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement); @@ -1508,32 +1508,39 @@ export class CollectionFreeFormView extends CollectionSubView { - const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; + updateIcon = () => CollectionFreeFormView.UpdateIcon( + this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), + this.props.docViewPath().lastElement().ContentDiv!, + this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), + this.props.PanelWidth(), this.props.PanelHeight(), 0, + (iconFile, nativeWidth, nativeHeight) => { + this.dataDoc.icon = new ImageField(iconFile); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; + }); + + public static UpdateIcon(filename:string, docViewContent:HTMLElement, width: number, height: number, + panelWidth:number, panelHeight: number, scrollTop:number, cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) { const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; - newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); - newDiv.style.height = (this.layoutDoc[HeightSym]()).toString(); + newDiv.style.width = width.toString(); + newDiv.style.height = height.toString(); this.replaceCanvases(docViewContent, newDiv); - const htmlString = this._mainCont && new XMLSerializer().serializeToString(newDiv); - const nativeWidth = this.layoutDoc[WidthSym](); - const nativeHeight = this.layoutDoc[HeightSym](); - + const htmlString = new XMLSerializer().serializeToString(newDiv); + const nativeWidth = width; + const nativeHeight = height; CreateImage( - "", + Utils.prepend(""), document.styleSheets, htmlString, nativeWidth, - nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), - NumCast(this.layoutDoc._scrollTop) + nativeWidth * panelHeight / panelWidth, + scrollTop ).then ((data_url: any) => { - VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then( - returnedfilename => setTimeout(action(() => { - - this.dataDoc.icon = new ImageField(returnedfilename); - this.dataDoc["icon-nativeWidth"] = nativeWidth; - this.dataDoc["icon-nativeHeight"] = nativeHeight; - }), 500)); + VideoBox.convertDataUri(data_url, filename).then( + returnedfilename => setTimeout(() => { + cb(returnedfilename as string, nativeWidth, nativeHeight); + }, 500)); }) .catch(function (error: any) { console.error('oops, something went wrong!', error); diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js index d9524dd6e..f3f1bcf5c 100644 --- a/src/client/views/nodes/WebBoxRenderer.js +++ b/src/client/views/nodes/WebBoxRenderer.js @@ -214,8 +214,8 @@ var ForeignHtmlRenderer = function (styleSheets) { .replace(/noscript/g, "div").replace(/
    <\/div>/g, "") // when scripting isn't available (ie, rendering web pages here),
    } placement="bottom"> @@ -57,12 +57,12 @@ export class TopBar extends React.Component { await CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); batch.end(); }}> - {"Snapshot"} + Snapshot
    Browsing mode for directly navigating to documents
    } placement="bottom">
    MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}> - {"Explore"} + Explore
    -- cgit v1.2.3-70-g09d2 From b316f7676c5b2acd9e5bd1202f1cdf33a81a6f6a Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 8 Jun 2022 13:46:23 -0400 Subject: tweaked doc decorations to be visible when clicking on items in headerBar. restored dashboard snapshotting. --- src/client/util/CurrentUserUtils.ts | 4 ++++ src/client/views/MainView.tsx | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 885679b64..a325122bc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1202,6 +1202,10 @@ export class CurrentUserUtils { docView.dataDoc.thumb = img; }); } + + const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); + Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); + CurrentUserUtils.openDashboard(userDoc, copy); } public static createNewDashboard = async (userDoc: Doc, id?: string) => { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index a30db66e4..641178cd9 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -83,7 +83,7 @@ export class MainView extends React.Component { @computed private get dashboardTabHeight() { return 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); } - @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); } + @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; } @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); } @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); } @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } @@ -658,7 +658,7 @@ export class MainView extends React.Component { {LinkDescriptionPopup.descriptionPopup ? : null} {DocumentLinksButton.LinkEditorDocView ? DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)} {LinkDocPreview.LinkInfo ? : (null)} -
    +
    -- cgit v1.2.3-70-g09d2 From 7c24d7bb4a24c5224b89585c940db070b82e89fc Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 8 Jun 2022 20:03:04 -0400 Subject: fixes from merge --- src/client/Network.ts | 1 - src/client/util/DragManager.ts | 4 ---- src/client/views/GestureOverlay.tsx | 4 +++- src/client/views/nodes/AudioBox.tsx | 2 -- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- 5 files changed, 4 insertions(+), 9 deletions(-) (limited to 'src/client/util') diff --git a/src/client/Network.ts b/src/client/Network.ts index 1255e5ce0..3597e7b2b 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -19,7 +19,6 @@ export namespace Networking { } export async function UploadFilesToServer(files: File | File[]): Promise[]> { - console.log(files) const formData = new FormData(); if (Array.isArray(files)) { if (!files.length) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index f438a8c11..9f8c49081 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -210,10 +210,6 @@ export namespace DragManager { options?: DragOptions, dropEvent?: () => any ) { - // stop an 'accidental' on-click drag that may have occured if the user is in presenting mode - // note: dragData.dropAction is only undefined when the element itself being dragged without being selected - if (Doc.UserDoc()?.presentationMode === 'recording' && dragData.dropAction === undefined) return false; - const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField); dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 30982bfae..317f5f5d7 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -645,7 +645,9 @@ export class GestureOverlay extends Touchable { (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y)); if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0]; this._points = controlPoints; - this.dispatchGesture(GestureUtils.Gestures.Stroke); + this.dispatchGesture(GestureUtils.Gestures.Stroke); + // TODO: nda - check inks to group here + checkInksToGroup(); } this._points = []; } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 70d730ad1..7ff3196da 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -4,8 +4,6 @@ import { action, computed, IReactionDisposer, observable, runInAction } from "mo import { observer } from "mobx-react"; import { DateField } from "../../../fields/DateField"; import { Doc, DocListCast } from "../../../fields/Doc"; -import { documentSchema } from "../../../fields/documentSchemas"; -import { makeInterface } from "../../../fields/Schema"; import { ComputedField } from "../../../fields/ScriptField"; import { Cast, DateCast, NumCast } from "../../../fields/Types"; import { AudioField, nullAudio } from "../../../fields/URLField"; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 39c9478bf..8e53abdba 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -197,7 +197,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument; const dragItem: HTMLElement[] = []; if (dragArray.length === 1) { - const doc = dragArray[0]; + const doc = this._itemRef.current || dragArray[0]; doc.className = miniView ? "presItem-miniSlide" : "presItem-slide"; dragItem.push(doc); } else if (dragArray.length >= 1) { -- cgit v1.2.3-70-g09d2 From 6b9b7fefe5fa485b58512fdab10934c26092ab15 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Jun 2022 00:27:52 -0400 Subject: restored multi-line scripting by not adding 'return' to scripts when it's not requested. --- src/client/util/Scripting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 3b0a47b54..223b0268c 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -223,7 +223,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`); } const paramString = paramList.join(", "); - const body = addReturn ? `return ${script};` : `return ${script};`; + const body = addReturn ? `return ${script};` : script; const reqTypes = requiredType ? `: ${requiredType}` : ''; const funcScript = `(function(${paramString})${reqTypes} { ${body} })`; host.writeFile("file.ts", funcScript); -- cgit v1.2.3-70-g09d2 From 140e2f643f97057ba9c89c502cff7843bd738cd6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Jun 2022 16:59:09 -0400 Subject: fixed issues with taking dashboard snapshots to update dashboard view --- src/client/util/CurrentUserUtils.ts | 9 ++- src/client/views/DashboardView.tsx | 12 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 ++-- src/client/views/topbar/TopBar.tsx | 64 +--------------------- 4 files changed, 19 insertions(+), 76 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b1329e349..2d2dda2b8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1183,13 +1183,13 @@ export class CurrentUserUtils { input.click(); } - public static async snapshotDashboard(userDoc: Doc) { + public static CaptureDashboardThumbnail() { const docView = CollectionDockingView.Instance.props.DocumentView?.(); const content = docView?.ContentDiv; if (docView && content) { const _width = Number(getComputedStyle(content).width.replace("px","")); const _height = Number(getComputedStyle(content).height.replace("px","")); - CollectionFreeFormView.UpdateIcon( + return CollectionFreeFormView.UpdateIcon( docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(), content, _width, _height, @@ -1199,10 +1199,13 @@ export class CurrentUserUtils { const proto = Cast(img.proto, Doc, null)!; proto["data-nativeWidth"] = _width; proto["data-nativeHeight"] = _height; - docView.dataDoc.thumb = img; + Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img; }); } + } + + public static async snapshotDashboard(userDoc: Doc) { const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); CurrentUserUtils.openDashboard(userDoc, copy); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 5fd9b550d..3cfece970 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -1,4 +1,5 @@ import { observable } from "mobx"; +import { extname } from 'path'; import { observer } from "mobx-react"; import * as React from 'react'; import { Doc, DocListCast } from "../../fields/Doc"; @@ -33,13 +34,16 @@ export class DashboardView extends React.Component {
    All Dashboards
    - {myDashboards.map((dashboard) => -
    { this.clickDashboard(e, dashboard) }}> - + {myDashboards.map((dashboard) => { + const url = ImageCast((dashboard.thumb as Doc)?.data)?.url; + const ext = url ? extname(url.href):""; + const href = url?.href.replace(ext, "_m"+ ext); // need to choose which resolution image to show. options are _s, _m, _l, _o (small/medium/large/original) + return
    this.clickDashboard(e, dashboard)}> +
    {StrCast(dashboard.title)}
    - )} + })} {myDashboards.map((dashboard) => { console.log(dashboard.thumb) })} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f4d2d55d5..ef3a896e1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1532,7 +1532,7 @@ export class CollectionFreeFormView extends CollectionSubView { - VideoBox.convertDataUri(data_url, filename).then( - returnedfilename => setTimeout(() => { - cb(returnedfilename as string, nativeWidth, nativeHeight); - }, 500)); + (async (data_url: any) => { + const returnedFilename = await VideoBox.convertDataUri(data_url, filename); + cb(returnedFilename as string, nativeWidth, nativeHeight); }) .catch(function (error: any) { console.error('oops, something went wrong!', error); diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index fcc72b73d..21ef807ff 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -21,7 +21,7 @@ import "./TopBar.scss"; @observer export class TopBar extends React.Component { navigateToHome = () => { - Doc.UserDoc().activeDashboard = undefined + CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activeDashboard = undefined); } render() { const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data); @@ -61,68 +61,6 @@ export class TopBar extends React.Component {
    - - - {/*
    -
    - {`${Doc.CurrentUserEmail}`} -
    -
    window.location.assign(Utils.prepend("/logout"))}> - {"Log out"} -
    -
    */} - {/*
    -
    -
    - {activeDashboard ? StrCast(activeDashboard.title) : "Dash"} -
    */} - - {/* */} - {/*
    */ } - {/*
    - Create a new dashboard
    } placement="bottom">
    { - const batch = UndoManager.StartBatch("new dash"); - await CurrentUserUtils.createNewDashboard(Doc.UserDoc()); - batch.end(); - }}> - New -
    - - Work on a copy of the dashboard layout
    } placement="bottom"> -
    { - const batch = UndoManager.StartBatch("snapshot"); - await CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); - batch.end(); - }}> - Snapshot -
    - - Browsing mode for directly navigating to documents
    } placement="bottom"> -
    MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}> - Explore -
    - -
    */} - {/*
    */ } - {/*
    */ } - {/*
    window.open( - "https://brown-dash.github.io/Dash-Documentation/", "_blank")}> - -
    -
    SettingsManager.Instance.open()}> - -
    */} - {/*
    - -
    -
    SettingsManager.Instance.open()}> - -
    -
    */} ); } -- cgit v1.2.3-70-g09d2 From d1f268c1788f54c62c0779aba224b5b7d30edb01 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Jun 2022 17:13:49 -0400 Subject: changed tab numbering to be relative to dashboard. --- src/client/util/CurrentUserUtils.ts | 5 ++--- src/client/views/collections/CollectionDockingView.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2d2dda2b8..3107650d9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1215,8 +1215,6 @@ export class CurrentUserUtils { const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; const dashboardCount = DocListCast(dashboards.data).length + 1; - const emptyPane = Cast(userDoc.emptyPane, Doc, null); - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; const freeformOptions: DocumentOptions = { x: 0, y: 400, @@ -1224,7 +1222,7 @@ export class CurrentUserUtils { _height: 1000, _fitWidth: true, _backgroundGridShow: true, - title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, + title: `Untitled Tab 1`, }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); @@ -1233,6 +1231,7 @@ export class CurrentUserUtils { // switching the tabs from the datadoc to the regular doc const dashboardTabs = DocListCast(dashboardDoc[DataSym].data); dashboardDoc.data = new List(dashboardTabs); + dashboardDoc["pane-count"] = 1; userDoc.activePresentation = presentation; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index a790a0475..87149a4b8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -428,10 +428,10 @@ export class CollectionDockingView extends CollectionSubView() { stackCreated = (stack: any) => { stack.header?.element.on('mousedown', (e: any) => { if (e.target === stack.header?.element[0] && e.button === 2) { - const emptyPane = CurrentUserUtils.EmptyPane; - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + const dashboard= CurrentUserUtils.ActiveDashboard; + dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`, }); this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); CollectionDockingView.AddSplit(docToAdd, "", stack); @@ -455,10 +455,10 @@ export class CollectionDockingView extends CollectionSubView() { .off('click') //unbind the current click handler .click(action(() => { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size - const emptyPane = CurrentUserUtils.EmptyPane; - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + const dashboard = CurrentUserUtils.ActiveDashboard; + dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}` }); this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); CollectionDockingView.AddSplit(docToAdd, "", stack); -- cgit v1.2.3-70-g09d2 From 43968431d3c94cea07f06421d29ac0f190c55cde Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Jun 2022 17:25:55 -0400 Subject: added activePage field for choosing dash page layout. --- src/client/util/CurrentUserUtils.ts | 1 + src/client/views/DashboardView.tsx | 3 ++- src/client/views/MainView.tsx | 21 +++++++++++---------- src/client/views/topbar/TopBar.tsx | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 3107650d9..3b1009532 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1093,6 +1093,7 @@ export class CurrentUserUtils { Docs.newAccount = !(field instanceof Doc); await Docs.Prototypes.initialize(); const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; + Docs.newAccount &&(userDoc.activePage = "home"); const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager const a1 = await Cast(link?.anchor1, Doc, null); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 3cfece970..b08151c0f 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -22,7 +22,8 @@ export class DashboardView extends React.Component { clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => { if (e.detail === 2) { - Doc.UserDoc().activeDashboard = dashboard + Doc.UserDoc().activeDashboard = dashboard; + Doc.UserDoc().activePage = "dashboard"; } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ad041384c..5fd76c388 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -662,16 +662,17 @@ export class MainView extends React.Component { {DocumentLinksButton.LinkEditorDocView ? DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)} {LinkDocPreview.LinkInfo ? : (null)} - {Doc.UserDoc().activeDashboard ? - <> -
    - -
    - - {this.mainDashboardArea} - - : - + {((page:string) => { + switch (page) { + case "dashboard": + default:return <> +
    + +
    + {this.mainDashboardArea} + ; + case "home": return ; + } })(StrCast(Doc.UserDoc().activePage)) } diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 21ef807ff..258891099 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -21,7 +21,7 @@ import "./TopBar.scss"; @observer export class TopBar extends React.Component { navigateToHome = () => { - CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activeDashboard = undefined); + CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activePage = "home"); } render() { const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data); -- cgit v1.2.3-70-g09d2 From ab5e48a2340f06628fc22d1267d081de9dbc572f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Jun 2022 23:24:34 -0400 Subject: fixed up creating dashboard icons to work with pdfs in freeformviews with a grid. no idea why having a grid breaks thrings. --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/DashboardView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 53 ++++++++++++++-------- src/client/views/nodes/PDFBox.tsx | 44 +++++++----------- 4 files changed, 53 insertions(+), 50 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 3b1009532..9a01f3e5d 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1194,7 +1194,7 @@ export class CurrentUserUtils { docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(), content, _width, _height, - _width, _height, 0, + _width, _height, 0, 1, true, docView.layoutDoc[Id] + "-icon", (iconFile, _nativeWidth, _nativeHeight) => { const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); const proto = Cast(img.proto, Doc, null)!; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index b08151c0f..e003968d6 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -36,9 +36,7 @@ export class DashboardView extends React.Component {
    {myDashboards.map((dashboard) => { - const url = ImageCast((dashboard.thumb as Doc)?.data)?.url; - const ext = url ? extname(url.href):""; - const href = url?.href.replace(ext, "_m"+ ext); // need to choose which resolution image to show. options are _s, _m, _l, _o (small/medium/large/original) + const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; return
    this.clickDashboard(e, dashboard)}>
    {StrCast(dashboard.title)}
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ef3a896e1..99f74b8a2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1500,15 +1500,22 @@ export class CollectionFreeFormView extends CollectionSubView { - this.dataDoc.icon = new ImageField(iconFile); - this.dataDoc["icon-nativeWidth"] = nativeWidth; - this.dataDoc["icon-nativeHeight"] = nativeHeight; + this.dataDoc.icon = new ImageField(iconFile); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; }); - public static UpdateIcon(filename:string, docViewContent:HTMLElement, width: number, height: number, - panelWidth:number, panelHeight: number, scrollTop:number, cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) { + public static UpdateIcon( + filename:string, docViewContent:HTMLElement, + width: number, height: number, + panelWidth:number, panelHeight: number, + scrollTop:number, + realNativeHeight: number, + noSuffix: boolean, + replaceRootFilename: string| undefined, + cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) + { const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; newDiv.style.width = width.toString(); newDiv.style.height = height.toString(); @@ -1538,10 +1553,10 @@ export class CollectionFreeFormView extends CollectionSubView { - const returnedFilename = await VideoBox.convertDataUri(data_url, filename); + const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename); cb(returnedFilename as string, nativeWidth, nativeHeight); }) .catch(function (error: any) { @@ -1771,7 +1786,7 @@ export class CollectionFreeFormView extends CollectionSubView
    {this.layoutDoc._backgroundGridShow ? - : (null)} + />
    : (null)} () { @@ -147,34 +148,23 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - return; // currently we render pdf icons as text labels + // currently we render pdf icons as text labels const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; - const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; - newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); - newDiv.style.height = (this.layoutDoc[HeightSym]()).toString(); - this.replaceCanvases(docViewContent, newDiv); - const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv); - const nativeWidth = this.layoutDoc[WidthSym](); - const nativeHeight = this.layoutDoc[HeightSym](); - - CreateImage( - "", - document.styleSheets, - htmlString, - nativeWidth, - nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), - NumCast(this.layoutDoc._scrollTop) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"]) - ).then - ((data_url: any) => { - VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then( - returnedfilename => setTimeout(action(() => { - this.dataDoc.icon = new ImageField(returnedfilename); - this.dataDoc["icon-nativeWidth"] = nativeWidth; - this.dataDoc["icon-nativeHeight"] = nativeHeight; - }), 500)); - }) - .catch(function (error: any) { - console.error('oops, something went wrong!', error); + const filename = this.layoutDoc[Id] + "-icon" + (new Date()).getTime(); + this._pdfViewer?._mainCont.current && CollectionFreeFormView.UpdateIcon( + filename, docViewContent, + this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), + this.props.PanelWidth(), this.props.PanelHeight(), + NumCast(this.layoutDoc._scrollTop), + NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"], 1), + true, + this.layoutDoc[Id] + "-icon", + (iconFile:string, nativeWidth:number, nativeHeight:number) => { + setTimeout(() => { + this.dataDoc.icon = new ImageField(iconFile); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; + }, 500); }); } -- cgit v1.2.3-70-g09d2 From f550bca52735d9bd2195ff453e43beca18bef309 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 9 Jun 2022 23:54:11 -0400 Subject: opening a shared dashboard now adds it to myDashboards --- src/client/util/CurrentUserUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9a01f3e5d..c801a6f2f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -931,7 +931,6 @@ export class CurrentUserUtils { sharedDocs.childContextMenuScripts = new List([addToDashboards!,]); sharedDocs.childContextMenuLabels = new List(["Add to Dashboards",]); sharedDocs.childContextMenuIcons = new List(["user-plus",]); - } doc.mySharedDocs = new PrefetchProxy(sharedDocs as Doc); } @@ -1111,6 +1110,9 @@ export class CurrentUserUtils { public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => { CurrentUserUtils.MainDocId = doc[Id]; + if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) { + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); + } if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); -- cgit v1.2.3-70-g09d2 From c159cec257544ff10c256181d1aedfccf3ae7843 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Thu, 9 Jun 2022 21:05:20 -0700 Subject: fix: close active dashboard --- src/client/util/CurrentUserUtils.ts | 6 +++++- src/client/views/topbar/TopBar.tsx | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 3b1009532..d32f1bf70 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1212,6 +1212,10 @@ export class CurrentUserUtils { CurrentUserUtils.openDashboard(userDoc, copy); } + public static closeActiveDashboard = () => { + Doc.UserDoc().activeDashboard = undefined; + } + public static createNewDashboard = async (userDoc: Doc, id?: string) => { const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; @@ -1237,7 +1241,7 @@ export class CurrentUserUtils { userDoc.activePresentation = presentation; Doc.AddDocToList(dashboards, "data", dashboardDoc); - CurrentUserUtils.openDashboard(userDoc, dashboardDoc); + // CurrentUserUtils.openDashboard(userDoc, dashboardDoc); } public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 79d48e13c..57ceac2dd 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -22,6 +22,7 @@ import "./TopBar.scss"; @observer export class TopBar extends React.Component { navigateToHome = () => { + CurrentUserUtils.closeActiveDashboard(); CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activePage = "home"); } render() { -- cgit v1.2.3-70-g09d2 From bc662b4e7eaaa1f33f41f64ce64d60579939b971 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 10 Jun 2022 10:18:05 -0400 Subject: fixed some problems with dragging doc onto empty tab bar. made topBar be just one line to save vertical space. added a context menu to Dashboard --- src/client/util/CurrentUserUtils.ts | 14 ++++--- src/client/util/DragManager.ts | 2 +- .../collectionLinear/CollectionLinearView.tsx | 1 + src/client/views/global/globalCssVariables.scss | 2 +- src/client/views/nodes/DocumentView.tsx | 11 +++--- src/client/views/topbar/TopBar.scss | 6 +-- src/client/views/topbar/TopBar.tsx | 45 ++++++++++++---------- 7 files changed, 46 insertions(+), 35 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e46bb3b3e..60bfc165b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -381,6 +381,7 @@ export class CurrentUserUtils { dontUndo: true, title, target, + dontRegisterView: true, hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined, _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), @@ -399,6 +400,7 @@ export class CurrentUserUtils { _chromeHidden: true, backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", + dontRegisterView: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), ignoreClick: true, _gridGap: 0, @@ -719,7 +721,7 @@ export class CurrentUserUtils { ]; doc.dockedBtns = CurrentUserUtils.linearButtonList({ title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, - linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true + childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() })))); } } @@ -833,12 +835,13 @@ export class CurrentUserUtils { }); if (doc.contextMenuBtns === undefined) { doc.contextMenuBtns = CurrentUserUtils.linearButtonList( - { title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }, + { title: "menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }, CurrentUserUtils.contextMenuTools(doc).map(params => !params.subMenu ? btnFunc(params) : CurrentUserUtils.linearButtonList({ title: params.title, + childDontRegisterViews: true, linearViewSubMenu: true, flexGap: 0, ignoreClick: true, linearViewExpandable: true, icon: params.title, _height: 30, linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, @@ -854,6 +857,7 @@ export class CurrentUserUtils { btnFunc(params) : CurrentUserUtils.linearButtonList({ title: params.title, + childDontRegisterViews: true, linearViewSubMenu: true, flexGap: 0, ignoreClick: true, linearViewExpandable: true, icon: params.title, _height: 30, linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, @@ -919,7 +923,7 @@ export class CurrentUserUtils { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, _chromeHidden: true, boxShadow: "0 0", - explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" + dontRegisterView: true, explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" }, sharingDocumentId + "outer", sharingDocumentId); (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; } @@ -943,7 +947,7 @@ export class CurrentUserUtils { doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, - explainer: "This is where documents that are Imported into Dash will go." + dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go." })); } } @@ -952,7 +956,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, + dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true, childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true })) as any as Doc; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 9f8c49081..c3c6c22df 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -455,7 +455,7 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction; } - if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { + if (((e.target as any)?.className === "lm_tabs" || ((e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { if (!startWindowDragTimer) { startWindowDragTimer = setTimeout(async () => { startWindowDragTimer = undefined; diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index c0a33a5e0..9b1cb1601 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -153,6 +153,7 @@ export class CollectionLinearView extends CollectionSubView() { PanelWidth={nested ? doc[WidthSym] : this.dimension} PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension} renderDepth={this.props.renderDepth + 1} + dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)} focus={emptyFunction} styleProvider={this.props.styleProvider} docViewPath={returnEmptyDoclist} diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index ce9cc05d6..a14634bdc 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -38,7 +38,7 @@ $small-text: 9px; // misc values $border-radius: 0.3em; $search-thumnail-size: 130; -$topbar-height: 55px; +$topbar-height: 32px; $antimodemenu-height: 36px; // dragged items diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1591840e6..60d16f044 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -764,7 +764,7 @@ export class DocumentViewInternal extends DocComponent this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" }); !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" }); - if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { + if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" }); const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; @@ -834,10 +834,10 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); - helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); - !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); - !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); + helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); + !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); + !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); + !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); } @@ -1204,6 +1204,7 @@ export class DocumentView extends React.Component { @observable public docView: DocumentViewInternal | undefined | null; + showContextMenu(pageX:number, pageY:number) { return this.docView?.onContextMenu(undefined, pageX, pageY); } get Document() { return this.props.Document; } get topMost() { return this.props.renderDepth === 0; } get rootDoc() { return this.docView?.rootDoc || this.Document; } diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss index 6662386d4..c5b340514 100644 --- a/src/client/views/topbar/TopBar.scss +++ b/src/client/views/topbar/TopBar.scss @@ -22,9 +22,9 @@ grid-auto-columns: 33.3% 33.3% 33.3%; align-items: center; - &:first-child { - height: 20px; - } + // &:first-child { + // height: 20px; + // } } .topbar-button-text { diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 57ceac2dd..5bee58ccb 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -3,14 +3,18 @@ import { Tooltip } from "@material-ui/core"; import { action } from "mobx"; import { observer } from "mobx-react"; import * as React from 'react'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { Cast, StrCast } from '../../../fields/Types'; +import { GetEffectiveAcl } from "../../../fields/util"; import { Utils } from '../../../Utils'; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { DocumentManager } from "../../util/DocumentManager"; +import { SelectionManager } from "../../util/SelectionManager"; import { SettingsManager } from "../../util/SettingsManager"; import { SharingManager } from "../../util/SharingManager"; import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { ContextMenu } from "../ContextMenu"; import { Borders, Colors } from "../global/globalEnums"; import { MainView } from "../MainView"; import "./TopBar.scss"; @@ -22,22 +26,38 @@ import "./TopBar.scss"; @observer export class TopBar extends React.Component { navigateToHome = () => { - CurrentUserUtils.closeActiveDashboard(); - CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activePage = "home"); + CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => { + Doc.UserDoc().activePage = "home"; + CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use + }); } render() { - const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data); const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null) return ( //TODO:glr Add support for light / dark mode
    -
    Home
    + {activeDashboard ?
    {Doc.CurrentUserEmail} (Placeholder)
    : (null)}
    +
    SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}> + {activeDashboard ? StrCast(activeDashboard.title) : "Dash"} +
    +
    { + const dashView = DocumentManager.Instance.getDocumentView(activeDashboard); + ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" }); + dashView?.showContextMenu(e.clientX+20, e.clientY+30); + }}> + +
    +
    {SharingManager.Instance.open(undefined, activeDashboard)}}> + {/* TODO: if this is my dashboard, display share + if this is a shared dashboard, display "view original or view annotated" */} + { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author === Doc.CurrentUserEmail ? "Share": "view original" } +
    window.open( "https://brown-dash.github.io/Dash-Documentation/", "_blank")}> @@ -47,21 +67,6 @@ export class TopBar extends React.Component {
    -
    -
    - {activeDashboard ?
    Freeform View (Placeholder)
    : (null)} -
    -
    -
    - {activeDashboard ? StrCast(activeDashboard.title) : "Dash"} -
    -
    -
    - {/* TODO: if this is my dashboard, display share - if this is a shared dashboard, display "view original or view annotated" */} -
    {SharingManager.Instance.open(undefined, activeDashboard)}}>Share
    -
    -
    ); -- cgit v1.2.3-70-g09d2 From 910131b13080a2649606ef8a618c77fd81248343 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 10 Jun 2022 10:39:00 -0400 Subject: fixed errors --- src/client/util/DragManager.ts | 2 +- src/client/views/nodes/RecordingBox/RecordingBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c3c6c22df..dbfba8992 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -455,7 +455,7 @@ export namespace DragManager { if (dragData instanceof DocumentDragData) { dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction; } - if (((e.target as any)?.className === "lm_tabs" || ((e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { + if (((e.target as any)?.className === "lm_tabs" || (e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) { if (!startWindowDragTimer) { startWindowDragTimer = setTimeout(async () => { startWindowDragTimer = undefined; diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index eac1c63f9..10393624b 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -55,7 +55,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { render() { return
    - {!this.result && } + {!this.result && }
    ; } } -- cgit v1.2.3-70-g09d2 From 04fd9f20ccdc204dad5724008b076cbf9ea7b874 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 10 Jun 2022 13:02:49 -0400 Subject: fixed some initialization problems with tabs --- src/client/util/History.ts | 6 ++-- .../views/collections/CollectionDockingView.tsx | 32 ++++++++++++---------- 2 files changed, 22 insertions(+), 16 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/History.ts b/src/client/util/History.ts index e6f75a7db..632348306 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -58,8 +58,10 @@ export namespace HistoryUtil { export function getState(): ParsedUrl { const state = copyState(history.state); - state.initializers = state.initializers || {}; - return state; + if (state) { + state.initializers = state.initializers || {}; + } + return state ?? {initializers:{}}; } // export function addHandler(handler: (state: ParsedUrl | null) => void) { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 4119fb5ba..d2687df17 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -188,10 +188,13 @@ export class CollectionDockingView extends CollectionSubView() { stack.addChild(docContentConfig, undefined); stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1]); } else { - const newItemStackConfig = { type: 'stack', content: [docContentConfig] }; - const newContentItem = glayRoot.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); + const newContentItem = () => { + const newItem = glayRoot.layoutManager.createContentItem({ type: 'stack', content: [docContentConfig] }, instance._goldenLayout); + newItem.callDownwards('_$init'); + return newItem; + } if (glayRoot.contentItems.length === 0) { // if no rows / columns - glayRoot.addChild(newContentItem); + glayRoot.addChild(newContentItem()); } else if (glayRoot.contentItems[0].isStack) { glayRoot.contentItems[0].addChild(docContentConfig); } else if ( @@ -203,32 +206,33 @@ export class CollectionDockingView extends CollectionSubView() { else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row switch (pullSide) { default: - case "right": glayRoot.contentItems[0].addChild(newContentItem); break; - case "left": glayRoot.contentItems[0].addChild(newContentItem, 0); break; + case "right": glayRoot.contentItems[0].addChild(newContentItem()); break; + case "left": glayRoot.contentItems[0].addChild(newContentItem(), 0); break; case "top": case "bottom": // if not going in a row layout, must add already existing content into column const rowlayout = glayRoot.contentItems[0]; const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); + const newItem = newContentItem(); instance._goldenLayout.saveScrollTops(rowlayout.element); rowlayout.parent.replaceChild(rowlayout, newColumn); if (pullSide === "top") { newColumn.addChild(rowlayout, undefined, true); - newColumn.addChild(newContentItem, 0, true); + newColumn.addChild(newItem, 0, true); } else if (pullSide === "bottom") { - newColumn.addChild(newContentItem, undefined, true); + newColumn.addChild(newItem, undefined, true); newColumn.addChild(rowlayout, 0, true); } instance._goldenLayout.restoreScrollTops(rowlayout.element); rowlayout.config.height = 50; - newContentItem.config.height = 50; + newItem.config.height = 50; } } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column switch (pullSide) { - case "top": glayRoot.contentItems[0].addChild(newContentItem, 0); break; - case "bottom": glayRoot.contentItems[0].addChild(newContentItem); break; + case "top": glayRoot.contentItems[0].addChild(newContentItem(), 0); break; + case "bottom": glayRoot.contentItems[0].addChild(newContentItem()); break; case "left": case "right": default: @@ -236,23 +240,23 @@ export class CollectionDockingView extends CollectionSubView() { const collayout = glayRoot.contentItems[0]; const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); + const newItem = newContentItem(); instance._goldenLayout.saveScrollTops(collayout.element); collayout.parent.replaceChild(collayout, newRow); if (pullSide === "left") { newRow.addChild(collayout, undefined, true); - newRow.addChild(newContentItem, 0, true); + newRow.addChild(newItem, 0, true); } else { - newRow.addChild(newContentItem, undefined, true); + newRow.addChild(newItem, undefined, true); newRow.addChild(collayout, 0, true); } instance._goldenLayout.restoreScrollTops(collayout.element); collayout.config.width = 50; - newContentItem.config.width = 50; + newItem.config.width = 50; } } instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig()); - newContentItem.callDownwards('_$init'); } return instance.layoutChanged(); -- cgit v1.2.3-70-g09d2 From 526fb50ba814d91cf9c69d75cb5b95a91c2dd924 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Mon, 13 Jun 2022 21:27:33 -0700 Subject: feat: pop up modal for naming dashboard --- src/client/util/CurrentUserUtils.ts | 9 +++-- src/client/views/DashboardView.scss | 2 ++ src/client/views/DashboardView.tsx | 71 ++++++++++++++++++++++++------------- 3 files changed, 55 insertions(+), 27 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 60bfc165b..aa7c2a39b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1222,7 +1222,8 @@ export class CurrentUserUtils { Doc.UserDoc().activeDashboard = undefined; } - public static createNewDashboard = async (userDoc: Doc, id?: string) => { + public static createNewDashboard = async (userDoc: Doc, id?: string, name?: string) => { + console.log(name) const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; const dashboardCount = DocListCast(dashboards.data).length + 1; @@ -1235,8 +1236,9 @@ export class CurrentUserUtils { _backgroundGridShow: true, title: `Untitled Tab 1`, }; + const title = name ? name : `Dashboard ${dashboardCount}` const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); + const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, "row"); freeformDoc.context = dashboardDoc; // switching the tabs from the datadoc to the regular doc @@ -1247,7 +1249,8 @@ export class CurrentUserUtils { userDoc.activePresentation = presentation; Doc.AddDocToList(dashboards, "data", dashboardDoc); - // CurrentUserUtils.openDashboard(userDoc, dashboardDoc); + // open this new dashboard + // Doc.UserDoc().activeDashboard = dashboardDoc; } public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss index 67587dd2b..06f331c13 100644 --- a/src/client/views/DashboardView.scss +++ b/src/client/views/DashboardView.scss @@ -2,6 +2,8 @@ padding: 50px; display: flex; flex-direction: row; + width: 100%; + position: absolute; .left-menu { display: flex; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 5aacec61f..fa8a98402 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -1,4 +1,4 @@ -import { action, observable } from "mobx"; +import { action, computed, observable } from "mobx"; import { extname } from 'path'; import { observer } from "mobx-react"; import * as React from 'react'; @@ -8,6 +8,7 @@ import { Cast, ImageCast, StrCast } from "../../fields/Types"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { UndoManager } from "../util/UndoManager"; import "./DashboardView.scss" +import { MainViewModal } from "./MainViewModal"; enum DashboardGroup { MyDashboards, SharedDashboards @@ -18,20 +19,17 @@ export class DashboardView extends React.Component { //TODO: delete dashboard, share dashboard, etc. - @observable - private selectedDashboardGroup = DashboardGroup.MyDashboards; + @observable private selectedDashboardGroup = DashboardGroup.MyDashboards; + + @observable private newDashboardName: string | undefined = undefined; + @action abortCreateNewDashboard = () => { this.newDashboardName = undefined } + @action setNewDashboardName(name: string) { this.newDashboardName = name} @action selectDashboardGroup = (group: DashboardGroup) => { this.selectedDashboardGroup = group } - newDashboard = async () => { - const batch = UndoManager.StartBatch("new dash"); - await CurrentUserUtils.createNewDashboard(Doc.UserDoc()); - batch.end(); - } - clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => { if (e.detail === 2) { Doc.UserDoc().activeDashboard = dashboard; @@ -48,24 +46,49 @@ export class DashboardView extends React.Component { } } + createNewDashboard = async (name: string) => { + const batch = UndoManager.StartBatch("new dash"); + await CurrentUserUtils.createNewDashboard(Doc.UserDoc(), undefined, name); + batch.end(); + this.abortCreateNewDashboard(); + } + + @computed + get namingInterface() { + return
    + this.setNewDashboardName((e.target as any).value)} /> + + +
    ; + } + render() { - return
    -
    -
    New
    -
    this.selectDashboardGroup(DashboardGroup.MyDashboards) }>My Dashboards
    -
    this.selectDashboardGroup(DashboardGroup.SharedDashboards) }>Shared Dashboards
    -
    -
    - {this.getDashboards().map((dashboard) => { - const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; - return
    this.clickDashboard(e, dashboard)}> - -
    {StrCast(dashboard.title)}
    -
    + return <> +
    +
    +
    { this.setNewDashboardName("") }}>New
    +
    this.selectDashboardGroup(DashboardGroup.MyDashboards)}>My Dashboards
    +
    this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>Shared Dashboards
    +
    +
    + {this.getDashboards().map((dashboard) => { + const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; + return
    this.clickDashboard(e, dashboard)}> + +
    {StrCast(dashboard.title)}
    +
    - })} + })} +
    -
    + ; + + } } -- cgit v1.2.3-70-g09d2 From fbdddaeba99f5dbffffa4668e53b27006b7895f6 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Mon, 13 Jun 2022 22:09:38 -0700 Subject: feat: automatically open dashboard on create --- src/client/util/CurrentUserUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aa7c2a39b..1538062bb 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1250,7 +1250,8 @@ export class CurrentUserUtils { Doc.AddDocToList(dashboards, "data", dashboardDoc); // open this new dashboard - // Doc.UserDoc().activeDashboard = dashboardDoc; + Doc.UserDoc().activeDashboard = dashboardDoc; + Doc.UserDoc().activePage = "dashboard"; } public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { -- cgit v1.2.3-70-g09d2 From da6283875ec7f59af602e14ed19fcab93533f377 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 14 Jun 2022 13:00:27 -0400 Subject: made shared with me doc count decrement when a document is viewed (double-clicked, dragged out) --- src/client/documents/Documents.ts | 4 ++-- src/client/util/CurrentUserUtils.ts | 12 ++++++---- src/client/views/collections/TreeView.tsx | 2 +- src/client/views/nodes/button/FontIconBadge.tsx | 30 ++++++++++++------------- src/client/views/nodes/button/FontIconBox.tsx | 2 +- src/fields/Doc.ts | 1 + 6 files changed, 28 insertions(+), 23 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b290b2d58..d2937b83f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -198,7 +198,6 @@ export class DocumentOptions { hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; - watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) templates?: List; hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images) @@ -251,6 +250,7 @@ export class DocumentOptions { numBtnMax?: number; numBtnMin?: number; switchToggle?: boolean; + badgeValue?: ScriptField; //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded @@ -294,7 +294,7 @@ export class DocumentOptions { treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically - + treeViewChildDoubleClick?: ScriptField; // // Action Button buttonMenu?: boolean; // whether a action button should be displayed buttonMenuDoc?: Doc; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aa7c2a39b..bb78fe4e7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -23,6 +23,7 @@ import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; +import { DocumentView } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; import { DocumentManager } from "./DocumentManager"; import { DragManager } from "./DragManager"; @@ -354,6 +355,7 @@ export class CurrentUserUtils { } static menuBtnDescriptions(doc: Doc) { + const badgeScript = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)") return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, @@ -361,7 +363,7 @@ export class CurrentUserUtils { { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, - { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, + { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue: badgeScript}, { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, ]; @@ -370,7 +372,7 @@ export class CurrentUserUtils { static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) => + const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) => Docs.Create.FontIconDocument({ icon, btnType: ButtonType.MenuButton, @@ -387,7 +389,7 @@ export class CurrentUserUtils { _removeDropProperties: new List(["dropAction", "_stayInCollection"]), _width: 60, _height: 60, - watchedDocuments, + badgeValue, onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) }) ); @@ -918,11 +920,12 @@ export class CurrentUserUtils { // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document if (doc.mySharedDocs === undefined) { let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); + const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name}) if (!sharedDocs) { sharedDocs = Docs.Create.TreeDocument([], { title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, - _chromeHidden: true, boxShadow: "0 0", + _chromeHidden: true, boxShadow: "0 0", treeViewChildDoubleClick: dblClkScript, dontRegisterView: true, explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" }, sharingDocumentId + "outer", sharingDocumentId); (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; @@ -1069,6 +1072,7 @@ export class CurrentUserUtils { // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; } + return doc; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 8824750a3..59dc5671b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -613,7 +613,7 @@ export class TreeView extends React.Component { return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); } - onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null)); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx index cf86b5e07..3b5aac221 100644 --- a/src/client/views/nodes/button/FontIconBadge.tsx +++ b/src/client/views/nodes/button/FontIconBadge.tsx @@ -7,30 +7,30 @@ import { DragManager } from "../../../util/DragManager"; import "./FontIconBadge.scss"; interface FontIconBadgeProps { - collection: Doc | undefined; + value: string | undefined; } @observer export class FontIconBadge extends React.Component { _notifsRef = React.createRef(); - onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - (e: PointerEvent) => { - const dragData = new DragManager.DocumentDragData([this.props.collection!]); - DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); - return true; - }, - returnFalse, emptyFunction, false); - } + // onPointerDown = (e: React.PointerEvent) => { + // setupMoveUpEvents(this, e, + // (e: PointerEvent) => { + // const dragData = new DragManager.DocumentDragData([this.props.collection!]); + // DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); + // return true; + // }, + // returnFalse, emptyFunction, false); + // } render() { - if (!(this.props.collection instanceof Doc)) return (null); - const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read + if (this.props.value === undefined) return (null); return
    -
    0 ? { "display": "initial" } : { "display": "none" }} - onPointerDown={this.onPointerDown} > - {length} +
    + {this.props.value}
    ; } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index a1b9023f3..97e6eddfe 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent() {
    {this.icon === "pres-trail" ? trailsIcon : } {menuLabel} - +
    ); break; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6abc27b23..7b72787d1 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1436,6 +1436,7 @@ ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.cop ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); }); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); +ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); }); ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); }); -- cgit v1.2.3-70-g09d2 From 6dc7a0d37f055c57135b91d4d54174ee156b61f5 Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Wed, 15 Jun 2022 19:46:10 -0700 Subject: share and remove dashboard from context menu --- src/client/util/CurrentUserUtils.ts | 7 +++++++ src/client/views/DashboardView.tsx | 19 ++++++++----------- src/client/views/topbar/TopBar.tsx | 3 --- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 199ef701d..c0318e86b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1226,6 +1226,13 @@ export class CurrentUserUtils { Doc.UserDoc().activeDashboard = undefined; } + public static async removeDashboard(dashboard: Doc) { + const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); + if (dashboards && dashboards.length > 0) { + Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); + } + } + public static createNewDashboard = async (userDoc: Doc, id?: string, name?: string) => { console.log(name) const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 716653ea5..37a70f165 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -14,6 +14,7 @@ import { DocumentManager } from "../util/DocumentManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ContextMenuProps } from "./ContextMenuItem"; import { simulateMouseClick } from "../../Utils"; +import { SharingManager } from "../util/SharingManager"; enum DashboardGroup { MyDashboards, SharedDashboards @@ -60,12 +61,6 @@ export class DashboardView extends React.Component { this.abortCreateNewDashboard(); } - showContextMenu = (e: React.MouseEvent) => { - // DocumentViewInternal.SelectAfterContextMenu = false; - // simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - // DocumentViewInternal.SelectAfterContextMenu = true; - } - @computed get namingInterface() { return
    @@ -78,7 +73,7 @@ export class DashboardView extends React.Component { _downX: number = 0; _downY: number = 0; @action - onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => { + onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 if (e) { e.preventDefault(); @@ -91,12 +86,12 @@ export class DashboardView extends React.Component { const cm = ContextMenu.Instance; cm.addItem({ description: "Share Dashboard", event: async () => { - // ... + SharingManager.Instance.open(undefined, dashboard) }, icon: "edit" }); cm.addItem({ description: "Delete Dashboard", event: async () => { - // ... + CurrentUserUtils.removeDashboard(dashboard) }, icon: "trash" }); cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15); @@ -116,7 +111,7 @@ export class DashboardView extends React.Component { {this.getDashboards().map((dashboard) => { const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; return
    {this.onContextMenu(dashboard, e)}} onClick={e => this.clickDashboard(e, dashboard)}>
    @@ -124,7 +119,9 @@ export class DashboardView extends React.Component {
    { this._downX = e.clientX; this._downY = e.clientY; - }} onClick={this.onContextMenu}> + }} + onContextMenu={(e) => {this.onContextMenu(dashboard, e)}} + >
    diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 0665e79ed..e4f48adce 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -76,9 +76,6 @@ export class TopBar extends React.Component {
    {CurrentUserUtils.ActiveDashboard ?
    { SharingManager.Instance.open(undefined, activeDashboard) }}> - {/* TODO: if I have edit access to the dashboard, display share - if this is a shared dashboard, display "view original or view annotated" */} - {/* { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author === Doc.CurrentUserEmail ? "Share": "view original" } */} {GetEffectiveAcl(Doc.GetProto(CurrentUserUtils.ActiveDashboard)) === AclAdmin ? "Share" : "view original"}
    : (null)}
    window.open( -- cgit v1.2.3-70-g09d2 From 950440f9c73746867454e20f0e5c3a38297728b9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 16 Jun 2022 09:28:00 -0400 Subject: added unviewed shared dashboards to dashboard view --- src/client/util/CurrentUserUtils.ts | 1 + src/client/views/DashboardView.tsx | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c0318e86b..3085e7e72 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1286,6 +1286,7 @@ export class CurrentUserUtils { public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } + public static get MySharedDocs() { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); } public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); } public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 9f497dcbf..a105e0790 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -15,6 +15,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ContextMenuProps } from "./ContextMenuItem"; import { simulateMouseClick } from "../../Utils"; import { SharingManager } from "../util/SharingManager"; +import { CollectionViewType } from "./collections/CollectionView"; enum DashboardGroup { MyDashboards, SharedDashboards @@ -54,6 +55,11 @@ export class DashboardView extends React.Component { } } + getSharedDashboards = () => { + const sharedDashs = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking); + return sharedDashs.filter((dashboard) => !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard)) + } + createNewDashboard = async (name: string) => { const batch = UndoManager.StartBatch("new dash"); await CurrentUserUtils.createNewDashboard(Doc.UserDoc(), undefined, name); @@ -129,6 +135,31 @@ export class DashboardView extends React.Component {
    })} +
    + +
    UNVIEWED SHARED DASHBOARDS
    +
    + {this.getSharedDashboards().map((dashboard) => { + const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href; + return
    {this.onContextMenu(dashboard, e)}} + onClick={e => this.clickDashboard(e, dashboard)}> + +
    +
    {StrCast(dashboard.title)}
    +
    { + this._downX = e.clientX; + this._downY = e.clientY; + }} + onClick={(e) => {this.onContextMenu(dashboard, e)}} + > + +
    +
    + +
    + + })}
    -- cgit v1.2.3-70-g09d2 From 21eb25198c27d65e398d11b018a8dc792297e35a Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 17 Jun 2022 09:33:01 -0400 Subject: updated fontIconBadge to use 'viewed' list to decrement value when items are viewed. cleaned up how sharedDoc is setup in currentUserUtils to allow updates to not require rebuilding DB --- src/client/documents/Documents.ts | 10 ++- src/client/util/CurrentUserUtils.ts | 75 +++++++++++++--------- src/client/views/collections/TreeView.tsx | 2 +- src/client/views/nodes/button/FontIconBadge.tsx | 32 ++++----- src/client/views/nodes/button/FontIconBox.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- src/fields/Doc.ts | 1 + src/fields/Types.ts | 4 ++ 8 files changed, 77 insertions(+), 53 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b290b2d58..5f009573e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -98,6 +98,7 @@ export class DocumentOptions { allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?"); childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection "); targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? "); + userColor?: string; // color associated with a Dash user (seen in header fields of shared documents) color?: string; // foreground color data doc backgroundColor?: STRt = new StrInfo("background color for data doc"); _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)", true); @@ -190,6 +191,10 @@ export class DocumentOptions { childLayoutString?: string; // template string for collection to use to render its children childDontRegisterViews?: boolean; childHideLinkButton?: boolean; // hide link buttons on all children + childContextMenuFilters?: List; + childContextMenuScripts?: List; + childContextMenuLabels?: List; + childContextMenuIcons?: List; hideLinkButton?: boolean; // whether the blue link counter button should be hidden hideDecorationTitle?: boolean; hideOpenButton?: boolean; @@ -198,7 +203,6 @@ export class DocumentOptions { hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template isTemplateDoc?: boolean; - watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc) templates?: List; hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images) @@ -250,7 +254,8 @@ export class DocumentOptions { numBtnType?: string; numBtnMax?: number; numBtnMin?: number; - switchToggle?: boolean; + switchToggle?: boolean; + badgeValue?: ScriptField; //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded @@ -294,6 +299,7 @@ export class DocumentOptions { treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items. treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically + treeViewChildDoubleClick?: ScriptField; // // Action Button buttonMenu?: boolean; // whether a action button should be displayed diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 60bfc165b..b4e18a8bb 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -7,7 +7,7 @@ import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; @@ -354,6 +354,7 @@ export class CurrentUserUtils { } static menuBtnDescriptions(doc: Doc) { + const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)") return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, @@ -361,7 +362,7 @@ export class CurrentUserUtils { { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, - { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, + { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue}, { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, ]; @@ -369,8 +370,9 @@ export class CurrentUserUtils { static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { if (doc.menuStack === undefined) { - await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) => + await this.setupLinkDocs(doc, linkDatabaseId); + await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing + const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) => Docs.Create.FontIconDocument({ icon, btnType: ButtonType.MenuButton, @@ -387,7 +389,7 @@ export class CurrentUserUtils { _removeDropProperties: new List(["dropAction", "_stayInCollection"]), _width: 60, _height: 60, - watchedDocuments, + badgeValue, onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) }) ); @@ -900,10 +902,7 @@ export class CurrentUserUtils { } // Sharing sidebar is where shared documents are contained - static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - if (doc.myPublishedDocs === undefined) { - doc.myPublishedDocs = new List(); - } + static async setupLinkDocs(doc: Doc, linkDatabaseId: string) { if (doc.myLinkDatabase === undefined) { let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); if (!linkDocs) { @@ -915,28 +914,41 @@ export class CurrentUserUtils { } doc.myLinkDatabase = new PrefetchProxy(linkDocs); } - // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document - if (doc.mySharedDocs === undefined) { - let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); - if (!sharedDocs) { - sharedDocs = Docs.Create.TreeDocument([], { - title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, - _chromeHidden: true, boxShadow: "0 0", - dontRegisterView: true, explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" - }, sharingDocumentId + "outer", sharingDocumentId); - (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment; - } - if (sharedDocs instanceof Doc) { - Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; - const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); - const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name }); - sharedDocs.childContextMenuFilters = new List([dashboardFilter!,]); - sharedDocs.childContextMenuScripts = new List([addToDashboards!,]); - sharedDocs.childContextMenuLabels = new List(["Add to Dashboards",]); - sharedDocs.childContextMenuIcons = new List(["user-plus",]); - } - doc.mySharedDocs = new PrefetchProxy(sharedDocs as Doc); + } + // A user's sharing document is where all documents that are shared to that user are placed. + // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field + // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents + static async setupSharedDocs(doc: Doc, sharingDocumentId: string) { + const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); + const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name }); + const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name}) + + const sharedDocOpts:DocumentOptions = { + title: "My Shared Docs", + userColor: "rgb(202, 202, 202)", + childContextMenuFilters: new List([dashboardFilter!,]), + childContextMenuScripts: new List([addToDashboards!,]), + childContextMenuLabels: new List(["Add to Dashboards",]), + childContextMenuIcons: new List(["user-plus",]), + treeViewChildDoubleClick: dblClkScript, + }; + const sharedRequiredDocOpts:DocumentOptions = { + "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, + childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, + // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar + _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, + explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" + }; + + const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer")); + if (!(sharedDocs instanceof Doc)) { + doc.mySharedDocs = new PrefetchProxy( + Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId)); + } else { + Object.entries(sharedRequiredDocOpts).forEach(pair => { + const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc); + targetDoc[pair[0]] = pair[1]; + }); } } @@ -1044,6 +1056,7 @@ export class CurrentUserUtils { doc.savedFilters = new List(); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; + doc.myPublishedDocs = doc.myPublishedDocs ?? new List(); doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); 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 diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 8824750a3..59dc5671b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -613,7 +613,7 @@ export class TreeView extends React.Component { return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!); } - onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null)); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx index cf86b5e07..df17d603f 100644 --- a/src/client/views/nodes/button/FontIconBadge.tsx +++ b/src/client/views/nodes/button/FontIconBadge.tsx @@ -6,31 +6,31 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils import { DragManager } from "../../../util/DragManager"; import "./FontIconBadge.scss"; -interface FontIconBadgeProps { - collection: Doc | undefined; +interface FontIconBadgeProps { + value: string | undefined; } @observer export class FontIconBadge extends React.Component { _notifsRef = React.createRef(); - onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - (e: PointerEvent) => { - const dragData = new DragManager.DocumentDragData([this.props.collection!]); - DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); - return true; - }, - returnFalse, emptyFunction, false); - } + // onPointerDown = (e: React.PointerEvent) => { + // setupMoveUpEvents(this, e, + // (e: PointerEvent) => { + // const dragData = new DragManager.DocumentDragData([this.props.collection!]); + // DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); + // return true; + // }, + // returnFalse, emptyFunction, false); + // } render() { - if (!(this.props.collection instanceof Doc)) return (null); - const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read + if (this.props.value === undefined) return (null); return
    -
    0 ? { "display": "initial" } : { "display": "none" }} - onPointerDown={this.onPointerDown} > - {length} +
    + {this.props.value}
    ; } diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index a1b9023f3..97e6eddfe 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent() {
    {this.icon === "pres-trail" ? trailsIcon : } {menuLabel} - +
    ); break; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f29b879b3..16a523b40 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -382,11 +382,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } - // creates links between terms in a document and documents which have a matching Id + // creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@' hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set) => { const editorView = this._editorView; if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) { - const autoLinkTerm = StrCast(target.title).replace(/^@/, ""); + const autoLinkTerm = StrCast(target.title).replace(/^@/, ""); const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm); var alink: Doc | undefined; flattened1.forEach((flat, i) => { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 6abc27b23..7b72787d1 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1436,6 +1436,7 @@ ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.cop ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); }); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); +ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); }); ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); }); diff --git a/src/fields/Types.ts b/src/fields/Types.ts index 7e2aa5681..bf40a0d7b 100644 --- a/src/fields/Types.ts +++ b/src/fields/Types.ts @@ -76,6 +76,10 @@ export function Cast(field: FieldResult, ctor: T, defaultVal return defaultVal === null ? undefined : defaultVal; } +export function DocCast(field: FieldResult, defaultVal?: Doc) { + return Cast(field, Doc, null) ?? defaultVal; +} + export function NumCast(field: FieldResult, defaultVal: number | null = 0) { return Cast(field, "number", defaultVal); } -- cgit v1.2.3-70-g09d2 From c0826fb904f5d2448635b5dae1fc4337fead939e Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Jun 2022 05:31:25 -0400 Subject: lots of changes to try to cleanup CurrentUserUtils so changes can be made without rebuilding the DB. --- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 1216 +++++++++----------- src/client/util/DocumentManager.ts | 3 +- src/client/util/Scripting.ts | 2 +- src/client/views/DocComponent.tsx | 2 +- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/MainView.tsx | 36 +- src/client/views/OverlayView.tsx | 2 +- src/client/views/PropertiesView.tsx | 5 +- .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/collections/CollectionMenu.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../collectionLinear/CollectionLinearView.tsx | 4 +- src/client/views/linking/LinkPopup.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 8 +- src/client/views/nodes/FilterBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 12 +- src/client/views/nodes/button/FontIconBox.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 8 +- src/fields/Doc.ts | 5 +- src/fields/util.ts | 3 + src/mobile/MobileInterface.tsx | 2 +- 22 files changed, 629 insertions(+), 711 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5f009573e..927996af2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -178,10 +178,15 @@ export class DocumentOptions { hidden?: boolean; _hidden?: boolean; pointerEvents?: string; // pointer events that the documentview should have - mediaState?: string; // status of media document: "pendingRecording", "recording", "paused", "playing" + mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing" + recording?: boolean; // whether WebCam is recording or not autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline. dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it. toolTip?: string; // tooltip to display on hover + contextMenuFilters?: List; + contextMenuScripts?: List; + contextMenuLabels?: List; + contextMenuIcons?: List; dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel) description?: string; // added for links layout?: string | Doc; // default layout string for a document @@ -238,7 +243,7 @@ export class DocumentOptions { isPushpin?: boolean; isGroup?: boolean; // whether a collection should use a grouping UI behavior _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document - + noteType?: string; // BACKGROUND GRID _backgroundGridShow?: boolean; @@ -286,6 +291,7 @@ export class DocumentOptions { clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script onDragStart?: ScriptField; //script to execute at start of drag operation -- e.g., when a "creator" button is dragged this script generates a different document to drop cloneFieldFilter?: List; // fields not to copy when the document is clonedclipboard?: Doc; + filterBoolean ?: string; useCors?: boolean; icon?: string; target?: Doc; // available for use in scripts as the primary target document diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b4e18a8bb..afb1442bf 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,16 +1,17 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; -import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, StrListCast } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; +import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types"; import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; -import { Utils } from "../../Utils"; +import { OmitKeys, Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; @@ -42,17 +43,17 @@ interface Button { toolTip?: string; icon?: string; btnType?: ButtonType; - click?: string; numBtnType?: NumButtonType; numBtnMin?: number; numBtnMax?: number; switchToggle?: boolean; - script?: string; width?: number; - list?: string[]; + btnList?: List; ignoreClick?: boolean; buttonText?: string; - hidden?: string; + scripts?: { script?: string; onClick?: string; } + funcs?: { [key:string]: string }; + subMenu?: Button[]; } export let resolvedPorts: { server: number, socket: number }; @@ -73,101 +74,134 @@ export class CurrentUserUtils { @observable public static headerBarHeight: number = 0; @observable public static searchPanelWidth: number = 0; - // sets up the default User Templates - slideView, headerView - static setupExperimentalTemplateButtons(doc: Doc) { - if (doc["template-experimental-buttons"] === undefined) { - const requiredTypeNameFields = [ - { - type: "slide", icon: "address-card", template: Docs.Create.MultirowDocument( - [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) - ], - { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } - ) - }, - { - type: "mobile", icon: "mobile", template: this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, }, - [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }), - this.mobileTextContainer({}, - [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")]) - ] - ) - }, - ]; - const requiredTypes = requiredTypeNameFields.map(({ type, icon, template }) => { - if (doc[`template-button-${type}`] === undefined) { - template.isTemplateDoc = makeTemplate(template); - doc[`template-button-${type}`] = CurrentUserUtils.createToolButton({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(template) as any as Doc, - title: type, - icon, - }); + static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) { + scripts && Object.keys(scripts).map(key => { + if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) { + doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any" }, {"_readOnly_": true}); + } + }); + functions && Object.keys(functions).map(key => { + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) { + doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true}); + } + }); + return doc; + } + static AssignOpts(doc:Doc|undefined, reqdOpts:DocumentOptions, items?:Doc[]) { + if (doc) { + const compareValues = (val1:any, val2:any) => { + if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) { + return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v)); + } + return val1 === val2; + } + Object.entries(reqdOpts).forEach(pair => { + const targetDoc = pair[0].startsWith("_") ? doc : Doc.GetProto(doc as Doc); + if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/,"")) || + !compareValues(pair[1], targetDoc[pair[0]])) { + targetDoc[pair[0]] = pair[1]; } - return doc[`template-button-${type}`] as Doc; }); - - doc["template-experimental-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, { - title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true, - hidden: ComputedField.MakeFunction("IsNoviceMode()") as any, - _stayInCollection: true, _hideContextMenu: true, _forceActive: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true - })); + items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item)); } - return doc["template-experimental-buttons"] as Doc; + return doc; } - // setup the different note type skins - static setupNoteTemplates(doc: Doc) { - if (doc["template-notes"] === undefined) { - const requiredTypeNameFields = [ - { type: "Note", backgroundColor: "yellow", icon: "sticky-note" }, - { type: "Idea", backgroundColor: "pink", icon: "lightbulb" }, - { type: "Topic", backgroundColor: "lightblue", icon: "book-open" }]; - const requiredTypes = requiredTypeNameFields.map(({ type, backgroundColor, icon }) => - doc[`template-note-${type}`] as Doc ?? - (() => { - const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor, system: true, icon }); - noteView.isTemplateDoc = makeTemplate(noteView, true, type); - return doc[`template-note-${type}`] = new PrefetchProxy(noteView); - })()); - - doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument(requiredTypes, { title: "Note Layouts", _height: 75, system: true })); - } + // initializes experimental advanced template views - slideView, headerView + static setupExperimentalTemplateButtons(doc: Doc, field="template-experimental-buttons") { + const tempDocs = DocCast(doc[field]); + const requiredTypeNameFields:{opts:DocumentOptions, template:() => Doc}[] = [ + { + opts:{type: "slide", icon: "address-card"}, template: () => Docs.Create.MultirowDocument( + [ + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), + Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) + ], + { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } + ) + }, + { + opts:{type: "mobile", icon: "mobile"}, template: () => this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, }, + [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }), + this.mobileTextContainer({}, + [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")]) + ] + ) + }, + ]; + const requiredTypes = requiredTypeNameFields.map(({ opts, template }) => { + const docType = DocListCast(tempDocs?.data)?.find(doc => doc.title === opts.type); + const reqdOpts = { + dragFactory: template(), + title: opts.type, + icon: opts.icon + }; + const reqdScripts = {onDragStart: 'copyDragFactory(this.dragFactory)'}; + const makeTemp = (doc:Doc) => { + doc.isTemplateDoc = makeTemplate(doc); + return doc; + } + return this.AssignScripts(!docType ? makeTemp(CurrentUserUtils.createToolButton(reqdOpts)) : this.AssignOpts(docType, reqdOpts)!, reqdScripts)!; + }); + + const reqdOpts = { + title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true, + _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, + _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, + }; + const reqdScripts = {dropConverter : "convertToButtons(dragData)"}; + const reqdFuncs = {hidden: "IsNoviceMode()"} + return this.AssignScripts(!tempDocs ? + (doc[field] = Docs.Create.MasonryDocument(requiredTypes, reqdOpts)) : + this.AssignOpts(tempDocs, reqdOpts, requiredTypes)!, + reqdScripts, reqdFuncs); + } + + /// Initializes templates that can be applied to notes + static setupNoteTemplates(doc: Doc, field="template-notes") { + const tempNotes = DocCast(doc[field]); + const reqdTempOpts:DocumentOptions[] = [ + { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"}, + { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" }, + { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }]; + const reqdNoteList = reqdTempOpts.map(opts => { + const reqdOpts = {...opts, title: "text", system: true}; + const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined; + const makeTemp = (doc:Doc, noteType?:string) => { + doc.isTemplateDoc = makeTemplate(doc, true, noteType??"Note"); + return doc; + } + return this.AssignOpts(noteType, reqdOpts) ?? makeTemp(Docs.Create.TextDocument("",reqdOpts), opts.noteType); + }); - return doc["template-notes"] as Doc; + const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true }; + return this.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts)); } - // creates Note templates, and initial "user" templates + /// Initializes collection of templates for notes and click functions static setupDocTemplates(doc: Doc) { - if (doc.templateDocs === undefined) { - doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([ - CurrentUserUtils.setupNoteTemplates(doc), - CurrentUserUtils.setupExperimentalTemplateButtons(doc), - CurrentUserUtils.setupClickEditorTemplates(doc) - ], { - title: "template layouts", _xMargin: 0, system: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) - })); - } + const templates = [ + CurrentUserUtils.setupNoteTemplates(doc), + CurrentUserUtils.setupClickEditorTemplates(doc) + ]; + const reqdOpts = { title: "template layouts", _xMargin: 0, system: true, }; + const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; + return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts); } // setup templates for different document types when they are iconified from Document Decorations - static setupDefaultIconTemplates(doc: Doc) { - const templateIconsDoc = Cast(doc["template-icons"], Doc, null); + static setupDefaultIconTemplates(doc: Doc, field="template-icons") { + const templateIconsDoc = DocCast(doc[field]); const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => { const iconFieldName = "icon" + (type ? "_" + type : ""); if (!templateIconsDoc?.[iconFieldName]) { const template = MakeTemplate(iconTemplate(), true, iconFieldName, templateField); if (templateIconsDoc) { - templateIconsDoc[iconFieldName] = new PrefetchProxy(template); - Doc.AddDocToList(templateIconsDoc, "data", template); - } else { - return template; + templateIconsDoc[iconFieldName] = template; } + return template; } }; const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); @@ -182,46 +216,57 @@ export class CurrentUserUtils { const fontBox = () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() }); - if (!templateIconsDoc) { - const newIconsList = [ - makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), - makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), - makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), - makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })), - makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), - makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), - makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.BUTTON, "data", fontBox), - //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now - makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })), - // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) - ].filter(d => d).map(d => d!); - - doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true }); - } + const iconTemplates = [ + makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), + makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), + makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), + makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })), + makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), + makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), + makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), + makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), + makeIconTemplate(DocumentType.BUTTON, "data", fontBox), + //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now + makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })), + // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) + ].filter(d => d).map(d => d!); + + const reqdOpts = { title: "icon templates", _height: 75, system: true }; + this.AssignOpts(templateIconsDoc, reqdOpts, iconTemplates) ?? (doc[field] = Docs.Create.TreeDocument(iconTemplates, reqdOpts)); } + /// initalizes the set of default versions of most document types static creatorBtnDescriptors(doc: Doc): { - title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean, - click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc + title: string, toolTip: string, icon: string, ignoreClick?: boolean, dragFactory?: Doc, + backgroundColor?: string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string}, }[] { const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List(["system"]) }); - if (doc.emptyPresentation === undefined) { - doc.emptyPresentation = Docs.Create.PresDocument({ ...standardOps(), title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); - } - if (doc.emptyCollection === undefined) { - doc.emptyCollection = Docs.Create.FreeformDocument([], { ...standardOps(), title: "freeform", _width: 150, _height: 100 }); - } - if (doc.emptyPane === undefined) { - doc.emptyPane = Docs.Create.FreeformDocument([], { ...standardOps(), title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }); - } - if (doc.emptySlide === undefined) { - doc.emptySlide = Docs.Create.TreeDocument([], { - ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined, - allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" - }); - } + const emptyThings:{key:string, // the field name where the empty thing will be stored + opts:DocumentOptions, // the document options that are required for the empty thing + funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing + creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist + }[] = [ + {key: "emptyPresentation", creator: Docs.Create.PresDocument, opts: { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }}, + {key: "emptyCollection", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "freeform", _width: 150, _height: 100 }}, + {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts : { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }}, + {key: "emptySlide", creator: (opts) => Docs.Create.TreeDocument([], opts), funcs: {title: 'self.text?.Text'}, opts: { + _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, + "dragFactory-count": undefined, allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" + }}, + {key: "emptyComparison", creator: Docs.Create.ComparisonDocument, opts: { title: "Comparer", _width: 300, _height: 300 }}, + {key: "emptyScript", creator: (opts) => Docs.Create.ScriptingDocument(undefined, opts), opts: { title: "script", _width: 200, _height: 250, }}, + {key: "emptyScreenshot", creator: Docs.Create.ScreenshotDocument, opts: { title: "empty screenshot", _width: 400, _height: 200 }}, + {key: "emptyWebCam", creator: (opts) => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, + {key: "emptyAudio", creator: (opts) => Docs.Create.AudioDocument(nullAudio, opts), opts: { title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }}, + {key: "emptyNote", creator: (opts) => Docs.Create.TextDocument("", opts), opts: { title: "text note", _width: 200, _autoHeight: true }}, + {key: "emptyButton", creator: Docs.Create.ButtonDocument, opts: { title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }}, + {key: "emptyWebpage", creator: (opts) => Docs.Create.WebDocument("http://www.bing.com/", opts), opts: { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }}, + {key: "emptyMap", creator: (opts) => Docs.Create.MapDocument([], opts), opts: { title: "map", _showSidebar: true, _width: 800, _height: 600, }} + ]; + + emptyThings.forEach(thing => + this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), thing.funcs)) + if (doc.emptyHeader === undefined) { const json = { doc: { @@ -242,196 +287,124 @@ export class CurrentUserUtils { selection: { type: "text", anchor: 1, head: 1 }, storedMarks: [] }; - const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { - ...standardOps(), title: "text", _height: 70, - _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, - }, "header"); const headerBtnHgt = 10; - headerTemplate[DataSym].layout = + const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { + ...standardOps(), title: "text", _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, + layout: "" + ` ` + " " + ` Metadata` + - ""; + "" + }, "header"); // "
    " + // " " + // " " + // "
    "; - (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); + Doc.GetProto(headerTemplate).isTemplateDoc = makeTemplate(Doc.GetProto(headerTemplate), true, "headerView"); doc.emptyHeader = headerTemplate; } - if (doc.emptyComparison === undefined) { - doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "Comparer", _width: 300, _height: 300 }); - } - if (doc.emptyScript === undefined) { - doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { ...standardOps(), title: "script", _width: 200, _height: 250, }); - } - if (doc.emptyScreenshot === undefined) { - doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 }); - } - if (doc.emptyWall === undefined) { - doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List(["system"]) }); - (doc.emptyWall as Doc).recording = true; - } - if (doc.emptyAudio === undefined) { - doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }); - } - if (doc.emptyNote === undefined) { - doc.emptyNote = Docs.Create.TextDocument("", { ...standardOps(), title: "text note", _width: 200, _autoHeight: true }); - } - if (doc.emptyButton === undefined) { - doc.emptyButton = Docs.Create.ButtonDocument({ ...standardOps(), title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }); - } - if (doc.emptyWebpage === undefined) { - doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { ...standardOps(), title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }); - } - if (doc.emptyMap === undefined) { - doc.emptyMap = Docs.Create.MapDocument([], { ...standardOps(), title: "map", _showSidebar: true, _width: 800, _height: 600, }); - } - if (doc.activeMobileMenu === undefined) { - this.setupActiveMobileMenu(doc); - } + return [ - { toolTip: "Tap to create a note in a new pane, drag for a note", title: "Note", icon: "sticky-note", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyNote as Doc, noviceMode: true, clickFactory: doc.emptyNote as Doc, }, - { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, }, - { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true }, - { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc }, - { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true }, - { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc }, - { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, - { toolTip: "Tap to create an audio recorder in a overlay pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openInOverlay(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, - { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, - // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, - { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc }, - { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, - { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc }, - { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, - { toolTip: "Tap to create a map in the new pane, drag for a map", title: "Map", icon: "map-marker-alt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyMap as Doc, noviceMode: true } - ]; - + { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, clickFactory: doc.emptyPane as Doc, }, + { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, scripts: {onClick: 'openInOverlay(copyDragFactory(this.dragFactory))',onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create a custom note", title: "Custom", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc, scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create a progressive slide",title: "Slide", icon: "file", dragFactory: doc.emptySlide as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreenshot as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs:{ hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a mobile view", title: "Phone", icon: "mobile", dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)', onDragStart: 'this.dragFactory'}, funcs: {hidden: 'IsNoviceMode()'} }, + { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } }, + // { toolTip: "Tap or drag to create a presentation", title: "Trails", icon: "pres-trail", dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} }, + ]; } - // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools - static setupCreatorButtons(doc: Doc) { - let alreadyCreatedButtons: string[] = []; - const dragCreatorSet = Cast(doc.myItemCreators, Doc, null); - if (dragCreatorSet) { - alreadyCreatedButtons = DocListCast(dragCreatorSet.data).map(d => StrCast(d.title)); - } - const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)); - const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({ - _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, - icon, - title, - toolTip, - btnType: ButtonType.ToolButton, - ignoreClick, - _dropAction: "alias", - onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, - onClick: click ? ScriptField.MakeScript(click) : undefined, - backgroundColor: backgroundColor ? backgroundColor : Colors.DARK_GRAY, - color: Colors.WHITE, - _hideContextMenu: true, - _removeDropProperties: new List(["_stayInCollection"]), - _stayInCollection: true, - dragFactory, - clickFactory, - hidden: !noviceMode ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined, - system: true, - })); + /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools + static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc { + const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => { + const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; + const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit, + _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias", + btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true, + _removeDropProperties: new List(["_stayInCollection"]), + }; + return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs); + }); - if (dragCreatorSet === undefined) { - doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { - title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true - })); - } else { - creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb)); - } - return doc.myItemCreators as Doc; + const reqdOpts = { + title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true, + _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, + }; + const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; + return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts); } - static menuBtnDescriptions(doc: Doc) { - const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)") + /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents + static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] { + const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())"; return [ - { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, - { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' }, - { title: "Files", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' }, - { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, - { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, - { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, - { title: "Shared with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue}, - { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, - { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" }, + { title: "Dashboards", target: CurrentUserUtils.MyDashboards, icon: "desktop", scripts:{onClick: 'selectMainMenu(self)'} }, + { title: "Search", target: CurrentUserUtils.MySearcher, icon: "search", scripts:{onClick: 'selectMainMenu(self)'} }, + { title: "Files", target: CurrentUserUtils.MyFilesystem, icon: "folder-open", scripts:{onClick: 'selectMainMenu(self)'} }, + { title: "Tools", target: CurrentUserUtils.MyTools, icon: "wrench", scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} }, + { title: "Imports", target: CurrentUserUtils.MyImports, icon: "upload", scripts:{onClick: 'selectMainMenu(self)'} }, + { title: "Recently Closed", target: CurrentUserUtils.MyRecentlyClosed, icon: "archive", scripts:{onClick: 'selectMainMenu(self)'} }, + { title: "Shared with me", target: CurrentUserUtils.MySharedDocs, icon: "users", scripts:{onClick: 'selectMainMenu(self)'}, funcs:{badgeValue:badgeValue}}, + { title: "Trails", target: CurrentUserUtils.MyTrails, icon: "pres-trail", scripts:{onClick: 'selectMainMenu(self)'} }, + { title: "User Doc", target: CurrentUserUtils.MyUserDocView, icon: "address-card",scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} }, ]; } - static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - if (doc.menuStack === undefined) { - await this.setupLinkDocs(doc, linkDatabaseId); - await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) => - Docs.Create.FontIconDocument({ - icon, - btnType: ButtonType.MenuButton, - _stayInCollection: true, - _hideContextMenu: true, - _chromeHidden: true, - system: true, - dontUndo: true, - title, - target, - dontRegisterView: true, - hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined, - _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - _width: 60, - _height: 60, - badgeValue, - onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) - }) - ); - - doc.searchBtn = menuBtns.find(btn => btn.title === "Search"); - - doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { - title: "menuItemPanel", - childDropAction: "alias", - _chromeHidden: true, - backgroundColor: Colors.DARK_GRAY, - boxShadow: "rgba(0,0,0,0)", - dontRegisterView: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - ignoreClick: true, - _gridGap: 0, - _yMargin: 0, - _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true - })); - } - return doc.menuStack as Doc; + /// setup the left sidebar container and panels that can be displayed within it + static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") { + this.AssignOpts(DocCast(doc[field]), {}) ?? (doc[field] = ((doc:Doc) => {doc.system = true; return doc;})(new Doc())); + CurrentUserUtils.setupSearcher(doc, "mySearcher"); + CurrentUserUtils.setupToolsBtnPanel(doc, "myTools"); + CurrentUserUtils.setupImportSidebar(doc, "myImports"); + CurrentUserUtils.setupDashboards(doc, "myDashboards"); + CurrentUserUtils.setupTrails(doc, "myTrails"); + CurrentUserUtils.setupFilesystem(doc, "myFilesystem"); + CurrentUserUtils.setupRecentlyClosedDocs(doc, "myRecentlyClosed"); + CurrentUserUtils.setupUserDocView(doc, "myUserDocView"); } + /// Initializes the left sidebar menu buttons and the panels they open up + static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") { + this.setupLeftSidebarPanel(doc); // the tools/panels opened up by the menu buttons + const myLeftSidebarMenu = DocCast(doc[field]); + const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => { + const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; + const reqdBtnOpts:DocumentOptions = { + title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true, dontRegisterView: true, + _width: 60, _height: 60, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + }; + return this.AssignScripts(this.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs); + }); - // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu - static setupActiveMobileMenu(doc: Doc) { - if (doc.activeMobileMenu === undefined) { - doc.activeMobileMenu = this.setupMobileMenu(); - } - return doc.activeMobileMenu as Doc; + const reqdStackOpts:DocumentOptions ={ + title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, + _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true + }; + const reqdScripts = { dropConverter: "convertToButtons(dragData)" } + return this.AssignScripts(this.AssignOpts(myLeftSidebarMenu, reqdStackOpts, menuBtns) ?? (doc[field] = Docs.Create.StackingDocument(menuBtns, reqdStackOpts)), reqdScripts); } - // Sets up mobileMenu stacking document - static setupMobileMenu() { - const menu = new PrefetchProxy(Docs.Create.StackingDocument(this.setupMobileButtons(), { - _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true, - })); - return menu; + // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu + static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") { + const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,}; + this.AssignOpts(DocCast(doc[field]), reqdOpts, this.setupMobileButtons()) ?? (doc[field] = Docs.Create.StackingDocument(this.setupMobileButtons(), reqdOpts)); } - // SEts up mobile buttons for inside mobile menu + // Sets up mobile buttons for inside mobile menu static setupMobileButtons(doc?: Doc, buttons?: string[]) { + return []; const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " }, { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." }, @@ -533,375 +506,329 @@ export class CurrentUserUtils { }); } - static setupLibrary(userDoc: Doc) { - return CurrentUserUtils.setupDashboards(userDoc); - } - - // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. - // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) - static setupToolsBtnPanel(doc: Doc) { - // setup a masonry view of all he creators - const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc); - const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc); - - doc.myCreators = doc.myCreators ?? new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { - title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true, - _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true, - })); - if (!DocListCast(doc.myCreators).includes(creatorBtns) || !DocListCast(doc.myCreators).includes(templateBtns)) Doc.GetProto(doc.myCreators as Doc).data = new List([creatorBtns, templateBtns]); - - doc.myColorPicker = doc.myColorPicker ?? new PrefetchProxy(Docs.Create.ColorDocument({ - title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true - })); - - doc.myTools = doc.myTools ?? new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], { - title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, - system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", - })) as any as Doc; - if (!DocListCast(doc.myTools).includes(doc.myCreators as Doc)) Doc.GetProto(doc.myTools as Doc).data = new List([doc.myCreators as Doc]); - } - - static async setupDashboards(doc: Doc) { - // setup dashboards library item - await doc.myDashboards; - if (doc.myDashboards === undefined) { - 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, freezeChildren: "remove|add", - treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: 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 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)'); - const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()'); - // (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, shareDashboard!, removeDashboard!]); - // (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]); - // (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "user-friends", "times"]); - (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); - (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); - (doc.myDashboards as any as Doc).childContextMenuIcons = new List(["plus", "chalkboard", "tv", "camera", "users", "times"]); - (doc.myDashboards as any as Doc).childContextMenuFilters = new List([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]); - } - return doc.myDashboards as any as Doc; + /// Search option on the left side button panel + static setupSearcher(doc: Doc, field:string) { + const reqdOpts:DocumentOptions = { + dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", + _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true, + }; + this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts)); } - static setupPresentations(doc: Doc) { - if (doc.myTrails === undefined) { - const newTrail = ScriptField.MakeScript(`createNewPresentation()`); - const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true }); - doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Trails", _showTitle: "title", _height: 100, - treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, - explainer: "All of the trails that you have created will appear here." - })); - (doc.myTrails as any as Doc).contextMenuScripts = new List([newTrail!]); - (doc.myTrails as any as Doc).contextMenuLabels = new List(["Create New Trail"]); - (doc.myTrails as any as Doc).childContextMenuIcons = new List(["plus"]); - } - return doc.myTrails as any as Doc; + /// Initializes the panel of draggable tools that is opened from the left sidebar. + static setupToolsBtnPanel(doc: Doc, field:string) { + const myTools = DocCast(doc[field]); + const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined); + //const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc); + const reqdToolOps:DocumentOptions = { + title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0", + _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + }; + this.AssignOpts(myTools, reqdToolOps, [creatorBtns, /*templateBtns*/]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, /*templateBtns*/], reqdToolOps)); } - static async setupFilesystem(doc: Doc) { - await doc.myFilesystem; - if (doc.myFilesystem === undefined) { - doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); - // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); - const newFolder = ScriptField.MakeFunction(`makeTopLevelFolder()`, { scriptContext: "any" })!; - const newFolderButton: Doc = Docs.Create.FontIconDocument({ - onClick: newFolder, _forceActive: true, toolTip: "Create new folder", - _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30, - buttonText: "New folder", icon: "folder-plus", system: true - }); - doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], { - title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, - treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, - explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." - })); - (doc.myFilesystem as any as Doc).contextMenuScripts = new List([newFolder]); - (doc.myFilesystem as any as Doc).contextMenuLabels = new List(["Create new folder"]); - (doc.myFilesystem as any as Doc).childContextMenuIcons = new List(["plus"]); + /// initializes the left sidebar dashboard pane + static setupDashboards(doc: Doc, field:string) { + var myDashboards = DocCast(doc[field]); + + const newDashboard = `createNewDashboard(Doc.UserDoc())`; + const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, + title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true }; + const reqdBtnScript = {onClick: newDashboard,} + const newDashboardButton = this.AssignScripts(this.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); + + const reqdOpts:DocumentOptions = { + title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true, + targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true, treeViewTruncateTitleWidth: 150, ignoreClick: true, + buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias", + _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, + contextMenuLabels: new List(["Create New Dashboard"]), + contextMenuIcons: new List(["plus"]), + childContextMenuLabels: new List(["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]),// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters + childContextMenuIcons: new List(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters + 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." + }; + myDashboards = this.AssignOpts(myDashboards, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); + const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; + const contextMenuScripts = [newDashboard]; + const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters + const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts + if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { + myDashboards.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); + } + if (Cast(myDashboards.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) { + myDashboards.childContextMenuScripts = new List(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); + } + if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) { + myDashboards.childContextMenuFilters = new List(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!)); } - return doc.myFilesystem as any as Doc; } - static setupRecentlyClosedDocs(doc: Doc) { - if (doc.myRecentlyClosedDocs === undefined) { - const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); - const clearDocsButton: Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true }); - doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true, - treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, - explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list." - - })); - (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List([clearAll!]); - (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Empty recently closed"]); - (doc.myRecentlyClosedDocs as any as Doc).contextMenuIcons = new List(["trash"]); - + /// initializes the left sidebar Trails pane + static setupTrails(doc: Doc, field:string) { + var myTrails = DocCast(doc[field]); + const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, + title: "New trail", toolTip: "Create new trail", btnType: ButtonType.ClickButton, buttonText: "New trail", icon: "plus", system: true }; + const reqdBtnScript = {onClick: `createNewPresentation()`}; + const newTrailButton = this.AssignScripts(this.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); + + const reqdOpts:DocumentOptions = { + title: "My Trails", _showTitle: "title", _height: 100, + treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton, + contextMenuIcons: new List(["plus"]), + contextMenuLabels: new List(["Create New Trail"]), + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, + explainer: "All of the trails that you have created will appear here." + }; + myTrails = this.AssignOpts(myTrails, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([],reqdOpts )); + const contextMenuScripts = [reqdBtnScript.onClick]; + if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { + myTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } } - static setupFilterDocs(doc: Doc) { - // setup Filter item - if (doc.currentFilter === undefined) { - doc.currentFilter = Docs.Create.FilterDocument({ - title: "Unnamed Filter", _height: 150, - treeViewHideTitle: true, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, childDropAction: "none", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true - }); - const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); - (doc.currentFilter as Doc).contextMenuScripts = new List([clearAll!]); - (doc.currentFilter as Doc).contextMenuLabels = new List(["Clear All"]); - (doc.currentFilter as Doc).filterBoolean = "AND"; + /// initializes the left sidebar File system pane + static setupFilesystem(doc: Doc, field:string) { + var myFilesystem = DocCast(doc[field]); + const reqdOrphansOpts:DocumentOptions = { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }; + this.AssignOpts(DocCast(doc.myFileOrphans), reqdOrphansOpts) ?? (doc.myFileOrphans = Docs.Create.TreeDocument([], reqdOrphansOpts)); + + const newFolder = `makeTopLevelFolder()`; + const newFolderOpts: DocumentOptions = { + _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30, + title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true + }; + const newFolderScript = { onClick: newFolder}; + const newFolderButton = this.AssignScripts(this.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript); + + const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, + title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true, + isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, + treeViewTruncateTitleWidth: 150, ignoreClick: true, childDropAction: "alias", + childContextMenuLabels: new List(["Create new folder"]), + childContextMenuIcons: new List(["plus"]), + explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." + }; + myFilesystem = this.AssignOpts(myFilesystem, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([DocCast(doc.myFileOrphans)], reqdOpts)); + const childContextMenuScripts = [newFolder]; + if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) { + myFilesystem.childContextMenuScripts = new List(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } } - static setupUserDoc(doc: Doc) { - if (doc.myUserDoc === undefined) { - doc.treeViewOpen = true; - doc.treeViewExpandedView = "fields"; - doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { - treeViewHideTitle: true, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true - })) as any as Doc; + /// initializes the panel displaying docs that have been recently closed + static setupRecentlyClosedDocs(doc: Doc, field:string) { + const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, + title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true, + treeViewTruncateTitleWidth: 150, ignoreClick: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", + contextMenuLabels: new List(["Empty recently closed"]), + contextMenuIcons:new List(["trash"]), + explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list." + }; + const recentlyClosed = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); + + const clearAll = (target:string) => `getProto(${target}).data = new List([])`; + const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, + title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", system: true, + toolTip: "Empty recently closed",}; + const clearBtnsScripts = {onClick: clearAll("self.target")} + const clearDocsButton = this.AssignScripts( + this.AssignOpts(DocCast(recentlyClosed?.clearDocsBtn), clearBtnsOpts) ?? (recentlyClosed.clearDocsBtn = Docs.Create.FontIconDocument(clearBtnsOpts)), + clearBtnsScripts); + + if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton; + + if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) { + recentlyClosed.contextMenuScripts = new List([ScriptField.MakeScript(clearAll("self"))!]) } } - static setupSidebarContainer(doc: Doc) { - if (doc.sidebar === undefined) { - const sidebarContainer = new Doc(); - sidebarContainer.system = true; - doc.sidebar = new PrefetchProxy(sidebarContainer); - } - return doc.sidebar as Doc; + /// creates a new, empty filter doc + static createFilterDoc() { + const clearAll = `getProto(self).data = new List([])`; + const reqdOpts:DocumentOptions = { + _lockedPosition: true, _autoHeight: true, _fitWidth: true, _height: 150, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, + title: "Unnamed Filter", filterBoolean: "AND", boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true, + childDropAction: "none", treeViewHideTitle: true, treeViewTruncateTitleWidth: 150, + childContextMenuLabels: new List(["Clear All"]), + childContextMenuScripts: new List([ScriptField.MakeFunction(clearAll)!]), + }; + return Docs.Create.FilterDocument(reqdOpts); } - // setup the list of sidebar mode buttons which determine what is displayed in the sidebar - static async setupSidebarButtons(doc: Doc) { - CurrentUserUtils.setupSidebarContainer(doc); - CurrentUserUtils.setupToolsBtnPanel(doc); - CurrentUserUtils.setupImportSidebar(doc); - CurrentUserUtils.setupDashboards(doc); - CurrentUserUtils.setupPresentations(doc); - CurrentUserUtils.setupFilesystem(doc); - CurrentUserUtils.setupRecentlyClosedDocs(doc); - CurrentUserUtils.setupUserDoc(doc); + /// initializes the left sidebar panel view of the UserDoc + static setupUserDocView(doc: Doc, field:string) { + const reqdOpts:DocumentOptions = { + _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view", + boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true, + treeViewHideTitle: true, treeViewTruncateTitleWidth: 150 + }; + if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }) + this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts)); } - static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { - ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true, + static linearButtonList = (title: string, opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, { + title, ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _lockedPosition: true, system: true, flexDirection: "row" - })) as any as Doc + }) - static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ + static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({ btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["_dropAction", "_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts, - })) as any as Doc - - /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window - static setupDockedButtons(doc: Doc) { - if (doc.dockedBtns === undefined) { - const dockBtn = (opts: DocumentOptions) => CurrentUserUtils.createToolButton({ _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...opts }); - const btnDescs = [ - { title: "undo", opts: () => ({ icon: "undo-alt", onClick: ScriptField.MakeScript("undo()"), toolTip: "Click to undo" }) }, - { title: "redo", opts: () => ({ icon: "redo-alt", onClick: ScriptField.MakeScript("redo()"), toolTip: "Click to redo" }) } - ]; - doc.dockedBtns = CurrentUserUtils.linearButtonList({ - title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, - childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true - }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() })))); + }) + + /// initializes the required buttons in the expanding button menu at the bottom of the Dash window + static setupDockedButtons(doc: Doc, field="myDockedBtns") { + const dockedBtns = DocCast(doc[field]); + const dockBtn = (title:string, onClick: string, opts: DocumentOptions) => { + const btn = this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === title), opts) ?? + CurrentUserUtils.createToolButton({title, ...opts}); + this.AssignScripts(btn, {onClick}) + return btn; } + const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet + { title: "undo", click: "undo()", opts: { icon: "undo-alt", toolTip: "Click to undo" }}, + { title: "redo", click:"redo()", opts: { icon: "redo-alt", toolTip: "Click to redo" }} + ]; + const btns = btnDescs.map(desc => dockBtn(desc.title, desc.click, {_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts})); + const dockBtnsReqdOpts = { + title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, + childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true + }; + reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); + reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); + return this.AssignOpts(dockedBtns, dockBtnsReqdOpts, btns) ?? (doc[field] = CurrentUserUtils.linearButtonList("dockedBtns", dockBtnsReqdOpts, btns)); } - static textTools(doc: Doc) { - const tools: Button[] = - [ - { - title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, - list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", - "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], - script: 'setFont(value, _readOnly_)' - }, - { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' }, - { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' }, - { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' }, - { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' }, - { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' }, - { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' }, - - // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'}, - // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'}, - // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'}, - { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' }, - { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' }, - { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' }, - { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' }, - ]; - return tools; + static textTools():Button[] { + return [ + { + title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]), + scripts :{script : 'setFont(value, _readOnly_)'} + }, + { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'} }, + { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts:{script: '{ return setFontColor(value, _readOnly_); }'}}, + { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, + { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline",scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} }, + { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} }, + { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} }, + + // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}}, + // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}}, + // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}}, + { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }}, + { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} }, + { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, + { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}}, + ]; } - static inkTools(doc: Doc) { - const tools: Button[] = [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", click: 'setActiveInkTool("pen", _readOnly_)' }, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' }, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' }, - // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' }, + static inkTools():Button[] { + return [ + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }}, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }}, + // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} }, // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' }, - { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" }, - { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' }, - { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }}, + { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} }, + { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} }, + { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, ]; - return tools; } - static schemaTools(doc: Doc) { - const tools: Button[] = - [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview(_readOnly_)', }]; - return tools; + static schemaTools():Button[] { + return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{onClick:'toggleSchemaPreview(_readOnly_)'}, }]; } - static webTools(doc: Doc) { - const tools: Button[] = - [ - { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' }, - { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' }, - //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' }, - { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' }, - ]; - - return tools; + static webTools() { + return [ + { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' }, + { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' }, + //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' }, + { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' }, + ]; } - static contextMenuTools(doc: Doc) { + static contextMenuTools():Button[] { return [ { title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, - list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, - CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, - CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, - CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, - CollectionViewType.Grid], - script: 'setView(value, _readOnly_)', + btnList: new List([CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, + CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, + CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, + CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, + CollectionViewType.Grid]), + scripts: {script: 'setView(value, _readOnly_)'}, }, // Always show - { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame(_readOnly_)', icon: "chevron-left", hidden: 'IsNoviceMode()' }, - { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame(_readOnly_)', icon: "chevron-right", hidden: 'IsNoviceMode()' }, - { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected - { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', }, - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform + { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left", scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} }, + { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: 'selectedDocumentType()' }}, // Only when a document is selected + { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: 'selectedDocumentType()'} }, + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: 'selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected - { title: "Text", type: "textTools", subMenu: CurrentUserUtils.textTools(doc), expanded: 'selectedDocumentType("rtf")' }, // Always available - { title: "Ink", type: "inkTools", subMenu: CurrentUserUtils.inkTools(doc), expanded: 'selectedDocumentType("ink")' }, // Always available - { title: "Web", type: "webTools", subMenu: CurrentUserUtils.webTools(doc), hidden: 'selectedDocumentType("web")' }, // Only when Web is selected - { title: "Schema", type: "schemaTools", subMenu: CurrentUserUtils.schemaTools(doc), hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected + { title: "Text", icon: "text", subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available + { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available + { title: "Web", icon: "web", subMenu: CurrentUserUtils.webTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.WEB}")`, hidden: `!selectedDocumentType("${DocumentType.WEB}")`} }, // Only when Web is selected + { title: "Schema", icon: "schema", subMenu: CurrentUserUtils.schemaTools(), funcs: { linearViewIsExpanded: `selectedDocumentType(undefined, "${CollectionViewType.Schema}")`, hidden: `!selectedDocumentType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected ]; } - // Sets up the default context menu buttons - static setupContextMenuButtons(doc: Doc) { - const btnFunc = (params: Button) => Docs.Create.FontIconDocument({ - title: params.title, icon: params.icon, toolTip: params.toolTip, color: Colors.WHITE, system: true, dontUndo: true, ignoreClick: params.ignoreClick, + /// initializes a context menu button for the top bar context menu + static setupContextMenuButton(params:Button, btnDoc?:Doc) { + const reqdOpts:DocumentOptions = { + title: params.title, btnType: params.btnType, icon: params.icon, toolTip: params.toolTip, ignoreClick: params.ignoreClick, + numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,_nativeHeight: 30, btnList: params.btnList, + backgroundColor: params.scripts?.onClick ? undefined: "transparent", /// a bit hacky. if an onClick is specified, then we assume we assume a toggle use onClick to get the backgroundColor (see below). Otherwise, assume a transparent background _nativeWidth: params.width ? params.width : 30, - _nativeHeight: 30, _width: params.width ? params.width : 30, _height: 30, - btnType: params.btnType, - numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax, - btnList: new List(params.list), + color: Colors.WHITE, system: true, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - script: params.script ? ScriptField.MakeScript(params.script, { value: "any" }) : undefined, - backgroundColor: params.click ? ComputedField.MakeFunction(params.click) as any : "transparent", - onClick: params.click ? ScriptField.MakeScript(params.click, { scriptContext: "any" }, { _readOnly_: false }) : undefined, - hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, - }); - if (doc.contextMenuBtns === undefined) { - doc.contextMenuBtns = CurrentUserUtils.linearButtonList( - { title: "menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }, - CurrentUserUtils.contextMenuTools(doc).map(params => - !params.subMenu ? - btnFunc(params) : - CurrentUserUtils.linearButtonList({ - title: params.title, - childDontRegisterViews: true, - linearViewSubMenu: true, flexGap: 0, ignoreClick: true, - linearViewExpandable: true, icon: params.title, _height: 30, - linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, - hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, - }, params.subMenu.map(btnFunc)))); - } else { - const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data); - let prev = ""; - CurrentUserUtils.contextMenuTools(doc).forEach(params => { - const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title); - if (!menuBtnDoc) { - const newMenuBtnDoc = !params.subMenu ? - btnFunc(params) : - CurrentUserUtils.linearButtonList({ - title: params.title, - childDontRegisterViews: true, - linearViewSubMenu: true, flexGap: 0, ignoreClick: true, - linearViewExpandable: true, icon: params.title, _height: 30, - linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined, - hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined, - }, params.subMenu.map(btnFunc)); - const after = menuBtnList.find(doc => doc.title === prev); - Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after); - } - const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined; - if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) { - let prevSub = ""; - params.subMenu.forEach(sub => { - if (!subMenuBtnList.find(doc => doc.title === sub.title)) { - const newSubMenuBtnDoc = btnFunc(sub); - const after = subMenuBtnList.find(doc => doc.title === prevSub); - Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub); - } - prevSub = params.title; - }); - } - prev = params.title; - }); + }; + const reqdFuncs:{[key:string]:any} = { + ...params.funcs, + backgroundColor: params.scripts?.onClick /// a bit hacky. if onClick is set, then we assume it returns a color value when queried with '_readOnly_'. This will be true for toggle buttons, but not generally } + return this.AssignScripts(this.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs); } - // sets up the default set of documents to be shown in the Overlay layer - static setupOverlays(doc: Doc) { - if (doc.myOverlayDocs === undefined) { - doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true })); - } + /// Initializes all the default buttons for the top bar context menu + static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") { + const ctxtMenuBtnsDoc = DocCast(doc[field]); + const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => { + const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); + if (!params.subMenu) { + return this.setupContextMenuButton(params, menuBtnDoc); + } else { + const reqdSubMenuOpts = { title: params.title, icon: params.icon, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, + linearViewSubMenu: true, linearViewExpandable: true, }; + const reqdSubMenuFuncs:{[key:string]:any} = { ...params.funcs}; + return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? + CurrentUserUtils.linearButtonList("submenu", reqdSubMenuOpts, params.subMenu.map(sub => + this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) + )), undefined, reqdSubMenuFuncs); + } + }); + const reqdCtxtOpts = { title: "menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts) ?? (doc[field] = CurrentUserUtils.linearButtonList("contextMenuButtons", reqdCtxtOpts, ctxtMenuBtns)); } - // the initial presentation Doc to use - static setupDefaultPresentation(doc: Doc) { - if (doc["template-presentation"] === undefined) { - doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ - title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _fitWidth: true, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true - })); - } + /// collection of documents rendered in the overlay layer above all tabs and other UI + static setupOverlays(doc: Doc, field = "myOverlayDocs") { + const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true }; + return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts)); } - - // Sharing sidebar is where shared documents are contained + + /// The database of all links on all documents static async setupLinkDocs(doc: Doc, linkDatabaseId: string) { if (doc.myLinkDatabase === undefined) { let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); @@ -915,6 +842,8 @@ export class CurrentUserUtils { doc.myLinkDatabase = new PrefetchProxy(linkDocs); } } + + /// Shared documents option on the left side button panel // A user's sharing document is where all documents that are shared to that user are placed. // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents @@ -941,37 +870,26 @@ export class CurrentUserUtils { }; const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer")); - if (!(sharedDocs instanceof Doc)) { - doc.mySharedDocs = new PrefetchProxy( - Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId)); - } else { - Object.entries(sharedRequiredDocOpts).forEach(pair => { - const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc); - targetDoc[pair[0]] = pair[1]; - }); - } + return this.AssignOpts(DocCast(sharedDocs), sharedRequiredDocOpts) ?? + (doc.mySharedDocs = Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId)); } - // Import sidebar is where shared documents are contained - static setupImportSidebar(doc: Doc) { - if (doc.myImportDocs === undefined) { - const newImportButton: Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true }); - doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { - title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, - childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, - dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go." - })); - } - } - - // Search sidebar is where searches within the document are performed - static setupSearchSidebar(doc: Doc) { - if (doc.mySearchPanel === undefined) { - doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({ - dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true, - childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true - })) as any as Doc; - } + /// Import option on the left side button panel + static setupImportSidebar(doc: Doc, field:string) { + const reqdOpts:DocumentOptions = { + title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", + _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, + childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, + dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go." + }; + const myImports = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.StackingDocument([], reqdOpts)); + + const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", + _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, + buttonText: "Import", icon: "upload", system: true }; + const reqdBtnScripts = { onClick: "importDocument()" }; + const newImportBtn = this.AssignOpts(DocCast(myImports.buttonMenuDoc), reqdBtnOpts) ?? (myImports.buttonMenuDoc = Docs.Create.FontIconDocument(reqdBtnOpts)); + this.AssignScripts(newImportBtn, reqdBtnScripts); } static setupClickEditorTemplates(doc: Doc) { @@ -1033,8 +951,8 @@ export class CurrentUserUtils { }, { fireImmediately: true }); // Document properties on load doc.system = true; - doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc.title = Doc.CurrentUserEmail; + doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; doc._raiseWhenDragged = true; doc._showLabel = true; doc._showMenuLabel = true; @@ -1053,29 +971,22 @@ export class CurrentUserUtils { doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); doc.noviceMode = BoolCast(doc.noviceMode, true); - doc.savedFilters = new List(); + !doc.savedFilters && (doc.savedFilters = new List()); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; doc.myPublishedDocs = doc.myPublishedDocs ?? new List(); - doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); + doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents + await this.setupLinkDocs(doc, linkDatabaseId); + await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing 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 - this.setupSearchSidebar(doc); // sets up the search sidebar this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile - this.setupOverlays(doc); // documents in overlay layer - this.setupContextMenuButtons(doc); // set up context menu buttons + this.setupOverlays(doc); // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard + this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected this.setupDockedButtons(doc); // the bottom bar of font icons - await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels - await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId); + this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered - - // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet - doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); - doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); - setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); if (doc.activeDashboard instanceof Doc) { @@ -1227,7 +1138,7 @@ export class CurrentUserUtils { public static async snapshotDashboard(userDoc: Doc) { const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); CurrentUserUtils.openDashboard(userDoc, copy); } @@ -1277,15 +1188,24 @@ export class CurrentUserUtils { return tbox; } - public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); } - public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); } - public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } - public static get MyHeaderBarDoc() { return Cast(Doc.UserDoc().myHeaderBarDoc, Doc, null); } - public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } - public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } - public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } - public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } - public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); } + public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); } + public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); } + public static get MySearcher() { return DocCast(Doc.UserDoc().mySearcher); } + public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); } + public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); } + public static get MyTools() { return DocCast(Doc.UserDoc().myTools); } + public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } + public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); } + public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); } + public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); } + public static get MyTrails() { return DocCast(Doc.UserDoc().myTrails); } + public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } + public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); } + public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); } + public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); } + public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); } + public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); } + public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); } public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } } @@ -1298,7 +1218,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { view && SelectionManager.SelectView(view, false); } }); -ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, "document containing all shared Docs"); +ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs"); ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard"); @@ -1329,15 +1249,15 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy const parentDoc: Doc = Cast(selected.context, Doc, null); selected = parentDoc; } - if (selected && docType && selected.type === docType) return false; - else if (selected && colType && selected.viewType === colType) return false; - else if (selected && !colType && !docType) return false; - else return true; + if (selected && docType && selected.type === docType) return true; + else if (selected && colType && selected.viewType === colType) return true; + else if (selected && !colType && !docType) return true; + else return false; }); ScriptingGlobals.add(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); + return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder); }); ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { if (readOnly) return; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index a82f99d5a..0435cd535 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,6 +10,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; +import { CurrentUserUtils } from './CurrentUserUtils'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; @@ -296,7 +297,7 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) { Doc.linkFollowHighlight(dv?.props.Document, false); } else { - const context = doc.context !== Doc.UserDoc().myFilesystem && Cast(doc.context, Doc, null); + const context = doc.context !== CurrentUserUtils.MyFilesystem && Cast(doc.context, Doc, null); const showDoc = context || doc; const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc; diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 223b0268c..3791bec73 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -223,7 +223,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`); } const paramString = paramList.join(", "); - const body = addReturn ? `return ${script};` : script; + const body = addReturn && !script.startsWith("{ return") ? `return ${script};` : script; const reqTypes = requiredType ? `: ${requiredType}` : ''; const funcScript = `(function(${paramString})${reqTypes} { ${body} })`; host.writeFile("file.ts", funcScript); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 8486b3d62..be5927497 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -141,7 +141,7 @@ export function ViewBoxAnnotatableComponent

    () const toRemove = value.filter(v => docs.includes(v)); if (toRemove.length !== 0) { - const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc; + const recent = CurrentUserUtils.MyRecentlyClosed; toRemove.forEach(doc => { leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 767efdbc2..16d7c9a23 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -235,7 +235,7 @@ export class KeyManager { if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) { SelectionManager.Views()[0].ComponentView?.search?.("", false, false); } else { - const searchBtn = Doc.UserDoc().searchBtn as Doc; + const searchBtn = CurrentUserUtils.MySearcher; if (searchBtn) { MainView.Instance.selectMenu(searchBtn); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5fd76c388..7e3916b3f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -10,7 +10,7 @@ import * as ReactDOM from 'react-dom'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; -import { PromiseValue, StrCast } from '../../fields/Types'; +import { DocCast, PromiseValue, StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; @@ -79,7 +79,7 @@ export class MainView extends React.Component { @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row) @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons @observable private _panelContent: string = "none"; - @observable private _sidebarContent: any = this.userDoc?.sidebar; + @observable private _sidebarContent: any = CurrentUserUtils.MyLeftSidebarPanel; @observable private _leftMenuFlyoutWidth: number = 0; @computed private get dashboardTabHeight() { return 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js @@ -92,7 +92,7 @@ export class MainView extends React.Component { @computed private get userDoc() { return Doc.UserDoc(); } @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); } @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } - @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; } + @computed private get headerBarDoc() { return CurrentUserUtils.MyHeaderBar; } @computed public get mainFreeform(): Opt { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } headerBarDocWidth = () => this.mainDocViewWidth(); @@ -249,24 +249,8 @@ export class MainView extends React.Component { @action createNewFolder = async () => { - if (!await this.userDoc.myFilesystem) { - this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); - const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!; - const newFolderButton: Doc = Docs.Create.FontIconDocument({ - onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", - btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true - }); - this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], { - title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, - explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." - })); - } const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); - Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder); + Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder); } @observable _exploreMode = false; @@ -397,7 +381,7 @@ export class MainView extends React.Component { addDocTab={this.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} - styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider} + styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider} rootSelected={returnTrue} removeDocument={returnFalse} ScreenToLocalTransform={this.mainContainerXf} @@ -423,7 +407,7 @@ export class MainView extends React.Component { @computed get leftMenuPanel() { return

    (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); + remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, "data", doc), true); moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc); - addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); + addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, "data", doc), true); buttonBarXf = () => { if (!this._docBtnRef.current) return Transform.Identity(); @@ -543,9 +527,9 @@ export class MainView extends React.Component { @computed get docButtons() { return !(this.userDoc.dockedBtns instanceof Doc) ? (null) : -
    +
    { + return CurrentUserUtils.MyOverlayDocs?.map(d => { let offsetx = 0, offsety = 0; const dref = React.createRef(); const onPointerMove = action((e: PointerEvent, down: number[]) => { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 16eb95cf4..2cf334ae1 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -931,7 +931,7 @@ export class PropertiesView extends React.Component { * If it doesn't exist, it creates it. */ checkFilterDoc() { - if (!this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc); + if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc(); } /** @@ -944,8 +944,7 @@ export class PropertiesView extends React.Component { this.selectedDoc._docRangeFilters = new List(); (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters; (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters; - this.selectedDoc.currentFilter = undefined; - CurrentUserUtils.setupFilterDocs(this.selectedDoc); + this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc(); } /** diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index d2687df17..1ebbe08ef 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -416,7 +416,7 @@ export class CollectionDockingView extends CollectionSubView() { } tabDestroyed = (tab: any) => { - Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc); + Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc); Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true); const dview = CollectionDockingView.Instance.props.Document; const fieldKey = CollectionDockingView.Instance.props.fieldKey; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 39f6466d6..1c25421f5 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -99,7 +99,7 @@ export class CollectionMenu extends AntimodeMenu{ } @computed get contMenuButtons() { - const selDoc = Doc.UserDoc().contextMenuBtns; + const selDoc = CurrentUserUtils.MyContextMenuBtns; return !(selDoc instanceof Doc) ? (null) :
    { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }), initialize: (button: Doc) => { - button['target-docFilters'] = (Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) instanceof ObjectField ? - ObjectField.MakeCopy((Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) as any as ObjectField) : undefined; + button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ? + ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined; button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined; }, }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 52e99f26b..c07e44fcc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -985,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; + if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
    - {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? null : + {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : e.stopPropagation()} > @@ -226,7 +226,7 @@ export class CollectionLinearView extends CollectionSubView() { } - {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) : + {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) : Currently playing: diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 4b33ef8ae..a6f6bd35f 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -68,8 +68,8 @@ export class LinkPopup extends React.Component { className="linkPopup-searchBox searchBox-input" /> */} 3 || Math.abs(this._downY - touch.clientY) > 3)) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) { @@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index e3708d7b9..5add09653 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -35,7 +35,7 @@ export class FilterBox extends ViewBoxBaseComponent() { constructor(props: Readonly) { super(props); const targetDoc = FilterBox.targetDoc; - if (targetDoc && !targetDoc.currentFilter) CurrentUserUtils.setupFilterDocs(targetDoc); + if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = CurrentUserUtils.createFilterDoc(); } public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 10974ca03..967158cbf 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -156,20 +156,22 @@ export class WebBox extends ViewBoxAnnotatableComponent { this._annotationKeySuffix = () => this._urlHash + "-annotations"; + const reqdFuncs:{[key:string]: string} = {}; // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created) - this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`); - this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`); + reqdFuncs[this.fieldKey + "-annotations"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`; + reqdFuncs[this.fieldKey + "-sidebar"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`; + CurrentUserUtils.AssignScripts(this.dataDoc, {}, reqdFuncs); }); - reaction(() => this.props.isSelected() || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), + reaction(() => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), async (selected) => { if (selected) { this._webPageHasBeenRendered = true; - } else if ((!this.props.isContentActive() || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail) + } else if ((!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail) !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty. this.updateThumb(); } - }, { fireImmediately: this.props.isSelected() || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegree(this.props.Document) ? true : false) }); + }, { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) }); this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight, autoHeight => { diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 97e6eddfe..f29dfe489 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent() {
    {this.icon === "pres-trail" ? trailsIcon : } {menuLabel} - +
    ); break; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 1dd6fef9b..52b03b0a5 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -623,7 +623,7 @@ export class PresBox extends ViewBoxBaseComponent() { */ @action updateMinimize = async () => { - if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { + if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.layoutDoc.presStatus = PresStatus.Edit; Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc); CollectionDockingView.AddSplit(this.rootDoc, "right"); @@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent() { } break; case "Escape": - if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); } + if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); } else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; } else this.layoutDoc.presStatus = "edit"; if (this._presTimer) clearTimeout(this._presTimer); @@ -2511,7 +2511,7 @@ export class PresBox extends ViewBoxBaseComponent() { const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1); const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0); - return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ? + return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ?
    e.stopPropagation()}>
    {"Loop"}
    }>
    () {
    : -
    +
    {this.topPanel} {this.toolbar} {this.newDocumentToolbarDropdown} diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 7b72787d1..94be286f5 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -941,6 +941,9 @@ export namespace Doc { export function isBrushedHighlightedDegree(doc: Doc) { return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegree(doc); } + export function isBrushedHighlightedDegreeUnmemoized(doc: Doc) { + return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegreeUnmemoized(doc); + } export class DocBrush { BrushedDoc: ObservableMap = new ObservableMap(); @@ -980,7 +983,7 @@ export namespace Doc { export function SearchQuery(): string { return manager._searchQuery; } export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); } export function UserDoc(): Doc { return manager._user_doc; } - export function SharingDoc(): Doc { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); } + export function SharingDoc(): Doc { return CurrentUserUtils.MySharedDocs; } export function LinkDBDoc(): Doc { return Cast(Doc.UserDoc().myLinkDatabase, Doc, null); } export function SetUserDoc(doc: Doc) { return (manager._user_doc = doc); } diff --git a/src/fields/util.ts b/src/fields/util.ts index d1e565774..37b9be31b 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -88,6 +88,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number delete target.__fields[prop]; } else { target.__fieldKeys && (target.__fieldKeys[prop] = true); + // if (target.__fields[prop] !== value) { + // console.log("ASSIGN " + prop + " " + value); + // } target.__fields[prop] = value; } //if (typeof value === "object" && !(value instanceof ObjectField)) debugger; diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 78ec706d7..c5c6fb688 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -68,7 +68,7 @@ export class MobileInterface extends React.Component { constructor(props: Readonly<{}>) { super(props); - this._library = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web + this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web MobileInterface.Instance = this; } -- cgit v1.2.3-70-g09d2 From b292a055401af6236e0537cfad603016d77a535a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Jun 2022 13:40:07 -0400 Subject: a bunch of changes to make some userDoc() field accesses more typesafe by going through CurrentUserUtils. Some normalization of naming/functionality in CurrentUserUtils --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- src/client/documents/Documents.ts | 6 +- src/client/util/CurrentUserUtils.ts | 118 +++++++++++---------- src/client/util/SettingsManager.tsx | 16 +-- src/client/util/SharingManager.tsx | 4 +- src/client/views/DashboardView.tsx | 4 +- src/client/views/DocComponent.tsx | 6 +- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/GestureOverlay.tsx | 12 +-- src/client/views/GlobalKeyHandler.ts | 8 +- src/client/views/InkStrokeProperties.ts | 4 +- src/client/views/InkTranscription.tsx | 2 +- src/client/views/InkingStroke.tsx | 2 +- src/client/views/MainView.tsx | 4 +- src/client/views/OverlayView.tsx | 8 +- src/client/views/PropertiesButtons.tsx | 4 +- src/client/views/PropertiesView.tsx | 86 ++++++++------- src/client/views/TemplateMenu.tsx | 11 +- .../views/collections/CollectionDockingView.tsx | 22 ++-- src/client/views/collections/CollectionMenu.tsx | 35 +++--- .../views/collections/CollectionStackingView.tsx | 2 +- .../CollectionStackingViewFieldColumn.tsx | 4 +- .../views/collections/CollectionTreeView.tsx | 8 +- src/client/views/collections/CollectionView.tsx | 14 +-- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 30 +++--- .../collections/collectionFreeForm/MarqueeView.tsx | 6 +- .../collectionSchema/CollectionSchemaHeaders.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 5 +- src/client/views/nodes/ColorBox.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 40 +++---- src/client/views/nodes/FilterBox.tsx | 47 ++++---- src/client/views/nodes/ImageBox.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 4 +- src/client/views/nodes/VideoBox.tsx | 6 +- src/client/views/nodes/WebBox.tsx | 6 +- src/client/views/nodes/button/FontIconBox.tsx | 35 +++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +-- .../views/nodes/formattedText/RichTextMenu.tsx | 1 - src/client/views/nodes/trails/PresBox.tsx | 30 +++--- src/client/views/nodes/trails/PresElementBox.tsx | 16 +-- src/client/views/pdf/PDFViewer.tsx | 2 +- src/client/views/topbar/TopBar.tsx | 10 +- src/client/views/webcam/DashWebRTCVideo.tsx | 2 +- src/fields/Doc.ts | 18 ++-- src/fields/util.ts | 2 +- src/mobile/MobileInterface.tsx | 20 ++-- 49 files changed, 360 insertions(+), 335 deletions(-) (limited to 'src/client/util') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index c5ff2db68..e14dc60b4 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -351,7 +351,7 @@ export class YoutubeBox extends React.Component { const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); return ( <>
    diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 927996af2..f96bffb3c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -687,7 +687,7 @@ export namespace Docs { ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) && - !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc); + !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, dataDoc); updateCachedAcls(dataDoc); updateCachedAcls(viewDoc); @@ -802,7 +802,7 @@ export namespace Docs { I.rotation = 0; I.data = new InkField(points); I.creationDate = new DateField; - I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; I.links = ComputedField.MakeFunction("links(self)"); I[Initializing] = false; @@ -1204,7 +1204,7 @@ export namespace DocUtils { created = Docs.Create.RecordingDocument((field).url.href, resolved); layout = RecordingBox.LayoutString; } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, 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 afb1442bf..7a38886fe 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -74,16 +74,16 @@ export class CurrentUserUtils { @observable public static headerBarHeight: number = 0; @observable public static searchPanelWidth: number = 0; - static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) { + static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) { scripts && Object.keys(scripts).map(key => { if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) { doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any" }, {"_readOnly_": true}); } }); - functions && Object.keys(functions).map(key => { + funcs && Object.keys(funcs).map(key => { const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) { - doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true}); + if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) { + doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true}); } }); return doc; @@ -180,14 +180,14 @@ export class CurrentUserUtils { } /// Initializes collection of templates for notes and click functions - static setupDocTemplates(doc: Doc) { + static setupDocTemplates(doc: Doc, field="myTemplates") { const templates = [ CurrentUserUtils.setupNoteTemplates(doc), CurrentUserUtils.setupClickEditorTemplates(doc) ]; const reqdOpts = { title: "template layouts", _xMargin: 0, system: true, }; const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; - return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts); + return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, templates) ?? (doc[field] = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts); } // setup templates for different document types when they are iconified from Document Decorations @@ -329,7 +329,7 @@ export class CurrentUserUtils { static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc { const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => { const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined; - const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit, + const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit, _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias", btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true, _removeDropProperties: new List(["_stayInCollection"]), @@ -562,6 +562,7 @@ export class CurrentUserUtils { if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) { myDashboards.childContextMenuFilters = new List(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!)); } + return myDashboards; } /// initializes the left sidebar Trails pane @@ -728,13 +729,13 @@ export class CurrentUserUtils { static inkTools():Button[] { return [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }}, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} }, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }}, - // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} }, - // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }}, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveTool("pen", _readOnly_);}' }}, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveTool("write", _readOnly_);}'} }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveTool("eraser", _readOnly_);}' }}, + // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveTool("circle", _readOnly_);}'} }, + // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveTool("line", _readOnly_);}' }}, { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} }, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} }, { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, @@ -941,41 +942,39 @@ export class CurrentUserUtils { } static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); - await DocListCastAsync((doc.globalGroupDatabase as Doc).data); - reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]), + doc.globalGroupDatabase ?? (doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument()); + await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); + reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]), async () => { - const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); }, { fireImmediately: true }); // Document properties on load - doc.system = true; - doc.title = Doc.CurrentUserEmail; - doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; - doc._raiseWhenDragged = true; - doc._showLabel = true; - doc._showMenuLabel = true; - doc.textAlign = StrCast(doc.textAlign, "left"); - doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); - doc.activeInkWidth = Number(StrCast(doc.activeInkWidth, "1")); - doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); - doc.activeFillColor = StrCast(doc.activeFillColor, ""); - doc.activeArrowStart = StrCast(doc.activeArrowStart, ""); - doc.activeArrowEnd = StrCast(doc.activeArrowEnd, ""); - doc.activeDash = StrCast(doc.activeDash, "0"); - doc.fontSize = StrCast(doc.fontSize, "12px"); - doc.fontFamily = StrCast(doc.fontFamily, "Arial"); - doc.fontColor = StrCast(doc.fontColor, "black"); - doc.fontHighlight = StrCast(doc.fontHighlight, ""); - doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false); - doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); - doc.noviceMode = BoolCast(doc.noviceMode, true); - !doc.savedFilters && (doc.savedFilters = new List()); + doc.system ?? (doc.system = true); + doc.title ?? (doc.title = Doc.CurrentUserEmail); + Doc.noviceMode ?? (Doc.noviceMode = true); + doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true); + doc._showLabel ?? (doc._showLabel = true); + doc._showMenuLabel ?? (doc._showMenuLabel = true); + doc.textAlign ?? (doc.textAlign = "left"); + doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");; + doc.activeInkWidth ?? (doc.activeInkWidth = 1); + doc.activeInkBezier ?? (doc.activeInkBezier = "0"); + doc.activeFillColor ?? (doc.activeFillColor = ""); + doc.activeArrowStart ?? (doc.activeArrowStart = ""); + doc.activeArrowEnd ?? (doc.activeArrowEnd = ""); + doc.activeDash ?? (doc.activeDash == "0"); + doc.fontSize ?? (doc.fontSize = "12px"); + doc.fontFamily ?? (doc.fontFamily = "Arial"); + doc.fontColor ?? (doc.fontColor = "black"); + doc.fontHighlight ?? (doc.fontHighlight = ""); + doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false); + doc.savedFilters ?? (doc.savedFilters = new List()); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; - doc.myPublishedDocs = doc.myPublishedDocs ?? new List(); - doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents + doc.myPublishedDocs ?? (doc.myPublishedDocs = new List()); + doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents await this.setupLinkDocs(doc, linkDatabaseId); await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon @@ -985,7 +984,7 @@ export class CurrentUserUtils { this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected this.setupDockedButtons(doc); // the bottom bar of font icons this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left - if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); + doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument()); setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); @@ -1022,7 +1021,7 @@ export class CurrentUserUtils { const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; Docs.newAccount &&(userDoc.activePage = "home"); const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); - (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager + (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager const a1 = await Cast(link?.anchor1, Doc, null); const a2 = await Cast(link?.anchor2, Doc, null); }); @@ -1097,7 +1096,7 @@ export class CurrentUserUtils { } } } else if (input.files && input.files.length !== 0) { - const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); + const importDocs = CurrentUserUtils.MyImports; const disposer = OverlayView.ShowSpinner(); DocListCastAsync(importDocs.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); @@ -1115,9 +1114,10 @@ export class CurrentUserUtils { } public static CaptureDashboardThumbnail() { + const activeDashboard = CurrentUserUtils.ActiveDashboard; const docView = CollectionDockingView.Instance.props.DocumentView?.(); const content = docView?.ContentDiv; - if (docView && content) { + if (docView && content && activeDashboard) { const _width = Number(getComputedStyle(content).width.replace("px","")); const _height = Number(getComputedStyle(content).height.replace("px","")); return CollectionFreeFormView.UpdateIcon( @@ -1130,20 +1130,22 @@ export class CurrentUserUtils { const proto = Cast(img.proto, Doc, null)!; proto["data-nativeWidth"] = _width; proto["data-nativeHeight"] = _height; - Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img; + Doc.GetProto(activeDashboard).thumb = img; }); } } public static async snapshotDashboard(userDoc: Doc) { - const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); - CurrentUserUtils.openDashboard(userDoc, copy); + if (CurrentUserUtils.ActiveDashboard) { + const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); + CurrentUserUtils.openDashboard(userDoc, copy); + } } public static closeActiveDashboard = () => { - Doc.UserDoc().activeDashboard = undefined; + CurrentUserUtils.ActiveDashboard = undefined; } public static createNewDashboard = async (userDoc: Doc, id?: string) => { @@ -1195,6 +1197,8 @@ export class CurrentUserUtils { public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); } public static get MyTools() { return DocCast(Doc.UserDoc().myTools); } public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); } + public static get MyFileOrphans() { return DocCast(Doc.UserDoc().myFileOrphans); } + public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); } public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); } public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); } public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); } @@ -1202,12 +1206,16 @@ export class CurrentUserUtils { public static get MyImports() { return DocCast(Doc.UserDoc().myImports); } public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); } public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); } - public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); } + public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); } public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); } + public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; } public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); } + public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; } + public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); } + public static set ActivePage(val) { Doc.UserDoc().activePage = val; } public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); } - public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } - @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } + public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; } + public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } } ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { @@ -1219,7 +1227,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { } }); ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs"); -ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode"); +ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard"); ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called"); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 6a26dfdc7..382274462 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> { @observable activeTab = "Accounts"; @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; } - @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; } + @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; } constructor(props: {}) { super(props); @@ -59,7 +59,7 @@ export class SettingsManager extends React.Component<{}> { } } - @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice"); + @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.noviceMode = (e.currentTarget as any)?.value === "Novice"); @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined); @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value); @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value); @@ -78,19 +78,21 @@ export class SettingsManager extends React.Component<{}> { @undoBatch @action changeColorScheme = action((e: React.ChangeEvent) => { + const activeDashboard= CurrentUserUtils.ActiveDashboard; + if (!activeDashboard) return; const scheme: ColorScheme = (e.currentTarget as any).value; switch (scheme) { case 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) + 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: - CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark; + 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 => { - 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) + 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; } @@ -254,7 +256,7 @@ export class SettingsManager extends React.Component<{}> {
    Modes
    - @@ -270,7 +272,7 @@ export class SettingsManager extends React.Component<{}> {
    Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} /> + onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
    Default access private
    diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index f439d4488..d77633b8d 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -369,7 +369,7 @@ export class SharingManager extends React.Component<{}> { const dropdownValues: string[] = Object.values(SharingPermissions); if (!uniform) dropdownValues.unshift("-multiple-"); if (override) dropdownValues.unshift("None"); - return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => + return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => (
    - {Doc.UserDoc().noviceMode ? (null) : + {Doc.noviceMode ? (null) :
    this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} />
    } diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index efc1644fe..9a0f25fe3 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -34,8 +34,8 @@ export class DashboardView extends React.Component { clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => { if (e.detail === 2) { - Doc.UserDoc().activeDashboard = dashboard; - Doc.UserDoc().activePage = "dashboard"; + CurrentUserUtils.ActiveDashboard = dashboard; + CurrentUserUtils.ActivePage = "dashboard"; } } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index be5927497..169bd3873 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -65,7 +65,7 @@ export function ViewBoxBaseComponent

    () { isContentActive = (outsideReaction?: boolean) => ( this.props.isContentActive?.() === false ? false : - (CurrentUserUtils.SelectedTool !== InkTool.None || + (CurrentUserUtils.ActiveTool !== InkTool.None || (this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction)) ? true : undefined)) @@ -200,7 +200,7 @@ export function ViewBoxAnnotatableComponent

    () if (effectiveAcl === AclAugment) { added.map(doc => { - if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); + if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); @@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent

    () doc.context = this.props.Document; if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; - inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); + CurrentUserUtils.ActiveDashboard && inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); }); const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List; if (annoDocs instanceof List) annoDocs.push(...added); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 0bbe473d7..9b8f7238d 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -347,7 +347,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV

    {this.menuButton}
    - {/* {Doc.UserDoc().noviceMode ? (null) :
    + {/* {Doc.noviceMode ? (null) :
    {this.moreButton}
    } */}
    ; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 6afe64868..e960f5cca 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -129,7 +129,7 @@ export class GestureOverlay extends Touchable { // and this seems to be the only way of differentiating pen and touch on touch events if (pt.radiusX > 1 && pt.radiusY > 1) { InkTranscription.Instance.createInkGroup(); - Doc.UserDoc().activeInkTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; this.prevPoints.set(pt.identifier, pt); } } @@ -498,18 +498,18 @@ export class GestureOverlay extends Touchable { setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { InkTranscription.Instance.createInkGroup(); - CurrentUserUtils.SelectedTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; return; } })); } - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { - CurrentUserUtils.SelectedTool = InkTool.Write; + CurrentUserUtils.ActiveTool = InkTool.Write; } this._points.push({ X: e.clientX, Y: e.clientY }); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); - // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)"); + // if (CurrentUserUtils.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)"); } } @@ -606,7 +606,7 @@ export class GestureOverlay extends Touchable { this._points = []; if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) { this.InkShape = ""; - Doc.UserDoc().activeInkTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; } } // if we're not drawing in a toolglass try to recognize as gesture diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 16d7c9a23..f5122df3f 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -57,7 +57,7 @@ export class KeyManager { public handle = action((e: KeyboardEvent) => { if (e.key?.toLowerCase() === "shift") DocumentDecorations.Instance.AddToSelection = true; - //if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true); + //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true); const keyname = e.key && e.key.toLowerCase(); this.handleGreedy(keyname); @@ -112,7 +112,7 @@ export class KeyManager { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; InkStrokeProperties.Instance._controlButton = false; - CurrentUserUtils.SelectedTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; DragManager.CompleteWindowDrag?.(true); var doDeselect = true; if (SnappingManager.GetIsDragging()) { @@ -241,9 +241,9 @@ export class KeyManager { } } break; - case "e": CurrentUserUtils.SelectedTool = InkTool.Eraser; + case "e": CurrentUserUtils.ActiveTool = InkTool.Eraser; break; - case "p": CurrentUserUtils.SelectedTool = InkTool.Pen; + case "p": CurrentUserUtils.ActiveTool = InkTool.Pen; break; case "o": const target = SelectionManager.Docs().lastElement(); diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 0449da483..471ad09e9 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -25,8 +25,8 @@ export class InkStrokeProperties { constructor() { InkStrokeProperties._Instance = this; - reaction(() => this._controlButton, button => button && (CurrentUserUtils.SelectedTool = InkTool.None)); - reaction(() => CurrentUserUtils.SelectedTool, tool => (tool !== InkTool.None) && (this._controlButton = false)); + reaction(() => this._controlButton, button => button && (CurrentUserUtils.ActiveTool = InkTool.None)); + reaction(() => CurrentUserUtils.ActiveTool, tool => (tool !== InkTool.None) && (this._controlButton = false)); } /** diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 8ab54918c..487bbcd00 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -254,7 +254,7 @@ export class InkTranscription extends React.Component { */ createInkGroup() { // TODO nda - if document being added to is a inkGrouping then we can just add to that group - if (CurrentUserUtils.SelectedTool === InkTool.Write) { + if (CurrentUserUtils.ActiveTool === InkTool.Write) { CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those const selected = ffView.unprocessedDocs; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index aa5a815ac..35d89d2b1 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -345,7 +345,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(), onContextMenu: () => { const cm = ContextMenu.Instance; - !Doc.UserDoc().noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" }); + !Doc.noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" }); cm?.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" }); cm?.addItem({ description: "Edit Points", event: action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton), icon: "paint-brush" }); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7e3916b3f..791c64630 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -381,7 +381,7 @@ export class MainView extends React.Component { addDocTab={this.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} - styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider} + styleProvider={this._sidebarContent.proto === CurrentUserUtils.MyDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider} rootSelected={returnTrue} removeDocument={returnFalse} ScreenToLocalTransform={this.mainContainerXf} @@ -656,7 +656,7 @@ export class MainView extends React.Component { {this.mainDashboardArea} ; case "home": return ; - } })(StrCast(Doc.UserDoc().activePage)) + } })(CurrentUserUtils.ActivePage) } diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index bdb3bb857..598fff29a 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import * as React from "react"; import ReactLoading from 'react-loading'; -import { Doc, WidthSym, HeightSym } from "../../fields/Doc"; +import { Doc, WidthSym, HeightSym, DocListCast } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { Cast, NumCast } from "../../fields/Types"; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils"; @@ -149,7 +149,7 @@ export class OverlayView extends React.Component { } removeOverlayDoc = (doc: Doc | Doc[]) => { - (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc)); + (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc)); return true; } @@ -158,7 +158,7 @@ export class OverlayView extends React.Component { }.bind(this)); @computed get overlayDocs() { - return CurrentUserUtils.MyOverlayDocs?.map(d => { + return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).map(d => { let offsetx = 0, offsety = 0; const dref = React.createRef(); const onPointerMove = action((e: PointerEvent, down: number[]) => { @@ -172,7 +172,7 @@ export class OverlayView extends React.Component { dragData.dropAction = "move"; dragData.removeDocument = (doc: Doc | Doc[]) => { const docs = (doc instanceof Doc) ? [doc] : doc; - docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocs, Doc, null), "data", d)); + docs.forEach(d => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, d)); return true; }; dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 74055f057..9c6d9a108 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -200,7 +200,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {list}
    - {Doc.UserDoc().noviceMode ? (null) :
    Edit onClick Script
    } + {Doc.noviceMode ? (null) :
    Edit onClick Script
    }
    ; } @computed @@ -229,7 +229,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree; const isTabView = this.selectedTabView; const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) =>
    {ele}
    ; - const isNovice = Doc.UserDoc().noviceMode; + const isNovice = Doc.noviceMode; return !this.selectedDoc ? (null) :
    {toggle(this.titleButton)} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 2cf334ae1..90c86fa18 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -11,7 +11,7 @@ import { Id } from "../../fields/FieldSymbols"; import { InkField } from "../../fields/InkField"; import { List } from "../../fields/List"; import { ComputedField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; +import { Cast, NumCast, StrCast, DocCast } from "../../fields/Types"; import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util"; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils"; import { DocumentType } from "../documents/DocumentTypes"; @@ -340,7 +340,7 @@ export class PropertiesView extends React.Component { */ @undoBatch changePermissions = (e: any, user: string) => { - const docs = SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.props.Document); + const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [this.selectedDoc]:[]) : SelectionManager.Views().map(docView => docView.props.Document); SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs); } @@ -355,7 +355,7 @@ export class PropertiesView extends React.Component { value={permission} onChange={e => this.changePermissions(e, user)}> {dropdownValues.filter(permission => - !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => + !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission => )} ; } @@ -419,7 +419,7 @@ export class PropertiesView extends React.Component { // all selected docs const docs = SelectionManager.Views().length < 2 ? - [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc[DataSym]] + [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]); const target = docs[0]; @@ -907,7 +907,7 @@ export class PropertiesView extends React.Component { {!this.openSharing ? (null) :
    - {!Doc.UserDoc().noviceMode ? (
    + {!Doc.noviceMode ? (
    this.layoutDocAcls = !this.layoutDocAcls)} @@ -931,42 +931,46 @@ export class PropertiesView extends React.Component { * If it doesn't exist, it creates it. */ checkFilterDoc() { - if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc(); + if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = CurrentUserUtils.createFilterDoc(); } /** * Creates a new currentFilter for this.filterDoc, */ createNewFilterDoc = () => { - const currentDocFilters = this.selectedDoc._docFilters; - const currentDocRangeFilters = this.selectedDoc._docRangeFilters; - this.selectedDoc._docFilters = new List(); - this.selectedDoc._docRangeFilters = new List(); - (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters; - (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters; - this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc(); + if (this.selectedDoc) { + const currentDocFilters = this.selectedDoc._docFilters; + const currentDocRangeFilters = this.selectedDoc._docRangeFilters; + this.selectedDoc._docFilters = new List(); + this.selectedDoc._docRangeFilters = new List(); + (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters; + (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters; + this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc(); + } } /** * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter */ updateFilterDoc = (doc: Doc) => { - if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc - const savedDocFilters = doc._docFiltersList; - const currentDocFilters = this.selectedDoc._docFilters; - this.selectedDoc._docFilters = new List(); - (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters; - this.selectedDoc.currentFilter = doc; - doc._docFiltersList = new List(); - this.selectedDoc._docFilters = savedDocFilters; - - const savedDocRangeFilters = doc._docRangeFiltersList; - const currentDocRangeFilters = this.selectedDoc._docRangeFilters; - this.selectedDoc._docRangeFilters = new List(); - (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters; - this.selectedDoc.currentFilter = doc; - doc._docRangeFiltersList = new List(); - this.selectedDoc._docRangeFilters = savedDocRangeFilters; + if (this.selectedDoc) { + if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc + const savedDocFilters = doc._docFiltersList; + const currentDocFilters = this.selectedDoc._docFilters; + this.selectedDoc._docFilters = new List(); + (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters; + this.selectedDoc.currentFilter = doc; + doc._docFiltersList = new List(); + this.selectedDoc._docFilters = savedDocFilters; + + const savedDocRangeFilters = doc._docRangeFiltersList; + const currentDocRangeFilters = this.selectedDoc._docRangeFilters; + this.selectedDoc._docRangeFilters = new List(); + (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters; + this.selectedDoc.currentFilter = doc; + doc._docRangeFiltersList = new List(); + this.selectedDoc._docRangeFilters = savedDocRangeFilters; + } } @computed get filtersSubMenu() { @@ -1059,13 +1063,13 @@ export class PropertiesView extends React.Component {
    - {!Doc.UserDoc().noviceMode && this.openFields ?
    + {!Doc.noviceMode && this.openFields ?
    {this.fieldsCheckbox}
    Layout
    : null} {!this.openFields ? (null) :
    - {Doc.UserDoc().noviceMode ? this.noviceFields : this.expandedField} + {Doc.noviceMode ? this.noviceFields : this.expandedField}
    }
    ; } @@ -1122,7 +1126,7 @@ export class PropertiesView extends React.Component { @undoBatch handleDescriptionChange = action((value: string) => { - if (LinkManager.currentLink) { + if (LinkManager.currentLink && this.selectedDoc) { this.selectedDoc.description = value; this.description = value; return true; @@ -1131,7 +1135,7 @@ export class PropertiesView extends React.Component { @undoBatch handleLinkRelationshipChange = action((value: string) => { - if (LinkManager.currentLink) { + if (LinkManager.currentLink && this.selectedDoc) { this.selectedDoc.linkRelationship = value; this.relationship = value; return true; @@ -1188,7 +1192,7 @@ export class PropertiesView extends React.Component { @undoBatch changeFollowBehavior = action((follow: string) => { - if (LinkManager.currentLink) { + if (LinkManager.currentLink && this.selectedDoc) { this.selectedDoc.followLinkLocation = follow; return true; } @@ -1219,18 +1223,18 @@ export class PropertiesView extends React.Component { } toggleAnchor = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove))); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove)))); } toggleArrow = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow)))); } toggleZoomToTarget1 = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom))); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor1).followLinkZoom = !DocCast(this.selectedDoc.anchor1).followLinkZoom)))); } toggleZoomToTarget2 = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom))); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom)))); } @computed @@ -1238,7 +1242,7 @@ export class PropertiesView extends React.Component { return this.handleLinkRelationshipChange(e.currentTarget.value)} @@ -1252,7 +1256,7 @@ export class PropertiesView extends React.Component { return this.handleDescriptionChange(e.currentTarget.value)} @@ -1273,7 +1277,7 @@ export class PropertiesView extends React.Component { // } render() { - const isNovice = BoolCast(Doc.UserDoc().noviceMode); + const isNovice = Doc.noviceMode; if (!this.selectedDoc && !this.isPres) { return
    diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 636f7042f..689ee4fc1 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView"; import { DefaultStyleProvider } from "./StyleProvider"; import './TemplateMenu.scss'; import React = require("react"); +import { CurrentUserUtils } from "../util/CurrentUserUtils"; @observer class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent, template: string) => void }> { @@ -111,22 +112,22 @@ export class TemplateMenu extends React.Component { const firstDoc = this.props.docViews[0].props.Document; const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", ""); const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data); - const addedTypes = Doc.UserDoc().noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); + const addedTypes = Doc.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); const layout = Doc.Layout(firstDoc); const templateMenu: Array = []; this.props.templates?.forEach((checked, template) => templateMenu.push()); templateMenu.push(); templateMenu.push(); - !Doc.UserDoc().noviceMode && templateMenu.push(); + !Doc.noviceMode && templateMenu.push(); addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template)); this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push( this.toggleLayout(e, template)} />)); return
      - {Doc.UserDoc().noviceMode ? (null) : } + {Doc.noviceMode ? (null) : } {templateMenu} - {Doc.UserDoc().noviceMode ? (null) : { stack.header?.element.on('mousedown', (e: any) => { - if (e.target === stack.header?.element[0] && e.button === 2) { - const dashboard= CurrentUserUtils.ActiveDashboard; + const dashboard = CurrentUserUtils.ActiveDashboard; + if (dashboard && e.target === stack.header?.element[0] && e.button === 2) { dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; const docToAdd = Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`, @@ -462,12 +462,14 @@ export class CollectionDockingView extends CollectionSubView() { .click(action(() => { // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size const dashboard = CurrentUserUtils.ActiveDashboard; - dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; - const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}` - }); - this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); - CollectionDockingView.AddSplit(docToAdd, "", stack); + if (dashboard) { + dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; + const docToAdd = Docs.Create.FreeformDocument([], { + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}` + }); + this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); + CollectionDockingView.AddSplit(docToAdd, "", stack); + } })); } @@ -480,6 +482,6 @@ ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab( "opens up document in a lightbox", "(doc: any)"); ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, "opens up document in tab on right side of the screen", "(doc: any)"); -ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); }, +ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); }, "opens up document in screen overlay layer", "(doc: any)"); ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 1c25421f5..9b1bb5b97 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -268,17 +268,20 @@ export class CollectionViewBaseChrome extends React.Component { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }), initialize: (button: Doc) => { - button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ? - ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined; - button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined; + const activeDash = CurrentUserUtils.ActiveDashboard; + if (activeDash) { + button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? + ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined; + button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined; + } }, }; - @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } - @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } - @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } - @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } - @computed get _doc_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; } + @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } + @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } + @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } + @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } + @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; } @computed get _tree_commands() { return undefined; } private get _buttonizableCommands() { switch (this.props.type) { @@ -467,7 +470,7 @@ export class CollectionViewBaseChrome extends React.Component { const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" }); - //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); + //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); CollectionDockingView.AddSplit(doc, "right"); } @@ -691,8 +694,8 @@ export class CollectionFreeFormViewChrome extends React.Component Back Frame
    } placement="bottom">
    @@ -879,7 +882,7 @@ export class CollectionStackingViewChrome extends React.Component key.indexOf("title") >= 0 || key.indexOf("author") >= 0 || key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 || diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index dddae4a34..277fcd59c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -598,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView {buttonMenu || noviceExplainer ?
    {buttonMenu ? this.buttonMenu : null} - {Doc.UserDoc().noviceMode && noviceExplainer ? + {Doc.noviceMode && noviceExplainer ?
    {noviceExplainer}
    diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 7f96217b8..f3a798143 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -224,8 +224,8 @@ export class CollectionStackingViewFieldColumn extends React.Component { Doc.GetProto(this.props.Document)[name] = ""; const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true }); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index e809bfbce..ba72fb7b9 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -67,7 +67,7 @@ export class CollectionTreeView extends CollectionSubView this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)); - isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None || + isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None || (this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction)) ? true : false) @@ -164,7 +164,7 @@ 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 (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { const layoutItems: ContextMenuProps[] = []; layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" }); layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" }); @@ -286,7 +286,7 @@ export class CollectionTreeView extends CollectionSubView {this.rootDoc.explainer}
    ; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 965f0a352..4b5c5e3fb 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -160,8 +160,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent func(CollectionViewType.Masonry), icon: "columns" }); subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" }); subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" }); - !Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); - !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); + !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" }); + !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" }); subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) { @@ -184,16 +184,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null; + !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null; if (this.rootDoc.childLayout instanceof Doc) { optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" }); } if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) { optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" }); } - !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); + !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { optionItems.push({ description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram" }); @@ -210,7 +210,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) }); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 022b9fa24..70db121d1 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -277,7 +277,7 @@ export class TabDocView extends React.Component { pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array }); if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) { - const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []); + const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); CollectionDockingView.AddSplit(curPres, "right"); setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 59dc5671b..704b8989a 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -114,7 +114,7 @@ export class TreeView extends React.Component { return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : this.props.treeView.dashboardMode ? this.fieldKey : this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying - this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); + this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); } @computed get doc() { return this.props.document; } @@ -560,8 +560,8 @@ export class TreeView extends React.Component { const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : ""; const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : ""; const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; - const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; - const layout = (Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; + const fields = () => Doc.noviceMode ? "" : "fields"; + const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"]; return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c07e44fcc..ffe146ae4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -458,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - Doc.UserDoc().activeInkTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (this.tryDragCluster(e, this._hitCluster)) { @@ -834,7 +834,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; + if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); - !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); + !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" }); //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" }); @@ -1640,27 +1640,27 @@ export class CollectionFreeFormView extends CollectionSubView s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" }); - !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; + !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" }); const viewctrls = ContextMenu.Instance.findByDescription("UI Controls..."); const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : []; - !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; - !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null; + !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; + !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); const options = ContextMenu.Instance.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode && + !this.props.isAnnotationOverlay && !Doc.noviceMode && optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" }); this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" }); - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); } !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); const mores = ContextMenu.Instance.findByDescription("More..."); const moreItems = mores && "subitems" in mores ? mores.subitems : []; - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) }); moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) }); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 051da795f..b62020a04 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -330,7 +330,7 @@ export class MarqueeView extends React.Component { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - if (CurrentUserUtils.SelectedTool === InkTool.None) { + if (CurrentUserUtils.ActiveTool === InkTool.None) { if (!(e.nativeEvent as any).marqueeHit) { (e.nativeEvent as any).marqueeHit = true; if (!this.props.trySelectCluster(e.shiftKey)) { @@ -367,7 +367,7 @@ export class MarqueeView extends React.Component { Doc.GetProto(doc).data = new List(selected); Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform"; - !this.props.isAnnotationOverlay && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(doc)); + !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc)); doc._panX = doc._panY = 0; return doc; })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); @@ -643,7 +643,7 @@ export class MarqueeView extends React.Component e.preventDefault()} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx index 0875c80b3..9653f2808 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx @@ -321,7 +321,7 @@ export class KeysDropdown extends React.Component { const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"]; const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); const showKeys = new Set(); - [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode || + [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode || whitelistKeys.includes(key) || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null); return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm)); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 669622455..8e24fce11 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -348,11 +348,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { @undoBatch @action static switchColor(color: ColorState) { - // Doc.UserDoc().backgroundColor = Utils.colorString(color); // bcz: this can't go here ... needs a proper home in the settings panel SetActiveInkColor(color.hex); SelectionManager.Views().map(view => { @@ -49,7 +48,7 @@ export class ColorBox extends ViewBoxBaseComponent() { style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }} > CurrentUserUtils.SelectedTool === InkTool.None && ColorBox.switchColor(c)} + onChange={c => CurrentUserUtils.ActiveTool === InkTool.None && ColorBox.switchColor(c)} color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())} presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e2b37d2b4..4d84a8ad2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -327,7 +327,7 @@ export class DocumentViewInternal extends DocComponent 3 || Math.abs(this._downY - touch.clientY) > 3)) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) { @@ -543,9 +543,9 @@ export class DocumentViewInternal extends DocComponent { - if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.SelectedTool === InkTool.Eraser) return; + if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return; // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document) - if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) { + if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) { if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { e.stopPropagation(); if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it @@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent { if (e.cancelBubble) return; - if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return; + if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return; - if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { + if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); @@ -697,7 +697,7 @@ export class DocumentViewInternal extends DocComponent { - if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) { + if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) { e.preventDefault(); e.stopPropagation(); //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); @@ -747,8 +747,8 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(templateDoc, "add:right"), icon: "eye" }); - !Doc.UserDoc().noviceMode && appearanceItems.push({ + !Doc.noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" }); + !Doc.noviceMode && appearanceItems.push({ description: "Add a Field", event: () => { const alias = Doc.MakeAlias(this.rootDoc); alias.layout = FormattedTextBox.LayoutString("newfield"); @@ -765,7 +765,7 @@ export class DocumentViewInternal extends DocComponent this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" }); + !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" }); const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; @@ -778,8 +778,8 @@ export class DocumentViewInternal extends DocComponent this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" }); if (!this.Document.annotationOn) { @@ -803,7 +803,7 @@ export class DocumentViewInternal extends DocComponent this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) }); funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) }); funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined }); @@ -813,8 +813,8 @@ export class DocumentViewInternal extends DocComponent SharingManager.Instance.open(this.props.DocumentView()), icon: "users" }); - if (!Doc.UserDoc().noviceMode) { + (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" }); + if (!Doc.noviceMode) { moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" }); moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" }); @@ -835,9 +835,9 @@ export class DocumentViewInternal extends DocComponent this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); - !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); - !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); - !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); + !Doc.noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); + !Doc.noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); + !Doc.noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); } @@ -860,7 +860,7 @@ export class DocumentViewInternal extends DocComponent Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view); isContentActive = (outsideReaction?: boolean) => { return this.props.isContentActive() === false ? false : ( - CurrentUserUtils.SelectedTool !== InkTool.None || + CurrentUserUtils.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || this.rootSelected() || this.props.Document.forceActive || diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 5add09653..17b57cb3b 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -8,7 +8,7 @@ import { List } from "../../../fields/List"; import { RichTextField } from "../../../fields/RichTextField"; import { listSpec } from "../../../fields/Schema"; import { ComputedField, ScriptField } from "../../../fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { Cast, NumCast, StrCast, DocCast } from "../../../fields/Types"; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; @@ -84,7 +84,7 @@ export class FilterBox extends ViewBoxBaseComponent() { return "data"; } @computed static get targetDocChildren() { - return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard.data); + return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard?.data); } @observable _loaded = false; @@ -115,7 +115,7 @@ export class FilterBox extends ViewBoxBaseComponent() { const keys = new Set(noviceFields); this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key))); - return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort(); + return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.noviceMode).sort(); } @@ -167,23 +167,25 @@ export class FilterBox extends ViewBoxBaseComponent() { removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false; public removeFilter = (filterName: string) => { const targetDoc = FilterBox.targetDoc; - const filterDoc = targetDoc.currentFilter as Doc; - const attributes = DocListCast(filterDoc.data); - const found = attributes.findIndex(doc => doc.title === filterName); - if (found !== -1) { - (filterDoc.data as List).splice(found, 1); - const docFilter = Cast(targetDoc._docFilters, listSpec("string")); - if (docFilter) { - let index: number; - while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) { - docFilter.splice(index, 1); + if (targetDoc) { + const filterDoc = targetDoc.currentFilter as Doc; + const attributes = DocListCast(filterDoc.data); + const found = attributes.findIndex(doc => doc.title === filterName); + if (found !== -1) { + (filterDoc.data as List).splice(found, 1); + const docFilter = Cast(targetDoc._docFilters, listSpec("string")); + if (docFilter) { + let index: number; + while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) { + docFilter.splice(index, 1); + } } - } - const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string")); - if (docRangeFilters) { - let index: number; - while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) { - docRangeFilters.splice(index, 3); + const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string")); + if (docRangeFilters) { + let index: number; + while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) { + docRangeFilters.splice(index, 3); + } } } } @@ -194,6 +196,7 @@ export class FilterBox extends ViewBoxBaseComponent() { */ facetClick = (facetHeader: string) => { const { targetDoc, targetDocChildren } = FilterBox; + if (!targetDoc) return; const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader); if (found !== -1) { this.removeFilter(facetHeader); @@ -267,7 +270,7 @@ export class FilterBox extends ViewBoxBaseComponent() { */ @action changeBool = (e: any) => { - (FilterBox.targetDoc.currentFilter as Doc).filterBoolean = e.currentTarget.value; + FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value); } /** @@ -322,7 +325,7 @@ export class FilterBox extends ViewBoxBaseComponent() { * Changes the title of the filterDoc */ onTitleValueChange = (val: string) => { - this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc.title}`; + this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`; return true; } @@ -373,7 +376,7 @@ export class FilterBox extends ViewBoxBaseComponent() {
    - diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 8c27b3508..60eb48114 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -179,7 +179,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent Utils.CopyText(this.choosePath(field.url)), icon: "expand-arrows-alt" }); - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" }); const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers..."); @@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent; } marqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index b0b050cea..b58a9affb 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -52,7 +52,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; } specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; - !Doc.UserDoc().noviceMode && funcs.push({ + !Doc.noviceMode && funcs.push({ description: "Clear Script Params", event: () => { const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []); params?.map(p => this.paramsDoc[p] = undefined); diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 5f4c17ee6..0b7264d79 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -570,7 +570,7 @@ export class MapBox extends ViewBoxAnnotatableComponent; + // pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />; return
    {/*console.log(apiKey)*/} {/* Loading
    :
    @@ -484,7 +484,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode))); this._disposers.youtubeReactionDisposer = reaction( - () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, + () => CurrentUserUtils.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true }); }; if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100); @@ -707,7 +707,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.ActiveTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 967158cbf..d14af49ea 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); + !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); funcs.push({ description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => { this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts; @@ -561,7 +561,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { setupMoveUpEvents(this, e, action(e => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; @@ -678,7 +678,7 @@ export class WebBox extends ViewBoxAnnotatableComponent e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}> diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index f29dfe489..3af6a3d51 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -1,6 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; +import { StringIterator } from 'lodash'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -70,7 +71,7 @@ export class FontIconBox extends DocComponent() { useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); }; specificContextMenu = (): void => { - if (!Doc.UserDoc().noviceMode) { + if (!Doc.noviceMode) { const cm = ContextMenu.Instance; cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" }); cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" }); @@ -269,7 +270,7 @@ export class FontIconBox extends DocComponent() { // Get items to place into the list const list = this.buttonList.map((value) => { - if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) { + if (Doc.noviceMode && !noviceList.includes(value)) { return; } return
    = 1) return Colors.MEDIUM_BLUE; + if (checkResult) { + if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE; return "transparent"; } selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); @@ -710,7 +711,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { export function checkInksToGroup() { // console.log("getting here to inks group"); - if (CurrentUserUtils.SelectedTool === InkTool.Write) { + if (CurrentUserUtils.ActiveTool === InkTool.Write) { CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other @@ -723,7 +724,7 @@ export function checkInksToGroup() { export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { // TODO nda - if document being added to is a inkGrouping then we can just add to that group - if (CurrentUserUtils.SelectedTool === InkTool.Write) { + if (CurrentUserUtils.ActiveTool === InkTool.Write) { CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those const selected = ffView.unprocessedDocs; @@ -785,38 +786,38 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { /** INK - * setActiveInkTool + * setActiveTool * setStrokeWidth * setStrokeColor **/ -ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) { +ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) { InkTranscription.Instance?.createInkGroup(); if (checkResult) { - return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? + return ((CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? Colors.MEDIUM_BLUE : "transparent"; } if (["circle", "square", "line"].includes(tool)) { if (GestureOverlay.Instance.InkShape === tool) { - Doc.UserDoc().activeInkTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; GestureOverlay.Instance.InkShape = InkTool.None; } else { - Doc.UserDoc().activeInkTool = InkTool.Pen; + CurrentUserUtils.ActiveTool = InkTool.Pen; GestureOverlay.Instance.InkShape = tool; } } else if (tool) { // pen or eraser - if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) { - Doc.UserDoc().activeInkTool = InkTool.None; - } else if (tool == "write") { + if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) { + CurrentUserUtils.ActiveTool = InkTool.None; + } else if (tool == InkTool.Write) { // console.log("write mode selected - create groupDoc here!", tool) - Doc.UserDoc().activeInkTool = tool; + CurrentUserUtils.ActiveTool = tool; GestureOverlay.Instance.InkShape = ""; } else { - Doc.UserDoc().activeInkTool = tool; + CurrentUserUtils.ActiveTool = tool as any; GestureOverlay.Instance.InkShape = ""; } } else { - Doc.UserDoc().activeInkTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; } }); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 16a523b40..90199618b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -648,7 +648,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const highlighting: ContextMenuProps[] = []; const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others", "Bold Text"]; const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"]; - (Doc.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option => + (Doc.noviceMode ? noviceHighlighting : expertHighlighting).forEach(option => highlighting.push({ description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => { e.stopPropagation(); @@ -663,11 +663,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp })); const uicontrols: ContextMenuProps[] = []; - !Doc.UserDoc().noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" }); + !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" }); uicontrols.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" }); uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" }); uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" }); - !Doc.UserDoc().noviceMode && uicontrols.push({ + !Doc.noviceMode && uicontrols.push({ description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto => proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt" }); @@ -677,7 +677,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : []; appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" }); // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); - !Doc.UserDoc().noviceMode && appearanceItems.push({ + !Doc.noviceMode && appearanceItems.push({ description: "Make Default Layout", event: () => { if (!this.layoutDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); @@ -1636,7 +1636,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp noSidebar={true} fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />; }; - return
    {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
    ; @@ -1647,7 +1647,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const active = this.props.isContentActive(); const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; - const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition); + const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition); if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide); const minimal = this.props.ignoreAutoHeight; const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 9bc2e5628..98343a261 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -67,7 +67,6 @@ export class RichTextMenu extends AntimodeMenu { runInAction(() => { RichTextMenu.Instance = this; this._canFade = false; - //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); this.Pinned = true; }); } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 52b03b0a5..0d2cffc2c 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -134,7 +134,7 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); } constructor(props: any) { super(props); - if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this); + if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this); if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations. Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data" @@ -178,16 +178,16 @@ export class PresBox extends ViewBoxBaseComponent() { this.layoutDoc._gridGap = 0; this.layoutDoc._yMargin = 0; this.turnOffEdit(true); - DocListCastAsync((Doc.UserDoc().myTrails as Doc).data).then(pres => - !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myTrails as Doc, "data", this.rootDoc)); + DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres => + !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", this.rootDoc)); this._disposers.selection = reaction(() => SelectionManager.Views(), views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()); } @action updateCurrentPresentation = (pres?: Doc) => { - if (pres) Doc.UserDoc().activePresentation = pres; - else Doc.UserDoc().activePresentation = this.rootDoc; + if (pres) CurrentUserUtils.ActivePresentation = pres; + else CurrentUserUtils.ActivePresentation = this.rootDoc; document.removeEventListener("keydown", PresBox.keyEventsWrapper, true); document.addEventListener("keydown", PresBox.keyEventsWrapper, true); this._presKeyEventsActive = true; @@ -623,9 +623,9 @@ export class PresBox extends ViewBoxBaseComponent() { */ @action updateMinimize = async () => { - if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { + if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.layoutDoc.presStatus = PresStatus.Edit; - Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc); + Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc); CollectionDockingView.AddSplit(this.rootDoc, "right"); } else { this.layoutDoc.presStatus = PresStatus.Edit; @@ -635,7 +635,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.rootDoc.y = pt[1] + 10; this.rootDoc._height = 30; this.rootDoc._width = 248; - Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc); + Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc); this.props.removeDocument?.(this.layoutDoc); } } @@ -732,7 +732,7 @@ export class PresBox extends ViewBoxBaseComponent() { removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight panelHeight = () => this.props.PanelHeight() - 40; - isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) && + isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition) && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) /** @@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent() { } break; case "Escape": - if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); } + if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); } else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; } else this.layoutDoc.presStatus = "edit"; if (this._presTimer) clearTimeout(this._presTimer); @@ -2247,7 +2247,7 @@ export class PresBox extends ViewBoxBaseComponent() { const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel"; const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; - const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); + const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation); const activeColor = Colors.LIGHT_BLUE; const inactiveColor = Colors.WHITE; return (mode === CollectionViewType.Carousel3D) ? (null) : ( @@ -2305,7 +2305,7 @@ export class PresBox extends ViewBoxBaseComponent() { value={mode}> - {Doc.UserDoc().noviceMode ? (null) : } + {Doc.noviceMode ? (null) : } }
    @@ -2508,10 +2508,10 @@ export class PresBox extends ViewBoxBaseComponent() { // needed to ensure that the childDocs are loaded for looking up fields this.childDocs.slice(); const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; - const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); + const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation); const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1); const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0); - return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ? + return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ?
    e.stopPropagation()}>
    {"Loop"}
    }>
    () {
    : -
    +
    {this.topPanel} {this.toolbar} {this.newDocumentToolbarDropdown} diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 50df00612..1a2f4b93f 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -309,7 +309,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @computed get recordingIsInOverlay() { let isInOverlay = false - DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => { + DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => { if (doc.slides === this.rootDoc) { isInOverlay = true return @@ -319,9 +319,9 @@ export class PresElementBox extends ViewBoxBaseComponent() { } removeAllRecordingInOverlay = () => { - DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => { + DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => { if (doc.slides === this.rootDoc) { - Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); + Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc); } }) } @@ -339,7 +339,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { this.removeAllRecordingInOverlay() if (activeItem.recording) { // if we already have an existing recording - Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null)); + Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null)); } } @@ -348,15 +348,15 @@ export class PresElementBox extends ViewBoxBaseComponent() { @action startRecording = (activeItem: Doc) => { // Remove every recording that already exists in overlay view - DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => { + DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => { if (doc.slides !== null) { - Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); + Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc); } }) if (activeItem.recording) { // if we already have an existing recording - Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null)); + Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null)); } else { // if we dont have any recording @@ -376,7 +376,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { // make recording box appear in the bottom right corner of the screen recording.x = window.innerWidth - recording[WidthSym]() - 20; recording.y = window.innerHeight - recording[HeightSym]() - 20; - Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording); + Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, recording); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0ded1bb3c..2869d4f2d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -340,7 +340,7 @@ export class PDFViewer extends React.Component { if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true, false); } - if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) { + if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { this.props.select(false); MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 0db3950a2..1de0c0b10 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -27,12 +27,12 @@ import "./TopBar.scss"; export class TopBar extends React.Component { navigateToHome = () => { CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => { - Doc.UserDoc().activePage = "home"; + CurrentUserUtils.ActivePage = "home"; CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use }); } render() { - const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null) + const activeDashboard = CurrentUserUtils.ActiveDashboard; return ( //TODO:glr Add support for light / dark mode
    @@ -44,11 +44,11 @@ export class TopBar extends React.Component { }}>{Doc.CurrentUserEmail}
    : (null)}
    -
    SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}> +
    activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}> {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
    { - const dashView = DocumentManager.Instance.getDocumentView(activeDashboard); + const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard); ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" }); ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => { const batch = UndoManager.StartBatch("snapshot"); @@ -69,7 +69,7 @@ export class TopBar extends React.Component {
    {SharingManager.Instance.open(undefined, activeDashboard)}}> {/* TODO: if this is my dashboard, display share if this is a shared dashboard, display "view original or view annotated" */} - { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author === Doc.CurrentUserEmail ? "Share": "view original" } + { CurrentUserUtils.ActiveDashboard && (Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author === Doc.CurrentUserEmail ? "Share": "view original") }
    window.open( "https://brown-dash.github.io/Dash-Documentation/", "_blank")}> diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index e0d328c89..6c0c9b301 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -71,7 +71,7 @@ export class DashWebRTCVideo extends React.Component; const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); return ( <> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 94be286f5..74213652b 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -21,7 +21,7 @@ import { FieldId, RefField } from "./RefField"; import { RichTextField } from "./RichTextField"; import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; -import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; +import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField"; import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); @@ -233,6 +233,10 @@ export class Doc extends RefField { } private [CachedUpdates]: { [key: string]: () => void | Promise } = {}; + public static get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode); } + public static set noviceMode(val) { Doc.UserDoc().noviceMode = val; } + public static get defaultAclPrivate() { return Doc.UserDoc().defaultAclPrivate; } + public static set defaultAclPrivate(val) { Doc.UserDoc().defaultAclPrivate = val; } public static CurrentUserEmail: string = ""; public static get CurrentUserEmailNormalized() { return normalizeEmail(Doc.CurrentUserEmail); } public async [HandleUpdate](diff: any) { @@ -805,7 +809,7 @@ export namespace Doc { Doc.AddDocToList(Doc.GetProto(copy)[DataSym], "aliases", copy); } copy.context = undefined; - Doc.UserDoc().defaultAclPrivate && (copy["acl-Public"] = "Not Shared"); + Doc.defaultAclPrivate && (copy["acl-Public"] = "Not Shared"); if (retitle) { copy.title = incrementTitleCopy(StrCast(copy.title)); } @@ -861,7 +865,7 @@ export namespace Doc { const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")"); target.layoutKey = targetKey; applied && (Doc.GetProto(applied).type = templateDoc.type); - Doc.UserDoc().defaultAclPrivate && (applied["acl-Public"] = "Not Shared"); + Doc.defaultAclPrivate && (applied["acl-Public"] = "Not Shared"); return applied; } return undefined; @@ -1234,19 +1238,19 @@ export namespace Doc { export function isDocPinned(doc: Doc) { //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + const curPres = CurrentUserUtils.ActivePresentation; return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; } export function copyDragFactory(dragFactory: Doc) { const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - ndoc && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(ndoc)); + ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); if (ndoc && dragFactory["dragFactory-count"] !== undefined) { dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); } - if (ndoc) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); + if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); return ndoc; } @@ -1448,7 +1452,7 @@ ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a docum ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); }); ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); }); ScriptingGlobals.add(function activePresentationItem() { - const curPres = Doc.UserDoc().activePresentation as Doc; + const curPres = CurrentUserUtils.ActivePresentation; return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)]; }); ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { diff --git a/src/fields/util.ts b/src/fields/util.ts index 37b9be31b..8fb35981b 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -145,7 +145,7 @@ export function inheritParentAcls(parent: Doc, child: Doc) { const dataDoc = parent[DataSym]; for (const key of Object.keys(dataDoc)) { // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private. - const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key]; + const permission = (key === "acl-Public" && Doc.defaultAclPrivate) ? AclPrivate : dataDoc[key]; key.startsWith("acl") && distributeAcls(key, permission, child); } } diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index c5c6fb688..fe8100997 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -51,7 +51,7 @@ library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, fa @observer export class MobileInterface extends React.Component { static Instance: MobileInterface; - private _library: Promise; + private _library: Doc; private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc()); @observable private _sidebarActive: boolean = false; //to toggle sidebar display @observable private _imageUploadActive: boolean = false; //to toggle image upload @@ -68,7 +68,7 @@ export class MobileInterface extends React.Component { constructor(props: Readonly<{}>) { super(props); - this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web + this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), "myDashboards"); // to access documents in Dash Web MobileInterface.Instance = this; } @@ -76,7 +76,7 @@ export class MobileInterface extends React.Component { componentDidMount = () => { // if the home menu is in list view -> adjust the menu toggle appropriately this._menuListView = this._homeDoc._viewType === "stacking" ? true : false; - CurrentUserUtils.SelectedTool = InkTool.None; // ink should intially be set to none + CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home AudioBox.Enabled = true; @@ -125,7 +125,7 @@ export class MobileInterface extends React.Component { * Method called when 'Library' button is pressed on the home screen */ switchToLibrary = async () => { - this._library.then(library => this.switchCurrentView(library)); + this.switchCurrentView(this._library); runInAction(() => this._homeMenu = false); this.toggleSidebar(); } @@ -140,7 +140,7 @@ export class MobileInterface extends React.Component { // Case 1: Parent document is 'dashboards' if (doc === Cast(this._library, Doc) as Doc) { this.dashboards = null; - this._library.then(library => this.switchCurrentView(library)); + this.switchCurrentView(this._library); // Case 2: Parent document is the 'home' menu (root node) } else if (doc === Cast(this._homeDoc, Doc) as Doc) { this._homeMenu = true; @@ -179,7 +179,7 @@ export class MobileInterface extends React.Component { @action returnMain = () => { this._parents = [this._homeDoc]; - this._library.then(library => this.switchCurrentView(library)); + this.switchCurrentView(this._library); this._homeMenu = false; this.dashboards = null; } @@ -419,10 +419,10 @@ export class MobileInterface extends React.Component { button.style.color = this._ink ? "black" : "white"; if (!this._ink) { - CurrentUserUtils.SelectedTool = InkTool.Pen; + CurrentUserUtils.ActiveTool = InkTool.Pen; this._ink = true; } else { - CurrentUserUtils.SelectedTool = InkTool.None; + CurrentUserUtils.ActiveTool = InkTool.None; this._ink = false; } } @@ -514,7 +514,7 @@ export class MobileInterface extends React.Component { return
    TabDocView.PinDoc(this._activeDoc, { unpin: isPinned })}> + onClick={e => TabDocView.PinDoc(this._activeDoc)}>
    ; } else return (null); @@ -573,7 +573,7 @@ export class MobileInterface extends React.Component { // For setting up the presentation document for the home menu @action setupDefaultPresentation = () => { - const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + const presentation = CurrentUserUtils.ActivePresentation; if (presentation) { this.switchCurrentView(presentation); -- cgit v1.2.3-70-g09d2 From c415fb65e3d87b851015fb2ac7c41361609e0719 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Jun 2022 16:27:39 -0400 Subject: fixed contextmenu when hitting enter to not iconify. fixed : menu to add equations properly. --- src/client/documents/Documents.ts | 18 ++---------------- src/client/util/CurrentUserUtils.ts | 9 +++++---- src/client/views/ContextMenu.tsx | 1 + src/client/views/DocumentDecorations.tsx | 7 +++++-- src/fields/Doc.ts | 2 +- 5 files changed, 14 insertions(+), 23 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f96bffb3c..e36103938 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1290,21 +1290,7 @@ export namespace DocUtils { })) as ContextMenuProps[], icon: "sticky-note" }); - const math: ContextMenuProps = ({ - description: ":Math", event: () => { - const created = Docs.Create.EquationDocument(); - if (created) { - created.author = Doc.CurrentUserEmail; - created.x = x; - created.y = y; - created.width = 300; - created.height = 35; - EquationBox.SelectOnLoad = created[Id]; - docAdder?.(created); - } - }, icon: "calculator" - }); - const documentList: ContextMenuProps[] = DocListCast(Cast(Doc.UserDoc().myItemCreators, Doc, null)?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ + const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data).lastElement()?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title), event: undoBatch((args: { x: number, y: number }) => { const newDoc = Doc.copyDragFactory(dragDoc); @@ -1312,13 +1298,13 @@ export namespace DocUtils { newDoc.author = Doc.CurrentUserEmail; newDoc.x = x; newDoc.y = y; + EquationBox.SelectOnLoad = newDoc[Id]; if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id]; docAdder?.(newDoc); } }), icon: Doc.toIcon(dragDoc), })) as ContextMenuProps[]; - documentList.push(math); ContextMenu.Instance.addItem({ description: "Create document", subitems: documentList, diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7a38886fe..1d5a9fa09 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -77,7 +77,7 @@ export class CurrentUserUtils { static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) { scripts && Object.keys(scripts).map(key => { if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) { - doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any" }, {"_readOnly_": true}); + doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any" }, {"_readOnly_": true}); } }); funcs && Object.keys(funcs).map(key => { @@ -248,7 +248,8 @@ export class CurrentUserUtils { }[] = [ {key: "emptyPresentation", creator: Docs.Create.PresDocument, opts: { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }}, {key: "emptyCollection", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "freeform", _width: 150, _height: 100 }}, - {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts : { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }}, + {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }}, + {key: "emptyMath", creator: (opts) => Docs.Create.EquationDocument(opts), opts: { title: "Equation", _backgroundGridShow: true, _width: 300, _height: 35 }}, {key: "emptySlide", creator: (opts) => Docs.Create.TreeDocument([], opts), funcs: {title: 'self.text?.Text'}, opts: { _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined, allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" @@ -265,7 +266,7 @@ export class CurrentUserUtils { ]; emptyThings.forEach(thing => - this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), thing.funcs)) + this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), undefined, thing.funcs)) if (doc.emptyHeader === undefined) { const json = { @@ -309,6 +310,7 @@ export class CurrentUserUtils { return [ { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, { toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, clickFactory: doc.emptyPane as Doc, }, + { toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyMath as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, scripts: {onClick: 'openInOverlay(copyDragFactory(this.dragFactory))',onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, @@ -1213,7 +1215,6 @@ export class CurrentUserUtils { public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; } public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); } public static set ActivePage(val) { Doc.UserDoc().activePage = val; } - public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); } public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; } public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } } diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 80ff16cf9..e2f98de1e 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -287,6 +287,7 @@ export class ContextMenu extends React.Component { } this.closeMenu(); e.preventDefault(); + e.stopPropagation(); } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index b666b5977..d22fb5f01 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -7,8 +7,8 @@ import { DateField } from '../../fields/DateField'; import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from "../../fields/Doc"; import { Document } from '../../fields/documentSchemas'; import { InkField } from "../../fields/InkField"; -import { ScriptField } from '../../fields/ScriptField'; -import { Cast, NumCast, StrCast } from "../../fields/Types"; +import { ComputedField, ScriptField } from '../../fields/ScriptField'; +import { Cast, FieldValue, NumCast, StrCast } from "../../fields/Types"; import { GetEffectiveAcl } from '../../fields/util'; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../Utils"; import { Docs } from "../documents/Documents"; @@ -461,6 +461,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || ""; } if (this._titleControlString.startsWith("#")) { + const cfield = ComputedField.WithoutComputed(() => FieldValue(selected.props.Document.title)); + console.log(cfield); + (cfield as any)?.script.run().result; return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-"; } return this._accumulatedTitle; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 74213652b..981514b25 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -233,7 +233,7 @@ export class Doc extends RefField { } private [CachedUpdates]: { [key: string]: () => void | Promise } = {}; - public static get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode); } + public static get noviceMode() { return Doc.UserDoc().noviceMode as boolean; } public static set noviceMode(val) { Doc.UserDoc().noviceMode = val; } public static get defaultAclPrivate() { return Doc.UserDoc().defaultAclPrivate; } public static set defaultAclPrivate(val) { Doc.UserDoc().defaultAclPrivate = val; } -- cgit v1.2.3-70-g09d2 From 3e22a44461095d95062e3b809b525a0a87342ac3 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 20 Jun 2022 22:18:42 -0400 Subject: more cleanup to get rid of unecessary references to Doc.UserDoc(). --- src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 48 +++++++++++++--------- src/client/util/DragManager.ts | 4 +- src/client/util/History.ts | 2 +- src/client/util/SettingsManager.tsx | 19 ++++----- src/client/util/SnappingManager.ts | 4 ++ src/client/views/DashboardView.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/MainView.tsx | 21 ++++------ .../views/collections/CollectionDockingView.tsx | 4 +- src/client/views/collections/CollectionMenu.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/button/FontIconBox.tsx | 30 +++++++------- .../nodes/button/colorDropdown/ColorDropdown.tsx | 3 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- src/client/views/nodes/trails/PresBox.tsx | 11 +---- src/client/views/topbar/TopBar.tsx | 2 +- src/mobile/MobileInterface.tsx | 2 +- 22 files changed, 93 insertions(+), 89 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e36103938..73b756a8d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -662,7 +662,7 @@ export namespace Docs { const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_"); dataProps["acl-Override"] = "None"; - dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; dataProps.system = viewProps.system; dataProps.isPrototype = true; @@ -679,7 +679,7 @@ export namespace Docs { const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); const viewFirstProps: { [id: string]: any } = {}; - viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; + viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; viewFirstProps["acl-Override"] = "None"; viewFirstProps.author = Doc.CurrentUserEmail; const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1d5a9fa09..9b5fc5b98 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -181,7 +181,12 @@ export class CurrentUserUtils { /// Initializes collection of templates for notes and click functions static setupDocTemplates(doc: Doc, field="myTemplates") { + const preEleOpts:DocumentOptions = { + title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data" + }; + this.AssignOpts(DocCast(doc.presElement), preEleOpts) ?? (doc.presElement= Docs.Create.PresElementBoxDocument(preEleOpts)); const templates = [ + DocCast(doc.presElement), CurrentUserUtils.setupNoteTemplates(doc), CurrentUserUtils.setupClickEditorTemplates(doc) ]; @@ -323,8 +328,7 @@ export class CurrentUserUtils { { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}}, { toolTip: "Tap or drag to create a mobile view", title: "Phone", icon: "mobile", dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)', onDragStart: 'this.dragFactory'}, funcs: {hidden: 'IsNoviceMode()'} }, { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } }, - // { toolTip: "Tap or drag to create a presentation", title: "Trails", icon: "pres-trail", dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} }, - ]; + ]; } /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools @@ -533,7 +537,7 @@ export class CurrentUserUtils { static setupDashboards(doc: Doc, field:string) { var myDashboards = DocCast(doc[field]); - const newDashboard = `createNewDashboard(Doc.UserDoc())`; + const newDashboard = `createNewDashboard()`; const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true }; const reqdBtnScript = {onClick: newDashboard,} @@ -830,6 +834,11 @@ export class CurrentUserUtils { const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true }; return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts)); } + + static setupPublished(doc:Doc, field = "myPublishedDocs") { + const reqdOpts = { title: "published docs", backgroundColor: "#aca3a6", system: true }; + return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); + } /// The database of all links on all documents static async setupLinkDocs(doc: Doc, linkDatabaseId: string) { @@ -958,7 +967,6 @@ export class CurrentUserUtils { Doc.noviceMode ?? (Doc.noviceMode = true); doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true); doc._showLabel ?? (doc._showLabel = true); - doc._showMenuLabel ?? (doc._showMenuLabel = true); doc.textAlign ?? (doc.textAlign = "left"); doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");; doc.activeInkWidth ?? (doc.activeInkWidth = 1); @@ -975,17 +983,17 @@ export class CurrentUserUtils { doc.savedFilters ?? (doc.savedFilters = new List()); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; - doc.myPublishedDocs ?? (doc.myPublishedDocs = new List()); doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents await this.setupLinkDocs(doc, linkDatabaseId); await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing 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.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile this.setupOverlays(doc); // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard + this.setupPublished(doc); // sets up the list doc of all docs that have been published (meaning that they can be auto-linked by typing their title into another text box) this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected this.setupDockedButtons(doc); // the bottom bar of font icons this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left + this.setupDocTemplates(doc); // sets up the template menu of templates doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument()); setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); @@ -1037,7 +1045,8 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; - public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => { + public static openDashboard = (doc: Doc, fromHistory = false) => { + const userDoc = Doc.UserDoc(); CurrentUserUtils.MainDocId = doc[Id]; if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) { Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); @@ -1045,7 +1054,7 @@ export class CurrentUserUtils { if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); - userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); + userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); } const state = CurrentUserUtils._urlState; if (state.sharing === true && !userDoc) { @@ -1138,11 +1147,11 @@ export class CurrentUserUtils { } - public static async snapshotDashboard(userDoc: Doc) { + public static async snapshotDashboard() { if (CurrentUserUtils.ActiveDashboard) { const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); - CurrentUserUtils.openDashboard(userDoc, copy); + CurrentUserUtils.openDashboard(copy); } } @@ -1150,9 +1159,9 @@ export class CurrentUserUtils { CurrentUserUtils.ActiveDashboard = undefined; } - public static createNewDashboard = async (userDoc: Doc, id?: string) => { - const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); - const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; + public static createNewDashboard = (id?: string) => { + const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); + const dashboards = CurrentUserUtils.MyDashboards; const dashboardCount = DocListCast(dashboards.data).length + 1; const freeformOptions: DocumentOptions = { x: 0, @@ -1172,10 +1181,10 @@ export class CurrentUserUtils { dashboardDoc.data = new List(dashboardTabs); dashboardDoc["pane-count"] = 1; - userDoc.activePresentation = presentation; + CurrentUserUtils.ActivePresentation = presentation; Doc.AddDocToList(dashboards, "data", dashboardDoc); - // CurrentUserUtils.openDashboard(userDoc, dashboardDoc); + // CurrentUserUtils.openDashboard(dashboardDoc); } public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) { @@ -1209,6 +1218,7 @@ export class CurrentUserUtils { public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); } public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); } public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); } + public static get MyPublishedDocs() { return DocCast(Doc.UserDoc().myPublishedDocs); } public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); } public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; } public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); } @@ -1230,8 +1240,8 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs"); ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); -ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard"); -ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called"); +ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(); }, "creates a snapshot copy of a dashboard"); +ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called"); ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called"); ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); @@ -1240,7 +1250,7 @@ ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.In ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); if (dashboards && dashboards.length > 1) { - if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!); + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); } }, @@ -1248,7 +1258,7 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); - CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); + CurrentUserUtils.openDashboard(dashboardAlias); }, "adds Dashboard to set of Dashboards"); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index dbfba8992..2b62945af 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -6,7 +6,7 @@ import { PrefetchProxy } from "../../fields/Proxy"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ScriptField } from "../../fields/ScriptField"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types"; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types"; import { emptyFunction, Utils } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; import * as globalCssVariables from "../views/global/globalCssVariables.scss"; @@ -72,6 +72,8 @@ export namespace DragManager { export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>; export let CompleteWindowDrag: Opt<(aborted: boolean) => void>; + export function GetRaiseWhenDragged() { return BoolCast(Doc.UserDoc()._raiseWhenDragged); } + export function SetRaiseWhenDragged(val:boolean) { Doc.UserDoc()._raiseWhenDragged = val } export function Root() { const root = document.getElementById("root"); if (!root) { diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 632348306..7dcff9c56 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -197,7 +197,7 @@ export namespace HistoryUtil { await Promise.all(Object.keys(init).map(id => initDoc(id, init[id]))); } if (field instanceof Doc) { - CurrentUserUtils.openDashboard(Doc.UserDoc(), field, true); + CurrentUserUtils.openDashboard(field, true); } } diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 382274462..22e33ab1e 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -10,7 +10,9 @@ import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager import { DocServer } from "../DocServer"; import { Networking } from "../Network"; import { MainViewModal } from "../views/MainViewModal"; +import { FontIconBox } from "../views/nodes/button/FontIconBox"; import { CurrentUserUtils } from "./CurrentUserUtils"; +import { DragManager } from "./DragManager"; import { GroupManager } from "./GroupManager"; import "./SettingsManager.scss"; import { undoBatch } from "./UndoManager"; @@ -155,19 +157,14 @@ export class SettingsManager extends React.Component<{}> {
    Show full toolbar
    - Doc.UserDoc()._raiseWhenDragged = !Doc.UserDoc()._raiseWhenDragged} - checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} /> + DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())} + checked={DragManager.GetRaiseWhenDragged()} />
    Raise on drag
    - Doc.UserDoc()._showLabel = !Doc.UserDoc()._showLabel} - checked={BoolCast(Doc.UserDoc()._showLabel)} /> -
    Show tool button labels
    -
    -
    - Doc.UserDoc()._showMenuLabel = !Doc.UserDoc()._showMenuLabel} - checked={BoolCast(Doc.UserDoc()._showMenuLabel)} /> -
    Show menu button labels
    + FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} + checked={FontIconBox.GetShowLabels()} /> +
    Show button labels
    ; } @@ -271,7 +268,7 @@ export class SettingsManager extends React.Component<{}> {
    - Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
    Default access private
    diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index 069f81d38..057843c68 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -1,5 +1,6 @@ import { observable, action, runInAction } from "mobx"; import { computedFn } from "mobx-utils"; +import { Doc } from "../../fields/Doc"; export namespace SnappingManager { @@ -30,6 +31,9 @@ export namespace SnappingManager { export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); } export function GetIsDragging() { return manager.IsDragging; } + export function SetShowSnapLines(show: boolean) { runInAction(() => Doc.UserDoc().showSnapLines = show); } + export function GetShowSnapLines() { return Doc.UserDoc().showSnapLines; } + /// bcz; argh!! TODO; These do not belong here, but there were include order problems with leaving them in util.ts // need to investigate further what caused the mobx update problems and move to a better location. const getCachedGroupByNameCache = computedFn(function (name: string) { return manager.cachedGroups.includes(name); }, true); diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 9a0f25fe3..a126218c4 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -28,7 +28,7 @@ export class DashboardView extends React.Component { newDashboard = async () => { const batch = UndoManager.StartBatch("new dash"); - await CurrentUserUtils.createNewDashboard(Doc.UserDoc()); + await CurrentUserUtils.createNewDashboard(); batch.end(); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 18cf785b9..4247501bb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -87,10 +87,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P if (titleFieldKey === "title") { d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"); if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) { - Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc); + Doc.RemoveDocFromList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc); } if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) { - Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc); + Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc); } } //@ts-ignore diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 61cca0421..6ee8065f5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -225,26 +225,21 @@ export class MainView extends React.Component { // Load the user's active dashboard, or create a new one if initial session after signup const received = CurrentUserUtils.MainDocId; if (received && !this.userDoc) { - reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true }); + reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(), { fireImmediately: true }); } else { PromiseValue(this.userDoc.activeDashboard).then(dash => { - if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash); - else CurrentUserUtils.createNewDashboard(this.userDoc); + if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash); + else CurrentUserUtils.createNewDashboard(); }); } } @action createNewPresentation = async () => { - if (!await this.userDoc.myTrails) { - this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true - })); - } const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); CollectionDockingView.AddSplit(pres, "left"); - this.userDoc.activePresentation = pres; - Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres); + CurrentUserUtils.ActivePresentation = pres; + Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", pres); } @action @@ -353,9 +348,9 @@ export class MainView extends React.Component { addDocTabFunc = (doc: Doc, location: string): boolean => { const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); const locationParams = locationFields.length > 1 ? locationFields[1] : ""; - if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc); switch (locationFields[0]) { - case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + case "dashboard": return CurrentUserUtils.openDashboard(doc); case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); case "lightbox": return LightboxView.AddDocTab(doc, location); @@ -563,7 +558,7 @@ export class MainView extends React.Component {
    ; } @computed get snapLines() { - return !this.userDoc.showSnapLines ? (null) :
    + return !SnappingManager.GetShowSnapLines() ? (null) :
    {SnappingManager.horizSnapLines().map(l => )} {SnappingManager.vertSnapLines().map(l => )} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 5f36a7a51..19355b8e1 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -121,7 +121,7 @@ export class CollectionDockingView extends CollectionSubView() { SelectionManager.DeselectAll(); const instance = CollectionDockingView.Instance; if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { - return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + return CurrentUserUtils.openDashboard(doc); } const newItemStackConfig = { type: 'stack', @@ -172,7 +172,7 @@ export class CollectionDockingView extends CollectionSubView() { @undoBatch @action public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { - if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document); + if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document); const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document); if (tab) { diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 9b1bb5b97..668d82387 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -164,7 +164,7 @@ export class CollectionMenu extends AntimodeMenu{ // // ; - // OLD BUTTONS + // //OLD BUTTONS // return this.getElement(!this.SelectedCollection ? [/*button*/] : // [ { - const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc; + const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc; //@ts-ignore target._viewType = e.target.selectedOptions[0].value; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 4b5c5e3fb..63616263e 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -205,7 +205,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" }); + optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" }); } !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" }); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 70db121d1..62d07b0e4 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -329,7 +329,7 @@ export class TabDocView extends React.Component { const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); const locationParams = locationFields.length > 1 ? locationFields[1] : ""; switch (locationFields[0]) { - case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + case "dashboard": return CurrentUserUtils.openDashboard(doc); case "close": return CollectionDockingView.CloseSplit(doc, locationParams); case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ffe146ae4..99eec7892 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -253,7 +253,7 @@ export class CollectionFreeFormView extends CollectionSubView Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; + !Doc.noviceMode ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: "compress-arrows-alt" }) : null; !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index b62020a04..081a1a924 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -6,7 +6,7 @@ import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { RichTextField } from "../../../../fields/RichTextField"; import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; +import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; import { ImageField } from "../../../../fields/URLField"; import { GetEffectiveAcl } from "../../../../fields/util"; import { intersectRect, returnFalse, Utils } from "../../../../Utils"; @@ -156,7 +156,7 @@ export class MarqueeView extends React.Component StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name); - (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc); + (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc); if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (this._timeout) { clearTimeout(this._timeout); diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 3af6a3d51..1b37e729b 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -79,6 +79,9 @@ export class FontIconBox extends DocComponent() { } } + static GetShowLabels() { return BoolCast(Doc.UserDoc()._showLabel); } + static SetShowLabels(show:boolean) { Doc.UserDoc()._showLabel = show; } + // Determining UI Specs @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); @observable private icon = StrCast(this.dataDoc.icon, "user") as any; @@ -111,7 +114,7 @@ export class FontIconBox extends DocComponent() { // Script for checking the outcome of the toggle const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; - const label = !Doc.UserDoc()._showLabel ? (null) : + const label = !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    ; @@ -212,7 +215,7 @@ export class FontIconBox extends DocComponent() { style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> - {!this.label || !Doc.UserDoc()._showLabel ? (null) :
    {this.label}
    } + {!this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    }
    @@ -283,7 +286,7 @@ export class FontIconBox extends DocComponent() {
    ; }); - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    ; @@ -337,7 +340,7 @@ export class FontIconBox extends DocComponent() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    ; @@ -349,7 +352,7 @@ export class FontIconBox extends DocComponent() {
    ; setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView return ( -
    this.colorPickerClosed = !this.colorPickerClosed)} onPointerDown={e => e.stopPropagation()}> @@ -381,7 +384,7 @@ export class FontIconBox extends DocComponent() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); // Button label - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    ; @@ -400,7 +403,7 @@ export class FontIconBox extends DocComponent() { ); } else { return ( -
    {label} @@ -423,7 +426,7 @@ export class FontIconBox extends DocComponent() { style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
    - {!this.label || !Doc.UserDoc()._showLabel ? (null) : + {!this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    }
    @@ -450,12 +453,12 @@ export class FontIconBox extends DocComponent() { render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    ; - const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : + const menuLabel = !this.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.label}
    ; @@ -497,7 +500,7 @@ export class FontIconBox extends DocComponent() { break; case ButtonType.ToolButton: button = ( -
    +
    {label}
    @@ -509,7 +512,7 @@ export class FontIconBox extends DocComponent() { break; case ButtonType.ClickButton: button = ( -
    +
    {label}
    @@ -675,10 +678,9 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent"; + return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : "transparent"; } if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor(); - else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false; }); ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx index 235495250..7f414ddbb 100644 --- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx +++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx @@ -5,6 +5,7 @@ import { IButtonProps } from '../ButtonInterface'; import { ColorState, SketchPicker } from 'react-color'; import { ScriptField } from '../../../../../fields/ScriptField'; import { Doc } from '../../../../../fields/Doc'; +import { FontIconBox } from '../FontIconBox'; export class ColorDropdown extends Component { render() { @@ -31,7 +32,7 @@ export class ColorDropdown extends Component { disableAlpha={!stroke} onChange={func} color={boolResult ? boolResult : "#FFFFFF"} presetColors={colorOptions} />; - const label = !this.props.label || !Doc.UserDoc()._showLabel ? (null) : + const label = !this.props.label || !FontIconBox.GetShowLabels() ? (null) :
    {this.props.label}
    ; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 90199618b..600730d87 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -355,7 +355,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp var tr = this._editorView.state.tr as any; const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor; tr = tr.removeMark(0, tr.doc.content.size, autoAnch); - DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks)); + DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks)); tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); this._editorView?.dispatch(tr); oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink); @@ -376,7 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!(cfield instanceof ComputedField)) { this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : ""); if (str.startsWith("@") && str.length > 1) { - Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc); + Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc); } } } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 0d2cffc2c..9f858539f 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -10,7 +10,7 @@ import { InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; import { PrefetchProxy } from "../../../../fields/Proxy"; import { listSpec } from "../../../../fields/Schema"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types"; import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; @@ -131,17 +131,10 @@ export class PresBox extends ViewBoxBaseComponent() { if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true; else return false; } - @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); } constructor(props: any) { super(props); if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this); - if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations. - Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ - title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data" - })); - } this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox - } @computed get selectedDocumentView() { if (SelectionManager.Views().length) return SelectionManager.Views()[0]; @@ -728,7 +721,7 @@ export class PresBox extends ViewBoxBaseComponent() { }); return true; } - childLayoutTemplate = () => !this.isTreeOrStack ? undefined : this.presElement; + childLayoutTemplate = () => !this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement); removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight panelHeight = () => this.props.PanelHeight() - 40; diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 1de0c0b10..6a4deca38 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -52,7 +52,7 @@ export class TopBar extends React.Component { ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" }); ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => { const batch = UndoManager.StartBatch("snapshot"); - await CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); + await CurrentUserUtils.snapshotDashboard(); batch.end(); }, icon: "edit" }); dashView?.showContextMenu(e.clientX+20, e.clientY+30); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index fe8100997..bf06faeb9 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -391,7 +391,7 @@ export class MobileInterface extends React.Component { * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx) */ @action - createNewDashboard = async (id?: string) => { + createNewDashboard = (id?: string) => { const scens = CurrentUserUtils.MyDashboards; const dashboardCount = DocListCast(scens.data).length + 1; const freeformOptions: DocumentOptions = { -- cgit v1.2.3-70-g09d2 From 0dd3bbfc119d48bd2c32f8e55b7051e3ddc530c3 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 21 Jun 2022 14:40:04 -0400 Subject: restored experimental templates. fixed (enabled) pointer events on nested column/row views when container is active. got rid of freezeDimensions which wasn't needed and introduced artifacts for views that set it (timeView, GridView) --- src/client/documents/Documents.ts | 1 + src/client/util/CurrentUserUtils.ts | 74 +++++++++++----------- src/client/util/DragManager.ts | 11 ++-- src/client/views/PropertiesView.tsx | 1 - .../views/collections/CollectionStackingView.tsx | 1 - .../views/collections/CollectionTimeView.tsx | 5 +- src/client/views/collections/CollectionView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - .../collectionGrid/CollectionGridView.tsx | 1 - .../CollectionMulticolumnView.tsx | 14 ++-- .../CollectionMultirowView.tsx | 6 +- .../collectionSchema/CollectionSchemaView.tsx | 1 - .../collections/collectionSchema/SchemaTable.tsx | 1 - src/client/views/nodes/DocumentContentsView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 7 +- src/client/views/nodes/button/FontIconBox.tsx | 4 +- 16 files changed, 61 insertions(+), 69 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 73b756a8d..59391d150 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -194,6 +194,7 @@ export class DocumentOptions { childLimitHeight?: number; // whether to limit the height of collection children. 0 - means height can be no bigger than width childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view) childLayoutString?: string; // template string for collection to use to render its children + childDocumentsActive?: boolean; // whether child documents are active when parent is document active childDontRegisterViews?: boolean; childHideLinkButton?: boolean; // hide link buttons on all children childContextMenuFilters?: List; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 9b5fc5b98..e80fe6776 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,6 +1,6 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; -import { DataSym, Doc, DocListCast, DocListCastAsync, StrListCast } from "../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, Opt, StrListCast } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; @@ -109,40 +109,43 @@ export class CurrentUserUtils { } // initializes experimental advanced template views - slideView, headerView - static setupExperimentalTemplateButtons(doc: Doc, field="template-experimental-buttons") { - const tempDocs = DocCast(doc[field]); - const requiredTypeNameFields:{opts:DocumentOptions, template:() => Doc}[] = [ + static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) { + const requiredTypeNameFields:{btnOpts:DocumentOptions, templateOpts:DocumentOptions, template:(opts:DocumentOptions) => Doc}[] = [ { - opts:{type: "slide", icon: "address-card"}, template: () => Docs.Create.MultirowDocument( + btnOpts: { title: "slide", icon: "address-card" }, + templateOpts: { _width: 400, _height: 300, title: "slideView", childDocumentsActive: true, _xMargin: 3, _yMargin: 3, system: true }, + template: (opts:DocumentOptions) => Docs.Create.MultirowDocument( [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) - ], - { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } - ) + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), + Docs.Create.TextDocument("", { title: "text", _fitWidth:true, _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) + ], opts) }, { - opts:{type: "mobile", icon: "mobile"}, template: () => this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, }, - [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }), - this.mobileTextContainer({}, - [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")]) - ] + btnOpts: { title: "mobile", icon: "mobile" }, + templateOpts: { title: "NEW MOBILE BUTTON", onClick: undefined, }, + template: (opts:DocumentOptions) => this.mobileButton(opts, + [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }), + this.mobileTextContainer({}, + [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")]) + ] ) }, ]; - const requiredTypes = requiredTypeNameFields.map(({ opts, template }) => { - const docType = DocListCast(tempDocs?.data)?.find(doc => doc.title === opts.type); - const reqdOpts = { - dragFactory: template(), - title: opts.type, - icon: opts.icon + const requiredTypes = requiredTypeNameFields.map(({ btnOpts, template, templateOpts }) => { + const tempBtn = DocListCast(tempDocs?.data)?.find(doc => doc.title === btnOpts.title); + const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' }; + const assignBtnAndTempOpts = (templateBtn:Opt, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => { + if (templateBtn) { + this.AssignOpts(templateBtn,btnOpts); + this.AssignOpts(DocCast(templateBtn.dragFactory), templateOptions) ?? (templateBtn.dragFactory = template(templateOpts)); + } + return templateBtn; }; - const reqdScripts = {onDragStart: 'copyDragFactory(this.dragFactory)'}; const makeTemp = (doc:Doc) => { doc.isTemplateDoc = makeTemplate(doc); return doc; } - return this.AssignScripts(!docType ? makeTemp(CurrentUserUtils.createToolButton(reqdOpts)) : this.AssignOpts(docType, reqdOpts)!, reqdScripts)!; + return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts); }); const reqdOpts = { @@ -150,12 +153,9 @@ export class CurrentUserUtils { _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; - const reqdScripts = {dropConverter : "convertToButtons(dragData)"}; - const reqdFuncs = {hidden: "IsNoviceMode()"} - return this.AssignScripts(!tempDocs ? - (doc[field] = Docs.Create.MasonryDocument(requiredTypes, reqdOpts)) : - this.AssignOpts(tempDocs, reqdOpts, requiredTypes)!, - reqdScripts, reqdFuncs); + const reqdScripts = { dropConverter : "convertToButtons(dragData)" }; + const reqdFuncs = { hidden: "IsNoviceMode()" }; + return this.AssignScripts(this.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs); } /// Initializes templates that can be applied to notes @@ -525,12 +525,12 @@ export class CurrentUserUtils { static setupToolsBtnPanel(doc: Doc, field:string) { const myTools = DocCast(doc[field]); const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined); - //const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc); + const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc,DocListCast(myTools?.data)?.length > 1 ? DocListCast(myTools.data)[1]:undefined); const reqdToolOps:DocumentOptions = { title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0", _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, }; - this.AssignOpts(myTools, reqdToolOps, [creatorBtns, /*templateBtns*/]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, /*templateBtns*/], reqdToolOps)); + this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps)); } /// initializes the left sidebar dashboard pane @@ -749,7 +749,7 @@ export class CurrentUserUtils { } static schemaTools():Button[] { - return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{onClick:'toggleSchemaPreview(_readOnly_)'}, }]; + return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{ onClick: '{return toggleSchemaPreview(_readOnly_);}'}, }]; } static webTools() { @@ -774,9 +774,9 @@ export class CurrentUserUtils { }, // Always show { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left", scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} }, { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, - { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: 'selectedDocumentType()' }}, // Only when a document is selected - { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: 'selectedDocumentType()'} }, - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: 'selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform + { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: '!selectedDocumentType()' }}, // Only when a document is selected + { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: '!selectedDocumentType()'} }, + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected { title: "Text", icon: "text", subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available @@ -983,7 +983,6 @@ export class CurrentUserUtils { doc.savedFilters ?? (doc.savedFilters = new List()); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; - doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents await this.setupLinkDocs(doc, linkDatabaseId); await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon @@ -995,7 +994,8 @@ export class CurrentUserUtils { this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left this.setupDocTemplates(doc); // sets up the template menu of templates doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument()); - + doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents + setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); doc.fieldInfos = await Docs.setupFieldInfos(); if (doc.activeDashboard instanceof Doc) { diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 2b62945af..09b463c2f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -223,10 +223,13 @@ export namespace DragManager { if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; docDragData.droppedDocuments = - await Promise.all(dragData.draggedDocuments.map(async d => !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) : - docDragData.dropAction === "alias" ? Doc.MakeAlias(d) : - docDragData.dropAction === "proto" ? Doc.GetProto(d) : - docDragData.dropAction === "copy" ? (await Doc.MakeClone(d)).clone : d)); + await Promise.all(dragData.draggedDocuments.map(async d => + !dragData.isDocDecorationMove && !dragData.userDropAction && ScriptCast(d.onDragStart) ? + addAudioTag(ScriptCast(d.onDragStart).script.run({ this: d }).result) : + docDragData.dropAction === "alias" ? Doc.MakeAlias(d) : + docDragData.dropAction === "proto" ? Doc.GetProto(d) : + docDragData.dropAction === "copy" ? + (await Doc.MakeClone(d)).clone : d)); !["same", "proto"].includes(docDragData.dropAction as any) && docDragData.droppedDocuments.forEach((drop: Doc, i: number) => { const dragProps = Cast(dragData.draggedDocuments[i].removeDropProperties, listSpec("string"), []); const remProps = (dragData?.removeDropProperties || []).concat(Array.from(dragProps)); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 90c86fa18..faab2ed26 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -304,7 +304,6 @@ export class PropertiesView extends React.Component { rootSelected={returnFalse} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} - freezeDimensions={true} dontCenter={"y"} isDocumentActive={returnFalse} isContentActive={emptyFunction} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 277fcd59c..684c919bd 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -250,7 +250,6 @@ export class CollectionStackingView extends CollectionSubView { - this._childClickedScript = ScriptField.MakeScript("openInLightbox(self, shiftKey)", { this: Doc.name, shiftKey: "boolean" });//, { detailView: detailView! }); + this._childClickedScript = ScriptField.MakeScript("openInLightbox(self)", { this: Doc.name }); this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); }); } @@ -138,8 +138,7 @@ export class CollectionTimeView extends CollectionSubView() { fitContentsToBox={returnTrue} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} - childFreezeDimensions={true} - dontScaleFilter={this.dontScaleFilter} + //dontScaleFilter={this.dontScaleFilter} layoutEngine={this.layoutEngine} />
    ; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 63616263e..b432104a1 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -75,7 +75,6 @@ export interface CollectionViewProps extends FieldViewProps { childHideResizeHandles?: () => boolean; childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection childLayoutString?: string; - childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height childIgnoreNativeSize?: boolean; childClickScript?: ScriptField; childDoubleClickScript?: ScriptField; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 99eec7892..542b1fce1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1265,7 +1265,6 @@ export class CollectionFreeFormView extends CollectionSubView this.props.isSelected() || this.props.isContentActive(); - isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; + isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return this.props.isSelected() || this.props.isContentActive(); - isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; + isChildContentActive = () => ((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false; getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { return { DataDoc={this._showDataDoc} styleProvider={DefaultStyleProvider} docViewPath={returnEmptyDoclist} - freezeDimensions={true} focus={DocUtils.DefaultFocus} renderDepth={this.props.renderDepth} rootSelected={returnFalse} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 70732e74c..db076c069 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -143,7 +143,6 @@ export class DocumentContentsView extends React.Component, onInput: Opt): JsxBindings { const docOnlyProps = [ // these are the properties in DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews - "freezeDimensions", "hideResizeHandles", "hideTitle", "treeViewDoc", diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 89096c948..263e1dc4c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -160,7 +160,6 @@ export interface DocumentViewSharedProps { // these props are specific to DocuentViews export interface DocumentViewProps extends DocumentViewSharedProps { // properties specific to DocumentViews but not to FieldView - freezeDimensions?: boolean; hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings @@ -1220,11 +1219,11 @@ export class DocumentView extends React.Component { @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } @computed get nativeWidth() { return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : - returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions)); + returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth)); } @computed get nativeHeight() { return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : - returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions)); + returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth)); } @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || @@ -1312,8 +1311,6 @@ export class DocumentView extends React.Component { ContentScale = () => this.nativeScaling; selfView = () => this; screenToLocalTransform = () => { - const oshift = this.fitWidth && this.ComponentView instanceof FormattedTextBox; - const shift = oshift ? -(this.props.PanelHeight() - this.rootDoc[HeightSym]()) / 2 : 0; return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).translate(0, shift).scale(1 / this.nativeScaling); } componentDidMount() { diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 1b37e729b..85efc67a5 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -903,9 +903,9 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { } else if (selected) { if (NumCast(selected.schemaPreviewWidth) > 0) { - selected.schemaPreviewWidth = 200; - } else { selected.schemaPreviewWidth = 0; + } else { + selected.schemaPreviewWidth = 200; } } }); -- cgit v1.2.3-70-g09d2 From 566b33847cff9217ed6e4e11bed2e1b92798bc95 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Tue, 21 Jun 2022 18:16:46 -0400 Subject: trying to get basic data viz view to work --- package-lock.json | 871 ++++++++++++------------ src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 9 + src/client/util/CurrentUserUtils.ts | 5 +- src/client/views/nodes/DataViz.scss | 0 src/client/views/nodes/DataViz.tsx | 14 + src/client/views/nodes/DocumentContentsView.tsx | 3 +- 7 files changed, 464 insertions(+), 439 deletions(-) create mode 100644 src/client/views/nodes/DataViz.scss create mode 100644 src/client/views/nodes/DataViz.tsx (limited to 'src/client/util') diff --git a/package-lock.json b/package-lock.json index 9dc7f1b04..e80f370d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -895,7 +895,7 @@ "@types/bcrypt-nodejs": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/@types/bcrypt-nodejs/-/bcrypt-nodejs-0.0.30.tgz", - "integrity": "sha512-gSWCu7EOXhcM0FYM8tanV0deaX1VM07sgBS8dSUmgnAQ5/3Xn6k+mWF+ZfE+c/OCW14IVnNdTOKcHgvL78oDFQ==", + "integrity": "sha1-TN2WtJKTs5MhIuS34pVD415rrlg=", "dev": true }, "@types/bezier-js": { @@ -1991,7 +1991,7 @@ "@types/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", "dev": true }, "@types/strip-json-comments": { @@ -2027,7 +2027,7 @@ "@types/typescript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz", - "integrity": "sha512-WMEWfMISiJ2QKyk5/dSdgL0ZwP//PZj0jmDU0hMh51FmLq4WIYzjlngsUQZXejQL+QtkXJUOGjb3G3UCvgZuSQ==", + "integrity": "sha1-xDNTnJi64oaCswfqp6D9IRW4PCg=", "dev": true, "requires": { "typescript": "*" @@ -2381,12 +2381,16 @@ "Base64": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz", - "integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw==" + "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg=" }, "D": { - "version": "1.0.0", + "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==" + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } }, "abab": { "version": "2.0.6", @@ -2424,7 +2428,7 @@ "acorn-globals": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha512-uWttZCk96+7itPxK8xCzY86PnxKTMrReKDqrHzv42VQY0K30PUO8WY13WMOuI+cOdX4EIdzdvQ8k6jkuGRFMYw==", + "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "requires": { "acorn": "^4.0.4" }, @@ -2432,7 +2436,7 @@ "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug==" + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" } } }, @@ -2461,7 +2465,7 @@ "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, "agent-base": { "version": "6.0.2", @@ -2536,7 +2540,7 @@ "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -2551,7 +2555,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -2561,12 +2565,12 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==" + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-align": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "requires": { "string-width": "^2.0.0" } @@ -2613,7 +2617,7 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, "anymatch": { "version": "2.0.0", @@ -2627,7 +2631,7 @@ "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "requires": { "remove-trailing-separator": "^1.0.1" } @@ -2712,7 +2716,7 @@ "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, "arr-flatten": { "version": "1.1.0", @@ -2722,7 +2726,7 @@ "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, "array-back": { "version": "2.0.0", @@ -2813,23 +2817,23 @@ "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==" + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { "array-uniq": "^1.0.1" @@ -2838,13 +2842,13 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "array.prototype.reduce": { "version": "1.0.4", @@ -2871,7 +2875,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { "version": "0.2.6", @@ -2884,7 +2888,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assertion-error": { "version": "1.1.0", @@ -2894,7 +2898,7 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==" + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { "version": "2.6.4", @@ -2912,7 +2916,7 @@ "async-foreach": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==" + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" }, "async-limiter": { "version": "1.0.1", @@ -2923,7 +2927,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -2973,7 +2977,7 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.11.0", @@ -2991,12 +2995,12 @@ "babel": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz", - "integrity": "sha512-ZDcCaI8Vlct8PJ3DvmyqUz+5X2Ylz3ZuuItBe/74yXosk2dwyVo/aN7MCJ8HJzhnnJ+6yP4o+lDgG9MBe91DLA==" + "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ=" }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { "chalk": "^1.1.3", @@ -3007,19 +3011,19 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { "ansi-styles": "^2.2.1", @@ -3032,7 +3036,7 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "strip-ansi": { @@ -3094,12 +3098,12 @@ "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -3120,7 +3124,7 @@ "babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "requires": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", @@ -3143,7 +3147,7 @@ "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "balanced-match": { "version": "1.0.2", @@ -3167,7 +3171,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { "is-descriptor": "^1.0.0" } @@ -3203,12 +3207,12 @@ "base16": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" }, "base64-arraybuffer": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==" + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" }, "base64-js": { "version": "1.5.1", @@ -3228,18 +3232,18 @@ "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, "bcrypt-nodejs": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz", - "integrity": "sha512-NmTbLm867btBHCBZ222FQXkQKzecB0KG6pTXFa6NeTVZaSnLfCsx7EK2PL3J+kX8xJThUquEBbhimRCKKZX9zA==" + "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=" }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "requires": { "tweetnacl": "^0.14.3" } @@ -3247,7 +3251,7 @@ "bezier-curve": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz", - "integrity": "sha512-h6uZJ6qdFfswS1rIRericgouhTeiVi/MnH10OKtCu2IZzXa+ZcjaxRLHY4u/evRsJcxYbbiNkgWQj2Z4UIcpEQ==" + "integrity": "sha1-o9+v6rEqlMRicw1QeYxSqEBdc3k=" }, "bezier-js": { "version": "4.1.1", @@ -3296,7 +3300,7 @@ "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "requires": { "inherits": "~2.0.0" } @@ -3364,7 +3368,7 @@ "bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true, "requires": { "array-flatten": "^2.1.0", @@ -3386,7 +3390,7 @@ "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "bootstrap": { "version": "4.6.1", @@ -3415,7 +3419,7 @@ "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" } } }, @@ -3448,7 +3452,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { "is-extendable": "^0.1.0" } @@ -3458,7 +3462,7 @@ "browser-assert": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz", - "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==" + "integrity": "sha1-mqpaKox0aFwq4Fv+Ru/WBvBowgA=" }, "browser-process-hrtime": { "version": "1.0.0", @@ -3503,12 +3507,12 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, "buffer-from": { "version": "1.1.2", @@ -3524,12 +3528,12 @@ "buffer-shims": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g==" + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" }, "built-in-math-eval": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/built-in-math-eval/-/built-in-math-eval-0.3.0.tgz", - "integrity": "sha512-5XD5cujru60ooKJ4sGZqoH5v2Xvgw7ezV54gJX/OnPkgDKoH3BnlMEi8xW6hl8xaEjxKHebgrsawroeZnGwIMA==", + "integrity": "sha1-JA3CHLOJQ5WIxhxGDrAHZJfvxBw=", "requires": { "math-codegen": "^0.3.5" } @@ -3660,7 +3664,7 @@ "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", "requires": { "callsites": "^2.0.0" }, @@ -3668,14 +3672,14 @@ "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==" + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" } } }, "caller-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", "requires": { "caller-callsite": "^2.0.0" } @@ -3702,7 +3706,7 @@ "camelcase-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" @@ -3711,14 +3715,14 @@ "camelcase": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==" + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" } } }, "camelize": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg==" + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "caniuse-lite": { "version": "1.0.30001351", @@ -3743,12 +3747,12 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "requires": { "align-text": "^0.1.3", "lazy-cache": "^1.0.3" @@ -3786,12 +3790,12 @@ "change-emitter": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", - "integrity": "sha512-YXzt1cQ4a2jqazhcuSWEOc1K2q8g9H6eWNsyZgi640LDzRWVQ2eDe+Y/kVdftH+vYdPF2rgDb3dLdpxE1jvAxw==" + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" }, "character-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", "requires": { "is-regex": "^1.0.3" } @@ -3799,12 +3803,12 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==" + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" }, "child_process": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", - "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==" + "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o=" }, "chokidar": { "version": "2.1.8", @@ -3833,7 +3837,7 @@ "chrome": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chrome/-/chrome-0.1.0.tgz", - "integrity": "sha512-6KYl20U4Taj6YipylsWr2etUvp9AElJKfGNSBmyGTymYmancnOb041ZNadolEZi2nboLXH7jMSqUmm4kpuTzfg==", + "integrity": "sha1-9h2beS/v6MGUxwVt3BAscmqGQyk=", "requires": { "exeq": "^2.2.0", "plist": "^1.1.0" @@ -3853,7 +3857,7 @@ "clamp": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", - "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==" + "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ=" }, "class-transformer": { "version": "0.2.3", @@ -3874,7 +3878,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { "is-descriptor": "^0.1.0" } @@ -3904,12 +3908,12 @@ "cli-boxes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==" + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" }, "clipboard": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", - "integrity": "sha512-smkaRaIQsrnKN1F3wd1/vY9Q+DeR4L8ZCXKeHCFC2j8RZuSBbuImcLdnIO4GTxmzJxQuDGNKkyfpGoPW7Ua5bQ==", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -3934,7 +3938,7 @@ "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" }, "yargs-parser": { "version": "7.0.0", @@ -3984,7 +3988,7 @@ "clj-fuzzy": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/clj-fuzzy/-/clj-fuzzy-0.3.3.tgz", - "integrity": "sha512-9cyh9A8+OphDZeKIG21MgyDHWDkWxTvagwvFLVjtdi6eToFENF7iDLlKwhHrnBQRSQwprKNhazG053nE/UgwfQ==" + "integrity": "sha1-seU0MJHFIC28UlMoY+HEp4RX8D0=" }, "clone-deep": { "version": "4.0.1", @@ -3999,7 +4003,7 @@ "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", "requires": { "mimic-response": "^1.0.0" }, @@ -4019,12 +4023,12 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -4050,7 +4054,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "1.9.1", @@ -4108,13 +4112,13 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==" + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" }, "component-emitter": { "version": "1.3.0", @@ -4124,7 +4128,7 @@ "component-inherit": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, "compress-brotli": { "version": "1.3.8", @@ -4189,7 +4193,7 @@ "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, "debug": { @@ -4204,7 +4208,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true } } @@ -4212,7 +4216,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -4272,7 +4276,7 @@ "connect-flash": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", - "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==" + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" }, "connect-history-api-fallback": { "version": "1.6.0", @@ -4301,12 +4305,12 @@ "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==" + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "readable-stream": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", - "integrity": "sha512-a6ibcfWFhgihuTw/chl+u3fB5ykBZFmnvpyZHebY0MCQE4vvYcsCLpCeaQ1BkH7HdJYavNSqF0WDLeo4IPHQaQ==", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", "requires": { "buffer-shims": "~1.0.0", "core-util-is": "~1.0.0", @@ -4330,7 +4334,7 @@ "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "constantinople": { "version": "3.1.2", @@ -4414,7 +4418,7 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookies": { "version": "0.8.0", @@ -4459,7 +4463,7 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==" + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "copy-webpack-plugin": { "version": "4.6.0", @@ -4489,7 +4493,7 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "serialize-javascript": { @@ -4503,7 +4507,7 @@ "core-js": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==" + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" }, "core-js-pure": { "version": "3.22.8", @@ -4567,7 +4571,7 @@ "create-error-class": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "requires": { "capture-stack-trace": "^1.0.0" } @@ -4632,7 +4636,7 @@ "cross-spawn": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha512-eZ+m1WNhSZutOa/uRblAc9Ut5MQfukFrFMtPSm3bZCA888NmMd5AWXWdgRZ80zd+pTk1P2JrGjg9pUPTvl2PWQ==", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", "requires": { "lru-cache": "^4.0.1", "which": "^1.2.9" @@ -4662,7 +4666,7 @@ "crypto-random-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==" + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" }, "css-box-model": { "version": "1.2.1", @@ -4675,7 +4679,7 @@ "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==" + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" }, "css-in-js-utils": { "version": "2.0.1", @@ -4818,7 +4822,7 @@ "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "requires": { "array-find-index": "^1.0.1" } @@ -4826,19 +4830,18 @@ "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==" + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, "requires": { "es5-ext": "^0.10.50", "type": "^1.0.1" @@ -4972,7 +4975,7 @@ "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { "assert-plus": "^1.0.0" } @@ -5024,7 +5027,7 @@ "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==" + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=" }, "debounce": { "version": "1.2.1", @@ -5042,12 +5045,12 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==" + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress-response": { "version": "4.2.1", @@ -5212,7 +5215,7 @@ "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { "array-union": "^1.0.1", @@ -5225,7 +5228,7 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } } @@ -5250,7 +5253,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegate": { "version": "3.2.0", @@ -5260,7 +5263,7 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "denque": { "version": "1.5.1", @@ -5363,7 +5366,7 @@ "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", "requires": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" @@ -5398,7 +5401,7 @@ "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "requires": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -5412,7 +5415,7 @@ "resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" }, "string-width": { "version": "4.2.3", @@ -5479,7 +5482,7 @@ "deps-regex": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz", - "integrity": "sha512-3tzwGYogSJi8HoG93R5x9NrdefZQOXgHgGih/7eivloOq6yC6O+yoFxZnkgP661twvfILONfoKRdF9GQOGx2RA==" + "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ=" }, "destroy": { "version": "1.2.0", @@ -5528,7 +5531,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -5536,7 +5539,7 @@ "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", "dev": true }, "dns-packet": { @@ -5552,7 +5555,7 @@ "dns-txt": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "dev": true, "requires": { "buffer-indexof": "^1.0.0" @@ -5561,7 +5564,7 @@ "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==" + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, "dom-converter": { "version": "0.2.0", @@ -5670,12 +5673,12 @@ "double-bits": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz", - "integrity": "sha512-BCLEIBq0O/DWoA7BsCu/R+RP0ZXiowP8BhtJT3qeuuQEBpnS8LK/Wo6UTJQv6v8mK1fj8n90YziHLwGdM5whSg==" + "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY=" }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==" + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, "duplexify": { "version": "3.7.1", @@ -5709,7 +5712,7 @@ "dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", "dev": true, "requires": { "xtend": "^4.0.0" @@ -5718,7 +5721,7 @@ "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -5735,7 +5738,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { "version": "1.4.149", @@ -5764,7 +5767,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, "encoding": { "version": "0.1.13", @@ -5839,7 +5842,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "ws": { "version": "7.4.6", @@ -5973,7 +5976,6 @@ "version": "0.10.61", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", - "dev": true, "requires": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", @@ -5983,8 +5985,7 @@ "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "requires": { "d": "1", "es5-ext": "^0.10.35", @@ -5994,13 +5995,12 @@ "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", - "integrity": "sha512-oj4jOSXvWglTsc3wrw86iom3LDPOx1nbipQk+jaG3dy+sMRM6ReSgVr/VlmBuF6lXUrflN9DCcQHeSbAwGUl4g==" + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, "requires": { "d": "^1.0.1", "ext": "^1.1.2" @@ -6014,12 +6014,12 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.14.3", @@ -6089,7 +6089,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "event-target-shim": { "version": "5.0.1", @@ -6117,7 +6117,7 @@ "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -6131,7 +6131,7 @@ "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -6162,12 +6162,12 @@ "exenv": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" }, "exeq": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/exeq/-/exeq-2.4.0.tgz", - "integrity": "sha512-B648qbDS00nQZv9UQGLT5RbZm/5dNBX10F8oWeXcgpFHSLm1249u95t/3sn2wXdQjLhlF+edAECdshFtSr1K0Q==", + "integrity": "sha1-Td8qaEZIxCeteZNJzzO9dTWPiEo=", "requires": { "bluebird": "^3.0.3", "native-or-bluebird": "^1.2.0" @@ -6176,7 +6176,7 @@ "exif": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/exif/-/exif-0.6.0.tgz", - "integrity": "sha512-gEwM4uanNMfLnDNKclZ7jPEA99E3rpy4ntoS6QW8u6murZjl1o8qRaPdMoC46Syg3d9/QaET0bYKhWlTwJCPgg==", + "integrity": "sha1-YKYmaAdlQst+T1cZnUrG830sX0o=", "requires": { "debug": "^2.2" }, @@ -6192,7 +6192,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -6204,7 +6204,7 @@ "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -6226,7 +6226,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { "is-descriptor": "^0.1.0" } @@ -6234,7 +6234,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { "is-extendable": "^0.1.0" } @@ -6242,7 +6242,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -6325,7 +6325,7 @@ "express-flash": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz", - "integrity": "sha512-QVUR0ZZRCaa8+iPHoUQaQJrQWcQuK/Q+19M7IUIdIEtvwhrA/ifHT7y1CVJI41YfGiOQnbGtn3uvd2vOdgu58A==", + "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=", "requires": { "connect-flash": "0.1.x" } @@ -6382,13 +6382,12 @@ "expressjs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/expressjs/-/expressjs-1.0.1.tgz", - "integrity": "sha512-eFnQ5bMJxTZ29XwRJPV8ee/OURBBMS6Fm+b5rvMMEyz6u2IxPEh2SRzMZt9WvgnV+SMLmnzkALE1DnGG1HxJCw==" + "integrity": "sha1-IgMoRpoY31rWFeK3oM6ZXxf7ru8=" }, "ext": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "dev": true, "requires": { "type": "^2.5.0" }, @@ -6396,8 +6395,7 @@ "type": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==", - "dev": true + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" } } }, @@ -6409,7 +6407,7 @@ "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -6443,7 +6441,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { "is-descriptor": "^1.0.0" } @@ -6451,7 +6449,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { "is-extendable": "^0.1.0" } @@ -6521,7 +6519,7 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "3.1.3", @@ -6536,7 +6534,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, "fast-text-encoding": { @@ -6575,7 +6573,7 @@ "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "requires": { "pend": "~1.2.0" } @@ -6583,7 +6581,7 @@ "ffmpeg": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/ffmpeg/-/ffmpeg-0.0.4.tgz", - "integrity": "sha512-3TgWUJJlZGQn+crJFyhsO/oNeRRnGTy6GhgS98oUCIfZrOW5haPPV7DUfOm3xJcHr5q3TJpjk2GudPutrNisRA==", + "integrity": "sha1-HEYN+OfaUSf2LO70v6BsWciWMMs=", "requires": { "when": ">= 0.0.1" } @@ -6612,7 +6610,7 @@ "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -6623,7 +6621,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { "is-extendable": "^0.1.0" } @@ -6633,7 +6631,7 @@ "filter-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==" + "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=" }, "finalhandler": { "version": "1.2.0", @@ -6667,7 +6665,7 @@ "find": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/find/-/find-0.1.7.tgz", - "integrity": "sha512-jPrupTOe/pO//3a9Ty2o4NqQCp0L46UG+swUnfFtdmtQVN8pEltKpAqR7Nuf6vWn0GBXx5w+R1MyZzqwjEIqdA==", + "integrity": "sha1-yGyHrxqxjyIrvjjeyGy8dg0Wpvs=", "requires": { "traverse-chain": "~0.1.0" } @@ -6675,7 +6673,7 @@ "find-cache-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "dev": true, "requires": { "commondir": "^1.0.1", @@ -6800,7 +6798,7 @@ "fluent-ffmpeg": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz", - "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==", + "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=", "requires": { "async": ">=0.2.9", "which": "^1.1.1" @@ -6867,7 +6865,7 @@ "for-each-property": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz", - "integrity": "sha512-xYs28PM0CKXETFzuGC6ZooH0voZlsSDZwidJcy92flQJi3PK7i3gZx23xHXCPOaD4zmet3bDo+wS7E7SujrlCw==", + "integrity": "sha1-z6hXrsFCLh0Sb/CHhPz2Jim8g/Y=", "requires": { "get-prototype-chain": "^1.0.1" } @@ -6875,7 +6873,7 @@ "for-each-property-deep": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/for-each-property-deep/-/for-each-property-deep-0.0.3.tgz", - "integrity": "sha512-qzP8QkODWVVRPpWiBZacSbBl67cTTWoBfxMG0wE46AsS1yl7qv05sGN+dHvD4s4tnvl/goe6Sp4qBI+rlVBgNg==", + "integrity": "sha1-MTCaSvw4qcygbxsiP1PWSm0IP60=", "requires": { "for-each-property": "0.0.4" } @@ -6883,12 +6881,12 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==" + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "fork-ts-checker-webpack-plugin": { "version": "1.6.0", @@ -6942,7 +6940,7 @@ "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "requires": { "map-cache": "^0.2.2" } @@ -6950,12 +6948,12 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -7022,7 +7020,7 @@ "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -7051,7 +7049,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.13", @@ -7226,7 +7224,7 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==" + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "get-intrinsic": { "version": "1.1.2", @@ -7246,12 +7244,12 @@ "get-prototype-chain": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-prototype-chain/-/get-prototype-chain-1.0.1.tgz", - "integrity": "sha512-2m7WZ0jveIg/dAbCbpUxEToaJ8Dmti5EkgDP8YM3UpHUT6SAORjE2odP8XQGNVGXMHi8q8cCCoy3HTByTaTVTw==" + "integrity": "sha1-oXGhFeoeSQbG7ThDofABwYUQQW8=" }, "get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==" + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=" }, "get-stream": { "version": "6.0.1", @@ -7270,12 +7268,12 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==" + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { "assert-plus": "^1.0.0" } @@ -7283,7 +7281,7 @@ "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" }, "glob": { "version": "7.2.3", @@ -7301,7 +7299,7 @@ "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "requires": { "is-glob": "^3.1.0", "path-dirname": "^1.0.0" @@ -7310,7 +7308,7 @@ "is-glob": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "requires": { "is-extglob": "^2.1.0" } @@ -7326,7 +7324,7 @@ "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "requires": { "ini": "^1.3.4" } @@ -7339,7 +7337,7 @@ "globby": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", "dev": true, "requires": { "array-union": "^1.0.1", @@ -7353,7 +7351,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -7394,7 +7392,7 @@ "golden-layout": { "version": "1.5.9", "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz", - "integrity": "sha512-iBXDQCXOTgUEQJo96zPbjDoy5bRIk9XW5l+q+pDgLnIyReqaa1aiQctNud4epsskyLt952BG521dew5Z1liSxA==", + "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=", "requires": { "jquery": "*" } @@ -7402,7 +7400,7 @@ "good-listener": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", "requires": { "delegate": "^3.1.2" } @@ -7569,7 +7567,7 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.5", @@ -7591,7 +7589,7 @@ "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { "ansi-regex": "^2.0.0" }, @@ -7599,7 +7597,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" } } }, @@ -7619,19 +7617,19 @@ "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" } } }, "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==" + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-property-descriptors": { "version": "1.0.0", @@ -7657,12 +7655,12 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -7672,7 +7670,7 @@ "has-values": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -7686,7 +7684,7 @@ "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "requires": { "is-buffer": "^1.1.5" } @@ -7719,7 +7717,7 @@ "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { "inherits": "^2.0.1", @@ -7748,7 +7746,7 @@ "html": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz", - "integrity": "sha512-lw/7YsdKiP3kk5PnR1INY17iJuzdAtJewxr14ozKJWbbR97znovZ0mh+WEMZ8rjc3lgTK+ID/htTjuyGKB52Kw==", + "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=", "requires": { "concat-stream": "^1.4.7" } @@ -7826,7 +7824,7 @@ "http-browserify": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz", - "integrity": "sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==", + "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=", "requires": { "Base64": "~0.2.0", "inherits": "~2.0.1" @@ -7840,7 +7838,7 @@ "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", "dev": true }, "http-errors": { @@ -7887,7 +7885,7 @@ "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -7906,12 +7904,12 @@ "https": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q=" }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==" + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "https-proxy-agent": { "version": "5.0.1", @@ -7968,7 +7966,7 @@ "icss-replace-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", "dev": true }, "icss-utils": { @@ -7988,7 +7986,7 @@ "iferr": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "dev": true }, "ignore": { @@ -8000,7 +7998,7 @@ "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" }, "iink-js": { "version": "1.5.4", @@ -8044,7 +8042,7 @@ "image-size-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz", - "integrity": "sha512-N505B5FSy2Xf5l/Haef+99TwfJqTu40hnU560+rC0Cm6cxtwVz2yRFh9WpOk1YEjfv3dI0PgVYAH0hmXQmjDcw==", + "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=", "requires": { "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", "readable-stream": "^1.0.33", @@ -8058,12 +8056,12 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -8081,7 +8079,7 @@ "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, "import-fresh": { "version": "3.3.0", @@ -8095,7 +8093,7 @@ "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==" + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" }, "import-local": { "version": "3.1.0", @@ -8109,7 +8107,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "in-publish": { "version": "2.0.1", @@ -8119,7 +8117,7 @@ "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "requires": { "repeating": "^2.0.0" } @@ -8127,12 +8125,12 @@ "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==" + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -8149,7 +8147,7 @@ "camelcase": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==" + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" } } }, @@ -8166,7 +8164,7 @@ "inline-style-prefixer": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz", - "integrity": "sha512-ne8XIyyqkRaNJ1JfL1NYzNdCNxq+MCBQhC8NgOQlzNm2vv3XxlP0VSLQUbSRCF6KPEoveCVEpayHoHzcMyZsMQ==", + "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=", "requires": { "bowser": "^1.7.3", "css-in-js-utils": "^2.0.0" @@ -8175,7 +8173,7 @@ "inspect-function": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz", - "integrity": "sha512-becs5gzcHwPrlHawscYkyQ/ShiOiosrXPhA5RVZ3qyWH4aWdD52RnMfXq/dwQXciHwiieD8aIPwdIWYv6eL+sQ==", + "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=", "requires": { "split-skip": "0.0.1", "unpack-string": "0.0.2" @@ -8184,7 +8182,7 @@ "split-skip": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz", - "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ==" + "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA=" } } }, @@ -8207,7 +8205,7 @@ "magicli": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", - "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", "requires": { "commander": "^2.9.0", "get-stdin": "^5.0.1", @@ -8258,7 +8256,7 @@ "inspect-function": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz", - "integrity": "sha512-becs5gzcHwPrlHawscYkyQ/ShiOiosrXPhA5RVZ3qyWH4aWdD52RnMfXq/dwQXciHwiieD8aIPwdIWYv6eL+sQ==", + "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=", "requires": { "split-skip": "0.0.1", "unpack-string": "0.0.2" @@ -8267,14 +8265,14 @@ "split-skip": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz", - "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ==" + "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA=" } } }, "magicli": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", - "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", "requires": { "commander": "^2.9.0", "get-stdin": "^5.0.1", @@ -8285,14 +8283,14 @@ "split-skip": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz", - "integrity": "sha512-weHOi8BolsDnGIwhhWHbA+wKSuSpvWwjRrdj8SdbIIis2vSwOE37CQP8x3EleuzxanUr3AK8BdUy4MkiOULPZg==" + "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE=" } } }, "split-skip": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz", - "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ==" + "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA=" } } }, @@ -8363,7 +8361,7 @@ "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, "ipaddr.js": { @@ -8380,7 +8378,7 @@ "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "requires": { "kind-of": "^3.0.2" }, @@ -8393,7 +8391,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -8412,7 +8410,7 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-bigint": { "version": "1.0.4", @@ -8425,7 +8423,7 @@ "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "requires": { "binary-extensions": "^1.0.0" } @@ -8468,7 +8466,7 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "requires": { "kind-of": "^3.0.2" }, @@ -8481,7 +8479,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -8516,12 +8514,12 @@ "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==" + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" }, "is-expression": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha512-vyMeQMq+AiH5uUnoBfMTwf18tO3bM6k1QXBE9D6ueAAquEfCZe3AJPtud9g6qS0+4X8xA7ndpZiDyeb2l2qOBw==", + "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", "requires": { "acorn": "~4.0.2", "object-assign": "^4.0.1" @@ -8530,7 +8528,7 @@ "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug==" + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" } } }, @@ -8542,7 +8540,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-finite": { "version": "1.1.0", @@ -8552,7 +8550,7 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-generator-function": { "version": "1.0.10", @@ -8573,12 +8571,12 @@ "is-in-browser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" }, "is-installed-globally": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "requires": { "global-dirs": "^0.1.0", "is-path-inside": "^1.0.0" @@ -8592,12 +8590,12 @@ "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==" + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "requires": { "kind-of": "^3.0.2" }, @@ -8610,7 +8608,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -8628,7 +8626,7 @@ "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, "is-path-cwd": { "version": "2.2.0", @@ -8659,7 +8657,7 @@ "is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "requires": { "path-is-inside": "^1.0.1" } @@ -8680,7 +8678,7 @@ "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==" + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" }, "is-regex": { "version": "1.1.4", @@ -8715,7 +8713,7 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-string": { "version": "1.0.7", @@ -8748,12 +8746,12 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, "is-weakref": { "version": "1.0.2", @@ -8776,28 +8774,28 @@ "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "isomorphic-fetch": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "requires": { "node-fetch": "^1.0.1", "whatwg-fetch": ">=0.10.0" @@ -8806,7 +8804,7 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "its-set": { "version": "1.2.3", @@ -8863,7 +8861,7 @@ "js-stringify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==" + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, "js-tokens": { "version": "4.0.0", @@ -8882,7 +8880,7 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { "version": "15.2.1", @@ -9002,7 +9000,7 @@ "json-css": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz", - "integrity": "sha512-B/0T0OxZH9tSb93tXV6VOYtXqrPz/Vgz2QrCT/4NXen8HGElYkYr9V+8IrSVTMj/ftxa8cG1kcu7f3iAMlaFlQ==" + "integrity": "sha1-65ZPg0ouTqobwvaY/12wB6JsfAA=" }, "json-parse-better-errors": { "version": "1.0.2", @@ -9027,7 +9025,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "1.0.1", @@ -9158,7 +9156,7 @@ "jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", "requires": { "is-promise": "^2.0.0", "promise": "^7.0.1" @@ -9256,7 +9254,7 @@ "klaw": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "requires": { "graceful-fs": "^4.1.9" } @@ -9264,7 +9262,7 @@ "latest-version": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "requires": { "package-json": "^4.0.0" } @@ -9272,7 +9270,7 @@ "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==" + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" }, "lazystream": { "version": "1.0.1", @@ -9301,7 +9299,7 @@ "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { "prelude-ls": "~1.1.2", @@ -9324,7 +9322,7 @@ "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -9336,7 +9334,7 @@ "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "requires": { "error-ex": "^1.2.0" } @@ -9381,22 +9379,22 @@ "lodash._getnative": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==" + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" }, "lodash.chunk": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", - "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==" + "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=" }, "lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" }, "lodash.debounce": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz", - "integrity": "sha512-lcmJwMpdPAtChA4hfiwxTtgFeNAaow701wWUgVUqeD0XJF7vMXIN+bu/2FJSGxT0NUbZy9g9VFrlOFfPjl+0Ew==", + "integrity": "sha1-gSIRw3ipTMKdWqTjNGzwv846ffU=", "requires": { "lodash._getnative": "^3.0.0" } @@ -9404,42 +9402,42 @@ "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" }, "lodash.difference": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, "lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", - "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, "lodash.merge": { "version": "4.6.2", @@ -9449,23 +9447,23 @@ "lodash.padend": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", - "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw==" + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=" }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, "lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" }, "lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" + "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" }, "log-symbols": { "version": "2.2.0", @@ -9493,7 +9491,7 @@ "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==" + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, "loose-envify": { "version": "1.4.0", @@ -9506,7 +9504,7 @@ "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -9555,7 +9553,7 @@ "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "requires": { "locate-path": "^2.0.0" } @@ -9563,7 +9561,7 @@ "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "requires": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -9580,7 +9578,7 @@ "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "requires": { "p-limit": "^1.1.0" } @@ -9588,7 +9586,7 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==" + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" } } }, @@ -9616,17 +9614,17 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==" + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==" + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "requires": { "object-visit": "^1.0.0" } @@ -9671,7 +9669,7 @@ "math-codegen": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/math-codegen/-/math-codegen-0.3.5.tgz", - "integrity": "sha512-SsFYMv33FxMKYxI1PBiaZT+8AeDITK+k/PKhbHNlOPHIz5FIPF4wy78yWqanN6luXdsXENUZgCIC6xH6bfUq1g==", + "integrity": "sha1-R5nuRnfe0Ud2bQA8ykt4ee3UDMo=", "requires": { "extend": "^3.0.0", "mr-parser": "^0.2.1" @@ -9680,7 +9678,7 @@ "mathquill": { "version": "0.10.1-a", "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz", - "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==", + "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=", "requires": { "jquery": "^1.12.3" }, @@ -9688,19 +9686,19 @@ "jquery": { "version": "1.12.4", "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz", - "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg==" + "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw=" } } }, "max-safe-integer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/max-safe-integer/-/max-safe-integer-1.0.1.tgz", - "integrity": "sha512-CHZ/Nopqh46UtA0YvLclxj9F95qExLmTnMS5fnYlXvX4zj1RUOC3cPaGEOMhdPE8SSudSQ6wkQUJLA5E/WeL4A==" + "integrity": "sha1-84BgvixWPYwC5tSK85Ei/YO29BA=" }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memfs": { "version": "3.4.4", @@ -9751,12 +9749,12 @@ "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" }, "meow": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "requires": { "camelcase-keys": "^2.0.0", "decamelize": "^1.1.2", @@ -9781,7 +9779,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "merge-stream": { "version": "2.0.0", @@ -9791,7 +9789,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "microevent.ts": { "version": "0.1.1", @@ -10025,7 +10023,7 @@ "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, "minimatch": { @@ -10040,13 +10038,13 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" @@ -10055,7 +10053,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "supports-color": { @@ -10179,7 +10177,7 @@ "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { "aproba": "^1.1.1", @@ -10240,14 +10238,14 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "mr-parser": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/mr-parser/-/mr-parser-0.2.1.tgz", - "integrity": "sha512-hug+mpbSSKnH13rFqy3zm+XiG+QTStiDAgMTHK355TIstQE0qBkBtSJsa5YHP94AuarVX9b/4dcebdTRZ9YiEw==" + "integrity": "sha1-hhi5ukF+KOn0OaQcaVtVTq/u2Sc=" }, "ms": { "version": "2.1.1", @@ -10267,7 +10265,7 @@ "multicast-dns-service-types": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, "nan": { @@ -10306,7 +10304,7 @@ "native-or-bluebird": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz", - "integrity": "sha512-0SH8UubxDfe382eYiwmd12qxAbiWGzlGZv6CkMA+DPojWa/Y0oH4hE0lRtFfFgJmPQFyKXeB8XxPbZz6TvvKaQ==" + "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck=" }, "negotiator": { "version": "0.6.3", @@ -10327,7 +10325,7 @@ "nextafter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz", - "integrity": "sha512-7PO+A89Tll2rSEfyrjtqO0MaI37+nnxBdnQcPypfbEYYuGaJxWGCqaOwQX4a3GHNTS08l1kazuiLEWZniZjMUQ==", + "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=", "requires": { "double-bits": "^1.1.0" } @@ -10358,7 +10356,7 @@ "node-ensure": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", - "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==" + "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=" }, "node-environment-flags": { "version": "1.0.5", @@ -10447,7 +10445,7 @@ "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "requires": { "abbrev": "1" } @@ -10488,7 +10486,7 @@ "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw==" + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" }, "string-width": { "version": "1.0.2", @@ -10552,12 +10550,12 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==" + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "aproba": { "version": "1.2.0", @@ -10576,7 +10574,7 @@ "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -10603,7 +10601,7 @@ "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==" + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" }, "is-fullwidth-code-point": { "version": "1.0.0", @@ -10701,7 +10699,7 @@ "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", - "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==" + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" }, "nopt": { "version": "5.0.0", @@ -14278,7 +14276,7 @@ "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { "path-key": "^2.0.0" } @@ -14305,7 +14303,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "nwsapi": { "version": "2.2.0", @@ -14316,7 +14314,7 @@ "oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" }, "oauth-sign": { "version": "0.9.0", @@ -14326,12 +14324,12 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -14341,7 +14339,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { "is-descriptor": "^0.1.0" } @@ -14354,7 +14352,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -14411,7 +14409,7 @@ "magicli": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", - "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", "requires": { "commander": "^2.9.0", "get-stdin": "^5.0.1", @@ -14424,7 +14422,7 @@ "object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "requires": { "isobject": "^3.0.0" } @@ -14454,7 +14452,7 @@ "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "requires": { "isobject": "^3.0.1" } @@ -14481,7 +14479,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" } @@ -14542,12 +14540,12 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==" + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", @@ -14566,12 +14564,12 @@ "p-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-1.0.0.tgz", - "integrity": "sha512-ttOxn4Yt0hzIsLLqKi/Ry9QRxW+UQKdoWHz7g99Ci57zPkqUU3kbWKAeHuv+HfRLe109acYLUY6kuVCOOqnt4g==" + "integrity": "sha1-y38svu/YegnrqGHhErZ1J+Yh4v0=" }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { "version": "2.3.0", @@ -14612,7 +14610,7 @@ "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "requires": { "got": "^6.7.1", "registry-auth-token": "^3.0.1", @@ -14628,7 +14626,7 @@ "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "requires": { "create-error-class": "^3.0.0", "duplexer3": "^0.1.4", @@ -14744,7 +14742,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==" + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "passport": { "version": "0.4.1", @@ -14766,7 +14764,7 @@ "passport-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", "requires": { "passport-strategy": "1.x.x" } @@ -14786,7 +14784,7 @@ "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, "path-browserify": { "version": "1.0.1", @@ -14796,27 +14794,27 @@ "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==" + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.7", @@ -14826,7 +14824,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "path-type": { "version": "4.0.0", @@ -14841,7 +14839,7 @@ "pause": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "pdf-parse": { "version": "1.1.1", @@ -14889,7 +14887,7 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, "perfect-scrollbar": { "version": "1.5.5", @@ -14899,7 +14897,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picocolors": { "version": "1.0.0", @@ -14919,12 +14917,12 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "requires": { "pinkie": "^2.0.0" } @@ -14985,7 +14983,7 @@ "plist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz", - "integrity": "sha512-dL9Xc2Aj3YyBnwvCNuHmFl2LWvQacm/HEAsoVwLiuu0POboMChETt5wexpU1P6F6MnibIucXlVsMFFgNUT2IyA==", + "integrity": "sha1-CEtQk93JJQbiWfh0uNmxr7jHlZM=", "requires": { "base64-js": "0.0.8", "util-deprecate": "1.0.2", @@ -14996,7 +14994,7 @@ "base64-js": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", - "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==" + "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" } } }, @@ -15036,7 +15034,7 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==" + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { "version": "7.0.39", @@ -15234,13 +15232,13 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==" + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "pretty-error": { "version": "4.0.0", @@ -15274,7 +15272,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "2.0.1", @@ -15297,7 +15295,7 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, "prop-types": { @@ -15343,7 +15341,7 @@ "prosemirror-find-replace": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/prosemirror-find-replace/-/prosemirror-find-replace-0.9.0.tgz", - "integrity": "sha512-LfhQ/Zr0PkkJpCsr9vTJ5ZPYh49mSVVG+hHJ6djT+chlCW+t2ilSxBpBG+2IeE/I5nlbcvuLLAbxeI1g3pTCpA==" + "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I=" }, "prosemirror-history": { "version": "1.3.0", @@ -15435,13 +15433,13 @@ "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.8.0", @@ -15673,12 +15671,12 @@ "pure-color": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { "version": "6.5.3", @@ -15699,7 +15697,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "dev": true }, "querystringify": { @@ -15721,7 +15719,7 @@ "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==" + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" }, "randombytes": { "version": "2.1.0", @@ -15826,7 +15824,7 @@ "react-base16-styling": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz", - "integrity": "sha512-EPuchwVvYPSFFIjGpH0k6wM0HQsmJ0vCk7BSl5ryxMVFIWW4hX4Kksu4PNtxfgOxDebTLkJQ8iC7zwAql0eusg==", + "integrity": "sha1-OFjyTpxN2MvT9wLz901YHKKRcmk=", "requires": { "base16": "^1.0.0", "lodash.curry": "^4.0.1", @@ -15895,7 +15893,7 @@ "react-dock": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/react-dock/-/react-dock-0.2.4.tgz", - "integrity": "sha512-ywUJPC/TIM9PO700skka0fH4aqbrH8RojUXejZFvjtqlc5KZ+xjHqFdo4A3j+dp+0NLFZ3Nai4xzcf3FUJ9BsQ==", + "integrity": "sha1-5yfcdVCztzEWY13LnA4E0Lev4Xw=", "requires": { "lodash.debounce": "^3.1.1", "prop-types": "^15.5.8" @@ -16252,7 +16250,7 @@ "react-themeable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz", - "integrity": "sha512-kl5tQ8K+r9IdQXZd8WLa+xxYN04lLnJXRVhHfdgwsUJr/SlKJxIejoc9z9obEkx1mdqbTw1ry43fxEUwyD9u7w==", + "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=", "requires": { "object-assign": "^3.0.0" }, @@ -16260,7 +16258,7 @@ "object-assign": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==" + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" } } }, @@ -16299,7 +16297,7 @@ "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -16309,7 +16307,7 @@ "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -16321,7 +16319,7 @@ "read-pkg-up": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -16330,7 +16328,7 @@ "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -16339,7 +16337,7 @@ "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "requires": { "pinkie-promise": "^2.0.0" } @@ -16385,12 +16383,12 @@ "readline": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", - "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" + "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw=" }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "requires": { "resolve": "^1.1.6" } @@ -16416,7 +16414,7 @@ "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "requires": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" @@ -16425,7 +16423,7 @@ "reduce-flatten": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", - "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ==" + "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=" }, "redux": { "version": "4.2.0", @@ -16476,7 +16474,7 @@ "registry-url": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "requires": { "rc": "^1.0.1" } @@ -16484,12 +16482,12 @@ "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==" + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "renderkid": { "version": "3.0.0", @@ -16575,12 +16573,12 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "requires": { "is-finite": "^1.0.0" } @@ -16684,7 +16682,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-from-string": { "version": "2.0.2", @@ -16699,7 +16697,7 @@ "require-package-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", - "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==" + "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk=" }, "require_optional": { "version": "1.0.1", @@ -16713,14 +16711,14 @@ "resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==" + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" } } }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "dev": true }, "resize-observer-polyfill": { @@ -16766,7 +16764,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "responselike": { "version": "2.0.0", @@ -16791,7 +16789,7 @@ "retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, "reveal.js": { @@ -16802,7 +16800,7 @@ "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "requires": { "align-text": "^0.1.1" } @@ -16831,7 +16829,7 @@ "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { "aproba": "^1.1.1" @@ -16853,7 +16851,7 @@ "safe-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" } @@ -16941,13 +16939,13 @@ "scss-loader": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz", - "integrity": "sha512-SbT/smRJjkvvdHSEdAYAplosVkrtaSwwgUlnQCOuDS5sOKNjrS/eYCMvKeV6+YxK5cCOCsOJZd3vltrXatFp+g==", + "integrity": "sha1-6uAXueDzjBKlMtslwiC5Avs05nE=", "dev": true }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha512-dYE8LhncfBUar6POCxMTm0Ln+erjeczqEvCJib5/7XNkdw1FkUGgwMPY360FY0FgPWQxHWCx29Jl3oejyGLM9Q==", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "requires": { "js-base64": "^2.1.8", "source-map": "^0.4.2" @@ -16956,7 +16954,7 @@ "source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "requires": { "amdefine": ">=0.0.4" } @@ -16971,17 +16969,17 @@ "section-iterator": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz", - "integrity": "sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ==" + "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio=" }, "select": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==" + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, "selfsigned": { @@ -17001,12 +16999,12 @@ "semver-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==" + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" }, "semver-diff": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "requires": { "semver": "^5.0.3" } @@ -17070,7 +17068,7 @@ "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { "accepts": "~1.3.4", @@ -17100,7 +17098,7 @@ "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { "depd": "~1.1.2", @@ -17112,13 +17110,13 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "setprototypeof": { @@ -17149,7 +17147,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { "version": "2.0.1", @@ -17165,7 +17163,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { "is-extendable": "^0.1.0" } @@ -17175,7 +17173,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { "version": "1.2.0", @@ -17337,7 +17335,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { "shebang-regex": "^1.0.0" } @@ -17345,7 +17343,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "shelljs": { "version": "0.8.5", @@ -17380,7 +17378,7 @@ "simple-assign": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz", - "integrity": "sha512-otdSSQzuVsmDoe5MnSm4ZgHd5sl0ak6A1CTjW1R/DUHQ8xoZuU1NUzf9x6n9Dvp3nxpvW51WNMQ/7rQ9432xDg==" + "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o=" }, "simple-concat": { "version": "1.0.1", @@ -17400,7 +17398,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "requires": { "is-arrayish": "^0.3.1" }, @@ -17415,13 +17413,13 @@ "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, "sliced": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" }, "snapdragon": { "version": "0.8.2", @@ -17449,7 +17447,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { "is-descriptor": "^0.1.0" } @@ -17457,7 +17455,7 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { "is-extendable": "^0.1.0" } @@ -17465,7 +17463,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -17482,7 +17480,7 @@ "define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "requires": { "is-descriptor": "^1.0.0" } @@ -17531,7 +17529,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -17595,12 +17593,12 @@ "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "socket.io-parser": { "version": "3.3.2", @@ -17710,7 +17708,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { "version": "0.5.3", @@ -17748,7 +17746,7 @@ "sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", "optional": true, "requires": { "memory-pager": "^1.0.2" @@ -17851,7 +17849,7 @@ "split-skip": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz", - "integrity": "sha512-weHOi8BolsDnGIwhhWHbA+wKSuSpvWwjRrdj8SdbIIis2vSwOE37CQP8x3EleuzxanUr3AK8BdUy4MkiOULPZg==" + "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE=" }, "split-string": { "version": "3.1.0", @@ -17864,7 +17862,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.17.0", @@ -17916,7 +17914,7 @@ "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "requires": { "is-descriptor": "^0.1.0" } @@ -17995,7 +17993,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -18069,7 +18067,7 @@ "magicli": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz", - "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==", + "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=", "requires": { "commander": "^2.9.0", "get-stdin": "^5.0.1", @@ -18116,7 +18114,7 @@ "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==" + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" } } }, @@ -18449,7 +18447,7 @@ "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "requires": { "is-buffer": "^1.1.5" } @@ -18497,7 +18495,7 @@ "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "requires": { "abbrev": "1" } @@ -18622,7 +18620,7 @@ "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true } } @@ -18921,8 +18919,7 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, "type-check": { "version": "0.3.2", @@ -19000,7 +18997,7 @@ "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "requires": { "graceful-fs": "^4.1.6" } @@ -19030,12 +19027,12 @@ "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==" + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "requires": { "center-align": "^0.1.1", "right-align": "^0.1.1", @@ -19116,7 +19113,7 @@ "pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" } } }, @@ -19189,7 +19186,7 @@ "has-value": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -19199,7 +19196,7 @@ "isobject": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "requires": { "isarray": "1.0.0" } @@ -19209,7 +19206,7 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==" + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" } } }, @@ -19281,7 +19278,7 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "dev": true } } @@ -19805,7 +19802,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, "debug": { @@ -19830,7 +19827,7 @@ "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { "errno": "^0.1.3", @@ -20290,7 +20287,7 @@ "lodash": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==" + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" } } }, diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index ca942a38a..2343c2f34 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -25,6 +25,7 @@ export enum DocumentType { EQUATION = "equation", // equation editor FUNCPLOT = "funcplot", // function plotter MAP = "map", + DATAVIZ = "dataviz", // special purpose wrappers that either take no data or are compositions of lower level types LINK = "link", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0bb6ce27f..c5ec9eaf6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -62,6 +62,7 @@ import { DocumentType } from "./DocumentTypes"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { MapBox } from "../views/nodes/MapBox/MapBox"; import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox"; +import DataViz from "../views/nodes/DataViz"; const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); class EmptyBox { @@ -517,6 +518,10 @@ export namespace Docs { layout: { view: EmptyBox, dataField: defaultDataKey }, options: { links: "@links(self)" } }], + [DocumentType.DATAVIZ, { + layout: { view: DataViz, dataField: defaultDataKey }, + options: { _fitWidth: true, _fitHeight: true, links: "@links(self)" } + }] ]); const suffix = "Proto"; @@ -922,6 +927,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); } + export function DataVizDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), undefined, { title: "Data Viz", ...options }); + } + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 60bfc165b..a96f5cb18 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -262,7 +262,10 @@ export class CurrentUserUtils { doc.emptyHeader = headerTemplate; } if (doc.emptyComparison === undefined) { - doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "Comparer", _width: 300, _height: 300 }); + doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "comparer", _width: 300, _height: 300 }); + } + if (doc.emptyDataViz === undefined) { + doc.emptyDataViz = Docs.Create.DataVizDocument({ ...standardOps(), title: "data viz", _width: 300, _height: 300 }); } if (doc.emptyScript === undefined) { doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { ...standardOps(), title: "script", _width: 200, _height: 250, }); diff --git a/src/client/views/nodes/DataViz.scss b/src/client/views/nodes/DataViz.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/views/nodes/DataViz.tsx b/src/client/views/nodes/DataViz.tsx new file mode 100644 index 000000000..60b27c19c --- /dev/null +++ b/src/client/views/nodes/DataViz.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import "./DataViz.scss"; +import { FieldView, FieldViewProps } from "./FieldView"; +import { ViewBoxBaseComponent } from '../DocComponent'; + +export class DataViz extends ViewBoxBaseComponent() { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(DataViz, fieldKey); + } + + render() { + return (
    Hi
    ) + } +} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 70732e74c..8715fc33a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -42,6 +42,7 @@ import React = require("react"); import XRegExp = require("xregexp"); import { MapBox } from "./MapBox/MapBox"; import { RecordingBox } from "./RecordingBox"; +import { DataViz } from "./DataViz"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -229,7 +230,7 @@ export class DocumentContentsView extends React.Component Date: Tue, 21 Jun 2022 19:30:17 -0400 Subject: running into jsx parsing error --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 3 ++- src/client/views/nodes/DataViz.tsx | 14 +++++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c5ec9eaf6..42191bd7e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -62,7 +62,7 @@ import { DocumentType } from "./DocumentTypes"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { MapBox } from "../views/nodes/MapBox/MapBox"; import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox"; -import DataViz from "../views/nodes/DataViz"; +import { DataViz } from "../views/nodes/DataViz"; const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); class EmptyBox { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index a96f5cb18..8ccb0d197 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -310,7 +310,8 @@ export class CurrentUserUtils { { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc }, { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, - { toolTip: "Tap to create a map in the new pane, drag for a map", title: "Map", icon: "map-marker-alt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyMap as Doc, noviceMode: true } + { toolTip: "Tap to create a map in the new pane, drag for a map", title: "Map", icon: "map-marker-alt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyMap as Doc, noviceMode: true }, + { toolTip: "Tap to create a data visualization in a new pane", title: "Data Viz", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyDataViz as Doc} ]; } diff --git a/src/client/views/nodes/DataViz.tsx b/src/client/views/nodes/DataViz.tsx index 60b27c19c..57c5c0259 100644 --- a/src/client/views/nodes/DataViz.tsx +++ b/src/client/views/nodes/DataViz.tsx @@ -4,11 +4,23 @@ import { FieldView, FieldViewProps } from "./FieldView"; import { ViewBoxBaseComponent } from '../DocComponent'; export class DataViz extends ViewBoxBaseComponent() { + + constructor(props: any) { + super(props); + } + + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataViz, fieldKey); } render() { - return (
    Hi
    ) + return ( +
    +
    + Hi +
    +
    + ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 5bf71bb9fde28de8cc2cedaf03708315933acc30 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Jun 2022 13:46:39 -0400 Subject: more cleanup in currentuserutils. fixed so that removing items from a list in code will remove them from the UI --- src/client/documents/Documents.ts | 6 +- src/client/util/CurrentUserUtils.ts | 209 ++++++++++----------- .../views/collections/CollectionStackingView.tsx | 3 +- 3 files changed, 104 insertions(+), 114 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d0c7d2436..01cfe90da 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -708,8 +708,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List(), options); } - export function ScriptingDocument(script: Opt, options: DocumentOptions = {}, fieldKey?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script, + export function ScriptingDocument(script: Opt|null, options: DocumentOptions = {}, fieldKey?: string) { + return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script: undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); } @@ -829,7 +829,7 @@ export namespace Docs { const nwid = options._nativeWidth || undefined; const nhght = options._nativeHeight || undefined; if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width); - return InstanceFromProto(Prototypes.get(DocumentType.WEB), url ? new WebField(url) : undefined, options); + return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : "http://www.bing.com/"), options); } export function HtmlDocument(html: string, options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e119828c7..52fbda9a9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -104,6 +104,7 @@ export class CurrentUserUtils { } }); items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item)); + items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), "data", item)); } return doc; } @@ -148,9 +149,9 @@ export class CurrentUserUtils { return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts); }); - const reqdOpts = { + const reqdOpts:DocumentOptions = { title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true, - _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, + _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true, childDocumentsActive: true, _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, }; const reqdScripts = { dropConverter : "convertToButtons(dragData)" }; @@ -197,17 +198,12 @@ export class CurrentUserUtils { // setup templates for different document types when they are iconified from Document Decorations static setupDefaultIconTemplates(doc: Doc, field="template-icons") { - const templateIconsDoc = DocCast(doc[field]); + const reqdOpts = { title: "icon templates", _height: 75, system: true }; + const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => { const iconFieldName = "icon" + (type ? "_" + type : ""); - if (!templateIconsDoc?.[iconFieldName]) { - const template = MakeTemplate(iconTemplate(), true, iconFieldName, templateField); - if (templateIconsDoc) { - templateIconsDoc[iconFieldName] = template; - } - return template; - } + return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate(), true, iconFieldName, templateField))) ; }; const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); const labelBox = (extra: object) => Docs.Create.LabelDocument({ @@ -235,68 +231,37 @@ export class CurrentUserUtils { makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })), // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) ].filter(d => d).map(d => d!); - - const reqdOpts = { title: "icon templates", _height: 75, system: true }; - this.AssignOpts(templateIconsDoc, reqdOpts, iconTemplates) ?? (doc[field] = Docs.Create.TreeDocument(iconTemplates, reqdOpts)); + this.AssignOpts(DocCast(doc[field]), {}, iconTemplates); } - /// initalizes the set of default versions of most document types + /// initalizes the set of "empty" versions of each document type with default fields. e.g.,. emptyNote, emptyPresentation static creatorBtnDescriptors(doc: Doc): { title: string, toolTip: string, icon: string, ignoreClick?: boolean, dragFactory?: Doc, backgroundColor?: string, clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string}, }[] { - const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List(["system"]) }); - const emptyThings:{key:string, // the field name where the empty thing will be stored - opts:DocumentOptions, // the document options that are required for the empty thing - funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing - creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist - }[] = [ - {key: "emptyPresentation", creator: Docs.Create.PresDocument, opts: { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }}, - {key: "emptyCollection", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "freeform", _width: 150, _height: 100 }}, - {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }}, - {key: "emptyMath", creator: (opts) => Docs.Create.EquationDocument(opts), opts: { title: "Equation", _backgroundGridShow: true, _width: 300, _height: 35 }}, - {key: "emptySlide", creator: (opts) => Docs.Create.TreeDocument([], opts), funcs: {title: 'self.text?.Text'}, opts: { - _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, - "dragFactory-count": undefined, allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white" - }}, - {key: "emptyComparison", creator: Docs.Create.ComparisonDocument, opts: { title: "Comparer", _width: 300, _height: 300 }}, - {key: "emptyScript", creator: (opts) => Docs.Create.ScriptingDocument(undefined, opts), opts: { title: "script", _width: 200, _height: 250, }}, - {key: "emptyScreenshot", creator: Docs.Create.ScreenshotDocument, opts: { title: "empty screenshot", _width: 400, _height: 200 }}, - {key: "emptyWebCam", creator: (opts) => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, - {key: "emptyAudio", creator: (opts) => Docs.Create.AudioDocument(nullAudio, opts), opts: { title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }}, - {key: "emptyNote", creator: (opts) => Docs.Create.TextDocument("", opts), opts: { title: "text note", _width: 200, _autoHeight: true }}, - {key: "emptyButton", creator: Docs.Create.ButtonDocument, opts: { title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }}, - {key: "emptyWebpage", creator: (opts) => Docs.Create.WebDocument("http://www.bing.com/", opts), opts: { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }}, - {key: "emptyMap", creator: (opts) => Docs.Create.MapDocument([], opts), opts: { title: "map", _showSidebar: true, _width: 800, _height: 600, }}, - {key: "emptyDataViz", creator: (opts) => Docs.Create.DataVizDocument(opts), opts: { title: "DataViz", _width: 300, _height: 300 }}, - ]; - - emptyThings.forEach(thing => - this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), undefined, thing.funcs)) - - if (doc.emptyHeader === undefined) { - const json = { - doc: { - type: "doc", - content: [ - { - type: "paragraph", attrs: {}, content: [{ - type: "dashField", - attrs: { fieldKey: "author", docid: "", hideKey: false }, - marks: [{ type: "strong" }] - }, { - type: "dashField", - attrs: { fieldKey: "creationDate", docid: "", hideKey: false }, - marks: [{ type: "strong" }] - }] + const standardOps = (key:string) => ({ title : "Untitled "+ key, _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List(["system"]) }); + const json = { + doc: { + type: "doc", + content: [ + { + type: "paragraph", attrs: {}, content: [{ + type: "dashField", + attrs: { fieldKey: "author", docid: "", hideKey: false }, + marks: [{ type: "strong" }] + }, { + type: "dashField", + attrs: { fieldKey: "creationDate", docid: "", hideKey: false }, + marks: [{ type: "strong" }] }] - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - const headerBtnHgt = 10; - const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { - ...standardOps(), title: "text", _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + const headerBtnHgt = 10; + const headerTemplate = (opts:DocumentOptions) => { + const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "text", layout: "" + ` ` + @@ -309,28 +274,57 @@ export class CurrentUserUtils { // " " + // " " + // "
    "; - Doc.GetProto(headerTemplate).isTemplateDoc = makeTemplate(Doc.GetProto(headerTemplate), true, "headerView"); - doc.emptyHeader = headerTemplate; + Doc.GetProto(header).isTemplateDoc = makeTemplate(Doc.GetProto(header), true, "headerView"); + Doc.GetProto(header).title = "Untitled Header"; + return header; } + const emptyThings:{key:string, // the field name where the empty thing will be stored + opts:DocumentOptions, // the document options that are required for the empty thing + funcs?:{[key:string]: any}, // computed fields that are rquired for the empth thing + creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist + }[] = [ + {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _autoHeight: true }}, + {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100 }}, + {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _backgroundGridShow: true, }}, + {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }}, + {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, + {key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }}, + {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _showSidebar: true, }}, + {key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }}, + {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, + {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }}, + {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, + {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, + {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,}}, + {key: "Presentation",creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 500, _viewType: CollectionViewType.Stacking, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }}, + {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _backgroundGridShow: true, }}, + {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _viewType: CollectionViewType.Tree, + treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, + allowOverlayDrop: true, treeViewType: TreeViewType.outline, + backgroundColor: "white", _xMargin: 0, _yMargin: 0, _singleLine: true + }, funcs: {title: 'self.text?.Text'}}, + ]; + + emptyThings.forEach(thing => + this.AssignScripts(this.AssignOpts(DocCast(doc["empty"+thing.key]), {...standardOps(thing.key), ...thing.opts}) ?? (doc["empty"+thing.key] = thing.creator({...standardOps(thing.key), ...thing.opts})), undefined, thing.funcs)) + return [ - { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, clickFactory: doc.emptyPane as Doc, }, - { toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyMath as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, scripts: {onClick: 'openInOverlay(copyDragFactory(this.dragFactory))',onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a custom note", title: "Custom", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc, scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'}, }, - { toolTip: "Tap or drag to create a progressive slide",title: "Slide", icon: "file", dragFactory: doc.emptySlide as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreenshot as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}}, - { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs:{ hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}}, - { toolTip: "Tap or drag to create a mobile view", title: "Phone", icon: "mobile", dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: {hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)', onDragStart: '{ return copyDragFactory(this.dragFactory);}'} }, + { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, }, + { toolTip: "Tap or drag to create a collection", title: "Col", icon: "folder", dragFactory: doc.emptyCollection as Doc, clickFactory: DocCast(doc.emptyTab), scripts: { onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, + { toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, }, + { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, }, + { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, }, + { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, }, + { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, }, + { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, funcs: { hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, funcs: { hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, }, + { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc, scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'}, }, { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } }, - ]; + ].map(tuple => ({scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, ...tuple, })) } /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools @@ -345,9 +339,10 @@ export class CurrentUserUtils { return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs); }); - const reqdOpts = { + const reqdOpts:DocumentOptions = { title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true, _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true, + childDocumentsActive: true }; const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts); @@ -357,34 +352,26 @@ export class CurrentUserUtils { static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] { const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())"; return [ - { title: "Dashboards", target: CurrentUserUtils.MyDashboards, icon: "desktop", scripts:{onClick: 'selectMainMenu(self)'} }, - { title: "Search", target: CurrentUserUtils.MySearcher, icon: "search", scripts:{onClick: 'selectMainMenu(self)'} }, - { title: "Files", target: CurrentUserUtils.MyFilesystem, icon: "folder-open", scripts:{onClick: 'selectMainMenu(self)'} }, - { title: "Tools", target: CurrentUserUtils.MyTools, icon: "wrench", scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} }, - { title: "Imports", target: CurrentUserUtils.MyImports, icon: "upload", scripts:{onClick: 'selectMainMenu(self)'} }, - { title: "Recently Closed", target: CurrentUserUtils.MyRecentlyClosed, icon: "archive", scripts:{onClick: 'selectMainMenu(self)'} }, - { title: "Shared with me", target: CurrentUserUtils.MySharedDocs, icon: "users", scripts:{onClick: 'selectMainMenu(self)'}, funcs:{badgeValue:badgeValue}}, - { title: "Trails", target: CurrentUserUtils.MyTrails, icon: "pres-trail", scripts:{onClick: 'selectMainMenu(self)'} }, - { title: "User Doc", target: CurrentUserUtils.MyUserDocView, icon: "address-card",scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} }, - ]; + { title: "Dashboards", target: this.setupDashboards(doc, "myDashboards"), icon: "desktop", }, + { title: "Search", target: this.setupSearcher(doc, "mySearcher"), icon: "search", }, + { title: "Files", target: this.setupFilesystem(doc, "myFilesystem"), icon: "folder-open", }, + { title: "Tools", target: this.setupToolsBtnPanel(doc, "myTools"), icon: "wrench", funcs: {hidden: "IsNoviceMode()"} }, + { title: "Imports", target: this.setupImportSidebar(doc, "myImports"), icon: "upload", }, + { title: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive", }, + { title: "Shared Docs", target: this.MySharedDocs, icon: "users", funcs:{badgeValue:badgeValue}}, + { title: "Trails", target: this.setupTrails(doc, "myTrails"), icon: "pres-trail", }, + { title: "User Doc View", target: this.setupUserDocView(doc, "myUserDocView"), icon: "address-card",funcs: {hidden: "IsNoviceMode()"} }, + ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}})); } - /// setup the left sidebar container and panels that can be displayed within it + /// the empty panel that is filled with whichever left menu button's panel has been selected static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") { this.AssignOpts(DocCast(doc[field]), {}) ?? (doc[field] = ((doc:Doc) => {doc.system = true; return doc;})(new Doc())); - CurrentUserUtils.setupSearcher(doc, "mySearcher"); - CurrentUserUtils.setupToolsBtnPanel(doc, "myTools"); - CurrentUserUtils.setupImportSidebar(doc, "myImports"); - CurrentUserUtils.setupDashboards(doc, "myDashboards"); - CurrentUserUtils.setupTrails(doc, "myTrails"); - CurrentUserUtils.setupFilesystem(doc, "myFilesystem"); - CurrentUserUtils.setupRecentlyClosedDocs(doc, "myRecentlyClosed"); - CurrentUserUtils.setupUserDocView(doc, "myUserDocView"); } /// Initializes the left sidebar menu buttons and the panels they open up static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") { - this.setupLeftSidebarPanel(doc); // the tools/panels opened up by the menu buttons + this.setupLeftSidebarPanel(doc); const myLeftSidebarMenu = DocCast(doc[field]); const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => { const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined; @@ -520,7 +507,7 @@ export class CurrentUserUtils { dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true, }; - this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts)); + return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts)); } /// Initializes the panel of draggable tools that is opened from the left sidebar. @@ -532,7 +519,7 @@ export class CurrentUserUtils { title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0", _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, }; - this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps)); + return this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps)); } /// initializes the left sidebar dashboard pane @@ -595,6 +582,7 @@ export class CurrentUserUtils { if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { myTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } + return myTrails; } /// initializes the left sidebar File system pane @@ -624,10 +612,11 @@ export class CurrentUserUtils { if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) { myFilesystem.childContextMenuScripts = new List(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } + return myFilesystem; } /// initializes the panel displaying docs that have been recently closed - static setupRecentlyClosedDocs(doc: Doc, field:string) { + static setupRecentlyClosed(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true, treeViewTruncateTitleWidth: 150, ignoreClick: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", @@ -651,6 +640,7 @@ export class CurrentUserUtils { if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) { recentlyClosed.contextMenuScripts = new List([ScriptField.MakeScript(clearAll("self"))!]) } + return recentlyClosed; } /// creates a new, empty filter doc @@ -674,7 +664,7 @@ export class CurrentUserUtils { treeViewHideTitle: true, treeViewTruncateTitleWidth: 150 }; if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }) - this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts)); + return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts)); } static linearButtonList = (title: string, opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, { @@ -903,7 +893,7 @@ export class CurrentUserUtils { buttonText: "Import", icon: "upload", system: true }; const reqdBtnScripts = { onClick: "importDocument()" }; const newImportBtn = this.AssignOpts(DocCast(myImports.buttonMenuDoc), reqdBtnOpts) ?? (myImports.buttonMenuDoc = Docs.Create.FontIconDocument(reqdBtnOpts)); - this.AssignScripts(newImportBtn, reqdBtnScripts); + return this.AssignScripts(newImportBtn, reqdBtnScripts); } static setupClickEditorTemplates(doc: Doc) { @@ -1055,7 +1045,6 @@ export class CurrentUserUtils { } if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard - !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); } const state = CurrentUserUtils._urlState; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 684c919bd..4e8c14039 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -229,6 +229,7 @@ export class CollectionStackingView extends CollectionSubView this.props.isSelected() || this.props.isContentActive(); + isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)); getDisplayDoc(doc: Doc, width: () => number) { const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); @@ -245,7 +246,7 @@ export class CollectionStackingView extends CollectionSubView Date: Wed, 22 Jun 2022 14:35:23 -0400 Subject: fixed overlay view dragging & opening up audio/video recordings in overlay. --- src/client/util/CurrentUserUtils.ts | 46 ++++++++++++++++--------------------- src/client/views/MainView.tsx | 7 ++---- src/client/views/OverlayView.scss | 1 + 3 files changed, 23 insertions(+), 31 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 52fbda9a9..ff6ef4809 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -146,7 +146,7 @@ export class CurrentUserUtils { doc.isTemplateDoc = makeTemplate(doc); return doc; } - return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts); + return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts); }); const reqdOpts:DocumentOptions = { @@ -201,34 +201,28 @@ export class CurrentUserUtils { const reqdOpts = { title: "icon templates", _height: 75, system: true }; const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); - const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => { + const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: (opts:DocumentOptions) => Doc) => { const iconFieldName = "icon" + (type ? "_" + type : ""); - return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate(), true, iconFieldName, templateField))) ; + return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate({onClick:deiconifyScript(), system: true}), true, iconFieldName, templateField))) ; }; const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); - const labelBox = (extra: object) => Docs.Create.LabelDocument({ - textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, system: true, onClick: deiconifyScript(), ...extra, - }); - const imageBox = (url: string, extra: object) => Docs.Create.ImageDocument(url, { - "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, - _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra - }); - const fontBox = () => Docs.Create.FontIconDocument({ - _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() + const labelBox = (opts: object) => Docs.Create.LabelDocument({ + textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); + const imageBox = (url: Opt, opts: object) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); + const fontBox = (opts:DocumentOptions) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const iconTemplates = [ - makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })), - makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })), - makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })), - makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })), - makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })), - makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })), - makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})), - makeIconTemplate(DocumentType.BUTTON, "data", fontBox), + makeIconTemplate(undefined, "title", (opts) => labelBox({ ...opts, _backgroundColor: "dimgray" })), + makeIconTemplate(DocumentType.AUDIO, "title", (opts) => labelBox({ ...opts, _backgroundColor: "lightgreen" })), + makeIconTemplate(DocumentType.PDF, "title", (opts) => labelBox({ ...opts, _backgroundColor: "pink" })), + makeIconTemplate(DocumentType.WEB, "title", (opts) => labelBox({...opts, _backgroundColor: "brown" })), + makeIconTemplate(DocumentType.RTF, "text", (opts) => labelBox({ ...opts, _showTitle: "creationDate" })), + makeIconTemplate(DocumentType.IMG, "data", (opts) => imageBox("", { ...opts, _height: undefined, })), + makeIconTemplate(DocumentType.COL, "icon", (opts) => imageBox(undefined, opts)), + makeIconTemplate(DocumentType.VID, "icon", (opts) => imageBox(undefined, opts)), + makeIconTemplate(DocumentType.BUTTON, "data", (opts) => fontBox(opts)), //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now - makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })), + makeIconTemplate("transcription" as any, "transcription", (opts) => labelBox({ ...opts, _backgroundColor: "orange" })), // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) ].filter(d => d).map(d => d!); this.AssignOpts(DocCast(doc[field]), {}, iconTemplates); @@ -315,10 +309,10 @@ export class CurrentUserUtils { { toolTip: "Tap or drag to create an equation", title: "Math", icon: "calculator", dragFactory: doc.emptyEquation as Doc, }, { toolTip: "Tap or drag to create a webpage", title: "Web", icon: "globe-asia", dragFactory: doc.emptyWebpage as Doc, }, { toolTip: "Tap or drag to create a comparison box", title: "Compare", icon: "columns", dragFactory: doc.emptyComparison as Doc, }, - { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, }, + { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone", dragFactory: doc.emptyAudio as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, }, { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, }, - { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, funcs: { hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'}}, { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, funcs: { hidden: 'IsNoviceMode()'} }, { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, funcs: { hidden: 'IsNoviceMode()'}}, { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, }, diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6ee8065f5..67a73feff 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,9 +8,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; -import { DocCast, PromiseValue, StrCast } from '../../fields/Types'; +import { PromiseValue, StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; @@ -32,8 +31,7 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/collectionLinear'; import { CollectionMenu } from './collections/CollectionMenu'; -import { TreeViewType } from './collections/CollectionTreeView'; -import { CollectionView, CollectionViewType } from './collections/CollectionView'; +import { CollectionViewType } from './collections/CollectionView'; import "./collections/TreeView.scss"; import { ComponentDecorations } from './ComponentDecorations'; import { ContextMenu } from './ContextMenu'; @@ -49,7 +47,6 @@ import { LightboxView } from './LightboxView'; import { LinkMenu } from './linking/LinkMenu'; import "./MainView.scss"; import { AudioBox } from './nodes/AudioBox'; -import { ButtonType } from './nodes/button/FontIconBox'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; import { DocumentView } from './nodes/DocumentView'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss index 302e7a5e3..033cdf1f7 100644 --- a/src/client/views/OverlayView.scss +++ b/src/client/views/OverlayView.scss @@ -15,6 +15,7 @@ flex-direction: column; top: 0; left: 0; + pointer-events: all; } .overlayWindow-outerDiv, -- cgit v1.2.3-70-g09d2 From 6e0554f57a67932d20fab6b1eb3e61e2f8eefc4f Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Jun 2022 17:05:07 -0400 Subject: yet more currentuserutils cleanup --- src/client/util/CurrentUserUtils.ts | 217 ++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 121 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index ff6ef4809..fe9a9b2d9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,6 +1,6 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; -import { DataSym, Doc, DocListCast, DocListCastAsync, Opt, StrListCast } from "../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; @@ -8,7 +8,7 @@ import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types"; +import { Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types"; import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { OmitKeys, Utils } from "../../Utils"; @@ -77,7 +77,7 @@ export class CurrentUserUtils { static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) { scripts && Object.keys(scripts).map(key => { if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) { - doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any" }, {"_readOnly_": true}); + doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any", documentView:Doc.name}, {"_readOnly_": true}); } }); funcs && Object.keys(funcs).map(key => { @@ -108,6 +108,9 @@ export class CurrentUserUtils { } return doc; } + static AssignDocField(doc:Doc, field:string, creator:(reqdOpts:DocumentOptions, items?:Doc[]) => Doc, reqdOpts:DocumentOptions, items?: Doc[], scripts?:{[key:string]:string}, funcs?:{[key:string]:string}) { + return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); + } // initializes experimental advanced template views - slideView, headerView static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) { @@ -138,7 +141,7 @@ export class CurrentUserUtils { const assignBtnAndTempOpts = (templateBtn:Opt, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => { if (templateBtn) { this.AssignOpts(templateBtn,btnOpts); - this.AssignOpts(DocCast(templateBtn.dragFactory), templateOptions) ?? (templateBtn.dragFactory = template(templateOpts)); + this.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions); } return templateBtn; }; @@ -182,10 +185,7 @@ export class CurrentUserUtils { /// Initializes collection of templates for notes and click functions static setupDocTemplates(doc: Doc, field="myTemplates") { - const preEleOpts:DocumentOptions = { - title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data" - }; - this.AssignOpts(DocCast(doc.presElement), preEleOpts) ?? (doc.presElement= Docs.Create.PresElementBoxDocument(preEleOpts)); + this.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"}); const templates = [ DocCast(doc.presElement), CurrentUserUtils.setupNoteTemplates(doc), @@ -193,7 +193,7 @@ export class CurrentUserUtils { ]; const reqdOpts = { title: "template layouts", _xMargin: 0, system: true, }; const reqdScripts = { dropConverter: "convertToButtons(dragData)" }; - return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, templates) ?? (doc[field] = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts); + return this.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts); } // setup templates for different document types when they are iconified from Document Decorations @@ -299,9 +299,7 @@ export class CurrentUserUtils { }, funcs: {title: 'self.text?.Text'}}, ]; - emptyThings.forEach(thing => - this.AssignScripts(this.AssignOpts(DocCast(doc["empty"+thing.key]), {...standardOps(thing.key), ...thing.opts}) ?? (doc["empty"+thing.key] = thing.creator({...standardOps(thing.key), ...thing.opts})), undefined, thing.funcs)) - + emptyThings.forEach(thing => this.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs)); return [ { toolTip: "Tap or drag to create a note", title: "Note", icon: "sticky-note", dragFactory: doc.emptyNote as Doc, }, @@ -360,7 +358,7 @@ export class CurrentUserUtils { /// the empty panel that is filled with whichever left menu button's panel has been selected static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") { - this.AssignOpts(DocCast(doc[field]), {}) ?? (doc[field] = ((doc:Doc) => {doc.system = true; return doc;})(new Doc())); + this.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true}); } /// Initializes the left sidebar menu buttons and the panels they open up @@ -381,14 +379,13 @@ export class CurrentUserUtils { title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true }; - const reqdScripts = { dropConverter: "convertToButtons(dragData)" } - return this.AssignScripts(this.AssignOpts(myLeftSidebarMenu, reqdStackOpts, menuBtns) ?? (doc[field] = Docs.Create.StackingDocument(menuBtns, reqdStackOpts)), reqdScripts); + return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" }); } // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") { const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,}; - this.AssignOpts(DocCast(doc[field]), reqdOpts, this.setupMobileButtons()) ?? (doc[field] = Docs.Create.StackingDocument(this.setupMobileButtons(), reqdOpts)); + this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts); } // Sets up mobile buttons for inside mobile menu @@ -497,11 +494,9 @@ export class CurrentUserUtils { /// Search option on the left side button panel static setupSearcher(doc: Doc, field:string) { - const reqdOpts:DocumentOptions = { - dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", - _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true, - }; - return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts)); + return this.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), { + dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", + _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true, }); } /// Initializes the panel of draggable tools that is opened from the left sidebar. @@ -513,7 +508,7 @@ export class CurrentUserUtils { title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0", _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, }; - return this.AssignOpts(myTools, reqdToolOps, [creatorBtns, templateBtns]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, templateBtns], reqdToolOps)); + return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]); } /// initializes the left sidebar dashboard pane @@ -537,7 +532,7 @@ export class CurrentUserUtils { childContextMenuIcons: new List(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters 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." }; - myDashboards = this.AssignOpts(myDashboards, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); + myDashboards = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; const contextMenuScripts = [newDashboard]; const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters @@ -571,7 +566,7 @@ export class CurrentUserUtils { _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, explainer: "All of the trails that you have created will appear here." }; - myTrails = this.AssignOpts(myTrails, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([],reqdOpts )); + myTrails = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const contextMenuScripts = [reqdBtnScript.onClick]; if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { myTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); @@ -582,8 +577,7 @@ export class CurrentUserUtils { /// initializes the left sidebar File system pane static setupFilesystem(doc: Doc, field:string) { var myFilesystem = DocCast(doc[field]); - const reqdOrphansOpts:DocumentOptions = { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }; - this.AssignOpts(DocCast(doc.myFileOrphans), reqdOrphansOpts) ?? (doc.myFileOrphans = Docs.Create.TreeDocument([], reqdOrphansOpts)); + const myFileOrphans = this.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); const newFolder = `makeTopLevelFolder()`; const newFolderOpts: DocumentOptions = { @@ -601,7 +595,7 @@ export class CurrentUserUtils { childContextMenuIcons: new List(["plus"]), explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." }; - myFilesystem = this.AssignOpts(myFilesystem, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([DocCast(doc.myFileOrphans)], reqdOpts)); + myFilesystem = this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]); const childContextMenuScripts = [newFolder]; if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) { myFilesystem.childContextMenuScripts = new List(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); @@ -618,16 +612,13 @@ export class CurrentUserUtils { contextMenuIcons:new List(["trash"]), explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list." }; - const recentlyClosed = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); + const recentlyClosed = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const clearAll = (target:string) => `getProto(${target}).data = new List([])`; const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton, buttonText: "Empty", icon: "trash", system: true, toolTip: "Empty recently closed",}; - const clearBtnsScripts = {onClick: clearAll("self.target")} - const clearDocsButton = this.AssignScripts( - this.AssignOpts(DocCast(recentlyClosed?.clearDocsBtn), clearBtnsOpts) ?? (recentlyClosed.clearDocsBtn = Docs.Create.FontIconDocument(clearBtnsOpts)), - clearBtnsScripts); + const clearDocsButton = this.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")}); if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton; @@ -657,80 +648,77 @@ export class CurrentUserUtils { boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true, treeViewHideTitle: true, treeViewTruncateTitleWidth: 150 }; - if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }) - return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts)); + if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" }); + return this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]); } - static linearButtonList = (title: string, opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, { - title, ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true, + static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, { + ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), _lockedPosition: true, system: true, flexDirection: "row" }) static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({ - btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["_dropAction", "_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts, + btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, + _removeDropProperties: new List(["_dropAction", "_hideContextMenu", "stayInCollection"]), + _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts, }) /// initializes the required buttons in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc, field="myDockedBtns") { const dockedBtns = DocCast(doc[field]); - const dockBtn = (title:string, onClick: string, opts: DocumentOptions) => { - const btn = this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === title), opts) ?? - CurrentUserUtils.createToolButton({title, ...opts}); - this.AssignScripts(btn, {onClick}) - return btn; - } + const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}) => + this.AssignScripts(this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? + CurrentUserUtils.createToolButton(opts), scripts); + const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet - { title: "undo", click: "undo()", opts: { icon: "undo-alt", toolTip: "Click to undo" }}, - { title: "redo", click:"redo()", opts: { icon: "redo-alt", toolTip: "Click to redo" }} + { scripts: { onClick: "undo()"}, opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }}, + { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }} ]; - const btns = btnDescs.map(desc => dockBtn(desc.title, desc.click, {_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts})); + const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts = { title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }; reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); - return this.AssignOpts(dockedBtns, dockBtnsReqdOpts, btns) ?? (doc[field] = CurrentUserUtils.linearButtonList("dockedBtns", dockBtnsReqdOpts, btns)); + return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns); } static textTools():Button[] { return [ - { - title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, - btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]), - scripts :{script : 'setFont(value, _readOnly_)'} - }, - { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'} }, - { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts:{script: '{ return setFontColor(value, _readOnly_); }'}}, - { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, - { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline",scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} }, - { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} }, - { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} }, + { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, scripts: {script: 'setFont(value, _readOnly_)'}, + btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) }, + { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, + { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_); }'}}, + { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, + { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} }, + { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} }, + { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} }, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}}, // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}}, // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}}, - { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }}, + { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }}, { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} }, - { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, - { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}}, + { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, + { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}}, ]; } static inkTools():Button[] { return [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveTool("pen", _readOnly_);}' }}, - { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveTool("write", _readOnly_);}'} }, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveTool("eraser", _readOnly_);}' }}, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts: {onClick:'{ return setActiveTool("pen", _readOnly_);}' }}, + { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts: {onClick:'{ return setActiveTool("write", _readOnly_);}'} }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts: {onClick:'{ return setActiveTool("eraser", _readOnly_);}' }}, // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveTool("circle", _readOnly_);}'} }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts: {onClick:'{ return setActiveTool("circle", _readOnly_);}'} }, // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveTool("line", _readOnly_);}' }}, - { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} }, - { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} }, - { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick: '{ return setActiveTool("line", _readOnly_);}' }}, + { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: "{ return setFillColor(value, _readOnly_);}"} }, + { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, + { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, ]; } @@ -740,52 +728,43 @@ export class CurrentUserUtils { static webTools() { return [ - { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' }, - { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' }, + { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", scripts: { onClick: '{ return webBack(_readOnly_); }' }}, + { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", scripts: { onClick: '{ return webForward(_readOnly_); }'}}, //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' }, - { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' }, + { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, scripts: { script: '{ return webSetURL(value, _readOnly_); }'} }, ]; } static contextMenuTools():Button[] { return [ - { - title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, - btnList: new List([CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, + { btnList: new List([CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid]), - scripts: {script: 'setView(value, _readOnly_)'}, - }, // Always show - { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left", scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} }, - { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, - { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: '!selectedDocumentType()' }}, // Only when a document is selected - { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: '!selectedDocumentType()'} }, - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform - // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected - { title: "Text", icon: "text", subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available - { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available - { title: "Web", icon: "web", subMenu: CurrentUserUtils.webTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.WEB}")`, hidden: `!selectedDocumentType("${DocumentType.WEB}")`} }, // Only when Web is selected - { title: "Schema", icon: "schema", subMenu: CurrentUserUtils.schemaTools(), funcs: { linearViewIsExpanded: `selectedDocumentType(undefined, "${CollectionViewType.Schema}")`, hidden: `!selectedDocumentType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected + title: "Perspective", toolTip: "View", width: 100,btnType: ButtonType.DropdownList,ignoreClick: true, scripts: { script: 'setView(value, _readOnly_)'}}, + { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setBackgroundColor(value, _readOnly_)'},funcs: {hidden: '!selectedDocumentType()'}}, // Only when a document is selected + { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setHeaderColor(value, _readOnly_)'}, funcs: {hidden: '!selectedDocumentType()'}}, + { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, scripts: { onClick: 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'}}, // Only when floating document is selected in freeform + { title: "Text", icon: "text", subMenu: CurrentUserUtils.textTools(), funcs: {linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available + { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: {linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available + { title: "Web", icon: "web", subMenu: CurrentUserUtils.webTools(), funcs: {linearViewIsExpanded: `selectedDocumentType("${DocumentType.WEB}")`, hidden: `!selectedDocumentType("${DocumentType.WEB}")`} }, // Only when Web is selected + { title: "Schema", icon: "schema", subMenu: CurrentUserUtils.schemaTools(), funcs: {linearViewIsExpanded: `selectedDocumentType(undefined, "${CollectionViewType.Schema}")`, hidden: `!selectedDocumentType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected ]; } /// initializes a context menu button for the top bar context menu static setupContextMenuButton(params:Button, btnDoc?:Doc) { const reqdOpts:DocumentOptions = { - title: params.title, btnType: params.btnType, icon: params.icon, toolTip: params.toolTip, ignoreClick: params.ignoreClick, - numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,_nativeHeight: 30, btnList: params.btnList, - backgroundColor: params.scripts?.onClick ? undefined: "transparent", /// a bit hacky. if an onClick is specified, then we assume we assume a toggle use onClick to get the backgroundColor (see below). Otherwise, assume a transparent background - _nativeWidth: params.width ? params.width : 30, - _width: params.width ? params.width : 30, - _height: 30, + ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, + backgroundColor: params.scripts?.onClick ? undefined: "transparent", /// a bit hacky. if an onClick is specified, then assume a toggle uses onClick to get the backgroundColor (see below). Otherwise, assume a transparent background color: Colors.WHITE, system: true, dontUndo: true, - _stayInCollection: true, - _hideContextMenu: true, - _lockedPosition: true, - _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + _nativeWidth: params.width ?? 30, _width: params.width ?? 30, + _height: 30, _nativeHeight: 30, + _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, + _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), }; const reqdFuncs:{[key:string]:any} = { ...params.funcs, @@ -802,28 +781,26 @@ export class CurrentUserUtils { if (!params.subMenu) { return this.setupContextMenuButton(params, menuBtnDoc); } else { - const reqdSubMenuOpts = { title: params.title, icon: params.icon, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, + const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, title:"submenu", + childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, linearViewSubMenu: true, linearViewExpandable: true, }; - const reqdSubMenuFuncs:{[key:string]:any} = { ...params.funcs}; return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? - CurrentUserUtils.linearButtonList("submenu", reqdSubMenuOpts, params.subMenu.map(sub => + this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) - )), undefined, reqdSubMenuFuncs); + )), undefined, params.funcs); } }); - const reqdCtxtOpts = { title: "menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; - return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts) ?? (doc[field] = CurrentUserUtils.linearButtonList("contextMenuButtons", reqdCtxtOpts, ctxtMenuBtns)); + const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, ctxtMenuBtns); } /// collection of documents rendered in the overlay layer above all tabs and other UI static setupOverlays(doc: Doc, field = "myOverlayDocs") { - const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true }; - return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts)); + return this.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true }); } static setupPublished(doc:Doc, field = "myPublishedDocs") { - const reqdOpts = { title: "published docs", backgroundColor: "#aca3a6", system: true }; - return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); + return this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true }); } /// The database of all links on all documents @@ -848,8 +825,11 @@ export class CurrentUserUtils { static async setupSharedDocs(doc: Doc, sharingDocumentId: string) { const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name }); - const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name}) - + const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}"; + + const sharedScripts = { + treeViewChildDoubleClick: dblClkScript, + } const sharedDocOpts:DocumentOptions = { title: "My Shared Docs", userColor: "rgb(202, 202, 202)", @@ -857,9 +837,6 @@ export class CurrentUserUtils { childContextMenuScripts: new List([addToDashboards!,]), childContextMenuLabels: new List(["Add to Dashboards",]), childContextMenuIcons: new List(["user-plus",]), - treeViewChildDoubleClick: dblClkScript, - }; - const sharedRequiredDocOpts:DocumentOptions = { "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment, childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar @@ -867,27 +844,25 @@ export class CurrentUserUtils { explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" }; - const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer")); - return this.AssignOpts(DocCast(sharedDocs), sharedRequiredDocOpts) ?? - (doc.mySharedDocs = Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId)); + await DocServer.GetRefField(sharingDocumentId + "outer"); + return this.AssignDocField(doc, "mySharedDocs", (opts) => Docs.Create.TreeDocument([], opts, sharingDocumentId + "outer", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); } /// Import option on the left side button panel - static setupImportSidebar(doc: Doc, field:string) { + static setupImportSidebar(doc: Doc, field:string) { const reqdOpts:DocumentOptions = { title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true, dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go." }; - const myImports = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.StackingDocument([], reqdOpts)); + const myImports = this.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts); const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true }; const reqdBtnScripts = { onClick: "importDocument()" }; - const newImportBtn = this.AssignOpts(DocCast(myImports.buttonMenuDoc), reqdBtnOpts) ?? (myImports.buttonMenuDoc = Docs.Create.FontIconDocument(reqdBtnOpts)); - return this.AssignScripts(newImportBtn, reqdBtnScripts); + return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, reqdBtnScripts); } static setupClickEditorTemplates(doc: Doc) { -- cgit v1.2.3-70-g09d2 From 4afe316e6649d915e2bb6f0eecbc9f3cddab7e24 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Jun 2022 23:08:56 -0400 Subject: fixing up Field Infos to work with new initialization. --- src/client/documents/Documents.ts | 124 ++++++++++++++---------------------- src/client/util/CurrentUserUtils.ts | 57 +++++++++++------ 2 files changed, 85 insertions(+), 96 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 01cfe90da..ad6a4dd3c 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -69,53 +69,57 @@ class EmptyBox { return ""; } } -abstract class FInfo { +export abstract class FInfo { description: string = ""; - type?: string; + fieldType?: string; values?: Field[]; - layoutField?: boolean; // is this field a layout (or datadoc) field // format?: string; // format to display values (e.g, decimal places, $, etc) // parse?: ScriptField; // parse a value from a string - constructor(d: string, l: boolean = false) { this.description = d; this.layoutField = l; } + constructor(d: string) { this.description = d; } } -class BoolInfo extends FInfo { type?= "boolean"; values?: boolean[] = [true, false]; } -class NumInfo extends FInfo { type?= "number"; values?: number[] = []; } -class StrInfo extends FInfo { type?= "string"; values?: string[] = []; } -class DocInfo extends FInfo { type?= "Doc"; values?: Doc[] = []; } -class DimInfo extends FInfo { type?= "DimUnit"; values?= [DimUnit.Pixel, DimUnit.Ratio]; } -class PEInfo extends FInfo { type?= "pointerEvents"; values?= ["all", "none"]; } -class DAInfo extends FInfo { type?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; } +class BoolInfo extends FInfo { fieldType?= "boolean"; values?: boolean[] = [true, false]; } +class NumInfo extends FInfo { fieldType?= "number"; values?: number[] = []; constructor(d:string, values?:number[]) { super(d); this.values = values; }} +class StrInfo extends FInfo { fieldType?= "string"; values?: string[] = []; constructor(d:string, values?:string[]) { super(d); this.values = values; }} +class DocInfo extends FInfo { fieldType?= "Doc"; values?: Doc[] = []; constructor(d:string, values?:Doc[]) { super(d); this.values = values; }} +class DimInfo extends FInfo { fieldType?= "DimUnit"; values?= [DimUnit.Pixel, DimUnit.Ratio]; } +class PEInfo extends FInfo { fieldType?= "pointerEvents"; values?= ["all", "none"]; } +class DAInfo extends FInfo { fieldType?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; } type BOOLt = BoolInfo | boolean; -type NUMt = NumInfo | number; -type STRt = StrInfo | string; -type DOCt = DocInfo | Doc; -type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; -type PEVt = PEInfo | "none" | "all"; +type NUMt = NumInfo | number; +type STRt = StrInfo | string; +type DOCt = DocInfo | Doc; +type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; +type PEVt = PEInfo | "none" | "all"; type DROPt = DAInfo | dropActionType; export class DocumentOptions { + x?: NUMt = new NumInfo("x coordinate of document in a freeform view"); + y?: NUMt = new NumInfo("y coordinage of document in a freeform view"); + z?: NUMt = new NumInfo("whether document is in overlay (1) or not (0)", [1,0]); system?: BOOLt = new BoolInfo("is this a system created/owned doc"); + type?: STRt = new StrInfo("type of document", Array.from(Object.keys(DocumentType))); + title?: string; _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else"); allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?"); childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection "); targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? "); - userColor?: string; // color associated with a Dash user (seen in header fields of shared documents) - color?: string; // foreground color data doc + userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)"); + color?: STRt = new StrInfo("foreground color data doc"); backgroundColor?: STRt = new StrInfo("background color for data doc"); - _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)", true); - _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents", true); - _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title", true); - _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes", true); - _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume", true); - _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view", true); - _panY?: NUMt = new NumInfo("vertical pan location of a freeform view", true); - _width?: NUMt = new NumInfo("displayed width of a document", true); - _height?: NUMt = new NumInfo("displayed height of document", true); - _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)", true); - _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)", true); - _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true); - _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true); - _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true); - _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents + _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)"); + _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents"); + _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title"); + _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes"); + _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume"); + _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view"); + _panY?: NUMt = new NumInfo("vertical pan location of a freeform view"); + _width?: NUMt = new NumInfo("displayed width of a document"); + _height?: NUMt = new NumInfo("displayed height of document"); + _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)"); + _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)"); + _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height"); + _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units"); + _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)"); + _fitContentsToBox?: BOOLt = new BoolInfo("whether a freeformview should zoom/scale to create a shrinkwrapped view of its content"); _contentBounds?: List; // the (forced) bounds of the document to display. format is: [left, top, right, bottom] _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed @@ -155,23 +159,20 @@ export class DocumentOptions { _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays) _timecodeToHide?: number; // the time that a document should be hidden _timelineLabel?: boolean; // whether the document exists on a timeline - "_carousel-caption-xMargin"?: number; - "_carousel-caption-yMargin"?: number; - "icon-nativeWidth"?: number; - "icon-nativeHeight"?: number; - "dragFactory-count"?: number; // number of items created from a drag button (used for setting title with incrementing index) - x?: number; - y?: number; - z?: number; // whether document is in overlay (1) or not (0 or undefined) + "_carousel-caption-xMargin"?: NUMt = new NumInfo("x margin of caption inside of a carouself collection"); + "_carousel-caption-yMargin"?: NUMt = new NumInfo("y margin of caption inside of a carouself collection"); + "icon-nativeWidth"?: NUMt = new NumInfo("native width of icon view"); + "icon-nativeHeight"?: NUMt = new NumInfo("native height of icon view"); + "dragFactory-count"?: NUMt = new NumInfo("number of items created from a drag button (used for setting title with incrementing index)"); lat?: number; lng?: number; infoWindowOpen?: boolean; author?: string; _layoutKey?: string; + fieldValues?: List; // possible field values used by fieldInfos + fieldType?: string; // type of afield used by fieldInfos unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf) - type?: string; - title?: string; - "acl-Public"?: string; // public permissions + "acl-Public"?: string; // public permissions "_acl-Public"?: string; // public permissions version?: string; // version identifier for a document label?: string; @@ -340,33 +341,6 @@ export class DocumentOptions { } export namespace Docs { - const _docOptions = new DocumentOptions(); - - export async function setupFieldInfos() { - return await DocServer.GetRefField("FieldInfos8") as Doc ?? - runInAction(() => { - const infos = new Doc("FieldInfos8", true); - const keys = Object.keys(new DocumentOptions()); - for (const key of keys) { - const options = (_docOptions as any)[key] as FInfo; - const finfo = new Doc(); - finfo.name = key; - switch (options.type) { - case "boolean": finfo.options = new List(options.values as any as boolean[]); break; - case "number": finfo.options = new List(options.values as any as number[]); break; - case "Doc": finfo.options = new List(options.values as any as Doc[]); break; - default: // string, pointerEvents, dimUnit, dropActionType - finfo.options = new List(options.values as any as string[]); break; - } - finfo.layoutField = options.layoutField; - finfo.description = options.description; - finfo.type = options.type; - infos[key] = finfo; - } - return infos; - }); - } - export let newAccount: boolean = false; export namespace Prototypes { @@ -798,8 +772,8 @@ export namespace Docs { I.tool = tool; I["text-align"] = "center"; I.title = "ink"; - I.x = options.x; - I.y = options.y; + I.x = options.x as number; + I.y = options.y as number; I._width = options._width as number; I._height = options._height as number; I._fontFamily = "cursive"; @@ -1266,8 +1240,8 @@ export namespace DocUtils { return DocServer.GetRefField(id).then(field => { if (field instanceof Doc) { const alias = Doc.MakeAlias(field); - alias.x = options.x || 0; - alias.y = options.y || 0; + alias.x = options.x as number || 0; + alias.y = options.y as number || 0; alias._width = (options._width as number) || 300; alias._height = (options._height as number) || (options._width as number) || 300; return alias; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index fe9a9b2d9..b9724051a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -13,7 +13,7 @@ import { ImageField, nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { OmitKeys, Utils } from "../../Utils"; import { DocServer } from "../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; +import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; @@ -39,6 +39,7 @@ import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; interface Button { + // DocumentOptions fields a button can set title?: string; toolTip?: string; icon?: string; @@ -51,6 +52,8 @@ interface Button { btnList?: List; ignoreClick?: boolean; buttonText?: string; + + // fields that do not correspond to DocumentOption fields scripts?: { script?: string; onClick?: string; } funcs?: { [key:string]: string }; subMenu?: Button[]; @@ -861,8 +864,7 @@ export class CurrentUserUtils { const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true }; - const reqdBtnScripts = { onClick: "importDocument()" }; - return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, reqdBtnScripts); + return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); } static setupClickEditorTemplates(doc: Doc) { @@ -875,9 +877,8 @@ export class CurrentUserUtils { targetScriptKey: "onChildClick", system: true }); - const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( - "openOnRight(self.doubleClickView)", - {}), { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick", system: true }); + const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( "openOnRight(self.doubleClickView)", {}), + { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick", system: true }); doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates", system: true }); } @@ -914,7 +915,7 @@ export class CurrentUserUtils { } static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - doc.globalGroupDatabase ?? (doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument()); + this.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {}); await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]), async () => { @@ -954,17 +955,35 @@ export class CurrentUserUtils { this.setupDockedButtons(doc); // the bottom bar of font icons this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left this.setupDocTemplates(doc); // sets up the template menu of templates - doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument()); - doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents + this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption + this.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {}); + this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); - doc.fieldInfos = await Docs.setupFieldInfos(); if (doc.activeDashboard instanceof Doc) { // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss) doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme; } return doc; } + static setupFieldInfos(doc:Doc, field="fieldInfos") { + const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object + const infos = this.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts); + const entries = Object.entries(new DocumentOptions()); + entries.forEach(pair => { + if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) { + const options = pair[1] as FInfo; + const opts:DocumentOptions = { system: true, title: pair[0], ...OmitKeys(options, ["values"]).omit, fieldIsLayout: pair[0].startsWith("_")}; + switch (options.fieldType) { + case "boolean": opts.fieldValues = new List(options.values as any); break; + case "number": opts.fieldValues = new List(options.values as any); break; + case "Doc": opts.fieldValues = new List(options.values as any); break; + default: opts.fieldValues = new List(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType + } + this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts); + } + }); + } public static async loadCurrentUser() { return rp.get(Utils.prepend("/getCurrentUser")).then(async response => { @@ -1007,17 +1026,14 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; public static openDashboard = (doc: Doc, fromHistory = false) => { - const userDoc = Doc.UserDoc(); CurrentUserUtils.MainDocId = doc[Id]; - if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) { - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); - } + Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); - if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard - userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); - } + // this has the side-effect of setting the main container since we're assigning the active/guest dashboard + Doc.UserDoc() ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); + const state = CurrentUserUtils._urlState; - if (state.sharing === true && !userDoc) { + if (state.sharing === true && !Doc.UserDoc()) { DocServer.Control.makeReadOnly(); } else { fromHistory || HistoryUtil.pushState({ @@ -1054,7 +1070,7 @@ export class CurrentUserUtils { const upload = Utils.prepend("/uploadDoc"); const formData = new FormData(); const file = input.files && input.files[0]; - if (file && file.type === 'application/zip') { + if (file?.type === 'application/zip') { formData.append('file', file); formData.append('remap', "true"); const response = await fetch(upload, { method: "POST", body: formData }); @@ -1067,9 +1083,8 @@ export class CurrentUserUtils { } } } else if (input.files && input.files.length !== 0) { - const importDocs = CurrentUserUtils.MyImports; const disposer = OverlayView.ShowSpinner(); - DocListCastAsync(importDocs.data).then(async list => { + DocListCastAsync(CurrentUserUtils.MyImports.data).then(async list => { const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); if (results.length !== input.files?.length) { alert("Error uploading files - possibly due to unsupported file types"); -- cgit v1.2.3-70-g09d2 From 8eb794e233f905daaa5b9a25c6720e567512653e Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 23 Jun 2022 12:15:47 -0400 Subject: fixed : contextMenu . fixed some fitWidth issues for text where heights weren't working right --- src/client/documents/Documents.ts | 27 +++------ src/client/util/CurrentUserUtils.ts | 4 +- src/client/views/ContextMenu.tsx | 64 ++++++---------------- src/client/views/ContextMenuItem.tsx | 10 +--- src/client/views/nodes/DocumentView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 3 +- 6 files changed, 33 insertions(+), 77 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ad6a4dd3c..fc73deb36 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -363,7 +363,7 @@ export namespace Docs { layout: { view: FormattedTextBox, dataField: "text" }, options: { _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true, - links: "@links(self)" + forceReflow: true, links: "@links(self)" } }], [DocumentType.SEARCH, { @@ -400,7 +400,7 @@ export namespace Docs { }], [DocumentType.AUDIO, { layout: { view: AudioBox, dataField: defaultDataKey }, - options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" } + options: { _height: 100, backgroundColor: "lightGray", forceReflow: true, nativeDimModifiable: true, links: "@links(self)" } }], [DocumentType.REC, { layout: { view: VideoBox, dataField: defaultDataKey }, @@ -412,7 +412,7 @@ export namespace Docs { }], [DocumentType.MAP, { layout: { view: MapBox, dataField: defaultDataKey }, - options: { _height: 600, _width: 800, links: "@links(self)" } + options: { _height: 600, _width: 800, nativeDimModifiable: true, links: "@links(self)" } }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: defaultDataKey }, @@ -450,11 +450,11 @@ export namespace Docs { }], [DocumentType.EQUATION, { layout: { view: EquationBox, dataField: defaultDataKey }, - options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true } + options: { links: "@links(self)", nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true } }], [DocumentType.FUNCPLOT, { layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { links: "@links(self)" } + options: { nativeDimModifiable: true, links: "@links(self)" } }], [DocumentType.BUTTON, { layout: { view: LabelBox, dataField: "onClick" }, @@ -493,7 +493,7 @@ export namespace Docs { }], [DocumentType.COMPARISON, { layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" } + options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" } }], [DocumentType.GROUPDB, { data: new List(), @@ -506,7 +506,7 @@ export namespace Docs { }], [DocumentType.DATAVIZ, { layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { _fitWidth: true, _fitHeight: true, links: "@links(self)" } + options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" } }] ]); @@ -530,15 +530,6 @@ export namespace Docs { const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); // fetch the actual prototype documents from the server const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds); - if (!Docs.newAccount) { - Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB - Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true; - Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true; - Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB - Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true; - Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true; - } - // update this object to include any default values: DocumentOptions for all prototypes prototypeIds.map(id => { const existing = actualProtos[id] as Doc; @@ -1273,8 +1264,8 @@ export namespace DocUtils { })) as ContextMenuProps[], icon: "sticky-note" }); - const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data).lastElement()?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ - description: ":" + StrCast(dragDoc.title), + const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ + description: ":" + StrCast(dragDoc.title).replace("Untitled ",""), event: undoBatch((args: { x: number, y: number }) => { const newDoc = Doc.copyDragFactory(dragDoc); if (newDoc) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b9724051a..28d20c56b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -282,13 +282,13 @@ export class CurrentUserUtils { }[] = [ {key: "Note", creator: opts => Docs.Create.TextDocument("", opts), opts: { _width: 200, _autoHeight: true }}, {key: "Collection", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 150, _height: 100 }}, - {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _backgroundGridShow: true, }}, + {key: "Equation", creator: opts => Docs.Create.EquationDocument(opts), opts: { _width: 300, _height: 35, _fitWidth:false, _backgroundGridShow: true, }}, {key: "Webpage", creator: opts => Docs.Create.WebDocument("",opts), opts: { _width: 400, _height: 512, _nativeWidth: 850, useCors: true, }}, {key: "Comparison", creator: Docs.Create.ComparisonDocument, opts: { _width: 300, _height: 300 }}, {key: "Audio", creator: opts => Docs.Create.AudioDocument(nullAudio, opts),opts: { _width: 200, _height: 100, }}, {key: "Map", creator: opts => Docs.Create.MapDocument([], opts), opts: { _width: 800, _height: 600, _showSidebar: true, }}, {key: "Screengrab", creator: Docs.Create.ScreenshotDocument, opts: { _width: 400, _height: 200 }}, - {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, + {key: "WebCam", creator: opts => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, recording:true, system: true, cloneFieldFilter: new List(["system"]) }}, {key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }}, {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument(opts), opts: { _width: 300, _height: 300 }}, diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index e2f98de1e..9034cacb2 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,10 +1,10 @@ import React = require("react"); -import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem"; -import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, IReactionDisposer, observable } from "mobx"; import { observer } from "mobx-react"; import "./ContextMenu.scss"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import Measure from "react-measure"; +import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem"; +import { Utils } from "../../Utils"; @observer export class ContextMenu extends React.Component { @@ -116,10 +116,6 @@ export class ContextMenu extends React.Component { this._defaultItem = item; } - getItems() { - return this._items; - } - static readonly buffer = 20; get pageX() { const x = this._pageX; @@ -199,37 +195,25 @@ export class ContextMenu extends React.Component { return eles; }; - return flattenItems(this._items, name => [name]); + return flattenItems(this._items.slice(), name => [name]); } @computed get flatItems(): OriginalMenuProps[] { return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; } - @computed get filteredViews() { - const createGroupHeader = (contents: any) => { - return ( -
    -
    {contents}
    -
    - ); - }; - const createItem = (item: ContextMenuProps, selected: boolean) => ; - let itemIndex = 0; - return this.filteredItems.map(value => { - if (Array.isArray(value)) { - return createGroupHeader(value.join(" -> ")); - } else { - return createItem(value, itemIndex++ === this.selectedIndex); - } - }); - } - @computed get menuItems() { if (!this._searchString) { return this._items.map((item, ind) => ); } - return this.filteredViews; + return this.filteredItems.map((value, index) => + Array.isArray(value) ? +
    +
    {value.join(" -> ")}
    +
    + : + + ); } @computed get itemsNeedSearch() { @@ -237,14 +221,8 @@ export class ContextMenu extends React.Component { } render() { - if (!this._display) { - return null; - } - const style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } : - { left: this.pageX, bottom: this.pageY }; - - const contents = ( - <> + return !this._display ? (null) : +
    {!this.itemsNeedSearch ? (null) : @@ -253,17 +231,7 @@ export class ContextMenu extends React.Component { } {this.menuItems} - - ); - return ( - { this._width = r.offset.width; this._height = r.offset.height; })}> - {({ measureRef }) => ( -
    - {contents} -
    - )} -
    - ); +
    ; } @action diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 25d00f701..c136de1c3 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -28,11 +28,10 @@ export type ContextMenuProps = OriginalMenuProps | SubmenuProps; export class ContextMenuItem extends React.Component { @observable private _items: Array = []; @observable private overItem = false; - @observable private subRef = React.createRef(); - constructor(props: ContextMenuProps | SubmenuProps) { - super(props); - if ((this.props as SubmenuProps).subitems) { + componentDidMount() { + this._items.length = 0; + if ((this.props as SubmenuProps)?.subitems) { (this.props as SubmenuProps).subitems?.forEach(i => this._items.push(i)); } } @@ -78,9 +77,6 @@ export class ContextMenuItem extends React.Component diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5d2adc321..8d4fd376f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1251,7 +1251,7 @@ export class DocumentView extends React.Component { @computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; } @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 ? (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2 : 0; } @computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; } - @computed get centeringY() { return this.fitWidth || this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } + @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight()); focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 600730d87..d3dee3c89 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -862,6 +862,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (nh) this.layoutDoc._nativeHeight = scrollHeight; } + @computed get contentScaling() { return Doc.NativeAspect(this.rootDoc, this.dataDoc, false) ? this.props.scaling?.() || 1 : 1;} componentDidMount() { !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. this._cachedLinks = DocListCast(this.Document.links); @@ -875,7 +876,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._disposers.componentHeights = reaction( // set the document height when one of the component heights changes and autoHeight is on () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }), ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => { - autoHeight && this.props.setHeight?.((this.props.scaling?.() || 1) * (marginsHeight + Math.max(sidebarHeight, textHeight))); + autoHeight && this.props.setHeight?.(this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight))); }, { fireImmediately: true }); this._disposers.links = reaction(() => DocListCast(this.dataDoc.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { -- cgit v1.2.3-70-g09d2 From 9c1f0a7d394affe955fb3f422647784b5bdd32b6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Jun 2022 09:30:40 -0400 Subject: fixed highlighting readonly mode. simplified away some async stuff on load. --- src/client/DocServer.ts | 2 +- src/client/util/CurrentUserUtils.ts | 38 +++++++++++++++++-------------------- src/client/util/SettingsManager.tsx | 5 ++--- 3 files changed, 20 insertions(+), 25 deletions(-) (limited to 'src/client/util') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index dbc4783d8..c9a30b8e3 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -180,7 +180,7 @@ export namespace DocServer { _isReadOnly = true; _CreateField = field => _cache[field[Id]] = field; _UpdateField = emptyFunction; - _RespondToUpdate = emptyFunction; + _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB } } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 28d20c56b..e5e12a7e5 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -807,16 +807,13 @@ export class CurrentUserUtils { } /// The database of all links on all documents - static async setupLinkDocs(doc: Doc, linkDatabaseId: string) { - if (doc.myLinkDatabase === undefined) { - let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); - if (!linkDocs) { - linkDocs = new Doc(linkDatabaseId, true); - (linkDocs as Doc).title = "LINK DATABASE: " + Doc.CurrentUserEmail; - (linkDocs as Doc).author = Doc.CurrentUserEmail; - (linkDocs as Doc).data = new List([]); - (linkDocs as Doc)["acl-Public"] = SharingPermissions.Augment; - } + static setupLinkDocs(doc: Doc, linkDatabaseId: string) { + if (!(Docs.newAccount ? undefined : DocCast(doc.myLinkDatabase))) { + const linkDocs = new Doc(linkDatabaseId, true); + linkDocs.title = "LINK DATABASE: " + Doc.CurrentUserEmail; + linkDocs.author = Doc.CurrentUserEmail; + linkDocs.data = new List([]); + linkDocs["acl-Public"] = SharingPermissions.Augment; doc.myLinkDatabase = new PrefetchProxy(linkDocs); } } @@ -825,14 +822,12 @@ export class CurrentUserUtils { // A user's sharing document is where all documents that are shared to that user are placed. // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents - static async setupSharedDocs(doc: Doc, sharingDocumentId: string) { + static setupSharedDocs(doc: Doc, sharingDocumentId: string) { const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name }); const dblClkScript = "{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}"; - const sharedScripts = { - treeViewChildDoubleClick: dblClkScript, - } + const sharedScripts = { treeViewChildDoubleClick: dblClkScript, } const sharedDocOpts:DocumentOptions = { title: "My Shared Docs", userColor: "rgb(202, 202, 202)", @@ -847,8 +842,7 @@ export class CurrentUserUtils { explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'" }; - await DocServer.GetRefField(sharingDocumentId + "outer"); - return this.AssignDocField(doc, "mySharedDocs", (opts) => Docs.Create.TreeDocument([], opts, sharingDocumentId + "outer", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); + this.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts); } /// Import option on the left side button panel @@ -914,16 +908,18 @@ export class CurrentUserUtils { return doc.clickFuncs as Doc; } - static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + + /// Updates the UserDoc to have all required fields, docs, etc. No changes should need to be + /// written to the server if the code hasn't changed. However, choices need to be made for each Doc/field + /// whether to revert to "default" values, or to leave them as the user/system last set them. + static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { this.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {}); - await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]), async () => { const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data); const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); }, { fireImmediately: true }); - // Document properties on load doc.system ?? (doc.system = true); doc.title ?? (doc.title = Doc.CurrentUserEmail); Doc.noviceMode ?? (Doc.noviceMode = true); @@ -945,8 +941,8 @@ export class CurrentUserUtils { doc.savedFilters ?? (doc.savedFilters = new List()); doc.filterDocCount = 0; doc.freezeChildren = "remove|add"; - await this.setupLinkDocs(doc, linkDatabaseId); - await this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing + this.setupLinkDocs(doc, linkDatabaseId); + this.setupSharedDocs(doc, sharingDocumentId); // sets up the right sidebar collection for mobile upload documents and sharing this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile this.setupOverlays(doc); // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 22e33ab1e..080237649 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -67,12 +67,11 @@ export class SettingsManager extends React.Component<{}> { @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value); @undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex)); @undoBatch switchUserColor = action((color: ColorState) => { Doc.SharingDoc().userColor = undefined; Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex); }); - @undoBatch - playgroundModeToggle = action(() => { + @undoBatch playgroundModeToggle = action(() => { this.playgroundMode = !this.playgroundMode; if (this.playgroundMode) { DocServer.Control.makeReadOnly(); - addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "pink !important" }); + addStyleSheetRule(SettingsManager._settingsStyle, "topbar-inner-container", { background: "red !important" }); } else DocServer.Control.makeEditable(); }); -- cgit v1.2.3-70-g09d2 From dccf5909f4a4bec35559b23a2f355ab4c7a94086 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Jun 2022 19:03:00 -0400 Subject: fixed myImports pane to show imports. fixed saving/loading zip files of a collection. fixed errors related to NumT casting to number --- src/client/util/CurrentUserUtils.ts | 38 ++++++++++------------ src/client/views/collections/CollectionSubView.tsx | 6 ++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 32 +++++------------- src/fields/Doc.ts | 17 +++++++++- 4 files changed, 45 insertions(+), 48 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e5e12a7e5..d085c5f5e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -858,7 +858,8 @@ export class CurrentUserUtils { const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true }; - return this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); + this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" }); + return myImports; } static setupClickEditorTemplates(doc: Doc) { @@ -1065,29 +1066,24 @@ export class CurrentUserUtils { input.onchange = async _e => { const upload = Utils.prepend("/uploadDoc"); const formData = new FormData(); - const file = input.files && input.files[0]; + const file = input.files?.[0]; if (file?.type === 'application/zip') { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = Docs.newAccount ? undefined : await DocServer.GetRefField(json); - if (doc instanceof Doc) { - setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => - docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } - } + const doc = await Doc.importDocument(file); + // NOT USING SOLR, so need to replace this with something else // if (doc instanceof Doc) { + // setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => + // docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. + // } + const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null); + doc instanceof Doc && list?.splice(0, 0, doc); } else if (input.files && input.files.length !== 0) { const disposer = OverlayView.ShowSpinner(); - DocListCastAsync(CurrentUserUtils.MyImports.data).then(async list => { - const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); - if (results.length !== input.files?.length) { - alert("Error uploading files - possibly due to unsupported file types"); - } - list?.splice(0, 0, ...results); - disposer(); - }); + const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); + if (results.length !== input.files?.length) { + alert("Error uploading files - possibly due to unsupported file types"); + } + const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null); + list?.splice(0, 0, ...results); + disposer(); } else { console.log("No file selected"); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 17fdba764..03450b798 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -274,7 +274,7 @@ export function CollectionSubView(moreProps?: X) { if (docid) { // prosemirror text containing link to dash document DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView (f instanceof Doc) && addDocument(f); } }); @@ -311,7 +311,7 @@ export function CollectionSubView(moreProps?: X) { const docid = text.replace(Doc.globalServerPath(), "").split("?")[0]; DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView (f instanceof Doc) && addDocument(f); } }); @@ -445,7 +445,7 @@ export function CollectionSubView(moreProps?: X) { if (completed) completed(set); else { if (isFreeformView && generatedDocuments.length > 1) { - addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!,); + addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!,); } else { generatedDocuments.forEach(addDocument); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 542b1fce1..3c2047db7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1660,8 +1660,9 @@ export class CollectionFreeFormView extends CollectionSubView Doc.Zip(this.props.Document) }); - moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) }); + moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) }); } !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" }); } @@ -1670,28 +1671,13 @@ export class CollectionFreeFormView extends CollectionSubView { - const upload = Utils.prepend("/uploadDoc"); - const formData = new FormData(); - const file = input.files && input.files[0]; - if (file) { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = await DocServer.GetRefField(json); - if (doc instanceof Doc) { - const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); - doc.x = xx, doc.y = yy; - this.props.addDocument?.(doc); - setTimeout(() => - SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { - docs.docs.forEach(d => LinkManager.Instance.addLink(d)); - }), 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } - } - } + input.onchange = _e => { + input.files && Doc.importDocument(input.files[0]).then(doc => { + if (doc instanceof Doc) { + const [xx, yy] = this.getTransform().transformPoint(x, y); + doc.x = xx, doc.y = yy; + this.props.addDocument?.(doc);} + }); }; input.click(); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 981514b25..4fe6eb1e7 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -658,7 +658,7 @@ export namespace Doc { const zip = new JSZip(); - zip.file(doc.title + ".json", docString); + zip.file("doc.json", docString); // // Generate a directory within the Zip file structure // var img = zip.folder("images"); @@ -1289,6 +1289,21 @@ export namespace Doc { } } + export async function importDocument(file:File) { + const upload = Utils.prepend("/uploadDoc"); + const formData = new FormData(); + if (file) { + formData.append('file', file); + formData.append('remap', "true"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json !== "error") { + const doc = await DocServer.GetRefField(json); + return doc; + } + } + return undefined; + } export namespace Get { -- cgit v1.2.3-70-g09d2 From 550b5e93097e0708199a9cf88714754d197b3b97 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Jun 2022 12:48:54 -0400 Subject: fixed initialization issues with creating user documents unnecessarily. fixed updating doc icon templates to apply to documents already created. --- src/client/documents/Documents.ts | 10 +- src/client/util/CurrentUserUtils.ts | 64 ++++++----- src/client/util/LinkManager.ts | 123 +++++++++++---------- src/client/views/Main.tsx | 3 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/fields/Doc.ts | 2 +- 6 files changed, 104 insertions(+), 100 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 84e6258ac..3780df5b9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -105,7 +105,6 @@ export class DocumentOptions { userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)"); color?: STRt = new StrInfo("foreground color data doc"); backgroundColor?: STRt = new StrInfo("background color for data doc"); - _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)"); _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents"); _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title"); _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes"); @@ -328,6 +327,7 @@ export class DocumentOptions { text?: string; textTransform?: string; // is linear view expanded letterSpacing?: string; // is linear view expanded + iconTemplate?: string; // name of icon template style selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui clipboard?: Doc; searchQuery?: string; // for quersyBox @@ -1288,9 +1288,7 @@ export namespace DocUtils { const batch = UndoManager.StartBatch("makeCustomViewClicked"); runInAction(() => { doc.layoutKey = "layout_" + templateSignature; - if (doc[doc.layoutKey] === undefined) { - createCustomView(doc, creator, templateSignature, docLayoutTemplate); - } + createCustomView(doc, creator, templateSignature, docLayoutTemplate); }); batch.end(); return doc; @@ -1318,7 +1316,9 @@ export namespace DocUtils { const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; if (docLayoutTemplate) { - Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + if (docLayoutTemplate !== doc[customName]) { + Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + } } else { let fieldTemplate: Opt; if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 5c77041e0..f585277d8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -205,29 +205,37 @@ export class CurrentUserUtils { const reqdOpts = { title: "icon templates", _height: 75, system: true }; const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts)); - const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: (opts:DocumentOptions) => Doc) => { + const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => { const iconFieldName = "icon" + (type ? "_" + type : ""); - return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate({onClick:deiconifyScript(), system: true}), true, iconFieldName, templateField))) ; + const curIcon = DocCast(templateIconsDoc[iconFieldName]); + let creator = labelBox; + switch (opts.iconTemplate) { + case DocumentType.IMG : creator = imageBox; break; + case DocumentType.FONTICON: creator = fontBox; break; + } + const allopts = {system: true, ...opts}; + return this.AssignScripts( (curIcon?.iconTemplate === opts.iconTemplate ? + this.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))), + {onClick:"deiconifyView(documentView)"}); }; - const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }); - const labelBox = (opts: object) => Docs.Create.LabelDocument({ + const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({ textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px", _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts }); - const imageBox = (url: Opt, opts: object) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); - const fontBox = (opts:DocumentOptions) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); + const imageBox = (opts: DocumentOptions, url?:string) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, iconTemplate:DocumentType.IMG, _width: 360 / 4, _height: 270 / 4, _showTitle: "title", ...opts }); + const fontBox = (opts:DocumentOptions, data?:string) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts }); const iconTemplates = [ - makeIconTemplate(undefined, "title", (opts) => labelBox({ ...opts, _backgroundColor: "dimgray" })), - makeIconTemplate(DocumentType.AUDIO, "title", (opts) => labelBox({ ...opts, _backgroundColor: "lightgreen" })), - makeIconTemplate(DocumentType.PDF, "title", (opts) => labelBox({ ...opts, _backgroundColor: "pink" })), - makeIconTemplate(DocumentType.WEB, "title", (opts) => labelBox({...opts, _backgroundColor: "brown" })), - makeIconTemplate(DocumentType.RTF, "text", (opts) => labelBox({ ...opts, _showTitle: "creationDate" })), - makeIconTemplate(DocumentType.IMG, "data", (opts) => imageBox("", { ...opts, _height: undefined, })), - makeIconTemplate(DocumentType.COL, "icon", (opts) => imageBox(undefined, opts)), - makeIconTemplate(DocumentType.VID, "icon", (opts) => imageBox(undefined, opts)), - makeIconTemplate(DocumentType.BUTTON, "data", (opts) => fontBox(opts)), + makeIconTemplate(undefined, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "dimgray"}), + makeIconTemplate(DocumentType.AUDIO, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "lightgreen"}), + makeIconTemplate(DocumentType.PDF, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "pink"}), + makeIconTemplate(DocumentType.WEB, "title", { iconTemplate:DocumentType.LABEL, backgroundColor: "brown"}), + makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}), + makeIconTemplate(DocumentType.IMG, "data", { iconTemplate:DocumentType.IMG, _height: undefined}), + makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.BUTTON,"data", { iconTemplate:DocumentType.FONTICON}), //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now - makeIconTemplate("transcription" as any, "transcription", (opts) => labelBox({ ...opts, _backgroundColor: "orange" })), - // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})) + makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }), + //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts)) ].filter(d => d).map(d => d!); this.AssignOpts(DocCast(doc[field]), {}, iconTemplates); } @@ -779,23 +787,23 @@ export class CurrentUserUtils { /// Initializes all the default buttons for the top bar context menu static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") { - const ctxtMenuBtnsDoc = DocCast(doc[field]); + const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; + const ctxtMenuBtnsDoc = this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined); const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => { const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title); if (!params.subMenu) { return this.setupContextMenuButton(params, menuBtnDoc); } else { - const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, title:"submenu", + const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, linearViewSubMenu: true, linearViewExpandable: true, }; return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? - this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => + (ctxtMenuBtnsDoc[StrCast(params.title)]= this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) - )), undefined, params.funcs); + ))), undefined, params.funcs); } }); - const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 }; - return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, ctxtMenuBtns); + return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns); } /// collection of documents rendered in the overlay layer above all tabs and other UI @@ -957,12 +965,13 @@ export class CurrentUserUtils { this.AssignDocField(doc, "globalScriptDatabase", (opts) => Docs.Prototypes.MainScriptDocument(), {}); this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents - setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); 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; } + new LinkManager(); + DocServer.UPDATE_SERVER_CACHE(); return doc; } static setupFieldInfos(doc:Doc, field="fieldInfos") { @@ -1009,12 +1018,7 @@ export class CurrentUserUtils { await Docs.Prototypes.initialize(); const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; Docs.newAccount &&(userDoc.activePage = "home"); - const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); - (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager - const a1 = await Cast(link?.anchor1, Doc, null); - const a2 = await Cast(link?.anchor2, Doc, null); - }); - return updated; + return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); }); } else { throw new Error("There should be a user id! Why does Dash think there isn't one?"); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 2100b1195..d51cd350d 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,7 +1,6 @@ -import { validationResult } from "express-validator/check"; import { action, observable, observe } from "mobx"; import { computedFn } from "mobx-utils"; -import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc"; +import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../fields/Doc"; import { List } from "../../fields/List"; import { ProxyField } from "../../fields/Proxy"; import { BoolCast, Cast, StrCast } from "../../fields/Types"; @@ -35,72 +34,74 @@ export class LinkManager { constructor() { LinkManager._instance = this; this.createLinkrelationshipLists(); - setTimeout(() => { - LinkManager.userLinkDBs = []; - const addLinkToDoc = (link: Doc) => { - const a1Prom = link?.anchor1; - const a2Prom = link?.anchor2; - Promise.all([a1Prom, a2Prom]).then((all) => { - const a1 = all[0]; - const a2 = all[1]; - 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; - const a2 = link?.anchor2; - Promise.all([a1, a2]).then(action(() => { + LinkManager.userLinkDBs = []; + const addLinkToDoc = (link: Doc) => { + const a1Prom = link?.anchor1; + const a2Prom = link?.anchor2; + Promise.all([a1Prom, a2Prom]).then((all) => { + const a1 = all[0]; + const a2 = all[1]; + 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].delete(link); - Doc.GetProto(a2)[DirectLinksSym].delete(link); - Doc.GetProto(link)[DirectLinksSym].delete(link); + Doc.GetProto(a1)[DirectLinksSym].add(link); + Doc.GetProto(a2)[DirectLinksSym].add(link); + Doc.GetProto(link)[DirectLinksSym].add(link); } })); - }; - const watchUserLinkDB = (userLinkDBDoc: Doc) => { - LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); - const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields - observe(userLinkDBDoc.data as Doc, change => { // observe pushes/splices on a user link DB 'data' field (should only happen for local changes) - switch (change.type as any) { - case "splice": - (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link))); - (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link))); - break; - case "update": //let oldValue = change.oldValue; - } - }, true); - observe(userLinkDBDoc, "data", // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link) - change => { - switch (change.type as any) { - case "update": - Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => { - const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length); - const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length); - - const added = newDocs?.filter(link => !(oldDocs || []).includes(link)); - const removed = oldDocs?.filter(link => !(newDocs || []).includes(link)); - added?.forEach((link: any) => addLinkToDoc(toRealField(link))); - removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); - }); - } - }, true); - }; - observe(LinkManager.userLinkDBs, change => { + }); + }; + const remLinkFromDoc = (link: Doc) => { + const a1 = link?.anchor1; + const a2 = link?.anchor2; + Promise.all([a1, a2]).then(action(() => { + if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { + Doc.GetProto(a1)[DirectLinksSym].delete(link); + Doc.GetProto(a2)[DirectLinksSym].delete(link); + Doc.GetProto(link)[DirectLinksSym].delete(link); + } + })); + }; + const watchUserLinkDB = (userLinkDBDoc: Doc) => { + LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); + const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields + observe(userLinkDBDoc.data as Doc, change => { // observe pushes/splices on a user link DB 'data' field (should only happen for local changes) switch (change.type as any) { - case "splice": (change as any).added.forEach(watchUserLinkDB); break; + case "splice": + (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link))); + (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link))); + break; case "update": //let oldValue = change.oldValue; } }, true); - LinkManager.addLinkDB(Doc.LinkDBDoc()); - }); + observe(userLinkDBDoc, "data", // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link) + change => { + switch (change.type as any) { + case "update": + Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => { + const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length); + const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length); + + const added = newDocs?.filter(link => !(oldDocs || []).includes(link)); + const removed = oldDocs?.filter(link => !(newDocs || []).includes(link)); + added?.forEach((link: any) => addLinkToDoc(toRealField(link))); + removed?.forEach((link: any) => remLinkFromDoc(toRealField(link))); + }); + } + }, true); + }; + observe(LinkManager.userLinkDBs, change => { + switch (change.type as any) { + case "splice": (change as any).added.forEach(watchUserLinkDB); break; + case "update": //let oldValue = change.oldValue; + } + }, true); + LinkManager.addLinkDB(Doc.LinkDBDoc()); + DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist => dblist?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager + const a1 = await Cast(link?.anchor1, Doc, null); + const a2 = await Cast(link?.anchor2, Doc, null); + })); } diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 49c2dcf34..17841c055 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -10,7 +10,6 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { LinkManager } from "../util/LinkManager"; import { RecordingApi } from "../util/RecordingApi"; import { CollectionView } from "./collections/CollectionView"; -import { DashboardView } from './DashboardView'; import { MainView } from "./MainView"; AssignAllExtensions(); @@ -24,6 +23,7 @@ AssignAllExtensions(); await CurrentUserUtils.loadUserDocument(info.id); } else { await Docs.Prototypes.initialize(); + new LinkManager(); } document.getElementById('root')!.addEventListener('wheel', event => { if (event.ctrlKey) { @@ -37,7 +37,6 @@ AssignAllExtensions(); d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000)); const expires = "expires=" + d.toUTCString(); document.cookie = `loadtime=${loading};${expires};path=/`; - new LinkManager(); new RecordingApi; ReactDOM.render(, document.getElementById('root')); })(); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 081a1a924..17f1106d0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -517,7 +517,7 @@ export class MarqueeView extends React.Component Date: Mon, 27 Jun 2022 12:56:18 -0400 Subject: restored removeDashboard --- src/client/util/CurrentUserUtils.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f585277d8..d98eddb2e 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1132,6 +1132,13 @@ export class CurrentUserUtils { CurrentUserUtils.ActiveDashboard = undefined; } + public static removeDashboard = async (dashboard:Doc) => { + const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); + if (dashboards && dashboards.length > 1) { + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); + Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); + } + } public static createNewDashboard = (id?: string, name?: string) => { const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); const dashboards = CurrentUserUtils.MyDashboards; @@ -1223,14 +1230,7 @@ ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.creat ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); -ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { - const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); - if (dashboards && dashboards.length > 1) { - if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); - Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); - } -}, - "Remove Dashboard from Dashboards"); +ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { const dashboardAlias = Doc.MakeAlias(dashboard); Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); -- cgit v1.2.3-70-g09d2 From 0b9f5fb60d5ee3b589e2e4628c5f1a737757492d Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Jun 2022 13:04:01 -0400 Subject: from last --- src/client/util/CurrentUserUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d98eddb2e..6e115ea3c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1029,6 +1029,7 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; public static openDashboard = (doc: Doc, fromHistory = false) => { + if (!doc) return; CurrentUserUtils.MainDocId = doc[Id]; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); @@ -1134,9 +1135,10 @@ export class CurrentUserUtils { public static removeDashboard = async (dashboard:Doc) => { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); - if (dashboards && dashboards.length > 1) { + if (dashboards?.length) { if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); + if (!dashboards.length) CurrentUserUtils.ActivePage = "home"; } } public static createNewDashboard = (id?: string, name?: string) => { -- cgit v1.2.3-70-g09d2 From b1eb13fc6264b1272daba824ff0e4cd2bca8a6fa Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 28 Jun 2022 12:23:44 -0400 Subject: cleaned up some more currentUserUtils - fixed some issues with prev/next key frame appearing. --- src/client/documents/Documents.ts | 29 +++++- src/client/util/CurrentUserUtils.ts | 111 ++++----------------- .../views/collections/CollectionDockingView.tsx | 37 +++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 19 +++- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/topbar/TopBar.tsx | 3 +- src/fields/Doc.ts | 25 +---- 7 files changed, 97 insertions(+), 129 deletions(-) (limited to 'src/client/util') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3780df5b9..4690d856d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -13,7 +13,7 @@ import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; -import { SharingPermissions } from "../../fields/util"; +import { inheritParentAcls, SharingPermissions } from "../../fields/util"; import { Upload } from "../../server/SharedMediaTypes"; import { aggregateBounds, OmitKeys, Utils } from "../../Utils"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; @@ -1266,7 +1266,7 @@ export namespace DocUtils { const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({ description: ":" + StrCast(dragDoc.title).replace("Untitled ",""), event: undoBatch((args: { x: number, y: number }) => { - const newDoc = Doc.copyDragFactory(dragDoc); + const newDoc = DocUtils.copyDragFactory(dragDoc); if (newDoc) { newDoc.author = Doc.CurrentUserEmail; newDoc.x = x; @@ -1479,9 +1479,34 @@ export namespace DocUtils { } return generatedDocuments; } + + // copies the specified drag factory document + export function copyDragFactory(dragFactory: Doc) { + if (!dragFactory) return undefined; + const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); + ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); + } + + if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); + + return ndoc; + } + export function delegateDragFactory(dragFactory: Doc) { + const ndoc = Doc.MakeDelegateWithProto(dragFactory); + if (ndoc && dragFactory["dragFactory-count"] !== undefined) { + dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; + Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); + } + return ndoc; + } } ScriptingGlobals.add("Docs", Docs); +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return DocUtils.copyDragFactory(dragFactory); }); +ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return DocUtils.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); ScriptingGlobals.add(function generateLinkTitle(self: Doc) { const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : ""; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6e115ea3c..2a0702a58 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -755,8 +755,8 @@ export class CurrentUserUtils { CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid]), title: "Perspective", toolTip: "View", width: 100,btnType: ButtonType.DropdownList,ignoreClick: true, scripts: { script: 'setView(value, _readOnly_)'}}, - { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, - { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}}, + { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}}, + { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}}, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setBackgroundColor(value, _readOnly_)'},funcs: {hidden: '!selectedDocumentType()'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script: 'setHeaderColor(value, _readOnly_)'}, funcs: {hidden: '!selectedDocumentType()'}}, { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, scripts: { onClick: 'toggleOverlay(_readOnly_)'}, funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'}}, // Only when floating document is selected in freeform @@ -1028,8 +1028,10 @@ export class CurrentUserUtils { public static _urlState: HistoryUtil.DocUrl; - public static openDashboard = (doc: Doc, fromHistory = false) => { - if (!doc) return; + /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there). + /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url) + public static openDashboard = (doc: Doc|undefined, fromHistory = false) => { + if (!doc) return false; CurrentUserUtils.MainDocId = doc[Id]; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc); @@ -1097,46 +1099,15 @@ export class CurrentUserUtils { }; input.click(); } + + public static snapshotDashboard() { return CollectionDockingView.TakeSnapshot(CurrentUserUtils.ActiveDashboard); } - public static CaptureDashboardThumbnail() { - const activeDashboard = CurrentUserUtils.ActiveDashboard; - const docView = CollectionDockingView.Instance.props.DocumentView?.(); - const content = docView?.ContentDiv; - if (docView && content && activeDashboard) { - const _width = Number(getComputedStyle(content).width.replace("px","")); - const _height = Number(getComputedStyle(content).height.replace("px","")); - return CollectionFreeFormView.UpdateIcon( - docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(), - content, - _width, _height, - _width, _height, 0, 1, true, docView.layoutDoc[Id] + "-icon", - (iconFile, _nativeWidth, _nativeHeight) => { - const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); - const proto = Cast(img.proto, Doc, null)!; - proto["data-nativeWidth"] = _width; - proto["data-nativeHeight"] = _height; - Doc.GetProto(activeDashboard).thumb = img; - }); - } - - } - - public static async snapshotDashboard() { - if (CurrentUserUtils.ActiveDashboard) { - const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy); - CurrentUserUtils.openDashboard(copy); - } - } - - public static closeActiveDashboard = () => { - CurrentUserUtils.ActiveDashboard = undefined; - } + public static closeActiveDashboard = () => { CurrentUserUtils.ActiveDashboard = undefined; } public static removeDashboard = async (dashboard:Doc) => { const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data); if (dashboards?.length) { - if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!); + if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)); Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard); if (!dashboards.length) CurrentUserUtils.ActivePage = "home"; } @@ -1214,14 +1185,6 @@ export class CurrentUserUtils { public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; } } -ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) { - const copy = Doc.copyDragFactory(dragFactory); - if (copy) { - CollectionDockingView.AddSplit(copy, "right"); - const view = DocumentManager.Instance.getFirstDocumentView(copy); - view && SelectionManager.SelectView(view, false); - } -}); ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs"); ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode"); ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering"); @@ -1232,52 +1195,14 @@ ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.creat ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard"); -ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); -ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { - const dashboardAlias = Doc.MakeAlias(dashboard); - Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); - CurrentUserUtils.openDashboard(dashboardAlias); -}, - "adds Dashboard to set of Dashboards"); - -ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) { - let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined; - if (selected && checkParent) { - const parentDoc: Doc = Cast(selected.context, Doc, null); - selected = parentDoc; - } - if (selected && docType && selected.type === docType) return true; - else if (selected && colType && selected.viewType === colType) return true; - else if (selected && !colType && !docType) return true; - else return false; +ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards"); +ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { CurrentUserUtils.openDashboard( Doc.MakeAlias(dashboard)); }, "adds Dashboard to set of Dashboards"); +ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) { + let selected = (sel => checkContext ? DocCast(sel?.context) : sel)(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); + return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true; }); ScriptingGlobals.add(function makeTopLevelFolder() { - const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); - TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined }; - return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder); -}); -ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { - if (readOnly) return; - const sel = SelectionManager.Views()[0]; - const col = (sel.ComponentView as CollectionFreeFormView); - const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); - if (currentFrame === undefined) { - sel.props.Document._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); - } - CollectionFreeFormDocumentView.updateKeyframe(col.childDocs, currentFrame || 0); - sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1); - sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame)); -}); -ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { - if (readOnly) return; - const sel = SelectionManager.Views()[0]; - const col = (sel.ComponentView as CollectionFreeFormView); - const currentFrame = Cast(sel.props.Document._currentFrame, "number", null); - if (currentFrame === undefined) { - sel.props.Document._currentFrame = 0; - CollectionFreeFormDocumentView.setupKeyframes(col.childDocs, 0); - } - CollectionFreeFormDocumentView.gotoKeyframe(col.childDocs.slice()); - sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1); + TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined }; + const opts = { title: "Untitled folder", _stayInCollection: true, isFolder: true }; + return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id)); }); \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 07fcd6a7d..0830b6fdf 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -4,12 +4,12 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import * as GoldenLayout from "../../../client/goldenLayout"; -import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { ImageField } from '../../../fields/URLField'; import { inheritParentAcls } from '../../../fields/util'; import { emptyFunction, incrementTitleCopy } from '../../../Utils'; import { DocServer } from "../../DocServer"; @@ -19,14 +19,15 @@ import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DragManager } from "../../util/DragManager"; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { LightboxView } from '../LightboxView'; import "./CollectionDockingView.scss"; +import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import { CollectionViewType } from './CollectionView'; import { TabDocView } from './TabDocView'; import React = require("react"); -import { SelectionManager } from '../../util/SelectionManager'; const _global = (window /* browser */ || global /* node */) as any; @observer @@ -373,13 +374,34 @@ export class CollectionDockingView extends CollectionSubView() { } } - public static async Copy(doc: Doc, clone = false) { + public CaptureThumbnail() { + const content = this.props.DocumentView?.()?.ContentDiv; + if (content) { + const _width = Number(getComputedStyle(content).width.replace("px","")); + const _height = Number(getComputedStyle(content).height.replace("px","")); + return CollectionFreeFormView.UpdateIcon( + this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), + content, + _width, _height, + _width, _height, 0, 1, true, this.layoutDoc[Id] + "-icon", + (iconFile, _nativeWidth, _nativeHeight) => { + const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); + const proto = Cast(img.proto, Doc, null)!; + proto["data-nativeWidth"] = _width; + proto["data-nativeHeight"] = _height; + this.dataDoc.thumb = img; + }); + } + + } + public static async TakeSnapshot(doc: Doc|undefined, clone = false) { + if (!doc) return undefined; let json = StrCast(doc.dockingConfig); if (clone) { - const cloned = (await Doc.MakeClone(doc)); + const cloned = await Doc.MakeClone(doc); Array.from(cloned.map.entries()).map(entry => json = json.replace(entry[0], entry[1][Id])); Doc.GetProto(cloned.clone).dockingConfig = json; - return cloned.clone; + return CurrentUserUtils.openDashboard(cloned.clone); } const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); const origtabids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || []; @@ -395,7 +417,8 @@ export class CollectionDockingView extends CollectionSubView() { json = json.replace(origtab[Id], newtab[Id]); return newtab; }); - return Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); + const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); + return CurrentUserUtils.openDashboard(await copy); } @action diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3c2047db7..b9da4faa4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -153,6 +153,21 @@ export class CollectionFreeFormView extends CollectionSubView { + const currentFrame = Cast(this.Document._currentFrame, "number", null); + if (currentFrame === undefined) { + this.Document._currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); + } + if (back) { + CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice()); + this.Document._currentFrame = Math.max(0, (currentFrame || 0) - 1); + } else { + CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0); + this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1); + this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame)); + } + } @action setKeyFrameEditing = (set: boolean) => this._keyframeEditing = set; getKeyFrameEditing = () => this._keyframeEditing; onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick); @@ -2086,4 +2101,6 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY }); Doc.linkFollowHighlight(dv?.props.Document, false); } -ScriptingGlobals.add(CollectionBrowseClick); \ No newline at end of file +ScriptingGlobals.add(CollectionBrowseClick); +ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); }); +ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 17f1106d0..ab8a34d5a 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -156,7 +156,7 @@ export class MarqueeView extends React.Component { - CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => { + CollectionDockingView.Instance.CaptureThumbnail()?.then(() => { CurrentUserUtils.ActivePage = "home"; CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use }); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index a5d952176..b30ca644d 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,7 +23,7 @@ import { listSpec } from "./Schema"; import { ComputedField, ScriptField } from "./ScriptField"; import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField"; -import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; +import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); export namespace Field { @@ -1242,27 +1242,6 @@ export namespace Doc { return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; } - export function copyDragFactory(dragFactory: Doc) { - const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc)); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true); - } - - if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc); - - return ndoc; - } - export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory["dragFactory-count"] !== undefined) { - dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1; - Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(); - } - return ndoc; - } - export function toIcon(doc?: Doc, isOpen?: boolean) { switch (StrCast(doc?.type)) { case DocumentType.IMG: return "image"; @@ -1454,8 +1433,6 @@ ScriptingGlobals.add(function getProto(doc: any) { return Doc.GetProto(doc); }); ScriptingGlobals.add(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); ScriptingGlobals.add(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); }); -ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); }); ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); }); ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); }); ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); }); -- cgit v1.2.3-70-g09d2 From ea6e63648b21c46672b1b7cb1da0cbaa6857d0c1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 30 Jun 2022 11:01:46 -0400 Subject: fixed up some more initialization --- src/client/util/CurrentUserUtils.ts | 37 +++++++--------------- src/client/util/DropConverter.ts | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 ++-- 3 files changed, 16 insertions(+), 29 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 2a0702a58..a39081d10 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -9,30 +9,25 @@ import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types"; -import { ImageField, nullAudio } from "../../fields/URLField"; +import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { OmitKeys, Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents"; import { DocumentType } from "../documents/DocumentTypes"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; -import { CollectionFreeFormView } from "../views/collections/collectionFreeForm"; import { TreeViewType } from "../views/collections/CollectionTreeView"; 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"; -import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView"; -import { DocumentView } from "../views/nodes/DocumentView"; import { OverlayView } from "../views/OverlayView"; -import { DocumentManager } from "./DocumentManager"; import { DragManager } from "./DragManager"; -import { makeTemplate, MakeTemplate } from "./DropConverter"; +import { MakeTemplate } from "./DropConverter"; import { HistoryUtil } from "./History"; import { LinkManager } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; -import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; @@ -149,11 +144,7 @@ export class CurrentUserUtils { } return templateBtn; }; - const makeTemp = (doc:Doc) => { - doc.isTemplateDoc = makeTemplate(doc); - return doc; - } - return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts); + return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts); }); const reqdOpts:DocumentOptions = { @@ -176,11 +167,7 @@ export class CurrentUserUtils { const reqdNoteList = reqdTempOpts.map(opts => { const reqdOpts = {...opts, title: "text", system: true}; const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined; - const makeTemp = (doc:Doc, noteType?:string) => { - doc.isTemplateDoc = makeTemplate(doc, true, noteType??"Note"); - return doc; - } - return this.AssignOpts(noteType, reqdOpts) ?? makeTemp(Docs.Create.TextDocument("",reqdOpts), opts.noteType); + return this.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note"); }); const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true }; @@ -280,8 +267,7 @@ export class CurrentUserUtils { // " " + // " " + // "
    "; - Doc.GetProto(header).isTemplateDoc = makeTemplate(Doc.GetProto(header), true, "headerView"); - Doc.GetProto(header).title = "Untitled Header"; + MakeTemplate(Doc.GetProto(header), true, "Untitled Header"); return header; } const emptyThings:{key:string, // the field name where the empty thing will be stored @@ -705,7 +691,7 @@ export class CurrentUserUtils { { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_); }'}}, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, - { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} }, + { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick: '{ return toggleUnderline(_readOnly_);}'} }, { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} }, { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} }, @@ -797,10 +783,11 @@ export class CurrentUserUtils { const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, linearViewSubMenu: true, linearViewExpandable: true, }; - return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ?? - (ctxtMenuBtnsDoc[StrCast(params.title)]= this.linearButtonList(reqdSubMenuOpts, params.subMenu.map(sub => - this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) - ))), undefined, params.funcs); + const items = params.subMenu?.map(sub => + this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) + ); + return this.AssignScripts( + this.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs); } }); return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns); @@ -985,7 +972,7 @@ export class CurrentUserUtils { switch (options.fieldType) { case "boolean": opts.fieldValues = new List(options.values as any); break; case "number": opts.fieldValues = new List(options.values as any); break; - case "Doc": opts.fieldValues = new List(options.values as any); break; + case Doc.name: opts.fieldValues = new List(options.values as any); break; default: opts.fieldValues = new List(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType } this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts); diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 076afd3a0..256ab5c44 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -20,7 +20,7 @@ export function MakeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined): boolean { +function makeTemplate(doc: Doc, first: boolean = true, rename: Opt = undefined): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.layout instanceof Doc) { // its already a template return true; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9ae604e9b..8e1698eba 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -29,7 +29,7 @@ import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from "../../../util/DragManager"; -import { makeTemplate } from '../../../util/DropConverter'; +import { MakeTemplate } from '../../../util/DropConverter'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from "../../../util/SelectionManager"; import { SnappingManager } from '../../../util/SnappingManager'; @@ -682,7 +682,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this.layoutDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; - this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + MakeTemplate(this.rootDoc, true, title); } else if (!this.rootDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; @@ -691,7 +691,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.rootDoc.isTemplateDoc = false; this.rootDoc.isTemplateForField = ""; this.rootDoc.layoutKey = "layout"; - this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + MakeTemplate(this.rootDoc, true, title); setTimeout(() => { this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template -- cgit v1.2.3-70-g09d2