diff options
Diffstat (limited to 'src/client/views/collections')
11 files changed, 203 insertions, 142 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index cad87ebcc..4a296493a 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -83,7 +83,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { active = (): boolean => { var isSelected = this.props.isSelected(); - return isSelected || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary); + return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0 || BoolCast(this.props.Document.excludeFromLibrary); } //TODO should this be observable? diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 77b698a07..13bb34037 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -552,11 +552,11 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { } } - panelWidth = () => Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); - panelHeight = () => Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight))); + 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/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 01744fb34..e0cedc210 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -22,30 +22,6 @@ overflow: scroll; } - .collectionSchemaView-previewRegion { - position: relative; - background: $light-color; - height: 100%; - - .collectionSchemaView-previewDoc { - height: 100%; - width: 100%; - position: absolute; - } - - .collectionSchemaView-input { - position: absolute; - max-width: 150px; - width: 100%; - bottom: 0px; - } - - .documentView-node:first-child { - position: relative; - background: $light-color; - } - } - .collectionSchemaView-dividerDragger { position: relative; height: 100%; @@ -62,6 +38,30 @@ } } +.collectionSchemaView-previewRegion { + position: relative; + background: $light-color; + height: auto !important; + + .collectionSchemaView-previewDoc { + height: 100%; + width: 100%; + position: absolute; + } + + .collectionSchemaView-input { + position: absolute; + max-width: 150px; + width: 100%; + bottom: 0px; + } + + .documentView-node:first-child { + position: relative; + background: $light-color; + } +} + .ReactTable { width: 100%; background: white; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 897796174..4537dcc85 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -30,7 +30,7 @@ import { undoBatch } from "../../util/UndoManager"; import { CollectionSchemaHeader, CollectionSchemaAddColumnHeader } from "./CollectionSchemaHeaders"; import { CellProps, CollectionSchemaCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell } from "./CollectionSchemaCells"; import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC"; -import { ComputedField } from "../../../new_fields/ScriptField"; +import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; @@ -899,6 +899,7 @@ interface CollectionSchemaPreviewProps { height: () => number; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; + onClick?: ScriptField; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; @@ -988,23 +989,24 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre DataDoc={this.props.DataDocument} Document={this.props.Document} fitToBox={this.props.fitToBox} - renderDepth={this.props.renderDepth + 1} - selectOnLoad={false} + onClick={this.props.onClick} showOverlays={this.props.showOverlays} addDocument={this.props.addDocument} removeDocument={this.props.removeDocument} moveDocument={this.props.moveDocument} + whenActiveChanged={this.props.whenActiveChanged} + ContainingCollectionView={this.props.CollectionView} + addDocTab={this.props.addDocTab} + parentActive={this.props.active} ScreenToLocalTransform={this.getTransform} + renderDepth={this.props.renderDepth + 1} + selectOnLoad={false} ContentScaling={this.contentScaling} PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} - ContainingCollectionView={this.props.CollectionView} focus={emptyFunction} backgroundColor={returnEmptyString} - parentActive={this.props.active} - whenActiveChanged={this.props.whenActiveChanged} bringToFront={emptyFunction} - addDocTab={this.props.addDocTab} zoomToScale={emptyFunction} getScale={returnOne} /> diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index 271ad2d58..01d4ea2b6 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -1,10 +1,15 @@ @import "../globalCssVariables"; -.collectionStackingView { +.collectionMasonryView { + display:inline; +} +.collectionStackingView{ + display: flex; +} +.collectionStackingView, .collectionMasonryView{ height: 100%; width: 100%; position: absolute; - display: flex; top: 0; overflow-y: auto; flex-wrap: wrap; @@ -31,14 +36,20 @@ .collectionStackingView-masonrySingle, .collectionStackingView-masonryGrid { width: 100%; - height: 100%; - position: absolute; display: grid; top: 0; left: 0; - width: 100%; + } + .collectionStackingView-masonrySingle { + height: 100%; position: absolute; } + .collectionStackingView-masonryGrid { + margin: auto; + height: max-content; + position: relative; + grid-auto-rows: 0px; + } .collectionStackingView-masonrySingle { width: 100%; @@ -80,12 +91,17 @@ height: 100%; margin: auto; } + + .collectionStackingView-masonrySection { + margin: auto; + } .collectionStackingView-sectionHeader { text-align: center; margin-left: 2px; margin-right: 2px; margin-top: 10px; + background: gray; // overflow: hidden; overflow is visible so the color menu isn't hidden -ftong .editableView-input { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index b87be5d68..d68ef2bbd 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -9,8 +9,8 @@ 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 { emptyFunction } from "../../../Utils"; +import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../new_fields/Types"; +import { emptyFunction, Utils } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; @@ -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) { @@ -37,7 +40,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); } @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } - @computed get sectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : ""; } + @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); } get layoutDoc() { // if this document's layout field contains a document (ie, a rendering template), then we will use that @@ -76,17 +79,35 @@ 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)) { + this._heightDisposer = reaction(() => { + if (this.singleColumn && BoolCast(this.props.Document.autoHeight)) { + let maxHght = 0; + Array.from(this.Sections.values()).map(s => { + let hgt = 50 + s.reduce((height, d, i) => { + let pair = Doc.GetLayoutDataDocPair(this.props.Document, this.props.DataDoc, this.props.fieldKey, d); + return height + this.getDocHeight(pair.layout, this.Sections.size) + (i === this.filteredChildren.length - 1 ? this.yMargin : this.gridGap); + }, this.yMargin); + maxHght = Math.max(hgt, maxHght); + }) + if (!maxHght) { 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(); + maxHght = hgt * this.props.ContentScaling(); } - }, { fireImmediately: true }); + return maxHght * 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 } + ); // reset section headers when a new filter is inputted this._sectionFilterDisposer = reaction( @@ -109,18 +130,22 @@ 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 height = () => this.getDocHeight(layoutDoc, this.singleColumn ? this.Sections.size : 1); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); return <CollectionSchemaPreview Document={layoutDoc} DataDocument={dataDoc} showOverlays={this.overlays} renderDepth={this.props.renderDepth} - fitToBox={true} + fitToBox={this.props.fitToBox} + onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler} width={width} height={height} getTransform={finalDxf} @@ -138,9 +163,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { getDocHeight(d: Doc, columnScale: number = 1) { let nw = NumCast(d.nativeWidth); let nh = NumCast(d.nativeHeight); - if (!BoolCast(d.ignoreAspect) && nw && nh) { + if (!d.ignoreAspect && nw && nh) { let aspect = nw && nh ? nh / nw : 1; - let wid = Math.min(d[WidthSym](), this.columnWidth / columnScale); + let wid = d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn ? this.columnWidth / columnScale : Math.min(d[WidthSym](), this.columnWidth / columnScale); return wid * aspect; } return d[HeightSym](); @@ -227,6 +252,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } headings = () => Array.from(this.Sections.keys()); section = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { + if (!this.singleColumn) return this.sectionMasonry(heading, docList); let key = this.sectionFilter; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]); @@ -249,6 +275,54 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { />; } + getDocTransform(doc: Doc, dref: HTMLDivElement) { + let { scale, translateX, translateY } = Utils.GetScreenTransform(dref); + let outerXf = Utils.GetScreenTransform(this._masonryGridRef!); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); + return this.props.ScreenToLocalTransform(). + translate(offset[0], offset[1]). + scale(NumCast(doc.width, 1) / this.columnWidth); + } + children(docs: Doc[]) { + this._docXfs.length = 0; + return docs.map((d, i) => { + let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc); + let width = () => (d.nativeWidth && !d.ignoreAspect && !this.props.Document.fillColumn ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth);/// (uniqueHeadings.length + 1); + let height = () => this.getDocHeight(layoutDoc); + let dref = React.createRef<HTMLDivElement>(); + let dxf = () => this.getDocTransform(layoutDoc, dref.current!); + let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); + this._docXfs.push({ dxf: dxf, width: width, height: height }); + return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} > + {this.getDisplayDoc(layoutDoc, d, dxf, width)} + </div>; + }); + } + + sectionMasonry(heading: SchemaHeaderField | undefined, docList: Doc[]) { + let cols = Math.max(1, Math.min(docList.length, + Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); + let templatecols = ""; + for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `; + return <div key={heading ? heading.heading : "empty"} className="collectionStackingView-masonrySection"> + {!heading ? (null) : + <div key={`${heading.heading}`} className="collectionStackingView-sectionHeader" style={{ background: heading.color }}> + {heading.heading} + </div>} + <div key={`${heading}-stack`} className={`collectionStackingView-masonryGrid`} + style={{ + padding: `${this.yMargin}px ${this.xMargin}px`, + width: `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`, + gridGap: this.gridGap, + gridTemplateColumns: templatecols, + }} + > + {this.children(docList)} + {this.columnDragger} + </div> + </div>; + } + @action addGroup = (value: string) => { if (value && this.sectionHeaders) { @@ -266,7 +340,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() { @@ -280,8 +366,8 @@ 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()} > + <div className={this.singleColumn ? "collectionStackingView" : "collectionMasonryView"} + 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 df03da376..4a8d7debd 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 && !BoolCast(d.ignoreAspect) ? Math.min(pair.layout[WidthSym](), 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(d[WidthSym](), 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) { @@ -328,7 +328,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC </div> : (null); for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth}px `; return ( - <div key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }} + <div className="collectionStackingViewFieldColumn" key={heading} style={{ width: `${100 / ((uniqueHeadings.length + ((this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? 1 : 0)) || 1)}%`, background: this._background }} ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}> {headingView} <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7a402798e..7e1adaa19 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -30,6 +30,10 @@ export class CollectionView extends React.Component<FieldViewProps> { public static LayoutString(fieldStr: string = "data", fieldExt: string = "") { return FieldView.LayoutString(CollectionView, fieldStr, fieldExt); } + constructor(props: any) { + super(props); + } + componentDidMount = () => { this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus), () => { @@ -65,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) => { @@ -86,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" }); } @@ -107,10 +106,11 @@ 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" }); + layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, 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 694bfbe4f..08a1f6902 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/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index c3e55d825..17111af58 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -50,10 +50,10 @@ export class SelectorContextMenu extends React.Component<SelectorProps> { render() { return ( <> - <p>Contexts:</p> - {this._docs.map(doc => <p><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)} - {this._otherDocs.length ? <hr></hr> : null} - {this._otherDocs.map(doc => <p><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)} + <p key="contexts">Contexts:</p> + {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)} + {this._otherDocs.length ? <hr key="hr" /> : null} + {this._otherDocs.map(doc => <p key="p"><a onClick={this.getOnClick(doc)}>{doc.col.title}</a></p>)} </> ); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 32c181557..d01e5cadc 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -40,8 +40,6 @@ import v5 = require("uuid/v5"); import { Timeline } from "../../animationtimeline/Timeline"; import { number } from "prop-types"; import { DocumentType, Docs } from "../../../documents/Documents"; -import { RouteStore } from "../../../../server/RouteStore"; -import { string, number, elementType } from "prop-types"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard); @@ -198,6 +196,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pheight() { return this.props.PanelHeight(); } private inkKey = "ink"; + constructor(props: any) { + super(props); + } + get parentScaling() { return (this.props as any).ContentScaling && this.fitToBox && !this.isAnnotationOverlay ? (this.props as any).ContentScaling() : 1; } @@ -634,6 +636,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, + onClick: this.props.onClick, ScreenToLocalTransform: pair.layout.z ? this.getTransformOverlay : this.getTransform, renderDepth: this.props.renderDepth + 1, selectOnLoad: pair.layout[Id] === this._selectOnLoaded, @@ -658,6 +661,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addDocument: this.props.addDocument, removeDocument: this.props.removeDocument, moveDocument: this.props.moveDocument, + onClick: this.props.onClick, ScreenToLocalTransform: this.getTransform, renderDepth: this.props.renderDepth, selectOnLoad: layoutDoc[Id] === this._selectOnLoaded, @@ -815,17 +819,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 () => { @@ -840,50 +835,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" }); } |
