aboutsummaryrefslogtreecommitdiff
path: root/src/client/documents/Documents.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/documents/Documents.ts')
-rw-r--r--src/client/documents/Documents.ts184
1 files changed, 79 insertions, 105 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7f82280c5..84e6258ac 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,7 +1,8 @@
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { action, runInAction } from "mobx";
import { basename } from "path";
import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Initializing, Opt, updateCachedAcls, WidthSym } from "../../fields/Doc";
+import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from "../../fields/Doc";
import { Id } from "../../fields/FieldSymbols";
import { HtmlField } from "../../fields/HtmlField";
import { InkField, PointData } from "../../fields/InkField";
@@ -14,7 +15,7 @@ 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 { Upload } from "../../server/SharedMediaTypes";
-import { OmitKeys, Utils, aggregateBounds } from "../../Utils";
+import { aggregateBounds, OmitKeys, Utils } from "../../Utils";
import { YoutubeBox } from "../apis/youtube/YoutubeBox";
import { DocServer } from "../DocServer";
import { Networking } from "../Network";
@@ -33,13 +34,14 @@ import { ContextMenuProps } from "../views/ContextMenuItem";
import { DFLT_IMAGE_NATIVE_DIM } from "../views/global/globalCssVariables.scss";
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke";
import { AudioBox } from "../views/nodes/AudioBox";
+import { FontIconBox } from "../views/nodes/button/FontIconBox";
import { ColorBox } from "../views/nodes/ColorBox";
import { ComparisonBox } from "../views/nodes/ComparisonBox";
+import { DataVizBox } from "../views/nodes/DataViz";
import { DocFocusOptions } from "../views/nodes/DocumentView";
import { EquationBox } from "../views/nodes/EquationBox";
import { FieldViewProps } from "../views/nodes/FieldView";
import { FilterBox } from "../views/nodes/FilterBox";
-import { FontIconBox } from "../views/nodes/button/FontIconBox";
import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox";
import { ImageBox } from "../views/nodes/ImageBox";
@@ -47,7 +49,9 @@ import { KeyValueBox } from "../views/nodes/KeyValueBox";
import { LabelBox } from "../views/nodes/LabelBox";
import { LinkBox } from "../views/nodes/LinkBox";
import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup";
+import { MapBox } from "../views/nodes/MapBox/MapBox";
import { PDFBox } from "../views/nodes/PDFBox";
+import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox";
import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
import { ScriptingBox } from "../views/nodes/ScriptingBox";
import { SliderBox } from "../views/nodes/SliderBox";
@@ -57,11 +61,7 @@ import { PresElementBox } from "../views/nodes/trails/PresElementBox";
import { VideoBox } from "../views/nodes/VideoBox";
import { WebBox } from "../views/nodes/WebBox";
import { SearchBox } from "../views/search/SearchBox";
-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 {
@@ -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<number>; // 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<any>; // 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;
@@ -194,6 +195,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<ScriptField>;
@@ -338,33 +340,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<boolean>(options.values as any as boolean[]); break;
- case "number": finfo.options = new List<number>(options.values as any as number[]); break;
- case "Doc": finfo.options = new List<Doc>(options.values as any as Doc[]); break;
- default: // string, pointerEvents, dimUnit, dropActionType
- finfo.options = new List<string>(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 {
@@ -386,8 +361,8 @@ export namespace Docs {
[DocumentType.RTF, {
layout: { view: FormattedTextBox, dataField: "text" },
options: {
- _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true,
- links: "@links(self)"
+ _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, treeViewGrowsHorizontally: true,
+ forceReflow: true, links: "@links(self)"
}
}],
[DocumentType.SEARCH, {
@@ -424,7 +399,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 },
@@ -436,7 +411,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 },
@@ -474,11 +449,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" },
@@ -517,7 +492,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<Doc>(),
@@ -528,6 +503,10 @@ export namespace Docs {
layout: { view: EmptyBox, dataField: defaultDataKey },
options: { links: "@links(self)" }
}],
+ [DocumentType.DATAVIZ, {
+ layout: { view: DataVizBox, dataField: defaultDataKey },
+ options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" }
+ }]
]);
const suffix = "Proto";
@@ -550,15 +529,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;
@@ -661,7 +631,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;
@@ -678,7 +648,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);
@@ -702,8 +672,8 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
}
- export function ScriptingDocument(script: Opt<ScriptField>, options: DocumentOptions = {}, fieldKey?: string) {
- return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script,
+ export function ScriptingDocument(script: Opt<ScriptField>|null, options: DocumentOptions = {}, fieldKey?: string) {
+ return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script: undefined,
{ ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
}
@@ -792,8 +762,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";
@@ -823,7 +793,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 = {}) {
@@ -934,6 +904,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<Doc>, 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);
}
@@ -1256,8 +1230,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;
@@ -1289,8 +1263,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) {