From 3f3ba063bb6e3bde20dafc8a9e6d7fe9254e22fe Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 2 Sep 2020 15:30:45 -0400 Subject: fixed behavior of menuButtons to toggle contents. added start of a filter panel. --- src/client/views/DocumentDecorations.tsx | 32 ++-- src/client/views/MainView.tsx | 9 +- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/FilterBox.scss | 44 ++++++ src/client/views/nodes/FilterBox.tsx | 197 ++++++++++++++++++++++++ src/client/views/search/SearchBox.tsx | 2 +- 8 files changed, 269 insertions(+), 22 deletions(-) create mode 100644 src/client/views/nodes/FilterBox.scss create mode 100644 src/client/views/nodes/FilterBox.tsx (limited to 'src/client/views') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6951cb592..7781d6069 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from '@material-ui/core'; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { AclAdmin, AclEdit, DataSym, Doc, Field } from "../../fields/Doc"; +import { AclAdmin, AclEdit, DataSym, Doc, Field, WidthSym, HeightSym } from "../../fields/Doc"; import { Document } from '../../fields/documentSchemas'; import { HtmlField } from '../../fields/HtmlField'; import { InkField } from "../../fields/InkField"; @@ -11,7 +11,7 @@ import { ScriptField } from '../../fields/ScriptField'; import { Cast, NumCast } from "../../fields/Types"; import { GetEffectiveAcl } from '../../fields/util'; import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from "../../Utils"; -import { DocUtils } from "../documents/Documents"; +import { DocUtils, Docs } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; @@ -178,17 +178,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } @action onMaximizeDown = (e: React.PointerEvent): void => { - if (e.ctrlKey) { - const selectedDocs = SelectionManager.SelectedDocuments(); - const alias = Doc.MakeAlias(selectedDocs[0].props.Document); - alias.context = undefined; - //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); - CollectionDockingView.AddSplit(alias, "right"); - e.stopPropagation(); - e.preventDefault(); - } else { - setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); - } + setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMaximizeClick); } @undoBatch @action @@ -196,7 +186,21 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (e.button === 0) { const selectedDocs = SelectionManager.SelectedDocuments(); if (selectedDocs.length) { - CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); + if (e.ctrlKey) { + const alias = Doc.MakeAlias(selectedDocs[0].props.Document); + alias.context = undefined; + //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); + CollectionDockingView.AddSplit(alias, "right"); + } else if (e.shiftKey) { + const alias = Doc.MakeAlias(selectedDocs[0].props.Document); + alias.context = undefined; + alias.x = -alias[WidthSym]() / 2; + alias.y = -alias[HeightSym]() / 2; + //CollectionDockingView.Instance?.OpenFullScreen(selectedDocs[0]); + CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right"); + } else { + CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right"); + } } } SelectionManager.DeselectAll(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7c7c5b72b..e6e80d9fb 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -372,9 +372,9 @@ export class MainView extends React.Component { @action selectMenu = (button: Doc) => { const title = StrCast(Doc.GetProto(button).title); - const closed = !this._flyoutWidth; + const willOpen = !this._flyoutWidth || this._panelContent !== title; this.closeFlyout(); - if (this._panelContent !== title || !this._flyoutWidth) { + if (willOpen) { switch (this._panelContent = title) { case "Settings": SettingsManager.Instance.open(); @@ -384,7 +384,7 @@ export class MainView extends React.Component { SearchBox.Instance.enter(undefined); break; default: - closed && this.expandFlyout(button); + this.expandFlyout(button); } } return true; @@ -434,6 +434,7 @@ export class MainView extends React.Component { this._lastButton && (this._lastButton.color = "white"); this._lastButton && (this._lastButton._backgroundColor = ""); this._panelContent = "none"; + this._sidebarContent.proto = undefined; this._flyoutWidth = 0; }); @@ -520,7 +521,7 @@ export class MainView extends React.Component { ; } - select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; + select = (ctrlPressed: boolean) => { }; @computed get search() { TraceMobx(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 3c230537c..5cdf337a9 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -191,7 +191,7 @@ export class TabDocView extends React.Component { if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) { this._isActive = this.props.glContainer.tab.isActive; this._isActive && setTimeout(() => this.view && SelectionManager.SelectDoc(this.view, false), 0); - (CollectionDockingView.Instance as any)._goldenLayout.isInitialised && CollectionDockingView.Instance.stateChanged(); + (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged(); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 5d5a1f7f3..1b2070c0f 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -25,6 +25,7 @@ import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { PresBox } from "./PresBox"; import { SearchBox } from "../search/SearchBox"; +import { FilterBox } from "./FilterBox"; import { ColorBox } from "./ColorBox"; import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo"; import { LinkAnchorBox } from "./LinkAnchorBox"; @@ -191,7 +192,7 @@ export class DocumentContentsView extends React.Component(Docu let stopPropagate = true; let preventDefault = true; !this.props.Document._isBackground && this.props.bringToFront(this.props.Document); - if (this._doubleTap && this.props.renderDepth && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click + if (this._doubleTap && ((this.props.renderDepth && this.props.Document.type !== DocumentType.FONTICON) || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss new file mode 100644 index 000000000..c5d6e2505 --- /dev/null +++ b/src/client/views/nodes/FilterBox.scss @@ -0,0 +1,44 @@ + + + .collectionTimeView-treeView { + display: flex; + flex-direction: column; + width: 200px; + height: 100%; + position: absolute; + right: 0; + top: 0; + border-left: solid 1px; + z-index: 1; + + .collectionTimeView-addfacet { + display: inline-block; + width: 200px; + height: 30px; + background: darkGray; + text-align: left; + + .collectionTimeView-button { + align-items: center; + display: flex; + width: 100%; + height: 100%; + + .collectionTimeView-span { + margin: auto; + } + } + + >div, + >div>div { + width: 100%; + height: 100%; + } + } + + .collectionTimeView-tree { + display: inline-block; + width: 100%; + height: calc(100% - 30px); + } + } \ No newline at end of file diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx new file mode 100644 index 000000000..7bffc9184 --- /dev/null +++ b/src/client/views/nodes/FilterBox.tsx @@ -0,0 +1,197 @@ +import React = require("react"); +import { action, computed } from "mobx"; +import { observer } from "mobx-react"; +import { ColorState, SketchPicker } from 'react-color'; +import { Doc, Opt, DocListCast, Field, DataSym } from "../../../fields/Doc"; +import { Utils, returnEmptyFilter, returnEmptyDoclist, returnZero, emptyPath, returnFalse, emptyFunction, returnOne } from "../../../Utils"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { InkTool } from "../../../fields/InkField"; +import { makeInterface, listSpec } from "../../../fields/Schema"; +import { StrCast, Cast } from "../../../fields/Types"; +import { SelectionManager } from "../../util/SelectionManager"; +import { undoBatch } from "../../util/UndoManager"; +import { ViewBoxBaseComponent } from "../DocComponent"; +import "./ColorBox.scss"; +import { FieldView, FieldViewProps } from './FieldView'; +import { DocumentType } from "../../documents/DocumentTypes"; +import { CollectionTreeView } from "../collections/CollectionTreeView"; +import { ScriptField, ComputedField } from "../../../fields/ScriptField"; +import { Docs } from "../../documents/Documents"; +import { RichTextField } from "../../../fields/RichTextField"; +import { List } from "../../../fields/List"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import './FilterBox.scss'; +import { CollectionDockingView } from "../collections/CollectionDockingView"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; + +type FilterBoxDocument = makeInterface<[typeof documentSchema]>; +const FilterBoxDocument = makeInterface(documentSchema); + +@observer +export class FilterBox extends ViewBoxBaseComponent(FilterBoxDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); } + + @computed get _allFacets() { + return ["author", "creationDate", "type", "text", "context"]; + // const noviceReqFields = ["author", "creationDate", "type", "text", "context"]; + // const noviceLayoutFields: string[] = [];//["_curPage"]; + // const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; + + // const facets = new Set([...noviceReqFields, ...noviceLayoutFields]); + // this.childDocs.filter(child => child).forEach(child => child && Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); + // Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.filter(child => child).forEach(child => Object.keys(child).forEach(key => facets.add(key))); + + // return Array.from(facets).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("ACL")) || noviceFields.includes(key)).sort(); + } + /** + * Responds to clicking the check box in the flyout menu + */ + facetClick = (facetHeader: string) => { + const targetDoc = CollectionDockingView.Instance.props.Document; + const found = DocListCast(this.dataDoc[this.props.fieldKey]).findIndex(doc => doc.title === facetHeader); + if (found !== -1) { + (this.dataDoc[this.props.fieldKey] as List).splice(found, 1); + const docFilter = Cast(targetDoc._docFilters, listSpec("string")); + if (docFilter) { + let index: number; + while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { + docFilter.splice(index, 3); + } + } + const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string")); + if (docRangeFilters) { + let index: number; + while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { + docRangeFilters.splice(index, 3); + } + } + } else { + const allCollectionDocs = DocListCast((targetDoc.data as any)[0].data); + var rtfields = 0; + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => { + const field = child[facetHeader] as Field; + const fieldStr = Field.toString(field); + if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++; + return set.add(fieldStr); + }, new Set())); + + let nonNumbers = 0; + let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; + facetValues.map(val => { + const num = Number(val); + if (Number.isNaN(num)) { + nonNumbers++; + } else { + minVal = Math.min(num, minVal); + maxVal = Math.max(num, maxVal); + } + }); + let newFacet: Opt; + if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) { + newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + newFacet._textBoxPadding = 4; + const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`; + newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" }); + } else if (nonNumbers / facetValues.length < .1) { + newFacet = Docs.Create.SliderDocument({ title: facetHeader, treeViewExpandedView: "layout", treeViewOpen: true }); + const newFacetField = Doc.LayoutFieldKey(newFacet); + const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + const extendedMinVal = minVal - Math.min(1, Math.abs(maxVal - minVal) * .05); + const extendedMaxVal = maxVal + Math.min(1, Math.abs(maxVal - minVal) * .05); + newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0]; + newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1]; + Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal; + Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal; + const scriptText = `setDocFilterRange(this?.target, "${facetHeader}", range)`; + newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); + } else { + newFacet = new Doc(); + newFacet.sytem = true; + newFacet.title = facetHeader; + newFacet.treeViewOpen = true; + newFacet.type = DocumentType.COL; + const capturedVariables = { layoutDoc: targetDoc, dataDoc: (targetDoc.data as any)[0][DataSym] }; + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables); + } + newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet); + } + } + filterBackground = () => "rgba(105, 105, 105, 0.432)"; + get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } // this makes the tree view collection ignore these filters (otherwise, the filters would filter themselves) + @computed get scriptField() { + const scriptText = "setDocFilter(this?.target, heading, this.title, checked)"; + const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); + return script ? () => script : undefined; + } + + @computed get filterView() { + const facetCollection = this.props.Document.proto as Doc; + const flyout = ( +
e.stopPropagation()}> + {this._allFacets.map(facet => )} +
+ ); + + return this.props.dontRegisterView ? (null) :
+
e.stopPropagation()}> + +
+ + Facet Filters +
+
+
+
+ +
+
; + } + render() { + return this.filterView; + } +} \ No newline at end of file diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 6c5278892..3907d2d5c 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -45,7 +45,7 @@ export class SearchBox extends ViewBoxBaseComponent = undefined; private _disposers: { [name: string]: IReactionDisposer } = {}; - private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; + private _blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; private docsforfilter: Doc[] | undefined = []; private realTotalResults: number = 0; -- cgit v1.2.3-70-g09d2