diff options
22 files changed, 402 insertions, 159 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 24c093213..4c9c9c17c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,5 +1,5 @@ import { action, runInAction } from "mobx"; -import { Doc } from "../../new_fields/Doc"; +import { Doc, Field } from "../../new_fields/Doc"; import { Cast, StrCast } from "../../new_fields/Types"; import { URLField } from "../../new_fields/URLField"; import { emptyFunction } from "../../Utils"; @@ -9,6 +9,11 @@ import { DocumentManager } from "./DocumentManager"; import { LinkManager } from "./LinkManager"; import { SelectionManager } from "./SelectionManager"; import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; +import { Docs } from "../documents/Documents"; +import { CompileScript } from "./Scripting"; +import { ScriptField } from "../../new_fields/ScriptField"; +import { List } from "../../new_fields/List"; +import { PrefetchProxy } from "../../new_fields/Proxy"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag( @@ -247,6 +252,28 @@ export namespace DragManager { }); } + export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize?: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) { + let dragData = new DragManager.DocumentDragData([], [undefined]); + runInAction(() => StartDragFunctions.map(func => func())); + StartDrag(eles, dragData, downX, downY, options, options && options.finishDrag ? options.finishDrag : + (dropData: { [id: string]: any }) => { + let bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: title }); + let compiled = CompileScript(script, { + params: { doc: Doc.name }, + typecheck: false, + editable: true + }); + if (compiled.compiled) { + let scriptField = new ScriptField(compiled); + bd.onClick = scriptField; + } + params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); + initialize && initialize(bd); + bd.buttonParams = new List<string>(params); + dropData.droppedDocuments = [bd]; + }); + } + export function StartLinkedDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions) { dragData.moveDocument = moveLinkedDocument; @@ -482,7 +509,7 @@ export namespace DragManager { // if (parent && dragEle) parent.appendChild(dragEle); }); if (target) { - if (finishDrag) finishDrag(dragData); + finishDrag && finishDrag(dragData); target.dispatchEvent( new CustomEvent<DropEvent>("dashOnDrop", { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 6d2abfaa2..9fdda4845 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -105,7 +105,6 @@ export const nodes: { [index: string]: NodeSpec } = { // } // }] }, - // :: NodeSpec An inline image (`<img>`) node. Supports `src`, // `alt`, and `href` attributes. The latter two default to the empty // string. @@ -197,7 +196,6 @@ export const nodes: { [index: string]: NodeSpec } = { ...listItem, content: 'paragraph block*' }, - }; const emDOM: DOMOutputSpecArray = ["em", 0]; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index a28088032..579806a89 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,7 +1,7 @@ import { library, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faLink, faTag, faTimes, faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faStopCircle, faCloudUploadAlt, faSyncAlt, faShare } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, reaction, runInAction } from "mobx"; +import { action, computed, observable, reaction, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../new_fields/Doc"; import { List } from "../../new_fields/List"; @@ -694,9 +694,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let canPush = this.targetDoc.data && this.targetDoc.data instanceof RichTextField; if (!canPush) return (null); let published = Doc.GetProto(this.targetDoc)[GoogleRef] !== undefined; - if (!published) { - this.targetDoc.autoHeight = true; - } let icon: IconProp = published ? (this.pushIcon as any) : cloud; return ( <div className={"linkButtonWrapper"}> diff --git a/src/client/views/MainOverlayTextBox.tsx b/src/client/views/MainOverlayTextBox.tsx index 0839e1114..e95e5b777 100644 --- a/src/client/views/MainOverlayTextBox.tsx +++ b/src/client/views/MainOverlayTextBox.tsx @@ -59,7 +59,7 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> let sxf = Utils.GetScreenTransform(box ? box.CurrentDiv : undefined); return new Transform(-sxf.translateX, -sxf.translateY, 1 / sxf.scale); }; - this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight, false) || box.props.height === "min-content"); + this.setTextDoc(box.props.fieldKey, box.CurrentDiv, xf, BoolCast(box.props.Document.autoHeight) || box.props.height === "min-content"); } else { this.TextDoc = undefined; @@ -131,10 +131,11 @@ export class MainOverlayTextBox extends React.Component<MainOverlayTextBoxProps> this.TextDoc; this.TextDataDoc; if (FormattedTextBox.InputBoxOverlay && this._textTargetDiv) { let wid = FormattedTextBox.InputBoxOverlay.props.Document.width; // need to force overlay to render when underlying text box is resized (eg, w/ DocDecorations) + let hgtx = FormattedTextBox.InputBoxOverlay.props.Document.height; // need to force overlay to render when underlying text box is resized (eg, w/ DocDecorations) let textRect = this._textTargetDiv.getBoundingClientRect(); let s = this._textXf().Scale; let location = this._textBottom ? textRect.bottom : textRect.top; - let hgt = this._textAutoHeight || this._textBottom ? "auto" : this._textTargetDiv.clientHeight; + let hgt = (this._textBox && this._textBox.props.Document.autoHeight) || this._textBottom ? "auto" : this._textTargetDiv.clientHeight; return <div ref={this._setouterdiv} className="mainOverlayTextBox-unscaled_div" style={{ transform: `translate(${textRect.left}px, ${location}px)` }} > <div className="mainOverlayTextBox-textInput" style={{ transform: `scale(${1 / s})`, width: "auto", height: "0px" }} > <div className="mainOverlayTextBox-textInput" onPointerDown={this.textBoxDown} ref={this._textProxyDiv} onScroll={this.textScroll} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f28844009..a02214deb 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -404,7 +404,7 @@ export class MainView extends React.Component { get mainContent() { let sidebar = CurrentUserUtils.UserDocument.sidebar; if (!(sidebar instanceof Doc)) return (null); - return <div> + return <div className="mainContent" style={{ width: "100%", height: "100%", position: "absolute" }}> <div className="mainView-libraryHandle" style={{ cursor: "ew-resize", left: `${this.flyoutWidth - 10}px`, backgroundColor: `${StrCast(sidebar.backgroundColor, "lightGray")}` }} onPointerDown={this.onPointerDown}> diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index e28c4d0e0..7da55fd1c 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -1,7 +1,11 @@ .metadataEntry-outerDiv { display: flex; + width: 310px; flex-direction: column; - width: 300px; + + input[type=checkbox] { + margin-left: 5px; + } } .metadataEntry-keys { diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index ec628c5a3..f1b101b8e 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; -import { Doc, Field } from '../../new_fields/Doc'; +import { Doc, Field, DocListCast, DocListCastAsync } from '../../new_fields/Doc'; import * as Autosuggest from 'react-autosuggest'; import { undoBatch } from '../util/UndoManager'; import { emptyFunction } from '../../Utils'; @@ -19,6 +19,8 @@ export interface MetadataEntryProps { export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{ @observable private _currentKey: string = ""; @observable private _currentValue: string = ""; + @observable private suggestions: string[] = []; + private _addChildren: boolean = false; @observable _allSuggestions: string[] = []; _suggestionDispser: IReactionDisposer | undefined; private userModified = false; @@ -84,16 +86,27 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{ e.stopPropagation(); const script = KeyValueBox.CompileKVPScript(this._currentValue); if (!script) return; + let doc = this.props.docs; if (typeof doc === "function") { doc = doc(); } doc = await doc; + let success: boolean; if (doc instanceof Doc) { success = KeyValueBox.ApplyKVPScript(doc, this._currentKey, script); } else { - success = doc.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)); + let childSuccess = true; + if (this._addChildren) { + for (let document of doc) { + let collectionChildren = await DocListCastAsync(document.data); + if (collectionChildren) { + childSuccess = collectionChildren.every(c => KeyValueBox.ApplyKVPScript(c, this._currentKey, script)); + } + } + } + success = doc.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)) && childSuccess; } if (!success) { if (this.props.onError) { @@ -154,6 +167,33 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{ this._suggestionDispser && this._suggestionDispser(); } + onClick = (e: React.ChangeEvent<HTMLInputElement>) => { + this._addChildren = !this._addChildren; + } + + private get considerChildOptions() { + let docSource = this.props.docs; + if (typeof docSource === "function") { + docSource = docSource(); + } + docSource = docSource as Doc[] | Doc; + if (docSource instanceof Doc) { + if (docSource.viewType === undefined) { + return (null); + } + } else if (Array.isArray(docSource)) { + if (!docSource.every(doc => doc.viewType !== undefined)) { + return null; + } + } + return ( + <div style={{ display: "flex" }}> + Children: + <input type="checkbox" onChange={this.onClick} ></input> + </div> + ); + } + render() { return ( <div className="metadataEntry-outerDiv"> @@ -169,6 +209,7 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{ ref={this.autosuggestRef} /> Value: <input className="metadataEntry-input" value={this._currentValue} onChange={this.onValueChange} onKeyDown={this.onValueKeyDown} /> + {this.considerChildOptions} </div> <div className="metadataEntry-keys" > <ul> diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 2f2579057..a60dc591c 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,9 +1,15 @@ import * as React from "react"; import { observer } from "mobx-react"; import { observable, action } from "mobx"; -import { Utils } from "../../Utils"; +import { Utils, emptyFunction, returnOne, returnTrue, returnEmptyString } from "../../Utils"; import './OverlayView.scss'; +import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; +import { DocListCast, Doc } from "../../new_fields/Doc"; +import { Id } from "../../new_fields/FieldSymbols"; +import { DocumentView } from "./nodes/DocumentView"; +import { Transform } from "../util/Transform"; +import { CollectionFreeFormDocumentView } from "./nodes/CollectionFreeFormDocumentView"; export type OverlayDisposer = () => void; @@ -134,8 +140,29 @@ export class OverlayView extends React.Component { render() { return ( - <div> + <div className="overlayView"> {this._elements} + {CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => ( + <CollectionFreeFormDocumentView key={d[Id]} + Document={d} + bringToFront={emptyFunction} + addDocument={undefined} + removeDocument={undefined} + ContentScaling={returnOne} + PanelWidth={returnOne} + PanelHeight={returnOne} + ScreenToLocalTransform={Transform.Identity} + renderDepth={1} + selectOnLoad={false} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + focus={emptyFunction} + backgroundColor={returnEmptyString} + addDocTab={emptyFunction} + pinToPres={emptyFunction} + ContainingCollectionView={undefined} + zoomToScale={emptyFunction} + getScale={returnOne} />))} </div> ); } diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index e7a5475ed..d8e161ab6 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -1,13 +1,20 @@ -import { action, observable } from 'mobx'; +import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; import "./PreviewCursor.scss"; +import { Docs } from '../documents/Documents'; +// import { Transform } from 'prosemirror-transform'; +import { Doc } from '../../new_fields/Doc'; +import { Transform } from "../util/Transform"; @observer export class PreviewCursor extends React.Component<{}> { private _prompt = React.createRef<HTMLDivElement>(); static _onKeyPress?: (e: KeyboardEvent) => void; + static _getTransform: () => Transform; + static _addLiveTextDoc: (doc: Doc) => void; + static _addDocument: (doc: Doc, allowDuplicates: false) => boolean; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; //when focus is lost, this will remove the preview cursor @@ -20,12 +27,67 @@ export class PreviewCursor extends React.Component<{}> { document.addEventListener("keydown", this.onKeyPress); document.addEventListener("paste", this.paste); } + paste = (e: ClipboardEvent) => { - console.log(e.clipboardData); - if (e.clipboardData) { - console.log(e.clipboardData.getData("text/html")); - console.log(e.clipboardData.getData("text/csv")); - console.log(e.clipboardData.getData("text/plain")); + if (PreviewCursor.Visible) { + if (e.clipboardData) { + let newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]); + runInAction(() => { PreviewCursor.Visible = false; }); + + + if (e.clipboardData.getData("text/plain") !== "") { + + // tests for youtube and makes video document + if (e.clipboardData.getData("text/plain").indexOf("www.youtube.com/watch") !== -1) { + const url = e.clipboardData.getData("text/plain").replace("youtube.com/watch?v=", "youtube.com/embed/"); + PreviewCursor._addDocument(Docs.Create.VideoDocument(url, { + title: url, width: 400, height: 315, + nativeWidth: 600, nativeHeight: 472.5, + x: newPoint[0], y: newPoint[1] + }), false); + return; + } + + // tests for URL and makes web document + let re: any = /^https?:\/\//g; + if (re.test(e.clipboardData.getData("text/plain"))) { + const url = e.clipboardData.getData("text/plain") + PreviewCursor._addDocument(Docs.Create.WebDocument(url, { + title: url, width: 300, height: 300, + // nativeWidth: 300, nativeHeight: 472.5, + x: newPoint[0], y: newPoint[1] + }), false); + return; + } + + // creates text document + let newBox = Docs.Create.TextDocument({ + width: 200, height: 100, + x: newPoint[0], + y: newPoint[1], + title: "-pasted text-" + }); + + newBox.proto!.autoHeight = true; + PreviewCursor._addLiveTextDoc(newBox); + return; + } + //pasting in images + if (e.clipboardData.getData("text/html") !== "" && e.clipboardData.getData("text/html").includes("<img src=")) { + let re: any = /<img src="(.*?)"/g; + let arr: any[] = re.exec(e.clipboardData.getData("text/html")); + + let img: Doc = Docs.Create.ImageDocument( + arr[1], { + width: 300, title: arr[1], + x: newPoint[0], + y: newPoint[1], + }); + PreviewCursor._addDocument(img, false); + return; + } + + } } } @@ -49,9 +111,16 @@ export class PreviewCursor extends React.Component<{}> { } } @action - public static Show(x: number, y: number, onKeyPress: (e: KeyboardEvent) => void) { + public static Show(x: number, y: number, + onKeyPress: (e: KeyboardEvent) => void, + addLiveText: (doc: Doc) => void, + getTransform: () => Transform, + addDocument: (doc: Doc, allowDuplicates: false) => boolean) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; + this._addLiveTextDoc = addLiveText; + this._getTransform = getTransform; + this._addDocument = addDocument; setTimeout(action(() => this.Visible = true), (1)); } render() { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 7e1aacd5d..6e284a76f 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -373,6 +373,7 @@ class TreeView extends React.Component<TreeViewProps> { return <> <div className="docContainer" id={`docContainer-${this.props.parentKey}`} ref={reference} onPointerDown={onItemDown} style={{ + color: this.props.document.isMinimized ? "red" : "black", background: Doc.IsBrushed(this.props.document) ? "#06121212" : "0", outline: BoolCast(this.props.document.workspaceBrush) ? "dashed 1px #06123232" : undefined, pointerEvents: this.props.active() || SelectionManager.GetIsDragging() ? "all" : "none" diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 352bcd4a6..1156d8fa7 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -41,16 +41,31 @@ let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); export class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> { //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\) + private _buttonizableCommands = [{ + // title: "set content", script: "getProto(this.target).data = aliasDocs(this.source.map(async p => await p));", params: ["target", "source"], // bcz: doesn't look like we can do async stuff in scripting... + title: "set content", script: "getProto(this.target).data = aliasDocs(this.source);", params: ["target", "source"], + immediate: (draggedDocs: Doc[]) => Doc.GetProto(this.props.CollectionView.props.Document).data = new List<Doc>(draggedDocs.map((d: any) => Doc.MakeAlias(d))) + }, + { + title: "set template", script: "this.target.childLayout = this.source ? this.source[0] : undefined", params: ["target", "source"], + immediate: (draggedDocs: Doc[]) => this.props.CollectionView.props.Document.childLayout = draggedDocs.length ? draggedDocs[0] : undefined + }, + { + title: "restore view", script: "this.target.panX = this.restoredPanX; this.target.panY = this.restoredPanY; this.target.scale = this.restoredScale;", params: ["target"], + immediate: (draggedDocs: Doc[]) => { this.props.CollectionView.props.Document.panX = 0; this.props.CollectionView.props.Document.panY = 0; this.props.CollectionView.props.Document.scale = 1 }, + initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document.panX; button.restoredPanY = this.props.CollectionView.props.Document.panY; button.restoredScale = this.props.CollectionView.props.Document.scale; } + }]; + private _picker: any; + private _commandRef = React.createRef<HTMLInputElement>(); + private _autosuggestRef = React.createRef<Autosuggest>(); + @observable private _currentKey: string = ""; @observable private _viewSpecsOpen: boolean = false; @observable private _dateWithinValue: string = ""; @observable private _dateValue: Date | string = ""; @observable private _keyRestrictions: [JSX.Element, string][] = []; @observable private suggestions: string[] = []; - _commandRef = React.createRef<HTMLInputElement>(); @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); } - private _picker: any; - getFilters = (script: string) => { let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g; let arr: any[] = re.exec(script); @@ -215,25 +230,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro subChrome = () => { switch (this.props.type) { - case CollectionViewType.Stacking: return ( - <CollectionStackingViewChrome - key="collchrome" - CollectionView={this.props.CollectionView} - type={this.props.type} />); - case CollectionViewType.Schema: return ( - <CollectionSchemaViewChrome - key="collchrome" - CollectionView={this.props.CollectionView} - type={this.props.type} - />); - case CollectionViewType.Tree: return ( - <CollectionTreeViewChrome - key="collchrome" - CollectionView={this.props.CollectionView} - type={this.props.type} - />); - default: - return null; + case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + default: return null; } } @@ -289,17 +289,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro @undoBatch @action protected drop(e: Event, de: DragManager.DropEvent): boolean { - if (de.data instanceof DragManager.DocumentDragData) { - if (de.data.draggedDocuments.length) { - if (this._currentKey === "Set Template") { - this.props.CollectionView.props.Document.childLayout = de.data.draggedDocuments[0]; - } - if (this._currentKey === "Set Content") { - Doc.GetProto(this.props.CollectionView.props.Document).data = new List<Doc>(de.data.draggedDocuments); - } - e.stopPropagation(); - return true; - } + if (de.data instanceof DragManager.DocumentDragData && de.data.draggedDocuments.length) { + this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.data.draggedDocuments)); + e.stopPropagation(); } return true; } @@ -318,10 +310,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro } } - @observable private _currentKey: string = ""; - @observable _allCommands: string[] = ["Set Template", "Set Content"]; - private autosuggestRef = React.createRef<Autosuggest>(); - renderSuggestion = (suggestion: string) => { return <p>{suggestion}</p>; } @@ -340,38 +328,41 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro this.suggestions = []; } getKeySuggestions = async (value: string): Promise<string[]> => { - return this._allCommands.filter(c => c.indexOf(value) !== -1); + return this._buttonizableCommands.filter(c => c.title.indexOf(value) !== -1).map(c => c.title); } autoSuggestDown = (e: React.PointerEvent) => { e.stopPropagation(); } + + private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 }; + private _sensitivity: number = 16; + dragCommandDown = (e: React.PointerEvent) => { - let de = new DragManager.DocumentDragData([this.props.CollectionView.props.Document], [undefined]); - DragManager.StartDocumentDrag([this._commandRef.current!], de, e.clientX, e.clientY, { - finishDrag: (dropData: { [id: string]: any }) => { - let bd = Docs.Create.ButtonDocument({ width: 150, height: 50, title: this._currentKey }); - let script = `getProto(target).data = copyField(this.source);`; - let compiled = CompileScript(script, { - params: { doc: Doc.name }, - capturedVariables: { target: this.props.CollectionView.props.Document }, - typecheck: false, - editable: true - }); - if (compiled.compiled) { - let scriptField = new ScriptField(compiled); - bd.onClick = scriptField; - } - dropData.droppedDocuments = [bd]; - }, - handlers: { - dragComplete: action(() => { - }), - }, - hideSource: false - }); + + this._startDragPosition = { x: e.clientX, y: e.clientY }; + document.addEventListener("pointermove", this.dragPointerMove); + document.addEventListener("pointerup", this.dragPointerUp); + e.stopPropagation(); e.preventDefault(); + } + + dragPointerMove = (e: PointerEvent) => { e.stopPropagation(); + e.preventDefault(); + let [dx, dy] = [e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y]; + if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) { + this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => + DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, + { target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY)); + document.removeEventListener("pointermove", this.dragPointerMove); + document.removeEventListener("pointerup", this.dragPointerUp); + } + } + dragPointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.dragPointerMove); + document.removeEventListener("pointerup", this.dragPointerUp); + } render() { @@ -419,7 +410,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro <div className="collectionViewBaseChrome-viewSpecsMenu-row"> <div className="collectionViewBaseChrome-viewSpecsMenu-rowLeft"> CREATED WITHIN: - </div> + </div> <select className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle" style={{ textTransform: "uppercase", textAlign: "center" }} value={this._dateWithinValue} @@ -442,15 +433,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro placeholder="Value" /> </div> <div className="collectionViewBaseChrome-viewSpecsMenu-lastRow"> - <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}> - ADD KEY RESTRICTION - </button> - <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}> - APPLY FILTER - </button> - <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}> - CLEAR - </button> + <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}> ADD KEY RESTRICTION </button> + <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}> APPLY FILTER </button> + <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}> CLEAR </button> </div> </div> </div> @@ -464,7 +449,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro renderSuggestion={this.renderSuggestion} onSuggestionsFetchRequested={this.onSuggestionFetch} onSuggestionsClearRequested={this.onSuggestionClear} - ref={this.autosuggestRef} /> + ref={this._autosuggestRef} /> </div> </div> </div> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 224e8047d..f185222ce 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -187,8 +187,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { componentDidMount() { this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)], - async (args) => args[1] instanceof Doc && - this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined))); + async (args) => { + this.childDocs.filter(doc => args[1] instanceof Doc || doc.layout instanceof Doc).map(async doc => { + if (!Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc)) { + Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined); + } + }); + }); } componentWillUnmount() { this._childLayoutDisposer && this._childLayoutDisposer(); @@ -291,7 +296,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (super.drop(e, de)) { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.droppedDocuments.length) { - let z = NumCast(de.data.draggedDocuments[0].z); + let z = NumCast(de.data.droppedDocuments[0].z); let x = (z ? xpo : xp) - de.data.xOffset; let y = (z ? ypo : yp) - de.data.yOffset; let dropX = NumCast(de.data.droppedDocuments[0].x); @@ -755,7 +760,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { ele: <CollectionFreeFormDocumentView key={doc[Id]} x={script ? pos.x : undefined} y={script ? pos.y : undefined} width={script ? pos.width : undefined} height={script ? pos.height : undefined} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />, - bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined + bounds: { x: pos.x || 0, y: pos.y || 0, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } }); } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 221237365..27eafd769 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -203,7 +203,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> onClick = (e: React.MouseEvent): void => { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress); + PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); // let the DocumentView stopPropagation of this event when it selects this document } else { // why do we get a click event when the cursor have moved a big distance? // let's cut it off here so no one else has to deal with it. diff --git a/src/client/views/nodes/ButtonBox.scss b/src/client/views/nodes/ButtonBox.scss index 5ed435505..75a790667 100644 --- a/src/client/views/nodes/ButtonBox.scss +++ b/src/client/views/nodes/ButtonBox.scss @@ -3,14 +3,29 @@ height: 100%; pointer-events: all; border-radius: inherit; - display:table; + display:flex; + flex-direction: column; } .buttonBox-mainButton { width: 100%; height: 100%; border-radius: inherit; + text-align: center; + display: table; +} +.buttonBox-mainButtonCenter { + height: 100%; display:table-cell; vertical-align: middle; - text-align: center; +} + +.buttonBox-params { + display:flex; + flex-direction: row; +} + +.buttonBox-missingParam { + width:100%; + background: lightgray; }
\ No newline at end of file diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index ca5f0acc2..54848344b 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -1,25 +1,19 @@ -import * as React from 'react'; -import { FieldViewProps, FieldView } from './FieldView'; -import { createSchema, makeInterface } from '../../../new_fields/Schema'; -import { ScriptField } from '../../../new_fields/ScriptField'; -import { DocComponent } from '../DocComponent'; -import { ContextMenu } from '../ContextMenu'; import { library } from '@fortawesome/fontawesome-svg-core'; import { faEdit } from '@fortawesome/free-regular-svg-icons'; -import { emptyFunction } from '../../../Utils'; -import { ScriptBox } from '../ScriptBox'; -import { CompileScript } from '../../util/Scripting'; -import { OverlayView } from '../OverlayView'; -import { Doc } from '../../../new_fields/Doc'; - -import './ButtonBox.scss'; +import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; -import { DocumentIconContainer } from './DocumentIcon'; -import { StrCast, BoolCast } from '../../../new_fields/Types'; +import * as React from 'react'; +import { Doc, DocListCastAsync } from '../../../new_fields/Doc'; +import { List } from '../../../new_fields/List'; +import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema'; +import { ScriptField } from '../../../new_fields/ScriptField'; +import { BoolCast, StrCast, Cast } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; -import { action, computed } from 'mobx'; -import { List } from '../../../new_fields/List'; +import { DocComponent } from '../DocComponent'; +import './ButtonBox.scss'; +import { FieldView, FieldViewProps } from './FieldView'; + library.add(faEdit as any); @@ -52,12 +46,24 @@ export class ButtonBox extends DocComponent<FieldViewProps, ButtonDocument>(Butt drop = (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.DocumentDragData) { Doc.GetProto(this.dataDoc).source = new List<Doc>(de.data.droppedDocuments); + e.stopPropagation(); } } + // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { + let params = Cast(this.props.Document.buttonParams, listSpec("string")); + let missingParams = params && params.filter(p => this.props.Document[p] === undefined); + params && params.map(async p => await DocListCastAsync(this.props.Document[p])); // bcz: really hacky form of prefetching ... return ( <div className="buttonBox-outerDiv" ref={this.createDropTarget} > - <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} >{this.Document.text || this.Document.title}</div> + <div className="buttonBox-mainButton" style={{ background: StrCast(this.props.Document.backgroundColor), color: StrCast(this.props.Document.color, "black") }} > + <div className="buttonBox-mainButtonCenter"> + {(this.Document.text || this.Document.title)} + </div> + </div> + <div className="buttonBox-params" > + {!missingParams || !missingParams.length ? (null) : missingParams.map(m => <div key={m} className="buttonBox-missingParam">{m}</div>)} + </div> </div> ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6c944c6b2..3cf86b6f9 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -628,6 +628,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" }); cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle! cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); + cm.addItem({ description: "Move To Overlay", icon: "laptop-code", event: () => ((o: Doc) => o && Doc.AddDocToList(o, "data", this.props.Document))(Cast(CurrentUserUtils.UserDocument.overlays, Doc) as Doc) }); cm.addItem({ description: "Download document", icon: "download", event: () => { const a = document.createElement("a"); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 5492a457d..d6ba1700a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -34,16 +34,13 @@ import "./FormattedTextBox.scss"; import React = require("react"); import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils'; import { DocumentDecorations } from '../DocumentDecorations'; -import { MainOverlayTextBox } from '../MainOverlayTextBox'; import { DictationManager } from '../../util/DictationManager'; import { ReplaceStep } from 'prosemirror-transform'; +import { DocumentType } from '../../documents/DocumentTypes'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); -// FormattedTextBox: Displays an editable plain text node that maps to a specified Key of a Document -// - export const Blank = `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`; export interface FormattedTextBoxProps { @@ -147,27 +144,29 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe paste = (e: ClipboardEvent) => { if (e.clipboardData && this._editorView) { - let pdfPasteText = `${Utils.GenerateDeterministicGuid("pdf paste")}`; - for (let i = 0; i < e.clipboardData.items.length; i++) { - let item = e.clipboardData.items.item(i); - if (item.type === "text/plain") { - item.getAsString((text) => { - let pdfPasteIndex = text.indexOf(pdfPasteText); - if (pdfPasteIndex > -1) { - let insertText = text.substr(0, pdfPasteIndex); - const tx = this._editorView!.state.tr.insertText(insertText); - // tx.setSelection(new Selection(tx.)) - const state = this._editorView!.state; - this._editorView!.dispatch(tx); - if (FormattedTextBox._toolTipTextMenu) { - // this._toolTipTextMenu.makeLinkWithState(state) - } - e.stopPropagation(); - e.preventDefault(); - } - }); - } - } + // let pdfPasteText = `${Utils.GenerateDeterministicGuid("pdf paste")}`; + // for (let i = 0; i < e.clipboardData.items.length; i++) { + // let item = e.clipboardData.items.item(i); + // console.log(item) + // if (item.type === "text/plain") { + // console.log("plain") + // item.getAsString((text) => { + // let pdfPasteIndex = text.indexOf(pdfPasteText); + // if (pdfPasteIndex > -1) { + // let insertText = text.substr(0, pdfPasteIndex); + // const tx = this._editorView!.state.tr.insertText(insertText); + // // tx.setSelection(new Selection(tx.)) + // const state = this._editorView!.state; + // this._editorView!.dispatch(tx); + // if (FormattedTextBox._toolTipTextMenu) { + // // this._toolTipTextMenu.makeLinkWithState(state) + // } + // e.stopPropagation(); + // e.preventDefault(); + // } + // }); + // } + // } } } @@ -258,10 +257,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let model: NodeType = (url.includes(".mov") || url.includes(".mp4")) ? schema.nodes.video : schema.nodes.image; this._editorView!.dispatch(this._editorView!.state.tr.insert(0, model.create({ src: url }))); e.stopPropagation(); - } else { - if (de.data instanceof DragManager.DocumentDragData) { - this.props.Document.layout = de.data.draggedDocuments[0]; - de.data.draggedDocuments[0].isTemplate = true; + } else if (de.data instanceof DragManager.DocumentDragData) { + const draggedDoc = de.data.draggedDocuments.length && de.data.draggedDocuments[0]; + if (draggedDoc && draggedDoc.type === DocumentType.TEXT && StrCast(draggedDoc.layout) !== "") { + this.props.Document.layout = draggedDoc; + draggedDoc.isTemplate = true; e.stopPropagation(); } } @@ -798,7 +798,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe tryUpdateHeight() { const ChromeHeight = this.props.ChromeHeight; let sh = this._ref.current ? this._ref.current.scrollHeight : 0; - if (this.props.Document.autoHeight && sh !== 0) { + if (!this.props.isOverlay && this.props.Document.autoHeight && sh !== 0) { let nh = this.props.Document.isTemplate ? 0 : NumCast(this.dataDoc.nativeHeight, 0); let dh = NumCast(this.props.Document.height, 0); this.props.Document.height = Math.max(10, (nh ? dh / nh * sh : sh) + (ChromeHeight ? ChromeHeight() : 0)); diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index b1afa3f7d..00c069e1f 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -65,6 +65,7 @@ display:flex; align-items: center; height:100%; + overflow:hidden; .imageBox-fadeBlocker { width:100%; height:100%; diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index 07774263c..43220df71 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -1,3 +1,5 @@ +@import "../globalCssVariables.scss"; + .webBox-cont, .webBox-cont-interactive { padding: 0vw; @@ -61,6 +63,14 @@ width: 40px; transform-origin: top left; } + + .switchToText { + color: $main-accent; + } + + .switchToText:hover { + color: $dark-color; + } } button:hover { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 9b66b2431..f0140d04b 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { FieldResult } from "../../../new_fields/Doc"; +import { FieldResult, Doc } from "../../../new_fields/Doc"; import { HtmlField } from "../../../new_fields/HtmlField"; import { InkTool } from "../../../new_fields/InkField"; import { Cast, NumCast } from "../../../new_fields/Types"; @@ -13,6 +13,12 @@ import { FieldView, FieldViewProps } from './FieldView'; import { KeyValueBox } from "./KeyValueBox"; import "./WebBox.scss"; import React = require("react"); +import { SelectionManager } from "../../util/SelectionManager"; +import { Docs } from "../../documents/Documents"; +import { faStickyNote } from "@fortawesome/free-solid-svg-icons"; +import { library } from "@fortawesome/fontawesome-svg-core"; + +library.add(faStickyNote) @observer export class WebBox extends React.Component<FieldViewProps> { @@ -64,6 +70,29 @@ export class WebBox extends React.Component<FieldViewProps> { } } + + switchToText = () => { + let url: string = ""; + let field = Cast(this.props.Document[this.props.fieldKey], WebField); + if (field) url = field.url.href; + + let newBox = Docs.Create.TextDocument({ + x: NumCast(this.props.Document.x), + y: NumCast(this.props.Document.y), + title: url, + width: 200, + height: 70, + documentText: "@@@" + url + }); + + SelectionManager.SelectedDocuments().map(dv => { + dv.props.addDocument && dv.props.addDocument(newBox, false); + dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); + }); + + Doc.BrushDoc(newBox); + } + urlEditor() { return ( <div className="webView-urlEditor" style={{ top: this.collapsed ? -70 : 0 }}> @@ -86,9 +115,19 @@ export class WebBox extends React.Component<FieldViewProps> { onChange={this.onURLChange} onKeyDown={this.onValueKeyDown} /> - <button className="submitUrl" onClick={this.submitURL}> - SUBMIT URL - </button> + <div style={{ + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + minWidth: "100px", + }}> + <button className="submitUrl" onClick={this.submitURL}> + SUBMIT + </button> + <div className="switchToText" title="Convert web to text doc" onClick={this.switchToText} style={{ display: "flex", alignItems: "center", justifyContent: "center" }} > + <FontAwesomeIcon icon={faStickyNote} size={"lg"} /> + </div> + </div> </div> </div> </div> diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index f6114d476..6f1ef38d1 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -525,13 +525,23 @@ export namespace Doc { return otherdoc; } export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) { + if (!templateDoc) { + target.layout = undefined; + target.nativeWidth = undefined; + target.nativeHeight = undefined; + target.onClick = undefined; + target.type = undefined; + return; + } let temp = Doc.MakeDelegate(templateDoc); target.nativeWidth = Doc.GetProto(target).nativeWidth = undefined; target.nativeHeight = Doc.GetProto(target).nativeHeight = undefined; + !templateDoc.nativeWidth && (target.nativeWidth = 0); + !templateDoc.nativeHeight && (target.nativeHeight = 0); target.width = templateDoc.width; target.height = templateDoc.height; target.onClick = templateDoc.onClick instanceof ObjectField && templateDoc.onClick[Copy](); - Doc.GetProto(target).type = DocumentType.TEMPLATE; + target.type = DocumentType.TEMPLATE; if (targetData && targetData.layout === target) { targetData.layout = temp; targetData.miniLayout = StrCast(templateDoc.miniLayout); @@ -541,8 +551,6 @@ export namespace Doc { target.miniLayout = StrCast(templateDoc.miniLayout); target.detailedLayout = target.layout; } - !templateDoc.nativeWidth && (target.nativeWidth = 0); - !templateDoc.nativeHeight && (target.nativeHeight = 0); } export function MakeTemplate(fieldTemplate: Doc, metaKey: string, templateDataDoc: Doc) { @@ -581,7 +589,8 @@ export namespace Doc { let miniLayout = await PromiseValue(d.miniLayout); let detailLayout = await PromiseValue(d.detailedLayout); d.layout !== miniLayout ? miniLayout && (d.layout = d.miniLayout) : detailLayout && (d.layout = detailLayout); - if (d.layout === detailLayout) Doc.GetProto(d).nativeWidth = Doc.GetProto(d).nativeHeight = undefined; + if (d.layout === detailLayout) d.nativeWidth = d.nativeHeight = 0; + if (StrCast(d.layout) !== "") d.nativeWidth = d.nativeHeight = undefined; }); } export function UseDetailLayout(d: Doc) { @@ -627,4 +636,5 @@ export namespace Doc { } Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(doc.title).replace(/\([0-9]*\)/, "") + `(${n})`; }); Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); -Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); });
\ No newline at end of file +Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); +Scripting.addGlobal(function aliasDocs(field: any) { return new List<Doc>(field.map((d: any) => Doc.MakeAlias(d))); });
\ No newline at end of file diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index f7ce24967..83e45d3ce 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -73,6 +73,12 @@ export class CurrentUserUtils { sidebar.boxShadow = "1 1 3"; doc.sidebar = sidebar; } + if (doc.overlays === undefined) { + const overlays = Docs.Create.FreeformDocument([], { title: "Overlays" }); + overlays.excludeFromLibrary = true; + Doc.GetProto(overlays).backgroundColor = "#aca3a6"; + doc.overlays = overlays; + } StrCast(doc.title).indexOf("@") !== -1 && (doc.title = StrCast(doc.title).split("@")[0] + "'s Library"); doc.width = 100; doc.preventTreeViewOpen = true; |