diff options
| author | geireann <60007097+geireann@users.noreply.github.com> | 2020-08-24 18:47:33 +0800 |
|---|---|---|
| committer | geireann <60007097+geireann@users.noreply.github.com> | 2020-08-24 18:47:33 +0800 |
| commit | 31fac41cb8a3bd19b02dddc116b11c962f3339d3 (patch) | |
| tree | 7d3b0bcf04318d479158915f5ee7fc8a30ef580c /src/client/views/collections | |
| parent | 78efe1087488265da4ea37373a2a9a22a7f8cf10 (diff) | |
| parent | a9e08e0504e8002bc5d991b6a13777577ddd8f9f (diff) | |
Merge branch 'master' into presentation_updates
Diffstat (limited to 'src/client/views/collections')
22 files changed, 309 insertions, 360 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 761d88989..f1959460c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,44 +1,43 @@ import 'golden-layout/src/css/goldenlayout-base.css'; import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, computed, Lambda, observable, reaction, runInAction, trace, IReactionDisposer } from "mobx"; +import { clamp } from 'lodash'; +import { action, computed, IReactionDisposer, Lambda, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import * as GoldenLayout from "../../../client/goldenLayout"; import { DateField } from '../../../fields/DateField'; -import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc"; +import { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; +import { InkTool } from '../../../fields/InkField'; +import { List } from '../../../fields/List'; import { FieldId } from "../../../fields/RefField"; +import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnOne, returnTrue, Utils, returnZero, returnEmptyFilter, setupMoveUpEvents, returnFalse, emptyPath, aggregateBounds } from "../../../Utils"; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from "../../util/DragManager"; +import { InteractionUtils } from '../../util/InteractionUtils'; import { Scripting } from '../../util/Scripting'; import { SelectionManager } from '../../util/SelectionManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from "../../util/UndoManager"; import { MainView } from '../MainView'; import { DocumentView } from "../nodes/DocumentView"; +import { PresBox } from '../nodes/PresBox'; import "./CollectionDockingView.scss"; -import { SubCollectionViewProps } from "./CollectionSubView"; +import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; +import { SubCollectionViewProps, CollectionSubView } from "./CollectionSubView"; +import { CollectionViewType } from './CollectionView'; import { DockingViewButtonSelector } from './ParentDocumentSelector'; import React = require("react"); -import { CollectionViewType } from './CollectionView'; -import { SnappingManager } from '../../util/SnappingManager'; -import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; -import { listSpec } from '../../../fields/Schema'; -import { clamp } from 'lodash'; -import { PresBox } from '../nodes/PresBox'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { InteractionUtils } from '../../util/InteractionUtils'; -import { InkTool } from '../../../fields/InkField'; -import { Select } from '@material-ui/core'; const _global = (window /* browser */ || global /* node */) as any; @observer -export class CollectionDockingView extends React.Component<SubCollectionViewProps> { +export class CollectionDockingView extends CollectionSubView(doc => doc) { @observable public static Instances: CollectionDockingView[] = []; @computed public static get Instance() { return CollectionDockingView.Instances[0]; } public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) { @@ -74,22 +73,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp DragManager.StartWindowDrag = this.StartOtherDrag; } public StartOtherDrag = (e: any, dragDocs: Doc[]) => { - let config: any; - if (dragDocs.length === 1) { - config = CollectionDockingView.makeDocumentConfig(dragDocs[0]); - } else { - config = { + const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : + { type: 'row', - content: dragDocs.map((doc, i) => { - CollectionDockingView.makeDocumentConfig(doc); - }) + content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc)) }; - } - const div = document.createElement("div"); - const dragSource = this._goldenLayout.createDragSource(div, config); - dragSource._dragListener.on("dragStop", () => { - dragSource.destroy(); - }); + const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config); + dragSource._dragListener.on("dragStop", () => dragSource.destroy()); dragSource._dragListener.onMouseDown(e); } @@ -97,7 +87,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp @action public OpenFullScreen(docView: DocumentView, libraryPath?: Doc[]) { if (docView.props.Document._viewType === CollectionViewType.Docking && docView.props.Document.layoutKey === "layout") { - return MainView.Instance.openWorkspace(docView.props.Document); + return MainView.Instance.openDashboard(docView.props.Document); } const document = Doc.MakeAlias(docView.props.Document); const newItemStackConfig = { @@ -155,7 +145,6 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp this._goldenLayout.root.callDownwards('setSize', [this._goldenLayout.width, this._goldenLayout.height]); this._goldenLayout.emit('stateChanged'); this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); - if (removed) CollectionDockingView.Instance._removedDocs.push(removed); this.stateChanged(); } @undoBatch @@ -411,8 +400,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp // Because this is in a set timeout, if this component unmounts right after mounting, // we will leak a GoldenLayout, because we try to destroy it before we ever create it setTimeout(() => this.setupGoldenLayout(), 1); - DocListCast((Doc.UserDoc().myWorkspaces as Doc).data).map(d => d.workspaceBrush = false); - this.props.Document.workspaceBrush = true; + DocListCast((Doc.UserDoc().myDashboards as Doc).data).map(d => d.dashboardBrush = false); + this.props.Document.dashboardBrush = true; } this._ignoreStateChange = ""; }, { fireImmediately: true }); @@ -422,7 +411,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } componentWillUnmount: () => void = () => { try { - this.props.Document.workspaceBrush = false; + this.props.Document.dashboardBrush = false; this._goldenLayout.unbind('itemDropped', this.itemDropped); this._goldenLayout.unbind('tabCreated', this.tabCreated); this._goldenLayout.unbind('stackCreated', this.stackCreated); @@ -442,9 +431,8 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp @action onResize = (event: any) => { const cur = this._containerRef.current; - // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed - this._goldenLayout?.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); + cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height); } @action @@ -480,11 +468,38 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")); - if (docids) { - const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc); - docs.map(doc => Doc.AddDocToList(Doc.GetProto(this.props.Document), this.props.fieldKey, doc)); - // Doc.GetProto(this.props.Document)[this.props.fieldKey] = new List<Doc>(docs); - } + const docs = !docids ? [] : (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc); + const sublists = DocListCast(this.props.Document[this.props.fieldKey]); + const tabs = Cast(sublists[0], Doc, null); + const other = Cast(sublists[1], Doc, null); + const tabdocs = DocListCast(tabs.data); + const otherdocs = DocListCast(other.data); + Doc.GetProto(tabs).data = new List<Doc>(docs); + const otherSet = new Set<Doc>(); + otherdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); + tabdocs.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); + Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values())); + } + + public static async Copy(doc: Doc) { + let json = StrCast(doc.dockingConfig); + const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); + const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || []; + const docs = (await Promise.all(docids.map(id => DocServer.GetRefField(id)))).filter(f => f).map(f => f as Doc); + const newtabs = docs.map(doc => { + const copy = Doc.MakeAlias(doc); + json = json.replace(doc[Id], copy[Id]); + return copy; + }); + const copy = Docs.Create.DockDocument(newtabs, json, { title: "Snapshot: " + doc.title }); + const docsublists = DocListCast(doc.data); + const copysublists = DocListCast(copy.data); + const docother = Cast(docsublists[1], Doc, null); + const copyother = Cast(copysublists[1], Doc, null); + const newother = DocListCast(docother.data).map(doc => Doc.MakeAlias(doc)); + Doc.GetProto(copyother).data = new List<Doc>(newother); + + return copy; } @undoBatch @@ -558,6 +573,12 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp }, returnFalse, emptyFunction); }; + tab.selectionDisposer = reaction(() => SelectionManager.SelectedDocuments(), + (sel) => { + const selected = sel.some(v => v.props.Document === doc); + selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && tab.header.parent.setActiveContentItem(tab.contentItem); + } + ); tab.buttonDisposer = reaction(() => ((view: Opt<DocumentView>) => view ? [view] : [])(DocumentManager.Instance.getDocumentView(doc)), (views) => { if (views.length) { @@ -582,14 +603,14 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } tab.closeElement.off('click') //unbind the current click handler .click(async function () { + tab.selectionDisposer?.(); tab.reactionDisposer?.(); tab.buttonDisposer?.(); const doc = await DocServer.GetRefField(tab.contentItem.config.props.documentId); if (doc instanceof Doc) { const theDoc = doc; - CollectionDockingView.Instance._removedDocs.push(theDoc); - const recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc); + const recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc); if (recent) { Doc.AddDocToList(recent, "data", doc, undefined, true, true); } @@ -608,13 +629,16 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } } } - _removedDocs: Doc[] = []; stackCreated = (stack: any) => { //stack.header.controlsContainer.find('.lm_popout').hide(); stack.header.element.on('mousedown', (e: any) => { if (e.target === stack.header.element[0] && e.button === 1) { - this.AddTab(stack, Docs.Create.FreeformDocument([], { _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: "Untitled Collection" })); + const emptyPane = Cast(Doc.UserDoc().emptyPane, Doc, null); + emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + this.AddTab(stack, Docs.Create.FreeformDocument([], { + _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}` + })); } }); @@ -649,11 +673,10 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp const doc = await DocServer.GetRefField(contentItem.config.props.documentId); if (doc instanceof Doc) { let recent: Doc | undefined; - if (recent = await Cast(Doc.UserDoc().myRecentlyClosed, Doc)) { + if (recent = await Cast(Doc.UserDoc().myInactiveDocs, Doc)) { Doc.AddDocToList(recent, "data", doc, undefined, true, true); } const theDoc = doc; - CollectionDockingView.Instance._removedDocs.push(theDoc); } }); //} @@ -669,7 +692,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp render() { if (this.props.renderDepth > 0) { - return <div style={{ width: "100%", height: "100%" }}>Nested workspaces can't be rendered</div>; + return <div style={{ width: "100%", height: "100%" }}>Nested dashboards can't be rendered</div>; } return <div className="collectiondockingview-container" id="menuContainer" onPointerDown={this.onPointerDown} onPointerUp={this.onPointerUp} ref={this._containerRef} />; @@ -755,35 +778,32 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { } componentDidMount() { + const color = () => StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white"); + const selected = () => SelectionManager.SelectedDocuments().some(v => Doc.AreProtosEqual(v.props.Document, this._document)); + const updateTabColor = () => this._tab && (this._tab.style.backgroundColor = selected() ? color() : ""); const observer = new _global.ResizeObserver(action((entries: any) => { for (const entry of entries) { this._panelWidth = entry.contentRect.width; this._panelHeight = entry.contentRect.height; } + updateTabColor(); })); observer.observe(this.props.glContainer._element[0]); this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); - this.props.glContainer.on("tab", this.onActiveContentItemChanged); - this.onActiveContentItemChanged(); - this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: StrCast(this._document?._backgroundColor, this._document && CollectionDockingView.Instance?.props.backgroundColor?.(this._document, 0) || "white") }), - (data) => { - const selected = data.views.some(v => Doc.AreProtosEqual(v.props.Document, this._document)); - this._tab && (this._tab.style.backgroundColor = selected ? data.color : ""); - } - ); + this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(); + this._tabReaction = reaction(() => ({ views: SelectionManager.SelectedDocuments(), color: color() }), () => updateTabColor(), { fireImmediately: true }); } componentWillUnmount() { this._tabReaction?.(); this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); - this.props.glContainer.off("tab", this.onActiveContentItemChanged); } @action.bound private onActiveContentItemChanged() { - if (this.props.glContainer.tab) { + if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) { this._isActive = this.props.glContainer.tab.isActive; - setTimeout(() => { + this._isActive && setTimeout(() => { const dv = this._document && DocumentManager.Instance.getFirstDocumentView(this._document); dv && SelectionManager.SelectDoc(dv, false); }); @@ -833,12 +853,12 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { return Transform.Identity(); } get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } - get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}%` : undefined; } + get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this._panelWidth * 100}% ` : undefined; } addDocTab = (doc: Doc, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { - return MainView.Instance.openWorkspace(doc); + return MainView.Instance.openDashboard(doc); } else if (location === "onRight") { return CollectionDockingView.AddRightSplit(doc, libraryPath); } else if (location === "close") { @@ -914,15 +934,16 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { backgroundColor={CollectionDockingView.Instance.props.backgroundColor} addDocTab={this.addDocTab} pinToPres={DockedFrameRenderer.PinDoc} - docFilters={returnEmptyFilter} + docFilters={CollectionDockingView.Instance.docFilters} + searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} fitToBox={true} /> <div className="miniOverlay" onPointerDown={this.miniDown} > <div className="miniThumb" style={{ - width: `${this.miniWidth}%`, - height: `${this.miniHeight}%`, - left: `${this.miniLeft}%`, - top: `${this.miniTop}%`, + width: `${this.miniWidth}% `, + height: `${this.miniHeight}% `, + left: `${this.miniLeft}% `, + top: `${this.miniTop}% `, }} /> </div> @@ -955,7 +976,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> { backgroundColor={CollectionDockingView.Instance.props.backgroundColor} addDocTab={this.addDocTab} pinToPres={DockedFrameRenderer.PinDoc} - docFilters={returnEmptyFilter} + docFilters={CollectionDockingView.Instance.docFilters} + searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index e1b07077e..866d7245a 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -89,6 +89,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } } DocumentLinksButton.StartLink = undefined; + DocumentLinksButton.StartLinkView = undefined; } @action @@ -166,6 +167,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={this.props.docFilters} + searchFilterDocs={this.props.searchFilterDocs} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> </div>; diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index c772dcfe7..1c96f69bf 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -140,7 +140,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr addDocument = (value: string, shiftDown?: boolean) => { this._createAliasSelected = false; const key = StrCast(this.props.parent.props.Document._pivotField); - const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _width: 200, title: value }); + const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value }); newDoc[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); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 388eda2b3..00415dab1 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -131,7 +131,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp _contentCommand = { params: ["target", "source"], title: "set content", script: "getProto(self.target).data = copyField(self.source);", - immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)), // Doc.aliasDocs(source), + immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)), initialize: emptyFunction, }; _onClickCommand = { @@ -180,12 +180,16 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp }; _saveFilterCommand = { params: ["target"], title: "save filter", - script: "self.target._docFilters = copyField(self['target-docFilters']);", - immediate: undoBatch((source: Doc[]) => this.target._docFilters = undefined), - initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; }, + script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']); + self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`, + immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }), + initialize: (button: Doc) => { + button['target-docFilters'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters as any as ObjectField) : undefined; + button['target-searchFilterDocs'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs as any as ObjectField) : undefined; + }, }; - @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } + @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index ea786800c..e37644f2f 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -3,7 +3,7 @@ import { action, observable, trace, computed, runInAction } from "mobx"; import { observer } from "mobx-react"; import { CellInfo } from "react-table"; import "react-table/react-table.css"; -import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath } from "../../../Utils"; +import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath, returnEmptyDoclist } from "../../../Utils"; import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { KeyCodes } from "../../util/KeyCodes"; @@ -199,6 +199,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { rootSelected: returnFalse, fieldKey: this.props.rowProps.column.id as string, docFilters: returnEmptyFilter, + searchFilterDocs: returnEmptyDoclist, ContainingCollectionView: this.props.CollectionView, ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document, isSelected: returnFalse, @@ -229,7 +230,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc); const onItemDown = async (e: React.PointerEvent) => { - if (this.props.Document._searchDoc !== undefined) { + if (this.props.Document._searchDoc) { const doc = Doc.GetProto(this.props.rowProps.original); const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc); let targetContext = undefined; @@ -315,7 +316,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { } } let search = false; - if (this.props.Document._searchDoc !== undefined) { + if (this.props.Document._searchDoc) { search = true; } @@ -511,7 +512,8 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell { addDocTab: this.props.addDocTab, pinToPres: this.props.pinToPres, ContentScaling: returnOne, - docFilters: returnEmptyFilter + docFilters: returnEmptyFilter, + searchFilterDocs: returnEmptyDoclist, }; @observable private _field = this.prop.Document[this.prop.fieldKey]; @observable private _doc = FieldValue(Cast(this._field, Doc)); @@ -691,7 +693,8 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell { addDocTab: this.props.addDocTab, pinToPres: this.props.pinToPres, ContentScaling: returnOne, - docFilters: returnEmptyFilter + docFilters: returnEmptyFilter, + searchFilterDocs: returnEmptyDoclist, }; let image = true; @@ -770,7 +773,8 @@ export class CollectionSchemaListCell extends CollectionSchemaCell { addDocTab: this.props.addDocTab, pinToPres: this.props.pinToPres, ContentScaling: returnOne, - docFilters: returnEmptyFilter + docFilters: returnEmptyFilter, + searchFilterDocs: returnEmptyDoclist, }; @observable private _field = this.prop.Document[this.prop.fieldKey]; @observable private _optionsList = this._field as List<any>; @@ -894,26 +898,22 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { export class CollectionSchemaButtons extends CollectionSchemaCell { render() { const doc = this.props.rowProps.original; - const searchMatch = (backward: boolean = true) => { doc.searchMatch = undefined; setTimeout(() => doc.searchMatch = backward, 0); }; + const searchMatch = (backward: boolean = true) => Doc.SearchMatchNext(doc, backward); // const reference = React.createRef<HTMLDivElement>(); // const onItemDown = (e: React.PointerEvent) => { // (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined : // SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e)); // }; - return !BoolCast(this.props.Document._searchDoc) ? <></> - : StrCast(doc.type) === DocumentType.PDF ? - <button style={{ position: "relative", height: 30, width: 28, left: 1, }} onClick={() => searchMatch(false)}> - <FontAwesomeIcon icon="arrow-down" size="sm" /> - </button> - : StrCast(doc.type) === DocumentType.RTF ? - <div style={{ paddingTop: 8, paddingLeft: 3, }} > - <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}> - <FontAwesomeIcon icon="arrow-up" size="sm" /> - </button> - <button style={{ padding: 2 }} onClick={() => searchMatch(false)} > - <FontAwesomeIcon icon="arrow-down" size="sm" /> - </button> - </div> : - <></>; + return !this.props.Document._searchDoc ? <></> + : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ? + <div style={{ paddingTop: 8, paddingLeft: 3, }} > + <button style={{ padding: 2, left: 77 }} onClick={() => searchMatch(true)}> + <FontAwesomeIcon icon="arrow-up" size="sm" /> + </button> + <button style={{ padding: 2 }} onClick={() => searchMatch(false)} > + <FontAwesomeIcon icon="arrow-down" size="sm" /> + </button> + </div> : + <></>; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index 34a8bfa24..03a213002 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -287,12 +287,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { const colpos = this._searchTerm.indexOf(":"); const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); if (temp === "") { - console.log("here we are first"); Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); this.updateFilter(); } else { - console.log("here we are first"); Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); this.tempfilter = temp; Doc.setDocFilter(this.props.Document, this._key, temp, "check"); @@ -304,8 +302,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); this.updateFilter(); let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); - const blockedkeys = ["system", "ACL-Public", "_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"]; - keyOptions = keyOptions.filter(n => !blockedkeys.includes(n)); + const blockedkeys = ["system", "title-custom", "limitHeight", "proto", "x", "y", "zIndex", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"]; + keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL")); if (keyOptions.length) { this.onSelect(keyOptions[0]); } else if (this._searchTerm !== "" && this.props.canAddNew) { @@ -338,8 +336,8 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 || this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1; - const blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"]; - keyOptions = keyOptions.filter(n => !blockedkeys.includes(n)); + const blockedkeys = ["proto", "x", "y", "zIndex", "_timeStampOnEnter", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "layout", "layout_keyValue", "links"]; + keyOptions = keyOptions.filter(n => !blockedkeys.includes(n) && !n.startsWith("_") && !n.startsWith("ACL")); const options = keyOptions.map(key => { return <div key={key} className="key-option" style={{ @@ -498,11 +496,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { } }); - keyOptions.forEach(key => { - Doc.setDocFilter(this.props.Document, this._key, key, undefined); - } - ); - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); + Doc.setDocFilter(this.props.Document, this._key, "", "remove"); this.props.col.setColor("rgb(241, 239, 235)"); this.closeResultsVisibility = "none"; } @@ -518,13 +512,9 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}> <input className="keys-search" style={{ width: "100%" }} ref={this._inputRef} type="text" value={this._searchTerm} placeholder="Column key" onKeyDown={this.onKeyDown} - onChange={e => { - this.onChange(e.target.value); - }} - onClick={(e) => { - //this._inputRef.current!.select(); - e.stopPropagation(); - }} onFocus={this.onFocus} ></input> + onChange={e => this.onChange(e.target.value)} + onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }} + onFocus={this.onFocus} ></input> <div style={{ display: this.closeResultsVisibility }}> <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg" style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} /> diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index fc5dffaec..49d9a5c68 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -99,6 +99,9 @@ direction: ltr; overflow: visible; } + .rt-noData { + display: none; + } .rt-thead { width: 100%; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ed8496544..a78061da6 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -9,7 +9,7 @@ import { Resize } from "react-table"; import "react-table/react-table.css"; import { Doc, Opt } from "../../../fields/Doc"; import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; +import { listSpec, makeInterface, emptySchema } from "../../../fields/Schema"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { Cast, NumCast, BoolCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; @@ -73,7 +73,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get menuCoordinates() { let searchx = 0; let searchy = 0; - if (this.props.Document._searchDoc !== undefined) { + if (this.props.Document._searchDoc) { const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0]; if (el !== undefined) { const rect = el.getBoundingClientRect(); @@ -305,17 +305,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this.columns = columns; if (filter) { Doc.setDocFilter(this.props.Document, newKey, filter, "match"); - if (this.props.Document.selectedDoc !== undefined) { - const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc; - Doc.setDocFilter(doc, newKey, filter, "match"); - } } else { this.props.Document._docFilters = undefined; - if (this.props.Document.selectedDoc !== undefined) { - const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc; - doc._docFilters = undefined; - } } } } @@ -454,6 +446,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { PanelHeight={this.previewHeight} ScreenToLocalTransform={this.getPreviewTransform} docFilters={this.docFilters} + searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} @@ -592,7 +585,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } render() { let name = "collectionSchemaView-container"; - if (this.props.Document._searchDoc !== undefined) { + if (this.props.Document._searchDoc) { name = "collectionSchemaView-searchContainer"; } if (!this.props.active()) setTimeout(() => this.closeHeader(), 0); @@ -615,7 +608,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return <div className={name} style={{ overflow: this.props.overflow === true ? "scroll" : undefined, backgroundColor: "white", - pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined, + pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined, width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > <div className="collectionSchemaView-tableContainer" diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 241c64f9a..a50dab54d 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -217,6 +217,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) opacity={opacity} focus={this.focusDocument} docFilters={this.docFilters} + searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} addDocument={this.props.addDocument} @@ -354,7 +355,7 @@ 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()) { - Doc.Layout(doc)._height = Math.min(1200, Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", ""))))); + Doc.Layout(doc)._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", ""))))); } })); this.observer.observe(ref); @@ -489,7 +490,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) transformOrigin: "top left", }} onScroll={action(e => { - if (!this.props.isSelected() && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll; + if (!this.props.isSelected(true) && this.props.renderDepth) e.currentTarget.scrollTop = this._scroll; else this._scroll = e.currentTarget.scrollTop; })} onDrop={this.onExternalDrop.bind(this)} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index ede75fba8..0a206a6c6 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -136,7 +136,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC addDocument = (value: string, shiftDown?: boolean) => { if (!value) return false; const key = StrCast(this.props.parent.props.Document._pivotField); - const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true }); + const newDoc = Docs.Create.TextDocument(value, { _height: 18, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, title: value, _autoHeight: true }); newDoc[key] = this.getValue(this.props.heading); const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0); const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; @@ -269,7 +269,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" }); ContextMenu.Instance.setDefaultItem("::", (name: string): void => { Doc.GetProto(this.props.parent.props.Document)[name] = ""; - const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true }); + const created = Docs.Create.TextDocument("", { title: name, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 250, _autoHeight: true }); if (created) { if (this.props.parent.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 075be41fd..cb3f486bb 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -111,6 +111,9 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: return this.props.ignoreFields?.includes("_docFilters") ? [] : [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; } + searchFilterDocs = () => { + return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)]; + } @computed get childDocs() { let rawdocs: (Doc | Promise<Doc>)[] = []; if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; @@ -128,47 +131,32 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; - let searchDocs = DocListCast(this.props.Document._searchDocs); - + let searchDocs = this.searchFilterDocs(); let docsforFilter: Doc[] = childDocs; - if (searchDocs !== undefined && searchDocs.length > 0) { + if (searchDocs.length > 0) { + searchDocs = [...searchDocs, ...docs.filter(d => d.z)]; docsforFilter = []; const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); - console.log(searchDocs); searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript); - console.log(this.docFilters()); - console.log(searchDocs); childDocs.forEach((d) => { + let notFiltered = searchDocs.includes(d) || d.z; if (d.data !== undefined) { - let newdocs = DocListCast(d.data); - if (newdocs.length > 0) { - let displaycheck: boolean | undefined = undefined; + let subDocs = DocListCast(d.data); + if (subDocs.length > 0) { let newarray: Doc[] = []; - while (newdocs.length > 0) { + while (subDocs.length > 0 && !notFiltered) { newarray = []; - newdocs.forEach((t) => { - if (d.data !== undefined) { - const newdocs = DocListCast(t.data); - newdocs.forEach((newdoc) => { - newarray.push(newdoc); - }); - } - if (searchDocs.includes(t)) { - displaycheck = true; - } + subDocs.forEach((t) => { + notFiltered = notFiltered || searchDocs.includes(t); + DocListCast(t.data).forEach((newdoc) => newarray.push(newdoc)); }); - newdocs = newarray; - } - if (displaycheck === true) { - docsforFilter.push(d); + subDocs = newarray; } } } - if (searchDocs.includes(d)) { - docsforFilter.push(d); - } + notFiltered && docsforFilter.push(d); }); return docsforFilter; } @@ -296,7 +284,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: this.addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); } } else if (text) { - this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 })); + this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 25 })); } return; } @@ -472,7 +460,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: completed?.(); } else { if (text && !text.includes("https://")) { - UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop"); + UndoManager.RunInBatch(() => this.addDocument(Docs.Create.TextDocument(text, { ...options, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: text.substring(0, 20), _width: 400, _height: 315 })), "drop"); } } disposer(); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0f6274663..9460095e4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,14 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { PrefetchProxy } from '../../../fields/Proxy'; import { Document, listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter } from '../../../Utils'; +import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from '../../util/DocumentManager'; @@ -88,23 +88,25 @@ class TreeView extends React.Component<TreeViewProps> { get doc() { return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive - get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } + get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; } @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; } - @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); } + @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.doc[DataSym]; } @computed get layoutDoc() { return Doc.Layout(this.doc); } @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } childDocList(field: string) { const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; - return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field - (layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields - DocListCast(this.doc[field])); // otherwise use the document's data field + return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field + (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields + DocListCastOrNull(this.doc[field])); // otherwise use the document's data field } @computed get childDocs() { return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } @@ -357,6 +359,7 @@ class TreeView extends React.Component<TreeViewProps> { focus={returnFalse} ScreenToLocalTransform={this.docTransform} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionDoc={this.props.containingCollection} ContainingCollectionView={undefined} addDocument={returnFalse} @@ -422,11 +425,12 @@ class TreeView extends React.Component<TreeViewProps> { <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} onPointerDown={action(() => { if (this.treeViewOpen) { - this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : - this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : - this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : - (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); + this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : + this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : + this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : + this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : + (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : + this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> @@ -463,15 +467,16 @@ class TreeView extends React.Component<TreeViewProps> { bringToFront={emptyFunction} dontRegisterView={BoolCast(this.props.treeViewDoc.dontRegisterChildViews)} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={this.props.containingCollection} />; return <> <div className="docContainer" ref={this._tref} title="click to edit title" id={`docContainer-${this.props.parentKey}`} style={{ - fontWeight: this.doc.searchMatch !== undefined ? "bold" : undefined, + fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined, textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined, - outline: BoolCast(this.doc.workspaceBrush) ? "dashed 1px #06123232" : undefined, + outline: BoolCast(this.doc.dashboardBrush) ? "dashed 1px #06123232" : undefined, pointerEvents: this.props.active() || SnappingManager.GetIsDragging() ? undefined : "none" }} > {view} @@ -725,9 +730,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll } onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout - if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myWorkspaces) { - ContextMenu.Instance.addItem({ description: "Create Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); - ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.remove(this.doc), icon: "minus" }); + if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myDashboards) { + ContextMenu.Instance.addItem({ description: "Create Dashboard", event: () => MainView.Instance.createNewDashboard(), icon: "plus" }); + ContextMenu.Instance.addItem({ description: "Delete Dashboard", event: () => this.remove(this.doc), icon: "minus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 99ba1c706..4fdf1411d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -18,7 +18,7 @@ import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx, GetEffectiveAcl, SharingPermissions, distributeAcls } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; @@ -208,7 +208,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus const value = DocListCast(targetDataDoc[this.props.fieldKey]); const toRemove = value.filter(v => docs.includes(v)); if (toRemove.length !== 0) { - const recent = Cast(Doc.UserDoc().myRecentlyClosed, Doc) as Doc; + const recent = Cast(Doc.UserDoc().myInactiveDocs, Doc) as Doc; toRemove.forEach(doc => { Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc); recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); @@ -541,6 +541,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus fieldKey={`${this.props.fieldKey}-filter`} CollectionView={this} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionDoc={this.props.ContainingCollectionDoc} ContainingCollectionView={this.props.ContainingCollectionView} PanelWidth={this.facetWidth} @@ -592,7 +593,7 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus ChildLayoutString: this.childLayoutString, }; const boxShadow = Doc.UserDoc().renderStyle === "comic" || this.props.Document.isBackground || this.collectionViewType === CollectionViewType.Linear ? undefined : - `${Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`; + `${Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "rgb(30, 32, 31) " : "#9c9396 "} ${StrCast(this.props.Document.boxShadow, "0.2vw 0.2vw 0.8vw")}`; return (<div className={"collectionView"} onContextMenu={this.onContextMenu} style={{ pointerEvents: this.props.Document.isBackground ? "none" : undefined, boxShadow }}> {this.showIsTagged()} diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 6ec9783e2..4dafa4ac4 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -12,9 +12,8 @@ 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 } from "../../../Utils"; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } 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"; @@ -22,11 +21,12 @@ import { COLLECTION_BORDER_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 } from "./CollectionSchemaCells"; +import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell } from "./CollectionSchemaCells"; import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders"; import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC"; import "./CollectionSchemaView.scss"; import { CollectionView } from "./CollectionView"; +import { DocumentType } from "../../documents/DocumentTypes"; enum ColumnType { @@ -91,7 +91,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @observable _cellIsEditing: boolean = false; @observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 }; - @observable _openCollections: Array<string> = []; + @observable _openCollections: Set<number> = new Set; @observable _showDoc: Doc | undefined; @observable _showDataDoc: any = ""; @@ -136,16 +136,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @action changeSorting = (col: any) => { - if (col.desc === undefined) { - // no sorting - this.props.changeColumnSort(col, true); - } else if (col.desc === true) { - // descending sort - this.props.changeColumnSort(col, false); - } else if (col.desc === false) { - // ascending sort - this.props.changeColumnSort(col, undefined); - } + this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true); } @action @@ -153,7 +144,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column<Doc>[] { - const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); const columns: Column<Doc>[] = []; const tableIsFocused = this.props.isFocused(this.props.Document, false); @@ -161,27 +151,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> { const focusedCol = this._focusedCell.col; const isEditable = !this.props.headerIsEditing; - //if (this.childDocs.reduce((found, doc) => found || doc.type === DocumentType.COL, false)) { - columns.push( - { - expander: true, - Header: "", - width: 60, - Expander: (rowInfo) => { - if (rowInfo.original.type === "collection") { - return rowInfo.isExpanded ? - <div className="collectionSchemaView-expander" onClick={() => this.onCloseCollection(rowInfo.original)}><FontAwesomeIcon icon={"caret-down"} size="lg" /></div> : - <div className="collectionSchemaView-expander" onClick={() => this.onExpandCollection(rowInfo.original)}><FontAwesomeIcon icon={"caret-right"} size="lg" /></div>; - } else { - return null; - } - } + columns.push({ + expander: true, Header: "", width: 60, + Expander: (rowInfo) => { + return rowInfo.original.type !== DocumentType.COL ? (null) : + <div className="collectionSchemaView-expander" onClick={action(() => (this._openCollections[rowInfo.isExpanded ? "delete" : "add"])(rowInfo.viewIndex))}> + <FontAwesomeIcon icon={rowInfo.isExpanded ? "caret-down" : "caret-right"} size="lg" /> + </div>; } - ); - // } - this.props.active; - - const cols = this.props.columns.map(col => { + }); + columns.push(...this.props.columns.map(col => { const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" : this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" : this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" : @@ -209,25 +188,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> { width={"100%"} />; - - const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up"; - - const header = - <div //className="collectionSchemaView-header" - //onClick={e => this.props.openHeader(col, menuContent, e.clientX, e.clientY)} - className="collectionSchemaView-menuOptions-wrapper" - style={{ - background: col.color, padding: "2px", - display: "flex", cursor: "default", height: "100%", - }}> - {/* <FontAwesomeIcon onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> */} - {keysDropdown} - <div onClick={e => this.changeSorting(col)} - style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}> - <FontAwesomeIcon icon={sortIcon} size="lg" /> - </div> - </div>; + const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}> + {keysDropdown} + <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}> + <FontAwesomeIcon icon={sortIcon} size="lg" /> + </div> + </div>; return { Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />, @@ -260,21 +227,20 @@ export class SchemaTable extends React.Component<SchemaTableProps> { showDoc: this.showDoc, }; - const colType = this.getColumnType(col); - if (colType === ColumnType.Number) return <CollectionSchemaNumberCell {...props} />; - if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />; - if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />; - if (colType === ColumnType.Doc) return <CollectionSchemaDocCell {...props} />; - if (colType === ColumnType.Image) return <CollectionSchemaImageCell {...props} />; - if (colType === ColumnType.List) return <CollectionSchemaListCell {...props} />; - if (colType === ColumnType.Date) return <CollectionSchemaDateCell {...props} />; - return <CollectionSchemaCell {...props} />; + switch (this.getColumnType(col)) { + case ColumnType.Number: return <CollectionSchemaNumberCell {...props} />; + case ColumnType.String: return <CollectionSchemaStringCell {...props} />; + case ColumnType.Boolean: return <CollectionSchemaCheckboxCell {...props} />; + case ColumnType.Doc: return <CollectionSchemaDocCell {...props} />; + case ColumnType.Image: return <CollectionSchemaImageCell {...props} />; + case ColumnType.List: return <CollectionSchemaListCell {...props} />; + case ColumnType.Date: return <CollectionSchemaDateCell {...props} />; + default: return <CollectionSchemaCell {...props} />; + } }, minWidth: 200, }; - }); - columns.push(...cols); - + })); columns.push({ Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />, accessor: (doc: Doc) => 0, @@ -283,8 +249,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> { const rowIndex = rowProps.index; const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!); const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused; - const props: CellProps = { - row: rowIndex, + return <CollectionSchemaButtons {...{ + row: rowProps.index, col: columnIndex, rowProps: rowProps, isFocused: isFocused, @@ -303,9 +269,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { setComputed: this.setComputed, getField: this.getField, showDoc: this.showDoc, - }; - - return <CollectionSchemaButtons {...props} />; + }} />; }, width: 28, resizable: false @@ -316,12 +280,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { constructor(props: SchemaTableProps) { super(props); - // convert old schema columns (list of strings) into new schema columns (list of schema header fields) - const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []); - if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") { - const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i); - this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders); - } else if (this.props.Document._schemaHeaders === undefined) { + if (this.props.Document._schemaHeaders === undefined) { this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date), new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]); } @@ -362,19 +321,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> { const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true); // TODO: editing border doesn't work :( return { - style: { - border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" - } + style: { border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" } }; } - @action - onCloseCollection = (collection: Doc): void => { - const index = this._openCollections.findIndex(col => col === collection[Id]); - if (index > -1) this._openCollections.splice(index, 1); - } - - @action onExpandCollection = (collection: Doc) => this._openCollections.push(collection[Id]); @action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing; @action @@ -413,7 +363,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: "", _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 100, _height: 30 })); this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col }; }); @@ -431,21 +381,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @action getColumnType = (column: SchemaHeaderField): ColumnType => { - // added functionality to convert old column type stuff to new column type stuff -syip if (column.type && column.type !== 0) { return column.type; } if (columnTypes.get(column.heading)) { - column.type = columnTypes.get(column.heading)!; - return columnTypes.get(column.heading)!; - } - const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc)); - if (!typesDoc) { - column.type = ColumnType.Any; - return ColumnType.Any; + return column.type = columnTypes.get(column.heading)!; } - column.type = NumCast(typesDoc[column.heading]); - return NumCast(typesDoc[column.heading]); + return column.type = ColumnType.Any; } @undoBatch @@ -474,11 +416,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { @computed get reactTable() { const children = this.childDocs; - const hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false); - const expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString()); - const expanded = {}; - //@ts-ignore - expandedRowsList.forEach(row => expanded[row] = true); + const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false); + const expanded: { [name: string]: any } = {}; + Array.from(this._openCollections.keys()).map(col => expanded[col.toString()] = true); const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :(((( return <ReactTable @@ -496,17 +436,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> { expanded={expanded} resized={this.resized} onResizedChange={this.props.onResizedChange} - SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) : + SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) : <div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>} />; } onContextMenu = (e: React.MouseEvent): void => { - if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - // ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" }); - ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" }); - } + ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" }); } getField = (row: number, col?: number) => { @@ -588,9 +525,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } onOpenClick = () => { - if (this._showDoc) { - this.props.addDocTab(this._showDoc, "onRight"); - } + this._showDoc && this.props.addDocTab(this._showDoc, "onRight"); } getPreviewTransform = (): Transform => { @@ -606,7 +541,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { {StrCast(this.props.Document._chromeStatus) !== "disabled" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div> : undefined} {!this._showDoc ? (null) : - <div className="collectionSchemaView-documentPreview" //onClick={() => { this.onOpenClick(); }} + <div className="collectionSchemaView-documentPreview" style={{ position: "absolute", width: 150, height: 150, background: "dimGray", display: "block", top: 0, left: 0, @@ -627,6 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> { PanelHeight={() => 150} ScreenToLocalTransform={this.getPreviewTransform} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index b00074cc6..646ae94fb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -359,7 +359,7 @@ export function computeTimelineLayout( groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined }); } - const divider = { type: "div", color: Cast(Doc.UserDoc().activeWorkspace, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; + const divider = { type: "div", color: Cast(Doc.UserDoc().activeDashboard, Doc, null)?.darkScheme ? "dimGray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined }; return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]); function layoutDocsAtTime(keyDocs: Doc[], key: number) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 01bfb0453..02ff3e78c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -591,7 +591,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P 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); + runInAction(() => { + DocumentLinksButton.StartLink = undefined; + DocumentLinksButton.StartLinkView = undefined; + }); const docpt = this.getTransform().transformPoint(e.clientX, e.clientY); this.scaleAtPt(docpt, 1); e.stopPropagation(); @@ -641,7 +644,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => { - // panning a workspace if (!e.cancelBubble) { const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); const pt = myTouches[0]; @@ -975,6 +977,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P ContainingCollectionView: this.props.CollectionView, ContainingCollectionDoc: this.props.Document, docFilters: this.docFilters, + searchFilterDocs: this.searchFilterDocs, focus: this.focusDocument, backgroundColor: this.getClusterColor, backgroundHalo: this.backgroundHalo, @@ -1045,7 +1048,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } else if (viewDef.type === "div") { return [x, y].some(val => val === undefined) ? undefined : { - ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z} onClick={e => this.onViewDefDivClick(e, viewDef)} + ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z + viewDef.payload} onClick={e => this.onViewDefDivClick(e, viewDef)} style={{ width, height, backgroundColor: color, transform }} />, bounds: viewDef }; @@ -1601,30 +1604,30 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF @computed get presPaths() { const presPaths = "presPaths" + (this.props.presPaths ? "" : "-hidden"); - return !(PresBox.Instance) ? (null) : (<> - {!this.props.presPaths ? (null) : <><div>{PresBox.Instance.order}</div> - <svg className={presPaths}> - <defs> - <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth"> - <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" /> - </marker> - <marker id="square" markerWidth="3" markerHeight="3" overflow="visible" - refX="5" refY="5" orient="auto" markerUnits="strokeWidth"> - <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" /> - </marker> - <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4" - orient="auto" overflow="visible"> - <rect x="1" y="1" width="5" height="5" fill="#69a6db" /> - </marker> - - <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7" - orient="auto" overflow="visible"> - <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" /> - </marker> - </defs>; - {PresBox.Instance.paths} - </svg></>} - </>); + return !PresBox.Instance || !this.props.presPaths ? (null) : <> + <div key="presorder">{PresBox.Instance.order}</div> + <svg key="svg" className={presPaths}> + <defs> + <marker id="arrow" markerWidth="3" overflow="visible" markerHeight="3" refX="5" refY="5" orient="auto" markerUnits="strokeWidth"> + <path d="M0,0 L0,6 L9,3 z" fill="#69a6db" /> + </marker> + <marker id="square" markerWidth="3" markerHeight="3" overflow="visible" + refX="5" refY="5" orient="auto" markerUnits="strokeWidth"> + <path d="M 5,1 L 9,5 5,9 1,5 z" fill="#69a6db" /> + </marker> + <marker id="markerSquare" markerWidth="7" markerHeight="7" refX="4" refY="4" + orient="auto" overflow="visible"> + <rect x="1" y="1" width="5" height="5" fill="#69a6db" /> + </marker> + + <marker id="markerArrow" markerWidth="5" markerHeight="5" refX="2" refY="7" + orient="auto" overflow="visible"> + <path d="M2,2 L2,13 L8,7 L2,2" fill="#69a6db" /> + </marker> + </defs> + {PresBox.Instance.paths} + </svg> + </>; } render() { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 15f277adb..8dbc2bd43 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -132,6 +132,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque const tbox = Docs.Create.TextDocument("", { _width: 200, _height: 100, x: x, y: y, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), _fontFamily: StrCast(Doc.UserDoc().fontFamily), + _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, title: "-typed text-" }); const template = FormattedTextBox.DefaultLayout; @@ -528,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque d.page = -1; return d; }); - const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" }); + const summary = Docs.Create.TextDocument("", { x: bounds.left + bounds.width / 2, y: bounds.top + bounds.height / 2, _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" }); const portal = Doc.MakeAlias(summary); Doc.GetProto(summary)[Doc.LayoutFieldKey(summary) + "-annotations"] = new List<Doc>(selected); Doc.GetProto(summary).layout_portal = CollectionView.LayoutString(Doc.LayoutFieldKey(summary) + "-annotations"); @@ -755,14 +756,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque </div>; } else { - //subtracted for offset var str: string = ""; for (var i = 0; i < this._pointsX.length; i++) { - var x = 0; - x = this._pointsX[i] - 64; - str += x.toString(); + const pt = this.props.getContainerTransform().transformPoint(this._pointsX[i], this._pointsY[i]); + str += pt[0].toString(); str += ","; - str += (this._pointsY[i] - 85).toString(); + str += pt[1].toString(); str += (" "); } diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.scss b/src/client/views/collections/collectionFreeForm/PropertiesView.scss index 535581f2e..e3ced887d 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.scss +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.scss @@ -303,7 +303,6 @@ margin-left: auto; .permissions-select { - z-index: 1; border: none; background-color: inherit; width: 75px; @@ -731,7 +730,9 @@ border: none; padding: 6px; padding-bottom: 2px; - + background: #eeeeee; + border-top: 1px solid; + border-left: 1px solid; &:hover { border: 0.75px solid rgb(122, 28, 28); diff --git a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx index fb138ecc0..e128f6aab 100644 --- a/src/client/views/collections/collectionFreeForm/PropertiesView.tsx +++ b/src/client/views/collections/collectionFreeForm/PropertiesView.tsx @@ -8,7 +8,7 @@ import { EditableView } from "../../EditableView"; import { KeyValueBox } from "../../nodes/KeyValueBox"; import { Cast, NumCast, StrCast } from "../../../../fields/Types"; import { ContentFittingDocumentView } from "../../nodes/ContentFittingDocumentView"; -import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils } from "../../../../Utils"; +import { returnFalse, returnOne, emptyFunction, emptyPath, returnTrue, returnZero, returnEmptyFilter, Utils, returnEmptyDoclist } from "../../../../Utils"; import { Id } from "../../../../fields/FieldSymbols"; import { Transform } from "../../../util/Transform"; import { PropertiesButtons } from "../../PropertiesButtons"; @@ -181,40 +181,38 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { const doc = this.dataDoc; doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); const rows: JSX.Element[] = []; - for (const key of Object.keys(ids).slice().sort()) { - if ((key[0] === key[0].toUpperCase() && key.substring(0, 3) !== "ACL" && key !== "UseCors") - || key[0] === "#" || key === "author" || - key === "creationDate" || key.indexOf("lastModified") !== -1) { - - const contents = doc[key]; - if (key[0] === "#") { + const noviceReqFields = ["author", "creationDate"]; + const noviceLayoutFields = ["curPage"]; + const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL") && key !== "UseCors")), + ...noviceReqFields, ...noviceLayoutFields]; + for (const key of noviceKeys.sort()) { + const contents = this.selectedDoc[key]; + if (key[0] === "#") { + rows.push(<div className="uneditable-field" key={key}> + <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span> + + </div>); + } else if (contents !== undefined) { + const value = Field.toString(contents as Field); + if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) { rows.push(<div className="uneditable-field" key={key}> - <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span> - - </div>); + <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span> + <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div> + </div>); } else { - const value = Field.toString(contents as Field); - if (key === "author" || key === "creationDate" || key.indexOf("lastModified") !== -1) { - rows.push(<div className="uneditable-field" key={key}> - <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span> - <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div> - </div>); - } else { - let contentElement: (JSX.Element | null)[] | JSX.Element = []; - contentElement = <EditableView key="editableView" - contents={contents !== undefined ? Field.toString(contents as Field) : "null"} - height={13} - fontSize={10} - GetValue={() => Field.toKeyValueString(doc, key)} - SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} - />; - - rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}> - <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span> - - {contentElement} - </div>); - } + const contentElement = <EditableView key="editableView" + contents={value} + height={13} + fontSize={10} + GetValue={() => Field.toKeyValueString(this.selectedDoc!, key)} + SetValue={(value: string) => KeyValueBox.SetField(noviceLayoutFields.includes(key) ? this.selectedDoc! : doc, key, value, true)} + />; + + rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}> + <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span> + + {contentElement} + </div>); } } } @@ -284,6 +282,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { focus={returnFalse} ScreenToLocalTransform={this.getTransform} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionDoc={undefined} ContainingCollectionView={undefined} addDocument={returnFalse} @@ -321,7 +320,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { onChange={e => this.changePermissions(e, user)}> {Object.values(SharingPermissions).map(permission => { return ( - <option key={permission} value={permission} selected={this.selectedDoc![`ACL-${user.replace(".", "_")}`] === permission}> + <option key={permission} value={permission}> {permission} </option>); })} diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx index 70ebca5e7..4e279c659 100644 --- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx +++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx @@ -304,7 +304,7 @@ export class CollectionGridView extends CollectionSubView(GridSchema) { (e: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { undoBatch(action(() => { - const text = Docs.Create.TextDocument("", { _width: 150, _height: 50 }); + const text = Docs.Create.TextDocument("", { _showTitle: Doc.UserDoc().showTitle ? "title" : undefined, _width: 150, _height: 50 }); FormattedTextBox.SelectOnLoad = text[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed Doc.AddDocToList(this.props.Document, this.props.fieldKey, text); this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY)))); diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 21d283547..0afcab5a3 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -234,6 +234,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu ScreenToLocalTransform={dxf} focus={this.props.focus} docFilters={this.docFilters} + searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} addDocument={this.props.addDocument} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index d02088a6c..53825eece 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -234,6 +234,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) ScreenToLocalTransform={dxf} focus={this.props.focus} docFilters={this.docFilters} + searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} ContainingCollectionView={this.props.CollectionView} addDocument={this.props.addDocument} |
