diff options
Diffstat (limited to 'src/client/views/collections')
30 files changed, 598 insertions, 785 deletions
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 4e30709a6..9b1e3b80d 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -8,9 +8,9 @@ import { Id } from '../../../fields/FieldSymbols'; import { makeInterface } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { returnFalse, Utils, OmitKeys } from '../../../Utils'; +import { OmitKeys, returnFalse, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import "./CollectionCarousel3DView.scss"; import { CollectionSubView } from './CollectionSubView'; @@ -42,12 +42,12 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume const displayDoc = (childPair: { layout: Doc, data: Doc }) => { const script = ScriptField.MakeScript("child._showCaption = 'caption'", { child: Doc.name }, { child: childPair.layout }); const onChildClick = script && (() => script); - return <ContentFittingDocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + return <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} onDoubleClick={this.onChildDoubleClick} onClick={onChildClick} renderDepth={this.props.renderDepth + 1} - LayoutTemplate={this.props.ChildLayoutTemplate} - LayoutTemplateString={this.props.ChildLayoutString} + LayoutTemplate={this.props.childLayoutTemplate} + LayoutTemplateString={this.props.childLayoutString} Document={childPair.layout} DataDoc={childPair.data} PanelWidth={this.panelWidth} diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 16ccdc6f4..512328835 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -1,20 +1,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { observable, computed } from 'mobx'; +import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { documentSchema, collectionSchema } from '../../../fields/documentSchemas'; +import { Doc } from '../../../fields/Doc'; +import { collectionSchema, documentSchema } from '../../../fields/documentSchemas'; import { makeInterface } from '../../../fields/Schema'; -import { NumCast, StrCast, ScriptCast, Cast } from '../../../fields/Types'; +import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { OmitKeys, returnFalse } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; +import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; +import { StyleProp } from '../StyleProvider'; import "./CollectionCarouselView.scss"; import { CollectionSubView } from './CollectionSubView'; -import { Doc } from '../../../fields/Doc'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { ContextMenu } from '../ContextMenu'; -import { ObjectField } from '../../../fields/ObjectField'; -import { returnFalse, returnZero, OmitKeys } from '../../../Utils'; -import { ScriptField } from '../../../fields/ScriptField'; type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; const CarouselDocument = makeInterface(documentSchema, collectionSchema); @@ -49,12 +47,12 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) return !(curDoc?.layout instanceof Doc) ? (null) : <> <div className="collectionCarouselView-image" key="image"> - <ContentFittingDocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} onDoubleClick={this.onContentDoubleClick} onClick={this.onContentClick} renderDepth={this.props.renderDepth + 1} - LayoutTemplate={this.props.ChildLayoutTemplate} - LayoutTemplateString={this.props.ChildLayoutString} + LayoutTemplate={this.props.childLayoutTemplate} + LayoutTemplateString={this.props.childLayoutString} Document={curDoc.layout} DataDoc={curDoc.data} PanelHeight={this.panelHeight} @@ -65,7 +63,7 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) </div> <div className="collectionCarouselView-caption" key="caption" style={{ - background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, "backgroundColor")), + background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BackgroundColor)), color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)), borderRadius: StrCast(this.layoutDoc._captionBorderRounding), }}> diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index c7c510306..756346356 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -7,11 +7,11 @@ import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { makeInterface } from '../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, returnOne, returnTrue, Utils } from '../../../Utils'; +import { emptyFunction, returnTrue, Utils } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; +import { DocumentView } from '../nodes/DocumentView'; import { LinkDescriptionPopup } from '../nodes/LinkDescriptionPopup'; import "./CollectionLinearView.scss"; import { CollectionSubView } from './CollectionSubView'; @@ -137,7 +137,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { width: nested ? pair.layout[WidthSym]() : this.dimension(), height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension(), }} > - <ContentFittingDocumentView + <DocumentView Document={pair.layout} DataDoc={pair.data} addDocument={this.props.addDocument} @@ -146,9 +146,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { pinToPres={emptyFunction} rootSelected={this.props.isSelected} removeDocument={this.props.removeDocument} - onClick={undefined} ScreenToLocalTransform={this.getTransform(dref)} - ContentScaling={returnOne} PanelWidth={nested ? pair.layout[WidthSym] : this.dimension} PanelHeight={nested ? pair.layout[HeightSym] : this.dimension} renderDepth={this.props.renderDepth + 1} diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 1af1a05aa..92288c1f2 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,16 +1,16 @@ -import { GoogleApiWrapper, Map as GeoMap, IMapProps, Marker } from "google-maps-react"; +import { GoogleApiWrapper, IMapProps, Map as GeoMap, Marker } from "google-maps-react"; +import { action, computed, Lambda, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../fields/Doc"; +import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { LinkManager } from "../../util/LinkManager"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import "./CollectionMapView.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); -import { DocumentManager } from "../../util/DocumentManager"; -import { UndoManager, undoBatch } from "../../util/UndoManager"; -import { computed, runInAction, Lambda, action } from "mobx"; import requestPromise = require("request-promise"); type MapSchema = makeInterface<[typeof documentSchema]>; @@ -88,7 +88,7 @@ export class CollectionMapView extends CollectionSubView<MapSchema, Partial<IMap zoom && (this.layoutDoc[`${fieldKey}-mapCenter-zoom`] = zoom); }); if (layout.isLinkButton && DocListCast(layout.links).length) { - await DocumentManager.Instance.FollowLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { + await LinkManager.traverseLink(undefined, layout, (doc: Doc, where: string, finished?: () => void) => { this.props.addDocTab(doc, where); finished?.(); }, false, this.props.ContainingCollectionDoc, batch.end, undefined); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index b35644c6b..ff69c7d05 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -146,7 +146,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr const onLayoutDoc = this.onLayoutDoc(key); (onLayoutDoc ? newDoc : newDoc[DataSym])[key] = this.getValue(this.props.heading); const docs = this.props.parent.childDocList; - return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument(newDoc); + return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument?.(newDoc) || false; } deleteRow = undoBatch(action(() => { diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index b2b23115f..85fcf6384 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -11,6 +11,7 @@ import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; import { List } from "../../../fields/List"; import { ObjectField } from "../../../fields/ObjectField"; +import { RichTextField } from "../../../fields/RichTextField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; @@ -33,7 +34,6 @@ import { PresBox } from "../nodes/PresBox"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { TabDocView } from "./TabDocView"; -import { RichTextField } from "../../../fields/RichTextField"; @observer export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -52,7 +52,7 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> { } componentDidMount() { - reaction(() => SelectionManager.SelectedDocuments().length && SelectionManager.SelectedDocuments()[0], + reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0], (doc) => doc && this.SetSelection(doc)); } @@ -177,7 +177,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp _viewCommand = { params: ["target"], title: "bookmark view", script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);", - immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = 0; }), + immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = (this.target._currentFrame === undefined ? undefined : 0); }), initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; }, }; _clusterCommand = { @@ -372,7 +372,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp } @computed get selectedDocumentView() { - return SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; + return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; } @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } @computed get notACollection() { @@ -514,7 +514,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom"> <button className={"antimodeMenu-button"} key="float" style={{ backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined, borderRight: "1px solid gray" }} - onClick={() => DocumentView.FloatDoc(this.props.docView)}> + onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}> <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} /> </button> </Tooltip>} @@ -554,7 +554,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu } @computed get selectedDocumentView() { - return SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; + return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; } @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } @computed get isText() { @@ -621,7 +621,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu @action editProperties = (value: any, field: string) => { - SelectionManager.SelectedDocuments().forEach(action((element: DocumentView) => { + SelectionManager.Views().forEach(action((element: DocumentView) => { const doc = Document(element.rootDoc); if (doc.type === DocumentType.INK) { switch (field) { @@ -657,8 +657,10 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu return <div className="btn-draw" key="draw"> {this._draw.map((icon, i) => <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom"> - <button className="antimodeMenu-button" onPointerDown={() => func(i, false)} onDoubleClick={() => func(i, true)} - style={{ backgroundColor: i === this._selected ? "121212" : "", fontSize: "20" }}> + <button className="antimodeMenu-button" + onPointerDown={() => func(i, false)} + onDoubleClick={() => func(i, true)} + style={{ backgroundColor: i === this._selected ? "525252" : "", fontSize: "20" }}> <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" /> </button> </Tooltip>)} diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 2636b98e5..f054e7b7f 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -36,11 +36,11 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine} addDocument={undoBatch((doc: Doc | Doc[]) => { (doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d)); - return this.props.addDocument(doc); + return this.props.addDocument?.(doc) || false; })} moveDocument={undoBatch((doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => { (doc instanceof Doc ? [doc] : doc).map(undoBatch((d) => Doc.deiconifyView(d))); - return this.props.moveDocument(doc, targetCollection, addDoc); + return this.props.moveDocument?.(doc, targetCollection, addDoc) || false; })} /> </div>; } @@ -91,7 +91,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { const doc = this.childDocs[0]; doc.x = e.clientX; doc.y = e.clientY; - this.props.addDocTab(doc, "inParent") && this.props.removeDocument(doc); + this.props.addDocTab(doc, "inParent") && (this.props.removeDocument?.(doc) || false); dist = 0; } } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 0b3dfe1e4..904b4c761 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -44,7 +44,7 @@ export interface CellProps { renderDepth: number; addDocTab: (document: Doc, where: string) => boolean; pinToPres: (document: Doc) => void; - moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, + moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; isFocused: boolean; changeFocusedCellByIndex: (row: number, col: number) => void; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index b408fd680..f50da0134 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -461,9 +461,6 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { } } - - get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } - @computed get scriptField() { const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)"; const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 8ae70ce20..c39f8b255 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -11,7 +11,7 @@ import { listSpec } from "../../../fields/Schema"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { Cast, NumCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnFalse, returnOne, setupMoveUpEvents } from "../../../Utils"; +import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; import { SelectionManager } from "../../util/SelectionManager"; import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; @@ -20,7 +20,8 @@ import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globa import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import '../DocumentDecorations.scss'; -import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView"; +import { DocumentView } from "../nodes/DocumentView"; +import { DefaultStyleProvider } from "../StyleProvider"; import "./CollectionSchemaView.scss"; import { CollectionSubView } from "./CollectionSubView"; import { SchemaTable } from "./SchemaTable"; @@ -351,7 +352,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action setFocused = (doc: Doc) => this._focusedTable = doc; @action setPreviewDoc = (doc: Opt<Doc>) => { - SelectionManager.SelectSchemaDoc(this, doc); + SelectionManager.SelectSchemaView(this, doc); this._previewDoc = doc; } @@ -397,11 +398,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { get previewPanel() { return <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}> {!this.previewDocument ? (null) : - <ContentFittingDocumentView + <DocumentView Document={this.previewDocument} DataDoc={undefined} - fitToBox={true} - FreezeDimensions={true} + fitContentsToDoc={true} + freezeDimensions={true} dontCenter={"y"} focus={emptyFunction} renderDepth={this.props.renderDepth} @@ -412,6 +413,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { docFilters={this.docFilters} docRangeFilters={this.docRangeFilters} searchFilterDocs={this.searchFilterDocs} + styleProvider={DefaultStyleProvider} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} @@ -422,7 +424,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} - ContentScaling={returnOne} />} </div>; } @@ -484,7 +485,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { const cm = ContextMenu.Instance; const options = cm.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; - optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument(this._previewDoc), icon: "trash" }); + optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: "trash" }); !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); cm.displayMenu(e.clientX, e.clientY); (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this. diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 0e4029764..d8a8723cd 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -3,8 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import Switch from 'rc-switch'; -import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc"; +import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; @@ -12,23 +11,24 @@ import { listSpec, makeInterface } from "../../../fields/Schema"; import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll, returnVal } from "../../../Utils"; +import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; +import { DocUtils } from "../../documents/Documents"; import { DragManager, dropActionType } from "../../util/DragManager"; +import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { EditableView } from "../EditableView"; -import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView"; +import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; +import { DocumentView, DocAfterFocusFunc, DocumentViewProps } from "../nodes/DocumentView"; +import { FieldViewProps } from "../nodes/FieldView"; +import { StyleProp } from "../StyleProvider"; import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow"; import "./CollectionStackingView.scss"; import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; import { CollectionSubView } from "./CollectionSubView"; import { CollectionViewType } from "./CollectionView"; -import { SnappingManager } from "../../util/SnappingManager"; -import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; -import { DocUtils } from "../../documents/Documents"; -import { DocAfterFocusFunc } from "../nodes/DocumentView"; const _global = (window /* browser */ || global /* node */) as any; type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>; @@ -53,14 +53,15 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField)); } @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); } @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout); } + @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); } @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); } - @computed get yMargin() { return Math.max(this.layoutDoc._showTitle && !this.layoutDoc._showTitleHover ? 30 : 0, this.props.yMargin || NumCast(this.layoutDoc._yMargin, 5)); } // 2 * this.gridGap)); } + @computed get yMargin() { return this.props.yMargin || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); } @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); } - @computed get isStackingView() { return BoolCast(this.layoutDoc._columnsStack, true); } + @computed get isStackingView() { return this.layoutDoc._viewType === CollectionViewType.Stacking; } @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; } @computed get showAddAGroup() { return (this.pivotField && (this.chromeStatus !== 'view-mode' && this.chromeStatus !== 'disabled')); } @computed get columnWidth() { - return Math.min(this.props.PanelWidth() / this.props.ContentScaling() /* / NumCast(this.layoutDoc._viewScale, 1)*/ - 2 * this.xMargin, + return Math.min(this.props.PanelWidth() /* / NumCast(this.layoutDoc._viewScale, 1)*/ - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250)); } @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; } @@ -152,7 +153,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, @action moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => { - return this.props.removeDocument(doc) && addDocument(doc); + return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false; } createRef = (ele: HTMLDivElement | null) => { this._masonryGridRef = ele; @@ -188,28 +189,36 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, getDisplayDoc(doc: Doc, dxf: () => Transform, width: () => number) { const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); - const opacity = () => this.Document._currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity; - return <ContentFittingDocumentView + const styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => { + if (property === StyleProp.Opacity && doc) { + if (this.props.childOpacity) { + return this.props.childOpacity(); + } + if (this.Document._currentFrame !== undefined) { + return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity; + } + } + return this.props.styleProvider?.(doc, props, property); + }; + return <DocumentView Document={doc} DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])} - styleProvider={this.props.styleProvider} - LayoutTemplate={this.props.ChildLayoutTemplate} - LayoutTemplateString={this.props.ChildLayoutString} - FreezeDimensions={this.props.freezeChildDimensions} renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} + styleProvider={styleProvider} + LayoutTemplate={this.props.childLayoutTemplate} + LayoutTemplateString={this.props.childLayoutString} + freezeDimensions={this.props.childFreezeDimensions} NativeWidth={this.props.childIgnoreNativeSize ? returnZero : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox NativeHeight={this.props.childIgnoreNativeSize ? returnZero : undefined} - dontCenter={this.props.childIgnoreNativeSize ? "xy" : ""} - fitToBox={false} + dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined} dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.dontRegisterChildViews, this.props.dontRegisterView)} rootSelected={this.rootSelected} dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType} onClick={this.onChildClickHandler} onDoubleClick={this.onChildDoubleClickHandler} ScreenToLocalTransform={dxf} - opacity={opacity} focus={this.focusDocument} docFilters={this.docFilters} docRangeFilters={this.docRangeFilters} @@ -219,12 +228,11 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} removeDocument={this.props.removeDocument} - contentsPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)} + contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)} parentActive={this.props.active} whenActiveChanged={this.props.whenActiveChanged} addDocTab={this.addDocTab} bringToFront={returnFalse} - ContentScaling={returnOne} scriptContext={this.props.scriptContext} pinToPres={this.props.pinToPres} />; @@ -232,14 +240,14 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, getDocWidth(d?: Doc) { if (!d) return 0; - const layoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.()); + const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); const nw = Doc.NativeWidth(layoutDoc); return Math.min(nw && !this.layoutDoc._columnsFill ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); } getDocHeight(d?: Doc) { if (!d) return 0; const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc; - const childLayoutDoc = Doc.Layout(d, this.props.ChildLayoutTemplate?.()); + const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); @@ -351,9 +359,11 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; this.observer = new _global.ResizeObserver(action((entries: any) => { if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { - const height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => NumCast(Doc.Layout(doc)._viewScale, 1) * Number(getComputedStyle(r).height.replace("px", ""))))); - if (this.props.annotationsKey) { - doc[this.props.annotationsKey + "-height"] = height; + const height = this.headerMargin + + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), + Math.max(...this.refList.map(r => NumCast(Doc.Layout(doc)._viewScale, 1) * Number(getComputedStyle(r).height.replace("px", ""))))); + if (this.props.isAnnotationOverlay) { + doc[this.props.fieldKey + "-height"] = height; } else { Doc.Layout(doc)._height = height * NumCast(Doc.Layout(doc)._viewScale, 1); } @@ -381,13 +391,13 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, const { scale, translateX, translateY } = Utils.GetScreenTransform(dref); const outerXf = Utils.GetScreenTransform(this._masonryGridRef!); const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); - const offsety = (this.props.ChromeHeight && this.props.ChromeHeight() < 0 ? this.props.ChromeHeight() : 0); + const offsety = 0; return this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + offsety); } forceAutoHeight = () => { const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; - Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); + Doc.Layout(doc)._height = this.headerMargin + this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); } sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => { @@ -409,7 +419,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, this.observer = new _global.ResizeObserver(action((entries: any) => { if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) { const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); - Doc.Layout(doc)._height = Math.max(height, NumCast(doc[this.props.fieldKey + "-height"])); + Doc.Layout(doc)._height = this.headerMargin + Math.max(height, NumCast(doc[this.props.fieldKey + "-height"])); } })); this.observer.observe(ref); @@ -469,8 +479,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, } - @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc)); } - @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc)); } + @computed get nativeWidth() { return Doc.NativeWidth(this.layoutDoc); } + @computed get nativeHeight() { return Doc.NativeHeight(this.layoutDoc); } @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index b7562c45e..ec6458d00 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -10,7 +10,7 @@ import { ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { ImageField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, setupMoveUpEvents } from "../../../Utils"; +import { emptyFunction, setupMoveUpEvents, returnFalse } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { DragManager } from "../../util/DragManager"; @@ -146,7 +146,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC newDoc.heading = heading; FormattedTextBox.SelectOnLoad = newDoc[Id]; FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? "" : " "; - return this.props.parent.props.addDocument(newDoc); + return this.props.parent.props.addDocument?.(newDoc) || false; } @action @@ -238,8 +238,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC DocUtils.addDocumentCreatorMenuItems((doc) => { FormattedTextBox.SelectOnLoad = doc[Id]; - return this.props.parent.props.addDocument(doc); - }, this.props.parent.props.addDocument, x, y, true); + return this.props.parent.props.addDocument?.(doc) || false; + }, this.props.parent.props.addDocument || returnFalse, x, y, true); Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof (dataDoc[fieldKey]) === "string").map(fieldKey => docItems.push({ @@ -249,7 +249,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC if (this.props.parent.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document); } - return this.props.parent.props.addDocument(created); + return this.props.parent.props.addDocument?.(created) || false; } }, icon: "compress-arrows-alt" })); @@ -263,7 +263,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC Doc.MakeMetadataFieldTemplate(created, container); return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created); } - return this.props.parent.props.addDocument(created); + return this.props.parent.props.addDocument?.(created) || false; } }, icon: "compress-arrows-alt" })); @@ -276,7 +276,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC if (this.props.parent.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document); } - this.props.parent.props.addDocument(created); + this.props.parent.props.addDocument?.(created); } }); const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 10459a497..d7b9d9745 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -8,48 +8,20 @@ import { ScriptField } from "../../../fields/ScriptField"; import { WebField } from "../../../fields/URLField"; import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; -import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils"; +import { Utils, returnFalse } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Networking } from "../../Network"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { InteractionUtils } from "../../util/InteractionUtils"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocComponent } from "../DocComponent"; -import { FieldViewProps } from "../nodes/FieldView"; import React = require("react"); import * as rp from 'request-promise'; import ReactLoading from 'react-loading'; -export interface CollectionViewProps extends FieldViewProps { - addDocument: (document: Doc | Doc[]) => boolean; - removeDocument: (document: Doc | Doc[]) => boolean; - moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; - PanelWidth: () => number; - PanelHeight: () => number; - VisibleHeight?: () => number; - setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; - rootSelected: (outsideReaction?: boolean) => boolean; - fieldKey: string; - NativeWidth?: () => number; - NativeHeight?: () => number; -} export interface SubCollectionViewProps extends CollectionViewProps { CollectionView: Opt<CollectionView>; - children?: never | (() => JSX.Element[]) | React.ReactNode; - ChildLayoutTemplate?: () => Doc; - childOpacity?: () => number; - childIgnoreNativeSize?: boolean; - ChildLayoutString?: string; - childClickScript?: ScriptField; - childDoubleClickScript?: ScriptField; - freezeChildDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height - overrideDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) - ignoreFields?: string[]; // used in TreeView to ignore specified fields (see LinkBox) - parentActive: (outsideReaction: boolean) => boolean; - isAnnotationOverlay?: boolean; - annotationsKey: string; - layoutEngine?: () => string; } export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) { @@ -95,14 +67,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly // setTimeout changes it outside of the @computed section setTimeout(() => { - if (!this.dataDoc[this.props.annotationsKey || this.props.fieldKey]) this.dataDoc[this.props.annotationsKey || this.props.fieldKey] = new List<Doc>(); + if (!this.dataDoc[this.props.fieldKey]) this.dataDoc[this.props.fieldKey] = new List<Doc>(); }, 1000); - return this.dataDoc[this.props.annotationsKey || this.props.fieldKey]; + return this.dataDoc[this.props.fieldKey]; } get childLayoutPairs(): { layout: Doc; data: Doc; }[] { const { Document, DataDoc } = this.props; - const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.annotationsKey ? DataDoc : undefined, doc)). + const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)). filter(pair => { // filter out any documents that have a proto that we don't have permissions to (which we determine by not having any keys return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length)); }); @@ -112,12 +84,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: return Cast(this.dataField, listSpec(Doc)); } docFilters = () => { - return this.props.ignoreFields?.includes("_docFilters") ? [] : - [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; + return [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; } docRangeFilters = () => { - return this.props.ignoreFields?.includes("_docRangeFilters") ? [] : - [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])]; + return [...this.props.docRangeFilters(), ...Cast(this.props.Document._docRangeFilters, listSpec("string"), [])]; } searchFilterDocs = () => { return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)]; @@ -132,7 +102,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection. // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image. const rootDoc = Cast(this.props.Document.rootDocument, Doc, null); - rawdocs = rootDoc && !this.props.annotationsKey ? [Doc.GetProto(rootDoc)] : []; + rawdocs = rootDoc && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDoc)] : []; } const docs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate).map(d => d as Doc); @@ -220,7 +190,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: } } - addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc); + addDocument = (doc: Doc | Doc[]) => this.props.addDocument?.(doc) || false; @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { @@ -329,23 +299,23 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && this.props.addDocument(f); + (f instanceof Doc) && this.addDocument(f); } }); } else { let srcUrl: string | undefined; let srcWeb: Doc | undefined; - if (SelectionManager.SelectedDocuments().length) { - srcWeb = SelectionManager.SelectedDocuments()[0].props.Document; + if (SelectionManager.Views().length) { + srcWeb = SelectionManager.Views()[0].props.Document; srcUrl = (srcWeb.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0]; } const reg = new RegExp(Utils.prepend(""), "g"); const modHtml = srcUrl ? html.replace(reg, srcUrl) : html; const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 }); Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text; - this.props.addDocument(htmlDoc); + this.addDocument(htmlDoc); if (srcWeb) { - const iframe = SelectionManager.SelectedDocuments()[0].ContentDiv?.getElementsByTagName("iframe")?.[0]; + const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName("iframe")?.[0]; const focusNode = (iframe?.contentDocument?.getSelection()?.focusNode as any); if (focusNode) { const rects = iframe?.contentWindow?.getSelection()?.getRangeAt(0).getClientRects(); @@ -503,7 +473,7 @@ import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DocumentType } from "../../documents/DocumentTypes"; import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox"; -import { CollectionView, CollectionViewType } from "./CollectionView"; +import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView"; import { SelectionManager } from "../../util/SelectionManager"; import { OverlayView } from "../OverlayView"; import { setTimeout } from "timers"; diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index c2d682361..cc625e12e 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -85,7 +85,12 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { @computed get contents() { return <div className="collectionTimeView-innards" key="timeline" style={{ width: "100%", pointerEvents: this.props.active() ? undefined : "none" }} onPointerDown={this.contentsDown}> - <CollectionFreeFormView {...this.props} childClickScript={this._childClickedScript} viewDefDivClick={this._viewDefDivClick} fitToBox={true} freezeChildDimensions={true} layoutEngine={this.layoutEngine} /> + <CollectionFreeFormView {...this.props} + fitContentsToDoc={true} + childClickScript={this._childClickedScript} + viewDefDivClick={this._viewDefDivClick} + childFreezeDimensions={true} + layoutEngine={this.layoutEngine} /> </div>; } diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index c5add7cfb..72ab51784 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -21,8 +21,12 @@ ul { list-style: none; - padding-left: 20px; + padding-left: $TREE_BULLET_WIDTH; margin-bottom: 1px; // otherwise vertical scrollbars may pop up for no apparent reason.... + > .contentFittingDocumentView { + width: unset; + height: unset; + } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 572aae260..18921e9e0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -6,10 +6,11 @@ import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { Document } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, Utils } from '../../../Utils'; +import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, Utils } from '../../../Utils'; import { DocUtils } from '../../documents/Documents'; +import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from "../../util/DragManager"; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; @@ -17,20 +18,18 @@ import { undoBatch, UndoManager } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from "../EditableView"; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; +import { StyleProp } from '../StyleProvider'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import { TreeView } from "./TreeView"; import React = require("react"); -import { DocumentManager } from '../../util/DocumentManager'; -import { FormattedTextBoxComment } from '../nodes/formattedText/FormattedTextBoxComment'; -import { DocumentView } from '../nodes/DocumentView'; export type collectionTreeViewProps = { treeViewHideTitle?: boolean; treeViewHideHeaderFields?: boolean; + treeViewSkipFields?: string[]; // prevents specific fields from being displayed (see LinkBox) onCheckedClick?: () => ScriptField; onChildClick?: () => ScriptField; }; @@ -92,7 +91,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll if (this.doc.resolvedDataDoc instanceof Promise) { this.doc.resolvedDataDoc.then((resolved: any) => doAddDoc(doc)); } else if (relativeTo === undefined) { - this.props.addDocument(doc); + this.props.addDocument?.(doc); } else { doAddDoc(doc); (doc instanceof Doc ? [doc] : doc).forEach(d => d.context = this.props.Document); @@ -106,7 +105,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll layoutItems.push({ description: (this.doc.treeViewPreventOpen ? "Persist" : "Abandon") + "Treeview State", event: () => this.doc.treeViewPreventOpen = !this.doc.treeViewPreventOpen, icon: "paint-brush" }); layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" }); layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" }); - layoutItems.push({ description: (this.doc.treeViewHideLinkLines ? "Show" : "Hide") + " Link Lines", event: () => this.doc.treeViewHideLinkLines = !this.doc.treeViewHideLinkLines, icon: "paint-brush" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" }); const existingOnClick = ContextMenu.Instance.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; @@ -165,7 +163,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll LayoutTemplateString={FormattedTextBox.LayoutString("text")} renderDepth={this.props.renderDepth + 1} rootSelected={returnTrue} - treeViewDoc={undefined} //dontRegisterView={true} styleProvider={this.props.styleProvider} PanelWidth={this.rtfWidth} @@ -185,7 +182,6 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} - ContentScaling={returnOne} /> </div>; } @@ -193,20 +189,21 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; + panelWidth = () => this.props.PanelWidth() - 20; // bcz: 20 is the 10 + 10 for the left and right padding. @computed get treeChildren() { TraceMobx(); - return this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; + return this.props.childDocuments || this.childDocs; } @computed get treeViewElements() { TraceMobx(); const dropAction = StrCast(this.doc.childDropAction) as dropActionType; const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); - const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc); + const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false; return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, this.props.ScreenToLocalTransform, - this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), + this.outerXf, this.active, this.panelWidth, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick, - this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null)); + this.onChildClick, this.props.treeViewSkipFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null)); } @computed get titleBar() { const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle; @@ -215,7 +212,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll render() { TraceMobx(); if (!(this.doc instanceof Doc)) return (null); - const background = this.props.styleProvider?.(this.doc, this.props, "backgroundColor"); + const background = this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor); const paddingX = `${NumCast(this.doc._xPadding, 10)}px`; const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`; // const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 8f402c427..83c639871 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -10,7 +10,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; -import { distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx, normalizeEmail, denormalizeEmail } from '../../../fields/util'; +import { denormalizeEmail, distributeAcls, GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { returnFalse, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -38,10 +38,7 @@ import { SubCollectionViewProps } from './CollectionSubView'; import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; -import { listSpec } from '../../../fields/Schema'; -const higflyout = require("@hig/flyout"); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; +import { ScriptField } from '../../../fields/ScriptField'; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); @@ -64,28 +61,26 @@ export enum CollectionViewType { Grid = "grid", Pile = "pileup" } -export interface CollectionViewCustomProps { +export interface CollectionViewProps extends FieldViewProps { + isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) + layoutEngine?: () => string; + parentActive: (outsideReaction: boolean) => boolean; filterAddDocument?: (doc: Doc | Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) + setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; + + // property overrides for child documents + children?: never | (() => JSX.Element[]) | React.ReactNode; + childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) childOpacity?: () => number; - hideFilter?: true; + childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection + childLayoutString?: string; + childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height childIgnoreNativeSize?: boolean; + childClickScript?: ScriptField; + childDoubleClickScript?: ScriptField; } - -export interface CollectionRenderProps { - addDocument: (document: Doc | Doc[]) => boolean; - removeDocument: (document: Doc | Doc[]) => boolean; - moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; - active: () => boolean; - parentActive: (outsideReaction: boolean) => boolean; - whenActiveChanged: (isActive: boolean) => void; - PanelWidth: () => number; - PanelHeight: () => number; - ChildLayoutTemplate?: () => Doc;// specify a layout Doc template to use for children of the collection - ChildLayoutString?: string;// specify a layout string to use for children of the collection -} - @observer -export class CollectionView extends Touchable<FieldViewProps & CollectionViewCustomProps> { +export class CollectionView extends Touchable<CollectionViewProps> { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } _isChildActive = false; //TODO should this be observable? @@ -97,22 +92,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - private AclMap = new Map<symbol, string>([ - [AclPrivate, SharingPermissions.None], - [AclReadonly, SharingPermissions.View], - [AclAddonly, SharingPermissions.Add], - [AclEdit, SharingPermissions.Edit], - [AclAdmin, SharingPermissions.Admin] - ]); - get collectionViewType(): CollectionViewType | undefined { const viewField = StrCast(this.props.Document._viewType); if (CollectionView._safeMode) { - if (viewField === CollectionViewType.Freeform || viewField === CollectionViewType.Schema) { - return CollectionViewType.Tree; - } - if (viewField === CollectionViewType.Invalid) { - return CollectionViewType.Freeform; + switch (viewField) { + case CollectionViewType.Freeform: + case CollectionViewType.Schema: return CollectionViewType.Tree; + case CollectionViewType.Invalid: return CollectionViewType.Freeform; } } return viewField as any as CollectionViewType; @@ -120,7 +106,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction) || - this.props.Document.forceActive || + (this.props.layerProvider?.(this.props.Document) !== false && (this.props.Document.forceActive || this.props.Document._isGroup)) || this._isChildActive || this.props.renderDepth === 0) ? true : @@ -136,7 +122,6 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus const docs = doc instanceof Doc ? [doc] : doc; - if (docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document))) return false; const targetDataDoc = this.props.Document[DataSym]; const docList = DocListCast(targetDataDoc[this.props.fieldKey]); @@ -160,7 +145,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus if (effectiveAcl === AclAddonly) { added.map(doc => { - this.props.layerProvider?.(doc, true); + this.props.layerProvider?.(doc, true);// assigns layer values to the newly added document... testing the utility of this Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); doc.context = this.props.Document; }); @@ -185,7 +170,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus doc._stayInCollection = undefined; doc.context = this.props.Document; }); - added.map(doc => this.props.layerProvider?.(doc, true)); + added.map(doc => this.props.layerProvider?.(doc, true));// assigns layer values to the newly added document... testing the utility of this (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added); targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); } @@ -253,32 +238,30 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus } screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()); - private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { + private SubView = (type: CollectionViewType, props: SubCollectionViewProps) => { TraceMobx(); - const props: SubCollectionViewProps = { ...this.props, ...renderProps, ScreenToLocalTransform: this.screenToLocalTransform, CollectionView: this, annotationsKey: "" }; switch (type) { - case CollectionViewType.Schema: return (<CollectionSchemaView key="collview" {...props} />); - case CollectionViewType.Docking: return (<CollectionDockingView key="collview" {...props} />); - case CollectionViewType.Tree: return (<CollectionTreeView key="collview" {...props} />); - //case CollectionViewType.Staff: return (<CollectionStaffView key="collview" {...props} />); - case CollectionViewType.Multicolumn: return (<CollectionMulticolumnView key="collview" {...props} />); - case CollectionViewType.Multirow: return (<CollectionMultirowView key="rpwview" {...props} />); - case CollectionViewType.Linear: { return (<CollectionLinearView key="collview" {...props} />); } - case CollectionViewType.Pile: { return (<CollectionPileView key="collview" {...props} />); } - case CollectionViewType.Carousel: { return (<CollectionCarouselView key="collview" {...props} />); } - case CollectionViewType.Carousel3D: { return (<CollectionCarousel3DView key="collview" {...props} />); } - case CollectionViewType.Stacking: { this.props.Document._columnsStack = true; return (<CollectionStackingView key="collview" {...props} />); } - case CollectionViewType.Masonry: { this.props.Document._columnsStack = false; return (<CollectionStackingView key="collview" {...props} />); } - case CollectionViewType.Time: { return (<CollectionTimeView key="collview" {...props} />); } - case CollectionViewType.Map: return (<CollectionMapView key="collview" {...props} />); - case CollectionViewType.Grid: return (<CollectionGridView key="gridview" {...props} />); - case CollectionViewType.Freeform: - default: { this.props.Document._freeformLayoutEngine = undefined; return (<CollectionFreeFormView key="collview" {...props} ChildLayoutString={props.ChildLayoutString} />); } + default: + case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />; + case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />; + case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />; + case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />; + case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />; + case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />; + case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />; + case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />; + case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />; + case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />; + case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />; + case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />; + case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />; + case CollectionViewType.Map: return <CollectionMapView key="collview" {...props} />; + case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />; + //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />; } } setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) { - const subItems: ContextMenuProps[] = []; subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" }); if (addExtras && CollectionView._safeMode) { @@ -386,7 +369,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus render() { TraceMobx(); - const props: CollectionRenderProps = { + const props: SubCollectionViewProps = { + ...this.props, addDocument: this.addDocument, removeDocument: this.removeDocument, moveDocument: this.moveDocument, @@ -395,13 +379,13 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus parentActive: this.props.parentActive, PanelWidth: this.bodyPanelWidth, PanelHeight: this.props.PanelHeight, - ChildLayoutTemplate: this.childLayoutTemplate, - ChildLayoutString: this.childLayoutString, + childLayoutTemplate: this.childLayoutTemplate, + childLayoutString: this.childLayoutString, + ScreenToLocalTransform: this.screenToLocalTransform, + CollectionView: this, }; - const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.treeViewOutlineMode || this.collectionViewType === CollectionViewType.Linear ? undefined : - this.props.styleProvider?.(this.props.Document, this.props, "boxShadow"); return (<div className={"collectionView"} onContextMenu={this.onContextMenu} - style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined, boxShadow }}> + style={{ pointerEvents: this.props.layerProvider?.(this.props.Document) === false ? "none" : undefined }}> {this.showIsTagged()} {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)} {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => Cast(d.data, ImageField, null)).map(d => diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 664b6115a..d77f70607 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -5,31 +5,31 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table"; import "react-table/react-table.css"; -import { Doc, DocListCast, Field, Opt, AclPrivate, AclReadonly, DataSym } from "../../../fields/Doc"; +import { DateField } from "../../../fields/DateField"; +import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { ComputedField } from "../../../fields/ScriptField"; import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils"; +import { ImageField } from "../../../fields/URLField"; +import { GetEffectiveAcl } from "../../../fields/util"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse } from "../../../Utils"; import { Docs, DocumentOptions } from "../../documents/Documents"; +import { DocumentType } from "../../documents/DocumentTypes"; import { CompileScript, Transformer, ts } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globalCssVariables.scss'; import { ContextMenu } from "../ContextMenu"; import '../DocumentDecorations.scss'; -import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView"; -import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell } from "./CollectionSchemaCells"; +import { DocumentView } from "../nodes/DocumentView"; +import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells"; import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders"; import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC"; import "./CollectionSchemaView.scss"; import { CollectionView } from "./CollectionView"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { GetEffectiveAcl } from "../../../fields/util"; -import { DateField } from "../../../fields/DateField"; -import { ImageField } from "../../../fields/URLField"; enum ColumnType { @@ -62,9 +62,9 @@ export interface SchemaTableProps { ContainingCollectionDoc: Opt<Doc>; fieldKey: string; renderDepth: number; - deleteDocument: (document: Doc | Doc[]) => boolean; - addDocument: (document: Doc | Doc[]) => boolean; - moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; + deleteDocument?: (document: Doc | Doc[]) => boolean; + addDocument?: (document: Doc | Doc[]) => boolean; + moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; ScreenToLocalTransform: () => Transform; active: (outsideReaction: boolean | undefined) => boolean; onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void; @@ -376,7 +376,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @undoBatch createRow = action(() => { - this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 })); + this.props.addDocument?.(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 })); this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col }; }); @@ -567,11 +567,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> { background: "dimGray", display: "block", top: 0, left: 0, transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)` }} - ref="overlay"><ContentFittingDocumentView + ref="overlay"><DocumentView Document={this._showDoc} DataDoc={this._showDataDoc} - fitToBox={true} - FreezeDimensions={true} + freezeDimensions={true} focus={emptyFunction} renderDepth={this.props.renderDepth} rootSelected={() => false} @@ -588,9 +587,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> { whenActiveChanged={emptyFunction} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} - bringToFront={returnFalse} - ContentScaling={returnOne}> - </ContentFittingDocumentView> + bringToFront={returnFalse}> + </DocumentView> </div>} </div>; } diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index e1fd47592..0d03936dc 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -6,14 +6,15 @@ import { clamp } from 'lodash'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; -import { DataSym, Doc, DocListCast, Opt, DocListCastAsync, StrListCast, WidthSym, HeightSym } from "../../../fields/Doc"; +import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { FieldId } from "../../../fields/RefField"; import { listSpec } from '../../../fields/Schema'; -import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; +import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from "../../util/DragManager"; @@ -22,15 +23,15 @@ import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { DocumentView, DocAfterFocusFunc, DocumentViewProps } from "../nodes/DocumentView"; +import { FieldViewProps } from '../nodes/FieldView'; import { PresBox, PresMovement } from '../nodes/PresBox'; +import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; import { CollectionDockingViewMenu } from './CollectionDockingViewMenu'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionViewType } from './CollectionView'; import "./TabDocView.scss"; import React = require("react"); -import { List } from '../../../fields/List'; -import { DocumentType } from '../../documents/DocumentTypes'; import Color = require('color'); const _global = (window /* browser */ || global /* node */) as any; @@ -50,6 +51,16 @@ export class TabDocView extends React.Component<TabDocViewProps> { @observable private _document: Doc | undefined; @observable private _view: DocumentView | undefined; + @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } + @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); } + @computed get renderBounds() { + const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; + const xbounds = bounds[2] - bounds[0]; + const ybounds = bounds[3] - bounds[1]; + const dim = Math.max(xbounds, ybounds); + return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; + } + get stack() { return (this.props as any).glContainer.parent.parent; } get tab() { return (this.props as any).glContainer.tab; } get view() { return this._view; } @@ -84,7 +95,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { toggle.style.borderLeft = "solid 1px black"; toggle.onclick = (e: MouseEvent) => { if (tab.contentItem === tab.header.parent.getActiveContentItem()) { - tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : "background"; + tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background; } }; tab.element[0].style.borderTopRightRadius = "8px"; @@ -104,15 +115,16 @@ export class TabDocView extends React.Component<TabDocViewProps> { }; // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected - titleEle.onpointerdown = (e: any) => { - if (e.target.className !== "lm_close_tab" && this.view) { - SelectionManager.SelectDoc(this.view, false); + titleEle.onpointerdown = action((e: any) => { + if (e.target.className !== "lm_close_tab") { + if (this.view) SelectionManager.SelectView(this.view, false); + else this._activated = true; if (Date.now() - titleEle.lastClick < 1000) titleEle.select(); titleEle.lastClick = Date.now(); (document.activeElement !== titleEle) && titleEle.focus(); } - }; - tab._disposers.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments().some(v => (v.topMost || v.props.treeViewDoc) && v.props.Document === doc), + }); + tab._disposers.selectionDisposer = reaction(() => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc), action((selected) => { if (selected) this._activated = true; const toggle = tab.element[0].children[1].children[0] as HTMLInputElement; @@ -210,7 +222,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { } componentDidMount() { - const selected = () => SelectionManager.SelectedDocuments().some(v => v.props.Document === this._document); + const selected = () => SelectionManager.Views().some(v => v.props.Document === this._document); new _global.ResizeObserver(action((entries: any) => { for (const entry of entries) { this._panelWidth = entry.contentRect.width; @@ -238,49 +250,6 @@ export class TabDocView extends React.Component<TabDocViewProps> { } } - NativeAspect = () => this.nativeAspect; - PanelWidth = () => this.panelWidth; - PanelHeight = () => this.panelHeight; - nativeWidth = () => this._nativeWidth; - nativeHeight = () => this._nativeHeight; - ContentScaling = () => this.contentScaling; - - ScreenToLocalTransform = () => { - if (this._mainCont?.children) { - const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont.children[0]?.firstChild as HTMLElement); - const scale = Utils.GetScreenTransform(this._mainCont).scale; - return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY).scale(1 / this.ContentScaling() / scale); - } - return Transform.Identity(); - } - @computed get nativeAspect() { - return this.nativeWidth() ? this.nativeWidth() / this.nativeHeight() : 0; - } - @computed get panelHeight() { - return this.NativeAspect() && this.NativeAspect() > this._panelWidth / this._panelHeight ? this._panelWidth / this.NativeAspect() : this._panelHeight; - } - @computed get panelWidth() { - return this.layoutDoc?.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), Doc.NativeWidth(this.layoutDoc)), this._panelWidth) : - (this.NativeAspect() && this.NativeAspect() < this._panelWidth / this._panelHeight ? this._panelHeight * this.NativeAspect() : this._panelWidth); - } - @computed get _nativeWidth() { return !this.layoutDoc?._fitWidth ? Doc.NativeWidth(this.layoutDoc) || this._panelWidth : 0; } - @computed get _nativeHeight() { return !this.layoutDoc?._fitWidth ? Doc.NativeHeight(this.layoutDoc) || this._panelHeight : 0; } - @computed get contentScaling() { - const nativeW = Doc.NativeWidth(this.layoutDoc); - const nativeH = Doc.NativeHeight(this.layoutDoc); - let scaling = 1; - if (nativeW && (this.layoutDoc?._fitWidth || this._panelHeight / nativeH > this._panelWidth / nativeW)) { - scaling = this._panelWidth / nativeW; // width-limited or fitWidth - } else if (nativeW && nativeH) { - scaling = this._panelHeight / nativeH; // height-limited - } - return scaling; - } - @computed get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.ContentScaling()) / 2 : 0; } - @computed get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.ContentScaling()) / this._panelWidth * 100}% ` : undefined; } - @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } - @computed static get darkScheme() { return BoolCast(CurrentUserUtils.ActiveDashboard?.darkScheme); } - // adds a tab to the layout based on the locaiton parameter which can be: // close[:{left,right,top,bottom}] - e.g., "close" will close the tab, "close:left" will close the left tab, // add[:{left,right,top,bottom}] - e.g., "add" will add a tab to the current stack, "add:right" will add a tab on the right @@ -303,14 +272,6 @@ export class TabDocView extends React.Component<TabDocViewProps> { } } - @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, TabDocView.styleProvider(this._document, undefined, "backgroundColor"))); } - @computed get renderBounds() { - const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; - const xbounds = bounds[2] - bounds[0]; - const ybounds = bounds[3] - bounds[1]; - const dim = Math.max(xbounds, ybounds); - return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; - } childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null); returnMiniSize = () => NumCast(this._document?._miniMapSize, 150); miniDown = (e: React.PointerEvent) => { @@ -339,24 +300,22 @@ export class TabDocView extends React.Component<TabDocViewProps> { ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} parentActive={returnFalse} - ChildLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. + childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this. noOverlay={true} // don't render overlay Docs since they won't scale active={returnTrue} select={emptyFunction} dropAction={undefined} isSelected={returnFalse} dontRegisterView={true} - annotationsKey={""} fieldKey={Doc.LayoutFieldKey(this._document!)} bringToFront={emptyFunction} rootSelected={returnTrue} addDocument={returnFalse} moveDocument={returnFalse} removeDocument={returnFalse} - ContentScaling={returnOne} PanelWidth={this.returnMiniSize} PanelHeight={this.returnMiniSize} - ScreenToLocalTransform={this.ScreenToLocalTransform} + ScreenToLocalTransform={Transform.Identity} renderDepth={0} whenActiveChanged={emptyFunction} focus={emptyFunction} @@ -366,7 +325,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { docFilters={CollectionDockingView.Instance.docFilters} docRangeFilters={CollectionDockingView.Instance.docRangeFilters} searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} - fitToBox={true} + fitContentsToDoc={true} /> <div className="miniOverlay" onPointerDown={this.miniDown} > <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} /> @@ -375,7 +334,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { <Tooltip title={<div className="dash-tooltip">{"toggle minimap"}</div>}> <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} - style={{ background: TabDocView.styleProvider(this._document, undefined, "backgroundColor") }} > + style={{ background: DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor) }} > <FontAwesomeIcon icon={"globe-asia"} size="lg" /> </div> </Tooltip> @@ -387,151 +346,56 @@ export class TabDocView extends React.Component<TabDocViewProps> { } afterFocus?.(false); } - setView = action((view: DocumentView) => this._view = view); active = () => this._isActive; - - // - // a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents - // currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab. - // if it matches, then the document gets pointer events, otherwise it does not. - // - layerProvider = (doc: Doc, assign?: boolean) => { - if (doc.z) return true; - if (assign) { - const activeLayer = StrCast(this._document?.activeLayer); - if (activeLayer) { - const layers = Cast(doc.layers, listSpec("string"), []); - if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer); - else if (!layers.length) doc.layers = new List<string>([activeLayer]); - if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer; - } - return true; - } else { - if (Doc.AreProtosEqual(doc, this._document)) return true; - const layers = Cast(doc.layers, listSpec("string"), []); - if (!layers.length && !this._document?.activeLayer) return true; - if (layers.includes(StrCast(this._document?.activeLayer))) return true; - return false; - } + ScreenToLocalTransform = () => { + const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0]?.firstChild as HTMLElement); + return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY); } + PanelWidth = () => this._panelWidth; + PanelHeight = () => this._panelHeight; - static toggleBackground = undoBatch(action((doc: Doc) => { - const layers = StrListCast(doc.layers); - if (!layers.includes("background")) { - if (!layers.length) doc.layers = new List<string>(["background"]); - else layers.push("background"); - } - else layers.splice(layers.indexOf("background"), 1); - doc._overflow = !layers.includes("background") ? "visible" : undefined; - if (!layers.includes("background")) { - //this.props.bringToFront(doc, true); - // const wid = this.Document[WidthSym](); // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it. - // const hgt = this.Document[HeightSym](); - // Doc.SetNativeWidth(this.props.Document[DataSym], wid); - // Doc.SetNativeHeight(this.props.Document[DataSym], hgt); - } - })); - // - // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab - // - public static styleProvider = (doc: Opt<Doc>, props: DocumentViewProps | undefined, property: string): any => { - switch (property) { - case "backgroundColor": { - if (Doc.UserDoc().renderStyle === "comic") return undefined; - let docColor = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor)); - if (!docColor) { - switch (doc?.type) { - case DocumentType.PRESELEMENT: docColor = TabDocView.darkScheme ? "" : ""; break; - case DocumentType.PRES: docColor = TabDocView.darkScheme ? "#3e3e3e" : "white"; break; - case DocumentType.FONTICON: docColor = "black"; break; - case DocumentType.RTF: docColor = TabDocView.darkScheme ? "#2d2d2d" : "#f1efeb"; break; - case DocumentType.LABEL: - case DocumentType.BUTTON: docColor = TabDocView.darkScheme ? "#2d2d2d" : "lightgray"; break; - case DocumentType.LINK: - case DocumentType.COL: - docColor = Doc.IsSystem(doc) ? (TabDocView.darkScheme ? "rgb(62,62,62)" : "lightgrey") : - StrCast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground); - break; - //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; - default: docColor = TabDocView.darkScheme ? "black" : "white"; break; - } - } - if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = Color(docColor).fade(0.5).toString(); - return docColor; - } - case "widgetColor": return TabDocView.darkScheme ? "lightgrey" : "dimgrey"; - case "hidden": return (BoolCast(doc?.hidden) /* || props?.layerProvider?.(doc) === false*/); - case "boxShadow": { - switch (doc?.type) { - case DocumentType.COL: return StrListCast(doc.layers).includes("background") ? undefined : - `${TabDocView.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`; - default: return undefined; - } - } - case "docContents": return undefined; - default: - if (property.startsWith("pointerEvents")) { - const layer = doc && props?.layerProvider?.(doc); - if (doc?.Opacity === 0 || doc?.type === DocumentType.INK || doc?.isInkMask) return "none"; - if (layer === false && !property.includes(":selected") && !SnappingManager.GetIsDragging()) return "none"; - if (doc?.type !== DocumentType.INK && layer === true) return "all"; - return undefined; - } - if (property.startsWith("decorations") && props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform) { - const isBackground = StrListCast(doc?.layers).includes("background"); - return doc && (isBackground || property.includes(":selected")) && (props?.renderDepth || 0) > 0 && - ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ? - <div className="documentView-lock" onClick={() => TabDocView.toggleBackground(doc)}> - <FontAwesomeIcon icon={isBackground ? "unlock" : "lock"} style={{ color: isBackground ? "red" : undefined }} size="lg" /> - </div> - : (null); - } - } - } - public static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => { + static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { if (doc) { - switch (property) { - case "docContents": - if (doc.type === DocumentType.COL) return null; + switch (property.split(":")[0]) { + default: return DefaultStyleProvider(doc, props, property); + case StyleProp.PointerEvents: return "none"; + case StyleProp.DocContents: const background = doc.type === DocumentType.PDF ? "red" : doc.type === DocumentType.IMG ? "blue" : doc.type === DocumentType.RTF ? "orange" : doc.type === DocumentType.VID ? "purple" : doc.type === DocumentType.WEB ? "yellow" : "gray"; - return <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />; - default: - if (property.startsWith("pointerEvents")) return "none"; - return TabDocView.styleProvider(doc, props, property); + return doc.type === DocumentType.COL ? + undefined : + <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />; } } } + @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); } @computed get docView() { TraceMobx(); return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) : - <><DocumentView key={this._document[Id]} + <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)} + renderDepth={0} Document={this._document} - getView={this.setView} DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} - bringToFront={emptyFunction} - rootSelected={returnTrue} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + PanelWidth={this.PanelWidth} + PanelHeight={this.PanelHeight} layerProvider={this.layerProvider} + styleProvider={DefaultStyleProvider} + docFilters={CollectionDockingView.Instance.docFilters} + docRangeFilters={CollectionDockingView.Instance.docRangeFilters} + searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} addDocument={undefined} removeDocument={undefined} - ContentScaling={this.ContentScaling} - PanelWidth={this.PanelWidth} - PanelHeight={this.PanelHeight} - NativeHeight={this.nativeHeight() ? this.nativeHeight : undefined} - NativeWidth={this.nativeWidth() ? this.nativeWidth : undefined} + addDocTab={this.addDocTab} ScreenToLocalTransform={this.ScreenToLocalTransform} - renderDepth={0} + dontCenter={"y"} + rootSelected={returnTrue} parentActive={this.active} whenActiveChanged={emptyFunction} focus={this.focusFunc} - styleProvider={TabDocView.styleProvider} - addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} - docFilters={CollectionDockingView.Instance.docFilters} - docRangeFilters={CollectionDockingView.Instance.docRangeFilters} - searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> + bringToFront={emptyFunction} + pinToPres={TabDocView.PinDoc} /> {this._document._viewType !== CollectionViewType.Freeform ? (null) : <>{this._document.hideMinimap ? (null) : this.renderMiniMap()} <Tooltip key="ttip" title={<div className="dash-tooltip">{"toggle minimap"}</div>}> @@ -544,18 +408,15 @@ export class TabDocView extends React.Component<TabDocViewProps> { } render() { - return (<div className="collectionDockingView-content" ref={ref => { - if (this._mainCont = ref) { - (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); - DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); - } - }} - style={{ - transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, - height: this.layoutDoc?._fitWidth ? undefined : "100%", - width: this.widthpercent - }}> - {this.docView} - </div >); + return ( + <div className="collectionDockingView-content" style={{ height: "100%", width: "100%" }} ref={ref => { + if (this._mainCont = ref) { + (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); + DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); + } + }} > + {this.docView} + </div > + ); } }
\ No newline at end of file diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index 84b5af7be..7a654c7cf 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -1,5 +1,13 @@ @import "../globalCssVariables"; +.treeView-label { + max-height: 1.5em; + text-overflow: ellipsis; + display: inline-block; + white-space: pre; + width: 100%; + overflow: hidden; +} .treeView-container, .treeView-container-active { .bullet-outline { @@ -17,6 +25,10 @@ left: -10px; position: absolute; } + .treeView-checkIcon { + left: -10px; + position: absolute; + } &:hover { .treeView-expandIcon { display: unset; @@ -25,7 +37,7 @@ } .bullet { position: relative; - width: 20px; + width: $TREE_BULLET_WIDTH; color: $intermediate-color; margin-top: 3px; transform: scale(1.3, 1.3); @@ -53,7 +65,7 @@ cursor: pointer; } -.treeView-border-outline, +.treeView-borderoutline, .treeView-border { display: flex; overflow: hidden; @@ -71,7 +83,7 @@ display: none; } .formattedTextBox-cont { - .formattedTextbox-sidebar { + .formattedTextbox-sidebar, .formattedTextbox-sidebar-inking { overflow: visible !important; border-left: unset; } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 675ba60c0..93d3be1fc 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,6 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, trace } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; +import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss'; import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -9,7 +10,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -20,11 +21,12 @@ import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from "../EditableView"; -import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView'; +import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { KeyValueBox } from '../nodes/KeyValueBox'; +import { StyleProp, testDocProps } from '../StyleProvider'; import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; @@ -43,13 +45,12 @@ export interface TreeViewProps { pinToPres: (document: Doc) => void; panelWidth: () => number; panelHeight: () => number; - ChromeHeight: undefined | (() => number); addDocument: (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; outdentDocument?: () => void; ScreenToLocalTransform: () => Transform; dontRegisterView?: boolean; - backgroundColor?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined; + styleProvider?: StyleProviderFunc | undefined; outerXf: () => { translateX: number, translateY: number }; treeView: CollectionTreeView; parentKey: string; @@ -59,11 +60,13 @@ export interface TreeViewProps { renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle onCheckedClick?: () => ScriptField; onChildClick?: () => ScriptField; - ignoreFields?: string[]; + skipFields?: string[]; firstLevel: boolean; whenActiveChanged: (isActive: boolean) => void; } +const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); }; + @observer /** * Renders a treeView of a collection of documents @@ -83,7 +86,8 @@ export class TreeView extends React.Component<TreeViewProps> { private _uniqueId = Utils.GenerateGuid(); private _editMaxWidth: number | string = 0; - @observable _dref: ContentFittingDocumentView | undefined | null; + + @observable _dref: DocumentView | undefined | null; @computed get doc() { TraceMobx(); return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive @@ -284,9 +288,9 @@ export class TreeView extends React.Component<TreeViewProps> { docWidth = () => { const layoutDoc = this.layoutDoc; const aspect = Doc.NativeAspect(layoutDoc); - if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - 20, layoutDoc[WidthSym]()); - if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - 20)); - return Doc.NativeWidth(layoutDoc) ? Math.min(layoutDoc[WidthSym](), this.props.panelWidth() - 20) : Math.min(this.layoutDoc[WidthSym](), this.props.panelWidth() - 20); + if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]()); + if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth())); + return Math.min(this.props.panelWidth() - treeBulletWidth(), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]()); } docHeight = () => { const layoutDoc = this.layoutDoc; @@ -311,7 +315,7 @@ export class TreeView extends React.Component<TreeViewProps> { doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); for (const key of Object.keys(ids).slice().sort()) { - if (this.props.ignoreFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; + if (this.props.skipFields?.includes(key) || key === "title" || key === "treeViewOpen") continue; const contents = doc[key]; let contentElement: (JSX.Element | null)[] | JSX.Element = []; @@ -325,9 +329,9 @@ export class TreeView extends React.Component<TreeViewProps> { const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents), this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, - this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView); + this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, + this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, + [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView); } else { contentElement = <EditableView key="editableView" contents={contents !== undefined ? Field.toString(contents as Field) : "null"} @@ -354,9 +358,9 @@ export class TreeView extends React.Component<TreeViewProps> { return rows; } - rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - 20); + rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - treeBulletWidth()); rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT; - rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), 20); + rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth()); expandPanelHeight = () => { if (this.layoutDoc._fitWidth) return this.docHeight(); const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym](); @@ -404,17 +408,18 @@ export class TreeView extends React.Component<TreeViewProps> { {!docs ? (null) : TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc, this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, - this.props.outerXf, this.props.active, this.props.panelWidth, this.props.ChromeHeight, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, - [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.ignoreFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)} + StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.ScreenToLocalTransform, + this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, + [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView)} </ul >; } else if (this.treeViewExpandedView === "fields") { - return <ul key={this.doc[Id] + this.doc.title}><div style={{ display: "inline-block" }} > - {this.expandedField} - </div></ul>; - } else { - return this.renderDocument(false); + return <ul key={this.doc[Id] + this.doc.title}> + <div style={{ display: "inline-block" }} > + {this.expandedField} + </div> + </ul>; } + return <ul>{this.renderEmbeddedDocument(false)}</ul>; } get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); } @@ -441,7 +446,7 @@ export class TreeView extends React.Component<TreeViewProps> { return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} key={"bullet"} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"} onClick={this.bulletClick} - style={this.outlineMode ? { opacity: NumCast(this.doc.opacity, 1) } : { + style={this.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : { color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}> @@ -449,14 +454,14 @@ export class TreeView extends React.Component<TreeViewProps> { !(this.doc.text as RichTextField)?.Text ? (null) : <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> : <div className="treeView-bulletIcons" > - <div className="treeView-expandIcon"> - <FontAwesomeIcon size="sm" icon={this.outlineMode ? [this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"] : + <div className={`treeView-${this.onCheckedClick ? "checkIcon" : "expandIcon"}`}> + <FontAwesomeIcon size="sm" icon={ checked === "check" ? "check" : (checked === "x" ? "times" : checked === "unchecked" ? "square" : !this.treeViewOpen ? "caret-right" : "caret-down")} /> </div> - <FontAwesomeIcon icon={iconType} /> + {this.onCheckedClick ? (null) : <FontAwesomeIcon icon={iconType} />} </div> } </div>; @@ -485,7 +490,7 @@ export class TreeView extends React.Component<TreeViewProps> { showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef.current?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : [{ script: ScriptField.MakeFunction(`openOnRight(self)`)!, label: "Open" }, { script: ScriptField.MakeFunction(`DocFocus(self)`)!, label: "Focus" }]; - truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, 0); + truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, this.props.panelWidth()); onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); @@ -496,6 +501,26 @@ export class TreeView extends React.Component<TreeViewProps> { e.preventDefault(); } } + titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { + if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView + + switch (property.split(":")[0]) { + case StyleProp.Opacity: return this.outlineMode ? undefined : 1; + case StyleProp.BackgroundColor: return StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); + case StyleProp.DocContents: return testDocProps(props) && !props?.treeViewDoc ? (null) : + <div className="treeView-label" style={{ // just render a title for a tree view label (identified by treeViewDoc being set in 'props') + maxWidth: props?.PanelWidth() || undefined, + background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor), + }}> + {StrCast(doc?.title)} + </div>; + case StyleProp.Decorations: return (null); + } + } + embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { + if (property.startsWith(StyleProp.Decorations)) return (null); + return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView + } onKeyDown = (e: React.KeyboardEvent) => { if (this.doc.treeViewHideHeader || this.outlineMode) { e.stopPropagation(); @@ -520,6 +545,7 @@ export class TreeView extends React.Component<TreeViewProps> { ref={this._docRef} Document={this.doc} DataDoc={undefined} + styleProvider={this.titleStyleProvider} treeViewDoc={this.props.treeView.props.Document} addDocument={undefined} addDocTab={this.props.addDocTab} @@ -531,11 +557,11 @@ export class TreeView extends React.Component<TreeViewProps> { moveDocument={this.move} removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} - ContentScaling={returnOne} + NativeHeight={returnZero} + NativeWidth={returnZero} PanelWidth={this.truncateTitleWidth} - PanelHeight={returnZero} + PanelHeight={() => 18} contextMenuItems={this.contextMenuItems} - opacity={this.outlineMode ? undefined : returnOne} renderDepth={1} focus={returnTrue} parentActive={returnTrue} @@ -546,7 +572,7 @@ export class TreeView extends React.Component<TreeViewProps> { docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} - ContainingCollectionDoc={this.props.containingCollection} + ContainingCollectionDoc={this.props.treeView.props.Document} />; return <> <div className={`docContainer${Doc.IsSystem(this.props.document) ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open" @@ -563,15 +589,18 @@ export class TreeView extends React.Component<TreeViewProps> { } renderBulletHeader = (contents: JSX.Element) => { - return <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader" - ref={this._header} - style={{ maxWidth: this._editMaxWidth }} - onClick={this.ignoreEvent} - onPointerDown={this.ignoreEvent} - onPointerEnter={this.onPointerEnter} - onPointerLeave={this.onPointerLeave}> - {contents} - </div>; + return <> + <div className={`treeView-header` + (this._editMaxWidth ? "-editing" : "")} key="titleheader" + ref={this._header} + style={{ maxWidth: this._editMaxWidth }} + onClick={this.ignoreEvent} + onPointerDown={this.ignoreEvent} + onPointerEnter={this.onPointerEnter} + onPointerLeave={this.onPointerLeave}> + {contents} + </div> + {this.renderBorder} + </>; } // renders the text version of a document as the header (e.g., useful for Slide views where the "") @@ -582,25 +611,25 @@ export class TreeView extends React.Component<TreeViewProps> { </>; } - renderDocument = (asText: boolean) => { + renderEmbeddedDocument = (asText: boolean) => { const panelWidth = asText || StrCast(Doc.LayoutField(this.layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.expandPanelWidth; const panelHeight = asText ? this.rtfOutlineHeight : StrCast(Doc.LayoutField(this.layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.expandPanelHeight; - return <ContentFittingDocumentView key={this.doc[Id]} ref={action((r: ContentFittingDocumentView | null) => this._dref = r)} + return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)} Document={this.doc} DataDoc={undefined} PanelWidth={panelWidth} PanelHeight={panelHeight} NativeWidth={!asText && this.layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined} NativeHeight={!asText && this.layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined} - fitToBox={!asText && this.isCollectionDoc !== undefined} + fitContentsToDoc={true} + hideTitle={asText} LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined} focus={asText ? this.refocus : returnFalse} dontRegisterView={asText ? undefined : this.props.dontRegisterView} ScreenToLocalTransform={this.docTransform} renderDepth={this.props.renderDepth + 1} rootSelected={returnTrue} - treeViewDoc={undefined} - styleProvider={this.props.backgroundColor} + styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} @@ -614,14 +643,13 @@ export class TreeView extends React.Component<TreeViewProps> { addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} - ContentScaling={returnOne} />; } @computed get renderDocumentAsHeader() { return <> {this.renderBullet} - {this.renderDocument(true)} + {this.renderEmbeddedDocument(true)} </>; } @@ -648,24 +676,16 @@ export class TreeView extends React.Component<TreeViewProps> { else this._editMaxWidth = ""; const hideTitle = this.doc.treeViewHideHeader || this.outlineMode; - return hideTitle && !StrCast(Doc.LayoutField(this.doc)).includes("CollectionView") ? - this.renderContent - : - <div className={`treeView-container${this._dref?.docView?.contentsActive() ? "-active" : ""}`} - ref={this.createTreeDropTarget} - onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()} - onKeyDown={this.onKeyDown}> - {hideTitle ? - <li className="collection-child"> - {this.renderBulletHeader(this.renderDocumentAsHeader)} - {this.renderBorder} - </li> : - <li className="collection-child"> - {this.renderBulletHeader(this.renderTitleAsHeader)} - {this.renderBorder} - </li> - } - </div>; + return <div className={`treeView-container${this._dref?.contentsActive() ? "-active" : ""}`} + ref={this.createTreeDropTarget} + onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()} + onKeyDown={this.onKeyDown}> + <li className="collection-child"> + {hideTitle && this.doc.type !== DocumentType.RTF ? + this.renderEmbeddedDocument(false) : + this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader : this.renderTitleAsHeader)} + </li> + </div>; } public static sortDocs(childDocs: Doc[], criterion: string | undefined) { @@ -710,19 +730,18 @@ export class TreeView extends React.Component<TreeViewProps> { dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => boolean, pinToPres: (document: Doc) => void, - backgroundColor: undefined | ((document: Opt<Doc>, props: Opt<DocumentViewProps>, property: string, layerProvider?: (doc: Doc, assign?: boolean) => boolean) => string | undefined), + styleProvider: undefined | StyleProviderFunc, screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: (outsideReaction?: boolean) => boolean, panelWidth: () => number, - ChromeHeight: undefined | (() => number), renderDepth: number, treeViewHideHeaderFields: () => boolean, treeViewPreventOpen: boolean, renderedIds: string[], onCheckedClick: undefined | (() => ScriptField), onChildClick: undefined | (() => ScriptField), - ignoreFields: string[] | undefined, + skipFields: string[] | undefined, firstLevel: boolean, whenActiveChanged: (isActive: boolean) => void, dontRegisterView: boolean | undefined) { @@ -733,7 +752,7 @@ export class TreeView extends React.Component<TreeViewProps> { const docs = TreeView.sortDocs(childDocs, StrCast(containingCollection?.[key + "-sortCriteria"])); - const rowWidth = () => panelWidth() - 20; + const rowWidth = () => panelWidth() - treeBulletWidth(); return docs.filter(child => child instanceof Doc).map((child, i) => { const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { @@ -783,10 +802,9 @@ export class TreeView extends React.Component<TreeViewProps> { renderDepth={renderDepth} removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove} addDocument={addDocument} - backgroundColor={backgroundColor} + styleProvider={styleProvider} panelWidth={rowWidth} panelHeight={rowHeight} - ChromeHeight={ChromeHeight} dontRegisterView={dontRegisterView} moveDocument={move} dropAction={dropAction} @@ -799,7 +817,7 @@ export class TreeView extends React.Component<TreeViewProps> { treeViewHideHeaderFields={treeViewHideHeaderFields} treeViewPreventOpen={treeViewPreventOpen} renderedIds={renderedIds} - ignoreFields={ignoreFields} + skipFields={skipFields} firstLevel={firstLevel} whenActiveChanged={whenActiveChanged} />; }); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 8cbda310a..858719a08 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -1,7 +1,6 @@ .collectionfreeformlinkview-linkLine { stroke: black; opacity: 0.8; - pointer-events: all; stroke-width: 3px; transition: opacity 0.5s ease-in; fill: transparent; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index c81bd068c..ae5688b48 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -1,14 +1,14 @@ +import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../../fields/Doc"; -import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from '../../../../Utils'; +import { Id } from "../../../../fields/FieldSymbols"; +import { NumCast, StrCast } from "../../../../fields/Types"; +import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { DocumentType } from "../../../documents/DocumentTypes"; +import { SnappingManager } from "../../../util/SnappingManager"; import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); -import { DocumentType } from "../../../documents/DocumentTypes"; -import { observable, action, reaction, IReactionDisposer, trace, computed } from "mobx"; -import { StrCast, Cast, NumCast } from "../../../../fields/Types"; -import { Id } from "../../../../fields/FieldSymbols"; -import { SnappingManager } from "../../../util/SnappingManager"; export interface CollectionFreeFormLinkViewProps { A: DocumentView; @@ -103,7 +103,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo const top = rect.top, height = rect.height; var el = el.parentNode; while (el && el !== document.body) { - rect = el?.getBoundingClientRect(); + rect = el.getBoundingClientRect?.(); if (rect?.width) { if (top <= rect.bottom === false && getComputedStyle(el).overflow === "hidden") return rect.bottom; // Check if the element is out of view due to a container scrolling diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 6a1a41ca7..4dab8f15b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,15 +1,14 @@ -import { computed, trace } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; +import { Utils } from "../../../../Utils"; +import { DocumentType } from "../../../documents/DocumentTypes"; import { DocumentManager } from "../../../util/DocumentManager"; import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinksView.scss"; import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView"; import React = require("react"); -import { Utils, emptyFunction } from "../../../../Utils"; -import { DocumentType } from "../../../documents/DocumentTypes"; -import { SnappingManager } from "../../../util/SnappingManager"; @observer export class CollectionFreeFormLinksView extends React.Component { @@ -28,10 +27,8 @@ export class CollectionFreeFormLinksView extends React.Component { } return drawnPairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc[] }[]); - return connections.filter(c => - c.a.props.Document.type === DocumentType.LINK - && !c.a.props.treeViewDoc?.treeViewHideLinkLines && !c.b.props.treeViewDoc?.treeViewHideLinkLines - ).map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />); + return connections.filter(c => c.a.props.Document.type === DocumentType.LINK) + .map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />); } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 548ad78a5..9f6938e67 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -1,16 +1,16 @@ +import { computed } from "mobx"; import { observer } from "mobx-react"; import * as mobxUtils from 'mobx-utils'; import CursorField from "../../../../fields/CursorField"; +import { FieldResult } from "../../../../fields/Doc"; +import { List } from "../../../../fields/List"; import { listSpec } from "../../../../fields/Schema"; import { Cast } from "../../../../fields/Types"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; -import { CollectionViewProps } from "../CollectionSubView"; +import { CollectionViewProps } from "../CollectionView"; import "./CollectionFreeFormView.scss"; import React = require("react"); import v5 = require("uuid/v5"); -import { computed } from "mobx"; -import { FieldResult } from "../../../../fields/Doc"; -import { List } from "../../../../fields/List"; @observer export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 136600164..f934fcd92 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,7 +1,7 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from "../../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; @@ -31,9 +31,9 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss" import { Timeline } from "../../animationtimeline/Timeline"; import { ContextMenu } from "../../ContextMenu"; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke"; -import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; +import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewProps } from "../../nodes/CollectionFreeFormDocumentView"; import { DocumentLinksButton } from "../../nodes/DocumentLinksButton"; -import { DocumentViewProps, DocAfterFocusFunc } from "../../nodes/DocumentView"; +import { DocumentViewProps, DocAfterFocusFunc, DocumentView } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import { PresBox } from "../../nodes/PresBox"; @@ -47,7 +47,9 @@ import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; -import { isUndefined } from "lodash"; +import { StyleProp, StyleLayers } from "../../StyleProvider"; +import { DocumentDecorations } from "../../DocumentDecorations"; +import { FieldViewProps } from "../../nodes/FieldView"; export const panZoomSchema = createSchema({ _panX: "number", @@ -81,6 +83,8 @@ export type collectionFreeformViewProps = { @observer export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, Partial<collectionFreeformViewProps>>(PanZoomDocument) { + public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive + private _lastX: number = 0; private _lastY: number = 0; private _downX: number = 0; @@ -95,24 +99,29 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P private _layoutPoolData = new ObservableMap<string, PoolData>(); private _layoutSizeData = new ObservableMap<string, { width?: number, height?: number }>(); private _cachedPool: Map<string, PoolData> = new Map(); + private _lastTap = 0; + private _nudgeTime = 0; + private _thumbIdentifier?: number; + + @observable private _hLines: number[] | undefined; + @observable private _vLines: number[] | undefined; @observable private _pullCoords: number[] = [0, 0]; @observable private _pullDirection: string = ""; + @observable private _showAnimTimeline = false; + @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. - public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables @observable _clusterSets: (Doc[])[] = []; @observable _timelineRef = React.createRef<Timeline>(); - @observable _marqueeRef = React.createRef<HTMLDivElement>(); - @observable canPanX: boolean = true; - @observable canPanY: boolean = true; + @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); } @computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; } - @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; } - @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent ? this.props.ContentScaling() : 1; } + @computed get fitToContent() { return (this.props.fitContentsToDoc || this.Document._fitToBox) && !this.isAnnotationOverlay; } + @computed get parentScaling() { return 1; } @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); } - @computed get nativeWidth() { return this.fitToContent ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document)); } - @computed get nativeHeight() { return this.fitToContent ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document)); } + @computed get nativeWidth() { return this.fitToContent ? 0 : Doc.NativeWidth(this.Document); } + @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); } private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; } private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } @@ -155,39 +164,26 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed this.addDocument(newBox); } + addDocument = action((newBox: Doc | Doc[]) => { let retVal = false; if (newBox instanceof Doc) { - retVal = this.props.addDocument(newBox); + retVal = this.props.addDocument?.(newBox) || false; retVal && this.bringToFront(newBox); retVal && this.updateCluster(newBox); } else { - retVal = this.props.addDocument(newBox); + retVal = this.props.addDocument?.(newBox) || false; // bcz: deal with clusters } if (retVal) { const newBoxes = (newBox instanceof Doc) ? [newBox] : newBox; for (const newBox of newBoxes) { if (newBox.activeFrame !== undefined) { - const x = newBox.x; - const y = newBox.y; - const w = newBox._width; - const h = newBox._height; - delete newBox["x-indexed"]; - delete newBox["y-indexed"]; - delete newBox["w-indexed"]; - delete newBox["h-indexed"]; - delete newBox["opacity-indexed"]; - delete newBox._width; - delete newBox._height; - delete newBox.x; - delete newBox.y; - delete newBox.opacity; + const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field]); + CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field}-indexed`]); + CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field]); delete newBox.activeFrame; - newBox.x = x; - newBox.y = y; - newBox._width = w; - newBox._height = h; + CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== "opacity" && (newBox[field] = vals[i])); } } if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) { @@ -199,7 +195,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P private selectDocuments = (docs: Doc[]) => { SelectionManager.DeselectAll(); - docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectDoc(dv, true)); + docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true)); } public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document._currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } @@ -211,8 +207,28 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY)); } + updateGroupBounds = () => { + if (!this.props.Document._isGroup) return; + const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() })); + const cbounds = aggregateBounds(clist, 0, 0); + const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2]; + const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)]; + const pbounds = { + x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1], + r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1] + }; + + this.layoutDoc._width = (pbounds.r - pbounds.x); + this.layoutDoc._height = (pbounds.b - pbounds.y); + this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2; + this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2; + this.layoutDoc.x = pbounds.x; + this.layoutDoc.y = pbounds.y; + } + @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) { + if (!this.ChildDrag && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false; if (!super.onInternalDrop(e, de)) return false; const refDoc = docDragData.droppedDocuments[0]; const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y); @@ -240,9 +256,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)]; layoutDoc._width = NumCast(layoutDoc._width, 300); layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300); - !Cast(d, listSpec("string"), []).includes("background") && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront + !StrListCast(d.layers).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront } + this.updateGroupBounds(); + (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments); return true; } @@ -270,8 +288,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return false; } else { const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" }); - this.props.addDocument(source); - linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation", ""); // TODODO this is where in text links get passed + this.props.addDocument?.(source); + de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation", ""); // TODODO this is where in text links get passed e.stopPropagation(); return true; } @@ -280,13 +298,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action onInternalDrop = (e: Event, de: DragManager.DropEvent) => { const [xp, yp] = this.getTransform().transformPoint(de.x, de.y); - if (this.isAnnotationOverlay !== true && de.complete.linkDragData) { - return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp); - } else if (de.complete.annoDragData?.dropDocument && super.onInternalDrop(e, de)) { - return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp); - } else if (de.complete.docDragData?.droppedDocuments.length && this.internalDocDrop(e, de, de.complete.docDragData, xp, yp)) { - return true; - } + if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp); + if (de.complete.annoDragData?.dropDocument && super.onInternalDrop(e, de)) return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp); + if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp); return false; } @@ -304,6 +318,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return cluster; }, -1); } + tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) { if (cluster !== -1) { const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0); @@ -312,7 +327,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!); const de = new DragManager.DocumentDragData(eles); de.moveDocument = this.props.moveDocument; - const [left, top] = clusterDocs[0].props.ScreenToLocalTransform().scale(clusterDocs[0].props.ContentScaling()).inverse().transformPoint(0, 0); + const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 }; de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top); de.dropAction = e.ctrlKey || e.altKey ? "alias" : undefined; DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, { hideSource: !de.dropAction }); @@ -370,10 +385,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P updateCluster(doc: Doc) { const childLayouts = this.childLayoutPairs.map(pair => pair.layout); if (this.props.Document._useClusters) { - this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)); + this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)); const preferredInd = NumCast(doc.cluster); doc.cluster = -1; - this._clusterSets.map((set, i) => set.map(member => { + this._clusterSets.forEach((set, i) => set.forEach(member => { if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) { doc.cluster = i; } @@ -381,7 +396,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) { doc.cluster = preferredInd; } - this._clusterSets.map((set, i) => { + this._clusterSets.forEach((set, i) => { if (doc.cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) { doc.cluster = i; } @@ -396,27 +411,26 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } } - getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => { - let clusterColor = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1 - if (property !== "backgroundColor") return clusterColor; + getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => { + let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1 + if (property !== StyleProp.BackgroundColor) return styleProp; const cluster = NumCast(doc?.cluster); if (this.Document._useClusters) { if (this._clusterSets.length <= cluster) { - setTimeout(() => doc && this.updateCluster(doc), 0); + setTimeout(() => doc && this.updateCluster(doc)); } else { // choose a cluster color from a palette const colors = ["#da42429e", "#31ea318c", "rgba(197, 87, 20, 0.55)", "#4a7ae2c4", "rgba(216, 9, 255, 0.5)", "#ff7601", "#1dffff", "yellow", "rgba(27, 130, 49, 0.55)", "rgba(0, 0, 0, 0.268)"]; - clusterColor = colors[cluster % colors.length]; + styleProp = colors[cluster % colors.length]; const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor); // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document - set && set.filter(s => !Cast(s.layers, listSpec("string"), []).includes("background")).map(s => clusterColor = StrCast(s.backgroundColor)); - set && set.filter(s => Cast(s.layers, listSpec("string"), []).includes("background")).map(s => clusterColor = StrCast(s.backgroundColor)); + set && set.filter(s => !StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor)); + set && set.filter(s => StrListCast(s.layers).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor)); } - } else if (doc && NumCast(doc.group, -1) !== -1) clusterColor = "gray"; - return clusterColor; + } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray"; + return styleProp; } - @action onPointerDown = (e: React.PointerEvent): void => { if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { @@ -424,13 +438,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)); if (e.button === 0 && (!e.shiftKey || this._hitCluster !== -1) && !e.altKey && !e.ctrlKey && this.props.active(true)) { - - // if (!this.props.Document.aliasOf && !this.props.ContainingCollectionView) { - // this.props.addDocTab(this.props.Document, "replace"); - // e.stopPropagation(); - // e.preventDefault(); - // return; - // } document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -460,12 +467,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); - // if (Doc.SelectedTool() === InkTool.Highlighter || Doc.SelectedTool() === InkTool.Pen) { - // e.stopPropagation(); - // e.preventDefault(); - // const point = this.getTransform().transformPoint(pt.pageX, pt.pageY); - // this._points.push({ X: point[0], Y: point[1] }); - // } if (Doc.GetSelectedTool() === InkTool.None) { this._lastX = pt.pageX; this._lastY = pt.pageY; @@ -510,7 +511,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return pass; }); this.addDocument(Docs.Create.FreeformDocument(sel, { title: "nested collection", x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 })); - sel.forEach(d => this.props.removeDocument(d)); + sel.forEach(d => this.props.removeDocument?.(d)); e.stopPropagation(); break; case GestureUtils.Gestures.StartBracket: @@ -528,12 +529,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (sets.length && sets[0]) { this._wordPalette.clear(); const colors = setDocs.map(sd => FieldValue(sd.color) as string); - sets.forEach((st: string, i: number) => { - const words = st.split(","); - words.forEach(word => { - this._wordPalette.set(word, colors[i]); - }); - }); + sets.forEach((st: string, i: number) => st.split(",").forEach(word => this._wordPalette.set(word, colors[i]))); } const inks = this.getActiveDocuments().filter(doc => { if (doc.type === "ink") { @@ -596,27 +592,20 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } } - _lastTap = 0; - - @action onPointerUp = (e: PointerEvent): void => { - if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) return; - - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - this.removeMoveListeners(); - this.removeEndListeners(); + if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + this.removeMoveListeners(); + this.removeEndListeners(); + } } onClick = (e: React.MouseEvent) => { if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { - if (Date.now() - this._lastTap < 300) { - runInAction(() => { - DocumentLinksButton.StartLink = undefined; - DocumentLinksButton.StartLinkView = undefined; - }); - const docpt = this.getTransform().transformPoint(e.clientX, e.clientY); - this.scaleAtPt(docpt, 1); + if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a double click + runInAction(() => DocumentLinksButton.StartLink = DocumentLinksButton.StartLinkView = undefined); + this.scaleAtPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); e.stopPropagation(); e.preventDefault(); } @@ -626,9 +615,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => { - // bcz: theres should be a better way of doing these than referencing these static instances directly - MarqueeOptionsMenu.Instance?.fadeOut(true);// I think it makes sense for the marquee menu to go away when panned. -syip2 - const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true); this._lastX = e.clientX; @@ -647,6 +633,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return; } if (!e.cancelBubble) { + if (this.props.Document._isGroup) return; // groups don't pan when dragged -- instead let the event go through to allow the group itself to drag if (Doc.GetSelectedTool() === InkTool.None) { if (this.tryDragCluster(e, this._hitCluster)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers @@ -769,7 +756,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } } - this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); @@ -797,7 +783,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.removeEndListeners(); } - @action zoom = (pointX: number, pointY: number, deltaY: number): void => { let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05; @@ -868,7 +853,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } bringToFront = action((doc: Doc, sendToBack?: boolean) => { - if (sendToBack || Cast(doc.layers, listSpec("string"), []).includes("background")) { + if (sendToBack || StrListCast(doc.layers).includes(StyleLayers.Background)) { doc.zIndex = 0; } else if (doc.isInkMask) { doc.zIndex = 5000; @@ -919,7 +904,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P !dontCenter && this.props.focus(doc); afterFocus && setTimeout(afterFocus, delay); } else { - const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height); + const contextHgt = NumCast(annotOn._height); const curScroll = NumCast(this.props.Document._scrollTop); let scrollTo = curScroll; if (curScroll + contextHgt < NumCast(doc.y)) { @@ -935,7 +920,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } else { !dontCenter && delay && this.props.focus(this.props.Document); afterFocus?.(!dontCenter && delay ? true : false); - } } @@ -950,7 +934,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY }; HistoryUtil.pushState(newState); - if (DocListCast(this.dataDoc[this.props.annotationsKey || this.props.fieldKey]).includes(doc)) { + if (DocListCast(this.dataDoc[this.props.fieldKey]).includes(doc)) { // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active... if (!doc.z) this.setPan(newPanX, newPanY, doc.focusSpeed || doc.focusSpeed === 0 ? `transform ${doc.focusSpeed}ms` : "transform 500ms", true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow } @@ -982,11 +966,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P pw && ph && (this.Document[this.scaleFieldKey] = scale * Math.min(pw / NumCast(doc._width), ph / NumCast(doc._height))); } - @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); } onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); - // @ts-ignore - backgroundHalo = computedFn(function (doc: Doc) { return this.Document._useClusters || NumCast(doc.group, -1) !== -1; }).bind(this); parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { @@ -996,33 +977,29 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P pinToPres: this.props.pinToPres, whenActiveChanged: this.props.whenActiveChanged, parentActive: this.parentActive, - fitToBox: false, DataDoc: childData, Document: childLayout, - LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate, - LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString, - FreezeDimensions: this.props.freezeChildDimensions, - setupDragLines: this.setupDragLines, - dontRegisterView: this.props.dontRegisterView, + ContainingCollectionView: this.props.CollectionView, + ContainingCollectionDoc: this.props.Document, + LayoutTemplate: childLayout.z ? undefined : this.props.childLayoutTemplate, + LayoutTemplateString: childLayout.z ? undefined : this.props.childLayoutString, rootSelected: childData ? this.rootSelected : returnFalse, - dropAction: StrCast(this.props.Document.childDropAction) as dropActionType, onClick: this.onChildClickHandler, onDoubleClick: this.onChildDoubleClickHandler, ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, - renderDepth: this.props.renderDepth + 1, PanelWidth: childLayout[WidthSym], PanelHeight: childLayout[HeightSym], - ContentScaling: returnOne, - ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.Document, docFilters: this.docFilters, docRangeFilters: this.docRangeFilters, searchFilterDocs: this.searchFilterDocs, focus: this.focusDocument, styleProvider: this.getClusterColor, - backgroundHalo: this.backgroundHalo, + freezeDimensions: this.props.childFreezeDimensions, + dropAction: StrCast(this.props.Document.childDropAction) as dropActionType, bringToFront: this.bringToFront, addDocTab: this.addDocTab, + renderDepth: this.props.renderDepth + 1, + dontRegisterView: this.props.dontRegisterView, }; } @@ -1032,14 +1009,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y)); doc.x = pt[0]; doc.y = pt[1]; - return this.props.addDocument(doc); + return this.props.addDocument?.(doc) || false; } else { (doc as any as Doc[]).forEach(doc => { const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y)); doc.x = pt[0]; doc.y = pt[1]; }); - return this.props.addDocument(doc); + return this.props.addDocument?.(doc) || false; } } if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) { @@ -1048,9 +1025,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } return this.props.addDocTab(doc, where); }); + getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc, docs: Doc[], state: any }): PoolData { const layoutDoc = Doc.Layout(params.pair.layout); - const { x, y, opacity } = this.Document._currentFrame === undefined ? params.pair.layout : + const { x, y, opacity } = this.Document._currentFrame === undefined ? + { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } : CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document._currentFrame || 0); const { z, color, zIndex } = params.pair.layout; return { @@ -1097,6 +1076,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc, replica: string) { return this._layoutPoolData.get(doc[Id] + (replica || "")); }.bind(this)); + childSizeProvider = computedFn(function childSizeProvider(this: any, doc: Doc, replica: string) { return this._layoutSizeData.get(doc[Id] + (replica || "")); }.bind(this)); @@ -1164,6 +1144,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P key={entry[1].pair.layout[Id] + (entry[1].replica || "")} {...this.getChildDocumentViewProps(entry[1].pair.layout, entry[1].pair.data)} replica={entry[1].replica} + CollectionFreeFormView={this} dataProvider={this.childDataProvider} sizeProvider={this.childSizeProvider} layerProvider={this.props.layerProvider} @@ -1172,8 +1153,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined} jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))} //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this - fitToBox={BoolCast(this.props.freezeChildDimensions)} // bcz: check this - FreezeDimensions={BoolCast(this.props.freezeChildDimensions)} + freezeDimensions={BoolCast(this.props.childFreezeDimensions)} />, bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica) })); @@ -1193,7 +1173,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this._layoutComputeReaction = reaction(() => this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); - if (!this.props.annotationsKey) { + if (!this.props.isAnnotationOverlay) { this._boundsReaction = reaction(() => this.contentBounds, bounds => (!this.fitToContent && this._layoutElements?.length) && setTimeout(() => { const rbounds = Cast(this.Document._renderContentBounds, listSpec("number"), [0, 0, 0, 0]); @@ -1220,9 +1200,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } - - // <div ref={this._marqueeRef}> - @action onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => { if ((e as any).handlePan || this.props.isAnnotationOverlay) return; @@ -1254,7 +1231,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.props.ContainingCollectionView?.removeDocument(this.props.Document); })); - @undoBatch layoutDocsInGrid = action(() => { const docs = this.childLayoutPairs; @@ -1279,7 +1255,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @undoBatch @action toggleNativeDimensions = () => { - Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0); + Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight); } @undoBatch @@ -1288,23 +1264,22 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true; } - private thumbIdentifier?: number; - onContextMenu = (e: React.MouseEvent) => { - if (this.props.annotationsKey || this.props.Document.annotationOn || !ContextMenu.Instance) return; + if (this.props.isAnnotationOverlay || this.props.Document.annotationOn || !ContextMenu.Instance) return; const appearance = ContextMenu.Instance.findByDescription("Appearance..."); const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : []; appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); + this.props.ContainingCollectionView && + appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" }); !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null; !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" }); const viewctrls = ContextMenu.Instance.findByDescription("UI Controls..."); const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : []; - !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null; !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null; !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); @@ -1312,15 +1287,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const options = ContextMenu.Instance.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode && - optionItems.push({ description: (this.showTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this.showTimeline = !this.showTimeline), icon: "eye" }); - this.props.ContainingCollectionView && - optionItems.push({ description: "Move Items Out of Collection", event: this.promoteCollection, icon: "table" }); + optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" }); optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" }); this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" }); if (!Doc.UserDoc().noviceMode) { optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" }); - } !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); const mores = ContextMenu.Instance.findByDescription("More..."); @@ -1349,11 +1321,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); doc.x = xx, doc.y = yy; this.props.addDocument?.(doc); - setTimeout(() => { + setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { docs.docs.forEach(d => LinkManager.Instance.addLink(d)); - }); - }, 2000); // need to give solr some time to update so that this query will find any link docs we've added. + }), 2000); // need to give solr some time to update so that this query will find any link docs we've added. } } } @@ -1361,57 +1332,38 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P input.click(); } - - @observable showTimeline = false; - - intersectRect(r1: { left: number, top: number, width: number, height: number }, - r2: { left: number, top: number, width: number, height: number }) { - return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top); - } - @action setupDragLines = (snapToDraggedDoc: boolean = false) => { const activeDocs = this.getActiveDocuments(); - if (activeDocs.length > 50) { - DragManager.SetSnapLines([], []); - return; - } const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight()); const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] }; const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) }); - const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => { - if (this.intersectRect(docDims(doc), rect)) { - snappableDocs.push(doc); - } - }; - const snappableDocs: Doc[] = []; // the set of documents in the visible viewport that we will try to snap to; + const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect); + const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) }; - this.getActiveDocuments().filter(doc => !Cast(doc.layers, listSpec("string"), []).includes("background") && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to - !snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z === undefined).map(doc => isDocInView(doc, selRect)); // if not, see if there are background docs to snap to - !snappableDocs.length && this.getActiveDocuments().filter(doc => doc.z !== undefined).map(doc => isDocInView(doc, otherBounds)); // if not, then why not snap to floating docs + let snappableDocs = activeDocs.filter(doc => !StrListCast(doc.layers).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to + !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to + !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs const horizLines: number[] = []; const vertLines: number[] = []; + const invXf = this.getTransform().inverse(); snappableDocs.filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => { const { left, top, width, height } = docDims(doc); - const topLeftInScreen = this.getTransform().inverse().transformPoint(left, top); - const docSize = this.getTransform().inverse().transformDirection(width, height); + const topLeftInScreen = invXf.transformPoint(left, top); + const docSize = invXf.transformDirection(width, height); horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]);// right line }); DragManager.SetSnapLines(horizLines, vertLines); } + onPointerOver = (e: React.PointerEvent) => { - if (SnappingManager.GetIsDragging()) { - this.setupDragLines(e.ctrlKey || e.shiftKey); - } + (DocumentDecorations.Instance.Interacting || (this.props.layerProvider?.(this.props.Document) !== false && SnappingManager.GetIsDragging())) && this.setupDragLines(e.ctrlKey || e.shiftKey); e.stopPropagation(); } - @observable private _hLines: number[] | undefined; - @observable private _vLines: number[] | undefined; - private childViews = () => { const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : []; return [ @@ -1426,13 +1378,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P eles.push(<CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />); return eles; } + @computed get placeholder() { return <div className="collectionfreeformview-placeholder" style={{ background: this.Document.backgroundColor }}> <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span> </div>; } - _nudgeTime = 0; nudge = action((x: number, y: number) => { if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out... @@ -1450,7 +1402,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10); } - @computed get grid() { + @computed get backgroundGrid() { const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-spacing"], 50)); const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.panX() % gridSpace - gridSpace) * this.zoomScaling(); const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.panY() % gridSpace - gridSpace) * this.zoomScaling(); @@ -1482,20 +1434,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } }} />; } + trySelectCluster = (addToSel: boolean) => { if (this._hitCluster !== -1) { - if (!addToSel) { - SelectionManager.DeselectAll(); - } + !addToSel && SelectionManager.DeselectAll(); const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === this._hitCluster); this.selectDocuments(eles); return true; } return false; } + @computed get marqueeView() { return <MarqueeView {...this.props} + ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined} nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge} addDocTab={this.addDocTab} trySelectCluster={this.trySelectCluster} @@ -1507,7 +1460,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}> <div ref={this._marqueeRef}> - {this.layoutDoc["_backgroundGrid-show"] ? this.grid : (null)} + {this.layoutDoc["_backgroundGrid-show"] && (!SnappingManager.GetIsDragging() || !Doc.UserDoc().showSnapLines) ? this.backgroundGrid : (null)} <CollectionFreeFormViewPannableContents isAnnotationOverlay={this.isAnnotationOverlay} centeringShiftX={this.centeringShiftX} @@ -1521,15 +1474,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P {this.children} </CollectionFreeFormViewPannableContents> </div> - {this.showTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)} + {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)} </MarqueeView>; } - @computed get contentScaling() { - if (this.props.annotationsKey && !this.props.forceScaling) return 0; - const nw = returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.Document)); - const nh = returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.Document)); + if (this.props.isAnnotationOverlay && !this.props.forceScaling) return 0; + const nw = this.nativeWidth; + const nh = this.nativeHeight; const hscale = nh ? this.props.PanelHeight() / nh : 1; const wscale = nw ? this.props.PanelWidth() / nw : 1; return wscale < hscale ? wscale : hscale; @@ -1555,7 +1507,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P width: this.contentScaling ? `${100 / this.contentScaling}%` : "", height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}> - {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? + {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} {!this.props.noOverlay ? <CollectionFreeFormOverlayView elements={this.elementFunc} /> : (null)} @@ -1576,6 +1528,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)} </svg> </div>} + + {this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ? + <div className="collectionFreeForm-groupDropper" ref={this.createDashEventsTarget} style={{ + width: this.ChildDrag ? "10000" : "100%", + height: this.ChildDrag ? "10000" : "100%", + left: this.ChildDrag ? "-5000" : 0, + top: this.ChildDrag ? "-5000" : 0, + position: "absolute", + background: "#0009930", + pointerEvents: "all" + }} /> : (null)} </div >; } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index a963b9158..d20d1abfc 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -8,7 +8,7 @@ import { RichTextField } from "../../../../fields/RichTextField"; import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; import { GetEffectiveAcl } from "../../../../fields/util"; -import { Utils, intersectRect } from "../../../../Utils"; +import { Utils, intersectRect, returnFalse } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; @@ -27,6 +27,7 @@ import { CollectionView } from "../CollectionView"; import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu"; import "./MarqueeView.scss"; import React = require("react"); +import { StyleLayers } from "../../StyleProvider"; interface MarqueeViewProps { getContainerTransform: () => Transform; @@ -37,6 +38,7 @@ interface MarqueeViewProps { isSelected: () => boolean; trySelectCluster: (addToSel: boolean) => boolean; nudge?: (x: number, y: number) => boolean; + ungroup?: () => void; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } @observer @@ -92,8 +94,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque cm.displayMenu(this._downX, this._downY); e.stopPropagation(); } else - if (e.key === ":") { - DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument, x, y); + if (e.key === "u" && this.props.ungroup) { + e.stopPropagation(); + this.props.ungroup(); + } + else if (e.key === ":") { + DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y); cm.displayMenu(this._downX, this._downY); e.stopPropagation(); @@ -121,7 +127,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque ns.map(line => { const indent = line.search(/\S|$/); const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line }); - this.props.addDocument(newBox); + this.props.addDocument?.(newBox); ypos += 40 * this.Transform.Scale; }); })(); @@ -143,11 +149,11 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque slide.x = x; slide.y = y; FormattedTextBox.SelectOnLoad = slide[Id]; - this.props.addDocument(slide); + this.props.addDocument?.(slide); //setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false)); e.stopPropagation(); - } else if (!e.ctrlKey && !e.metaKey && SelectionManager.SelectedDocuments().length < 2) { - FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.ChildLayoutString ? e.key : ""; + } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) { + FormattedTextBox.SelectOnLoadChar = FormattedTextBox.DefaultLayout && !this.props.childLayoutString ? e.key : ""; FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch"); this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0)); e.stopPropagation(); @@ -191,7 +197,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", _width: 300, _height: 100 }); - this.props.addDocument(newCol); + this.props.addDocument?.(newCol); } } @@ -333,25 +339,25 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque delete = () => { const selected = this.marqueeSelect(false); SelectionManager.DeselectAll(); - selected.forEach(doc => this.props.removeDocument(doc)); + selected.forEach(doc => this.props.removeDocument?.(doc)); this.cleanupInteractions(false); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); } - getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[]) => { + getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[], makeGroup: Opt<boolean>) => { const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => { Doc.GetProto(doc).data = new List<Doc>(selected); - Doc.GetProto(doc).title = "nested freeform"; + Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform"; doc._panX = doc._panY = 0; return doc; })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true)); newCollection.system = undefined; newCollection.layers = new List<string>(layers); - newCollection.backgroundColor = this.props.isAnnotationOverlay ? "#00000015" : layers.includes("background") ? "cyan" : undefined; newCollection._width = this.Bounds.width; newCollection._height = this.Bounds.height; + newCollection._isGroup = makeGroup; newCollection.x = this.Bounds.left; newCollection.y = this.Bounds.top; selected.forEach(d => d.context = newCollection); @@ -363,9 +369,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => { const selected = this.marqueeSelect(false); SelectionManager.DeselectAll(); - selected.forEach(d => this.props.removeDocument(d)); + selected.forEach(d => this.props.removeDocument?.(d)); const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2); - this.props.addDocument(newCollection!); + this.props.addDocument?.(newCollection!); this.props.selectDocuments([newCollection!]); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); @@ -410,9 +416,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @undoBatch @action - collection = (e: KeyboardEvent | React.PointerEvent | undefined) => { + collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => { const selected = this.marqueeSelect(false); - if (e instanceof KeyboardEvent ? e.key === "c" : true) { + if (e instanceof KeyboardEvent ? "cg".includes(e.key) : true) { selected.map(action(d => { const dx = NumCast(d.x); const dy = NumCast(d.y); @@ -424,10 +430,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque d.y = dy - this.Bounds.top - this.Bounds.height / 2; return d; })); - this.props.removeDocument(selected); + this.props.removeDocument?.(selected); } - const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, []); - this.props.addDocument(newCollection); + const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, [], group); + this.props.addDocument?.(newCollection); this.props.selectDocuments([newCollection]); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); @@ -494,7 +500,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque // } const lines = results.filter((r: any) => r.category === "line"); const text = lines.map((l: any) => l.recognizedText).join("\r\n"); - this.props.addDocument(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text })); + this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text })); }); } } @@ -503,7 +509,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @action summary = (e: KeyboardEvent | React.PointerEvent | undefined) => { const selected = this.marqueeSelect(false).map(d => { - this.props.removeDocument(d); + this.props.removeDocument?.(d); d.x = NumCast(d.x) - this.Bounds.left; d.y = NumCast(d.y) - this.Bounds.top; return d; @@ -523,8 +529,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque @action background = (e: KeyboardEvent | React.PointerEvent | undefined) => { - const newCollection = this.getCollection([], undefined, ["background"]); - this.props.addDocument(newCollection); + const newCollection = this.getCollection([], undefined, [StyleLayers.Background], undefined); + this.props.addDocument?.(newCollection); MarqueeOptionsMenu.Instance.fadeOut(true); this.hideMarquee(); setTimeout(() => this.props.selectDocuments([newCollection])); @@ -542,11 +548,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque this.delete(); e.stopPropagation(); } - if ("cbtsSp".indexOf(e.key) !== -1) { + if ("cbtsSpg".indexOf(e.key) !== -1) { this._commandExecuted = true; e.stopPropagation(); e.preventDefault(); (e as any).propagationIsStopped = true; + if (e.key === "g") this.collection(e, true); if (e.key === "c" || e.key === "t") this.collection(e); if (e.key === "s" || e.key === "S") this.summary(e); if (e.key === "b") this.background(e); @@ -634,7 +641,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque render() { return <div className="marqueeView" - style={{ overflow: !this.props.ContainingCollectionView && this.props.annotationsKey ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }} + style={{ overflow: (!this.props.ContainingCollectionView && this.props.isAnnotationOverlay) ? "visible" : StrCast(this.props.Document._overflow), cursor: MarqueeView.DragMarquee && this ? "crosshair" : "hand" }} onDragOver={e => e.preventDefault()} onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}> {this._visible ? this.marqueeDiv : null} diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index f3e385746..58db080ad 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -6,7 +6,7 @@ import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from '../../../../fields/FieldSymbols'; import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { SnappingManager } from '../../../util/SnappingManager'; @@ -14,7 +14,7 @@ import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; -import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { CollectionSubView } from '../CollectionSubView'; import "./CollectionGridView.scss"; @@ -161,17 +161,13 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { * @returns the `ContentFittingDocumentView` of the node */ getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { - return <ContentFittingDocumentView + return <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} Document={layout} DataDoc={layout.resolvedDataDoc as Doc} - styleProvider={this.props.styleProvider} - ContainingCollectionDoc={this.props.Document} PanelWidth={width} PanelHeight={height} - ContentScaling={returnOne} - FreezeDimensions={true} - fitToBox={true} + freezeDimensions={true} ScreenToLocalTransform={dxf} onClick={this.onChildClickHandler} renderDepth={this.props.renderDepth + 1} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 7ce269c92..85013b960 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -3,18 +3,18 @@ import { observer } from 'mobx-react'; import * as React from "react"; import { Doc } from '../../../../fields/Doc'; import { documentSchema } from '../../../../fields/documentSchemas'; +import { List } from '../../../../fields/List'; import { makeInterface } from '../../../../fields/Schema'; -import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../fields/Types'; +import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { returnFalse } from '../../../../Utils'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; -import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; +import { DocumentView } from '../../nodes/DocumentView'; import { CollectionSubView } from '../CollectionSubView'; import "./CollectionMulticolumnView.scss"; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; -import { List } from '../../../../fields/List'; -import { returnZero, returnFalse, returnOne } from '../../../../Utils'; type MulticolumnDocument = makeInterface<[typeof documentSchema]>; const MulticolumnDocument = makeInterface(documentSchema); @@ -213,17 +213,16 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu return this.props.addDocTab(doc, where); } getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { - return <ContentFittingDocumentView + return <DocumentView Document={layout} DataDoc={layout.resolvedDataDoc as Doc} styleProvider={this.props.styleProvider} - LayoutTemplate={this.props.ChildLayoutTemplate} - LayoutTemplateString={this.props.ChildLayoutString} - FreezeDimensions={this.props.freezeChildDimensions} + LayoutTemplate={this.props.childLayoutTemplate} + LayoutTemplateString={this.props.childLayoutString} + freezeDimensions={this.props.childFreezeDimensions} renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} - fitToBox={false} rootSelected={this.rootSelected} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} onClick={this.onChildClickHandler} @@ -243,7 +242,6 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu addDocTab={this.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} - ContentScaling={returnOne} />; } /** diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 7700ea128..4f5c8af95 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -1,20 +1,20 @@ +import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; -import { makeInterface } from '../../../../fields/Schema'; -import { documentSchema } from '../../../../fields/documentSchemas'; -import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import * as React from "react"; import { Doc } from '../../../../fields/Doc'; -import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../fields/Types'; -import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; -import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils'; -import "./CollectionMultirowView.scss"; -import { computed, trace, observable, action } from 'mobx'; +import { documentSchema } from '../../../../fields/documentSchemas'; +import { List } from '../../../../fields/List'; +import { makeInterface } from '../../../../fields/Schema'; +import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { returnFalse } from '../../../../Utils'; +import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; +import { undoBatch } from '../../../util/UndoManager'; +import { DocumentView } from '../../nodes/DocumentView'; +import { CollectionSubView } from '../CollectionSubView'; +import "./CollectionMultirowView.scss"; import HeightLabel from './MultirowHeightLabel'; import ResizeBar from './MultirowResizer'; -import { undoBatch } from '../../../util/UndoManager'; -import { DragManager, dropActionType } from '../../../util/DragManager'; -import { List } from '../../../../fields/List'; type MultirowDocument = makeInterface<[typeof documentSchema]>; const MultirowDocument = makeInterface(documentSchema); @@ -213,17 +213,16 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) return this.props.addDocTab(doc, where); } getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) { - return <ContentFittingDocumentView + return <DocumentView Document={layout} DataDoc={layout.resolvedDataDoc as Doc} styleProvider={this.props.styleProvider} - LayoutTemplate={this.props.ChildLayoutTemplate} - LayoutTemplateString={this.props.ChildLayoutString} - FreezeDimensions={this.props.freezeChildDimensions} + LayoutTemplate={this.props.childLayoutTemplate} + LayoutTemplateString={this.props.childLayoutString} + freezeDimensions={this.props.childFreezeDimensions} renderDepth={this.props.renderDepth + 1} PanelWidth={width} PanelHeight={height} - fitToBox={false} rootSelected={this.rootSelected} dropAction={StrCast(this.props.Document.childDropAction) as dropActionType} onClick={this.onChildClickHandler} @@ -243,7 +242,6 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) addDocTab={this.addDocTab} pinToPres={this.props.pinToPres} bringToFront={returnFalse} - ContentScaling={returnOne} />; } /** |
