diff options
-rw-r--r-- | src/client/util/TooltipTextMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 4 | ||||
-rw-r--r-- | src/client/views/InkingCanvas.tsx | 20 | ||||
-rw-r--r-- | src/client/views/ScriptBox.tsx | 30 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 57 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingViewFieldColumn.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 19 | ||||
-rw-r--r-- | src/client/views/collections/CollectionViewChromes.tsx | 19 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 64 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 105 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 15 | ||||
-rw-r--r-- | src/new_fields/Doc.ts | 7 | ||||
-rw-r--r-- | src/new_fields/Types.ts | 4 |
15 files changed, 193 insertions, 162 deletions
diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 8dc5cdb2a..48f02d38a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -963,7 +963,7 @@ export class TooltipTextMenu { }); } } - if (!ref_node.isLeaf) { + if (!ref_node.isLeaf && ref_node.childCount > 0) { ref_node = ref_node.child(0); } return ref_node; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6616d5d58..d537e2dac 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -537,12 +537,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... - let fixedAspect = e.ctrlKey || (!BoolCast(proto.ignoreAspect) && nwidth && nheight); + let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight); if (fixedAspect && (!nwidth || !nheight)) { proto.nativeWidth = nwidth = doc.width || 0; proto.nativeHeight = nheight = doc.height || 0; } - if (nwidth > 0 && nheight > 0 && !BoolCast(proto.ignoreAspect)) { + if (nwidth > 0 && nheight > 0 && !BoolCast(doc.ignoreAspect)) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true); diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 1c221e3df..b08133d80 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -165,14 +165,18 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { } return paths; }, [] as JSX.Element[]); - return [<svg className={`inkingCanvas-paths-ink`} key="Pens" - style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} > - {paths.filter(path => path.props.tool !== InkTool.Highlighter)} - </svg>, - <svg className={`inkingCanvas-paths-markers`} key="Markers" - style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}> - {paths.filter(path => path.props.tool === InkTool.Highlighter)} - </svg>]; + let markerPaths = paths.filter(path => path.props.tool === InkTool.Highlighter); + let penPaths = paths.filter(path => path.props.tool !== InkTool.Highlighter); + return [!penPaths.length ? (null) : + <svg className={`inkingCanvas-paths-ink`} key="Pens" + style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} > + {} + </svg>, + !markerPaths.length ? (null) : + <svg className={`inkingCanvas-paths-markers`} key="Markers" + style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }}> + {} + </svg>]; } render() { diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index d073945e5..2b862a81e 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -5,7 +5,11 @@ import { observable, action } from "mobx"; import "./ScriptBox.scss"; import { OverlayView } from "./OverlayView"; import { DocumentIconContainer } from "./nodes/DocumentIcon"; -import { Opt } from "../../new_fields/Doc"; +import { Opt, Doc } from "../../new_fields/Doc"; +import { emptyFunction } from "../../Utils"; +import { ScriptCast } from "../../new_fields/Types"; +import { CompileScript } from "../util/Scripting"; +import { ScriptField } from "../../new_fields/ScriptField"; export interface ScriptBoxProps { onSave: (text: string, onError: (error: string) => void) => void; @@ -62,4 +66,26 @@ export class ScriptBox extends React.Component<ScriptBoxProps> { </div> ); } -}
\ No newline at end of file + public static EditClickScript(doc: Doc, fieldKey: string) { + let overlayDisposer: () => void = emptyFunction; + const script = ScriptCast(doc[fieldKey]); + let originalText: string | undefined = undefined; + if (script) originalText = script.script.originalScript; + // tslint:disable-next-line: no-unnecessary-callback-wrapper + let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => { + const script = CompileScript(text, { + params: { this: Doc.name }, + typecheck: false, + editable: true, + transformer: DocumentIconContainer.getTransformer() + }); + if (!script.compiled) { + onError(script.errors.map(error => error.messageText).join("\n")); + return; + } + doc[fieldKey] = new ScriptField(script); + overlayDisposer(); + }} showDocumentIcons />; + overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${doc.title || ""} OnClick` }); + } +} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c0c82a44e..13bb34037 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -555,8 +555,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight))); - nativeWidth = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeWidth, this._panelWidth) : 0; - nativeHeight = () => !BoolCast(this._document!.ignoreAspect) ? NumCast(this._document!.nativeHeight, this._panelHeight) : 0; + nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; contentScaling = () => { const nativeH = this.nativeHeight(); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 9d2671356..ba3d8e6a7 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -9,7 +9,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types"; import { emptyFunction } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; @@ -20,6 +20,9 @@ import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; +import { ContextMenu } from "../ContextMenu"; +import { ContextMenuProps } from "../ContextMenuItem"; +import { ScriptBox } from "../ScriptBox"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -76,17 +79,24 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { componentDidMount() { // is there any reason this needs to exist? -syip - this._heightDisposer = reaction(() => [this.props.Document.autoHeight, this.yMargin, this.props.Document[WidthSym](), this.gridGap, this.columnWidth, this.childDocs.map(d => [d.height, d.width, d.zoomBasis, d.nativeHeight, d.nativeWidth, d.isMinimized])], - () => { - if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) { - let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => { - let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); - return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); - }, this.yMargin); - (this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc) - .height = hgt * (this.props as any).ContentScaling(); + this._heightDisposer = reaction(() => { + if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) { + let hgt = this.Sections.size * 50 + this.filteredChildren.reduce((height, d, i) => { + let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); + return height + this.getDocHeight(pair.layout) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); + }, this.yMargin); + return hgt * this.props.ContentScaling(); + } + return -1; + }, + (hgt: number) => { + if (hgt !== -1) { + let doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; + doc.height = hgt; } - }, { fireImmediately: true }); + }, + { fireImmediately: true } + ); // reset section headers when a new filter is inputted this._sectionFilterDisposer = reaction( @@ -109,9 +119,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } overlays = (doc: Doc) => { - return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: "title", caption: "caption" } : {}; + return doc.type === DocumentType.IMG || doc.type === DocumentType.VID ? { title: StrCast(this.props.Document.showTitles), caption: StrCast(this.props.Document.showCaptions) } : {}; } + @computed get onChildClickHandler() { return ScriptCast(this.Document.onChildClick); } + @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : ScriptCast(this.Document.onChildClick); } + getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { let height = () => this.getDocHeight(layoutDoc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); @@ -121,7 +134,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { showOverlays={this.overlays} renderDepth={this.props.renderDepth} fitToBox={this.props.fitToBox} - onClick={this.props.onClick} + onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler} width={width} height={height} getTransform={finalDxf} @@ -141,7 +154,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let nh = NumCast(d.nativeHeight); if (!d.ignoreAspect && nw && nh) { let aspect = nw && nh ? nh / nw : 1; - let wid = this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(d[WidthSym](), this.columnWidth / columnScale); + let wid = this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(Math.min(d[WidthSym](), NumCast(d.nativeWidth)), this.columnWidth / columnScale); return wid * aspect; } return d[HeightSym](); @@ -267,7 +280,19 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } onToggle = (checked: Boolean) => { - this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode"; + this.props.CollectionView.props.Document.chromeStatus = checked ? "collapsed" : "view-mode"; + } + + onContextMenu = (e: React.MouseEvent): void => { + // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout + if (!e.isPropagationStopped()) { + let subItems: ContextMenuProps[] = []; + subItems.push({ description: `${this.props.Document.fillColumn ? "Variable Size" : "Autosize"} Column`, event: () => this.props.Document.fillColumn = !this.props.Document.fillColumn, icon: "plus" }); + subItems.push({ description: `${this.props.Document.showTitles ? "Hide Titles" : "Show Titles"}`, event: () => this.props.Document.showTitles = !this.props.Document.showTitles ? "title" : "", icon: "plus" }); + subItems.push({ description: `${this.props.Document.showCaptions ? "Hide Captions" : "Show Captions"}`, event: () => this.props.Document.showCaptions = !this.props.Document.showCaptions ? "caption" : "", icon: "plus" }); + subItems.push({ description: "Edit onChildClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onChildClick") }); + ContextMenu.Instance.addItem({ description: "Stacking Options ...", subitems: subItems, icon: "eye" }); + } } render() { @@ -282,7 +307,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { // let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx); return ( <div className="collectionStackingView" - ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > + ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) : this.section(undefined, this.filteredChildren)} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 817f9f4cb..9824a501b 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -84,7 +84,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC let headings = this.props.headings(); let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx); let pair = Doc.GetLayoutDataDocPair(parent.props.Document, parent.props.DataDoc, parent.props.fieldKey, d); - let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(NumCast(d.nativeWidth), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1); + let width = () => (d.nativeWidth && !d.ignoreAspect && !parent.props.Document.fillColumn ? Math.min(Math.min(d[WidthSym](), NumCast(d.nativeWidth)), parent.columnWidth / (uniqueHeadings.length + 1)) : parent.columnWidth / (uniqueHeadings.length + 1));/// (uniqueHeadings.length + 1); let height = () => parent.getDocHeight(pair.layout, uniqueHeadings.length + 1);// / (d.nativeWidth && !BoolCast(d.ignoreAspect) ? uniqueHeadings.length + 1 : 1); let dref = React.createRef<HTMLDivElement>(); // if (uniqueHeadings.length > 0) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8b939259c..aaaa623fb 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -30,7 +30,7 @@ export class CollectionView extends React.Component<FieldViewProps> { public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); } - constructor(props:any) { + constructor(props: any) { super(props); } @@ -69,7 +69,7 @@ export class CollectionView extends React.Component<FieldViewProps> { @action private collapse = (value: boolean) => { this._collapsed = value; - this.props.Document.chromeStatus = value ? "collapsed" : "visible"; + this.props.Document.chromeStatus = value ? "collapsed" : "enabled"; } private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { @@ -90,12 +90,7 @@ export class CollectionView extends React.Component<FieldViewProps> { onContextMenu = (e: React.MouseEvent): void => { if (!this.isAnnotationOverlay && !e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 let subItems: ContextMenuProps[] = []; - subItems.push({ - description: "Freeform", event: () => { - this.props.Document.viewType = CollectionViewType.Freeform; - delete this.props.Document.usePivotLayout; - }, icon: "signature" - }); + subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; delete this.props.Document.usePivotLayout; }, icon: "signature" }); if (CollectionBaseView.InSafeMode()) { ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } @@ -111,10 +106,10 @@ export class CollectionView extends React.Component<FieldViewProps> { } } ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" }); - ContextMenu.Instance.addItem({ description: "Apply Template", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); - ContextMenu.Instance.addItem({ - description: this.props.Document.chromeStatus !== "disabled" ? "Hide Chrome" : "Show Chrome", event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" - }); + let existing = ContextMenu.Instance.findByDescription("Layout..."); + let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; + layoutItems.push({ description: "Create Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); + !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" }); } } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 52c47e7e8..bccc0a5b2 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -37,7 +37,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro @observable private _dateWithinValue: string = ""; @observable private _dateValue: Date | string = ""; @observable private _keyRestrictions: [JSX.Element, string][] = []; - @observable private _collapsed: boolean = false; @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); } private _picker: any; @@ -61,7 +60,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro throw new Error("how did you get here, if chrome status is 'disabled' on a collection, a chrome shouldn't even be instantiated!"); } else if (chromeStatus === "collapsed") { - this._collapsed = true; if (this.props.collapse) { this.props.collapse(true); } @@ -163,9 +161,9 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro @action toggleCollapse = () => { - this._collapsed = !this._collapsed; + this.props.CollectionView.props.Document.chromeStatus = this.props.CollectionView.props.Document.chromeStatus === "enabled" ? "collapsed" : "enabled"; if (this.props.collapse) { - this.props.collapse(this._collapsed); + this.props.collapse(this.props.CollectionView.props.Document.chromeStatus !== "enabled"); } } @@ -218,16 +216,17 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro } render() { + let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled"; return ( - <div className="collectionViewChrome-cont" style={{ top: this._collapsed ? -70 : 0 }}> + <div className="collectionViewChrome-cont" style={{ top: collapsed ? -70 : 0 }}> <div className="collectionViewChrome"> <div className="collectionViewBaseChrome"> <button className="collectionViewBaseChrome-collapse" style={{ - top: this._collapsed ? 70 : 10, - transform: `rotate(${this._collapsed ? 180 : 0}deg) scale(${this._collapsed ? 0.5 : 1}) translate(${this._collapsed ? "-100%, -100%" : "0, 0"})`, - opacity: (this._collapsed && !this.props.CollectionView.props.isSelected()) ? 0 : 0.9, - left: (this._collapsed ? 0 : "unset"), + top: collapsed ? 70 : 10, + transform: `rotate(${collapsed ? 180 : 0}deg) scale(${collapsed ? 0.5 : 1}) translate(${collapsed ? "-100%, -100%" : "0, 0"})`, + opacity: (collapsed && !this.props.CollectionView.props.isSelected()) ? 0 : 0.9, + left: (collapsed ? 0 : "unset"), }} title="Collapse collection chrome" onClick={this.toggleCollapse}> <FontAwesomeIcon icon="caret-up" size="2x" /> @@ -243,7 +242,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option> <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View</option> </select> - <div className="collectionViewBaseChrome-viewSpecs" style={{ display: this._collapsed ? "none" : "grid" }}> + <div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}> <input className="collectionViewBaseChrome-viewSpecsInput" placeholder="FILTER DOCUMENTS" value={this.filterValue ? this.filterValue.script.originalScript : ""} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 30010e826..0501bf929 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -816,17 +816,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onContextMenu = (e: React.MouseEvent) => { let layoutItems: ContextMenuProps[] = []; - layoutItems.push({ - description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, - event: this.fitToContainer, - icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" - }); - layoutItems.push({ - description: "reset view", event: () => { - this.props.Document.panX = this.props.Document.panY = 0; - this.props.Document.scale = 1; - }, icon: "compress-arrows-alt" - }); + layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); + layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); layoutItems.push({ description: `${this.props.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: async () => { @@ -841,50 +832,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { event: async () => this.props.Document.clusterOverridesDefaultBackground = !this.props.Document.clusterOverridesDefaultBackground, icon: !this.props.Document.useClusters ? "chalkboard" : "chalkboard" }); - layoutItems.push({ - description: "Arrange contents in grid", - event: this.arrangeContents, - icon: "table" - }); - ContextMenu.Instance.addItem({ - description: "Layout...", - subitems: layoutItems, - icon: "compass" - }); - ContextMenu.Instance.addItem({ - description: "Analyze Strokes", - event: this.analyzeStrokes, - icon: "paint-brush" - }); - ContextMenu.Instance.addItem({ - description: "Import document", icon: "upload", event: () => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".zip"; - input.onchange = async _e => { - const files = input.files; - if (!files) return; - const file = files[0]; - let formData = new FormData(); - formData.append('file', file); - formData.append('remap', "true"); - const upload = Utils.prepend("/uploadDoc"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json === "error") { - return; - } - const doc = await DocServer.GetRefField(json); - if (!doc || !(doc instanceof Doc)) { - return; - } - const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY); - doc.x = x, doc.y = y; - this.addDocument(doc, false); - }; - input.click(); - } - }); + layoutItems.push({ description: "Arrange contents in grid", event: this.arrangeContents, icon: "table" }); + ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); + + let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers..."); + let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; + analyzers.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); + !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" }); } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 2466f13f6..c73e960d8 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -85,8 +85,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & { let list = { ...OmitKeys(this.props, ['parentActive'], (obj: any) => obj.active = this.props.parentActive).omit, Document: this.layoutDoc, - DataDoc: this.dataDoc, - onClick: this.props.onClick + DataDoc: this.dataDoc }; return { props: list }; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b8e2eb436..a702ab80e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -8,13 +8,15 @@ import { Copy, Id } from '../../../new_fields/FieldSymbols'; import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; -import { BoolCast, Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../new_fields/Types"; +import { ScriptField } from '../../../new_fields/ScriptField'; +import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from '../../../server/RouteStore'; import { emptyFunction, returnTrue, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { ClientUtils } from '../../util/ClientUtils'; +import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, dropActionType } from "../../util/DragManager"; import { LinkManager } from '../../util/LinkManager'; @@ -29,20 +31,16 @@ import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; +import { MainView } from '../MainView'; import { OverlayView } from '../OverlayView'; import { PresentationView } from "../presentationview/PresentationView"; +import { ScriptBox } from '../ScriptBox'; import { ScriptingRepl } from '../ScriptingRepl'; import { Template } from "./../Templates"; import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); -import { DictationManager } from '../../util/DictationManager'; -import { MainView } from '../MainView'; -import { ScriptBox } from '../ScriptBox'; -import { CompileScript } from '../../util/Scripting'; -import { DocumentIconContainer } from './DocumentIcon'; -import { ScriptField } from '../../../new_fields/ScriptField'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -297,8 +295,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onClick = async (e: React.MouseEvent) => { if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing. e.stopPropagation(); - if (this.onClickHandler) { - this.onClickHandler.script.run({ this: this.props.Document }); + if (this.onClickHandler && this.onClickHandler.script) { + this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }); e.preventDefault(); return; } @@ -573,37 +571,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); - cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); - cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); - cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); - cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" }); - cm.addItem({ - description: "Edit OnClick script", icon: "edit", event: () => { - let overlayDisposer: () => void = emptyFunction; - const script = this.Document.onClick; - let originalText: string | undefined = undefined; - if (script) originalText = script.script.originalScript; - // tslint:disable-next-line: no-unnecessary-callback-wrapper - let scriptingBox = <ScriptBox initialText={originalText} onCancel={() => overlayDisposer()} onSave={(text, onError) => { - const script = CompileScript(text, { - params: { this: Doc.name }, - typecheck: false, - editable: true, - transformer: DocumentIconContainer.getTransformer() - }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join("\n")); - return; - } - this.Document.onClick = new ScriptField(script); - overlayDisposer(); - }} showDocumentIcons />; - overlayDisposer = OverlayView.Instance.addWindow(scriptingBox, { x: 400, y: 200, width: 500, height: 400, title: `${this.Document.title || ""} OnClick` }); - } - }); - let makes: ContextMenuProps[] = []; + let existingMake = ContextMenu.Instance.findByDescription("Make..."); + let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : []; makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); + makes.push({ description: "Edit OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") }); makes.push({ description: "Make Portal", event: () => { let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" }); @@ -618,15 +590,19 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu }, icon: "window-restore" }); - cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" }); - if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) { - cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" }); - } - cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); + !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" }); let existing = ContextMenu.Instance.findByDescription("Layout..."); let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; + + layoutItems.push({ description: `${this.props.Document.chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.props.Document.chromeStatus = (this.props.Document.chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" }); + layoutItems.push({ description: `${this.props.Document.autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.props.Document.autoHeight = !this.props.Document.autoHeight, icon: "plus" }); + layoutItems.push({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "snowflake" }); + layoutItems.push({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); layoutItems.push({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" }); layoutItems.push({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" }); + if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) { + layoutItems.push({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" }); + } !existing && cm.addItem({ description: "Layout...", subitems: layoutItems, icon: "compass" }); if (!ClientUtils.RELEASE) { let copies: ContextMenuProps[] = []; @@ -634,6 +610,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu copies.push({ description: "Copy ID", event: () => Utils.CopyText(this.props.Document[Id]), icon: "fingerprint" }); cm.addItem({ description: "Copy...", subitems: copies, icon: "copy" }); } + let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers..."); + let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; + analyzers.push({ description: "Transcribe Speech", event: this.listen, icon: "microphone" }); + !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" }); + cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); + 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: "Download document", icon: "download", event: () => { const a = document.createElement("a"); @@ -643,6 +625,36 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu a.click(); } }); + + cm.addItem({ + description: "Import document", icon: "upload", event: () => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = ".zip"; + input.onchange = async _e => { + const files = input.files; + if (!files) return; + const file = files[0]; + let formData = new FormData(); + formData.append('file', file); + formData.append('remap', "true"); + const upload = Utils.prepend("/uploadDoc"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json === "error") { + return; + } + const doc = await DocServer.GetRefField(json); + if (!doc || !(doc instanceof Doc)) { + return; + } + const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY); + doc.x = x, doc.y = y; + this.props.addDocument && this.props.addDocument(doc, false); + }; + input.click(); + } + }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; @@ -715,7 +727,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; } + @computed get brushedDegree() { return Doc.IsBrushedDegree(this.layoutDoc); } + render() { + trace(); let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ? this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc); @@ -733,7 +748,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu }); } let showTextTitle = showTitle && StrCast(this.layoutDoc.layout).startsWith("<FormattedTextBox") ? showTitle : undefined; - let brushDegree = Doc.IsBrushedDegree(this.layoutDoc); + let brushDegree = 0;//this.brushedDegree; return ( <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont} @@ -779,7 +794,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu height={72} fontSize={12} GetValue={() => StrCast((this.layoutDoc.isTemplate || !this.dataDoc ? this.layoutDoc : this.dataDoc)[showTitle!])} - SetValue={(value: string) => (Doc.GetProto(this.layoutDoc)[showTitle!] = value) ? true : true} + SetValue={(value: string) => ((this.layoutDoc.isTemplate ? this.layoutDoc : Doc.GetProto(this.layoutDoc))[showTitle!] = value) ? true : true} /> </div> } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index cf4f7f668..732576328 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -475,6 +475,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } onPointerDown = (e: React.PointerEvent): void => { + if (this.onClick && e.button === 0) { + e.preventDefault(); + } if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); if (FormattedTextBox._toolTipTextMenu && FormattedTextBox._toolTipTextMenu.tooltip) { @@ -643,12 +646,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } specificContextMenu = (e: React.MouseEvent): void => { - let subitems: ContextMenuProps[] = []; - subitems.push({ - description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height", - event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt" - }); - ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" }); + // let subitems: ContextMenuProps[] = []; + // subitems.push({ + // description: BoolCast(this.props.Document.autoHeight) ? "Manual Height" : "Auto Height", + // event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt" + // }); + // ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" }); } render() { let self = this; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 4cfd7929b..564446bb8 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -516,6 +516,8 @@ export namespace Doc { otherdoc.miniLayout = StrCast(templateDoc.miniLayout); otherdoc.detailedLayout = otherdoc.layout; otherdoc.type = DocumentType.TEMPLATE; + !templateDoc.nativeWidth && (otherdoc.nativeWidth = 0); + !templateDoc.nativeHeight && (otherdoc.nativeHeight = 0); return otherdoc; } export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetData?: Doc) { @@ -534,6 +536,8 @@ 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) { @@ -610,4 +614,7 @@ 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); });
\ No newline at end of file diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts index 09cbff25e..0ca35fab2 100644 --- a/src/new_fields/Types.ts +++ b/src/new_fields/Types.ts @@ -2,6 +2,7 @@ import { Field, Opt, FieldResult, Doc } from "./Doc"; import { List } from "./List"; import { RefField } from "./RefField"; import { DateField } from "./DateField"; +import { ScriptField } from "./ScriptField"; export type ToType<T extends InterfaceValue> = T extends "string" ? string : @@ -86,6 +87,9 @@ export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) export function DateCast(field: FieldResult) { return Cast(field, DateField, null); } +export function ScriptCast(field: FieldResult) { + return Cast(field, ScriptField, null); +} type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T; |