From 2e3b83df9b0cb69db6f5febae70a2e36a0a90822 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 2 Sep 2020 10:46:29 -0400 Subject: made highlight colors more obvious. fixed tags to update when #key is set in textdoc. --- src/client/views/search/SearchBox.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/client/views/search') diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 3d564f073..6c5278892 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -192,8 +192,11 @@ export class SearchBox extends ViewBoxBaseComponent 0) { newarray = []; - docs.forEach((d) => { - d.data && newarray.push(...DocListCast(d.data)); + docs.forEach(d => { + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); + const data = d[annos ? fieldKey + "-annotations" : fieldKey]; + data && newarray.push(...DocListCast(data)); const hlights = new Set(); this.documentKeys(d).forEach(key => Field.toString(d[key] as Field).toLowerCase().includes(query) && hlights.add(key)); -- cgit v1.2.3-70-g09d2 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/documents/DocumentTypes.ts | 7 +- src/client/documents/Documents.ts | 8 + src/client/util/CurrentUserUtils.ts | 17 ++ 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 +- src/fields/Doc.ts | 9 +- 12 files changed, 304 insertions(+), 28 deletions(-) create mode 100644 src/client/views/nodes/FilterBox.scss create mode 100644 src/client/views/nodes/FilterBox.tsx (limited to 'src/client/views/search') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 1bef6fa08..37a148e55 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -13,7 +13,8 @@ export enum DocumentType { INK = "inks", // ink stroke SCREENSHOT = "screenshot", // view of a desktop application FONTICON = "fonticonbox", // font icon - SEARCH = "search", // search query + FILTER = "filter", + SEARCH = "search", // search query LABEL = "label", // simple text label BUTTON = "button", // onClick button WEBCAM = "webcam", // webcam @@ -32,10 +33,10 @@ export enum DocumentType { YOUTUBE = "youtube", // youtube directory (view of you tube search results) DOCHOLDER = "docholder", // nested document (view of a document) SEARCHITEM = "searchitem", - COMPARISON = "comparison", // before/after view with slider (view of 2 images) + COMPARISON = "comparison", // before/after view with slider (view of 2 images) GROUP = "group", // group of users LINKDB = "linkdb", // database of links ??? why do we have this - SCRIPTDB = "scriptdb", // database of scripts + SCRIPTDB = "scriptdb", // database of scripts GROUPDB = "groupdb" // database of groups } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index feb84843d..fbcecbec6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -50,6 +50,7 @@ import { PresElementBox } from "../views/presentationview/PresElementBox"; import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; +import { FilterBox } from "../views/nodes/FilterBox"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @@ -246,6 +247,10 @@ export namespace Docs { layout: { view: SearchBox, dataField: defaultDataKey }, options: { _width: 400 } }], + [DocumentType.FILTER, { + layout: { view: FilterBox, dataField: defaultDataKey }, + options: { _width: 400 } + }], [DocumentType.COLOR, { layout: { view: ColorBox, dataField: defaultDataKey }, options: { _nativeWidth: 220, _nativeHeight: 300 } @@ -821,6 +826,9 @@ export namespace Docs { export function FontIconDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.FONTICON), undefined, { hideLinkButton: true, ...(options || {}) }); } + export function FilterDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.FILTER), undefined, { ...(options || {}) }); + } export function PresElementBoxDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 734050b05..164c3ab67 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -516,6 +516,7 @@ export class CurrentUserUtils { { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' }, + { title: "Filter", target: Cast(doc.myFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' }, { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, { title: "Catalog", target: undefined as any, icon: "file", click: 'selectMainMenu(self)' }, { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, @@ -775,6 +776,21 @@ export class CurrentUserUtils { (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Clear All"]); } } + static setupFilterDocs(doc: Doc) { + // setup Recently Closed library item + doc.myFilter === undefined; + if (doc.myFilter === undefined) { + doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({ + title: "FilterDoc", _height: 500, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true + })); + const clearAll = ScriptField.MakeScript(`self.data = new List([])`); + (doc.myFilter as any as Doc).contextMenuScripts = new List([clearAll!]); + (doc.myFilter as any as Doc).contextMenuLabels = new List(["Clear All"]); + } + } static setupUserDoc(doc: Doc) { @@ -806,6 +822,7 @@ export class CurrentUserUtils { CurrentUserUtils.setupDashboards(doc); CurrentUserUtils.setupPresentations(doc); CurrentUserUtils.setupRecentlyClosedDocs(doc); + CurrentUserUtils.setupFilterDocs(doc); CurrentUserUtils.setupUserDoc(doc); } 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; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index bc31c1a21..08d949b5e 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -23,6 +23,7 @@ import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, u import { LinkManager } from "../client/util/LinkManager"; import JSZip = require("jszip"); import { saveAs } from "file-saver"; +import { CollectionDockingView } from "../client/views/collections/CollectionDockingView"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -1048,7 +1049,8 @@ export namespace Doc { doc.layoutKey = deiconify || "layout"; } export function setDocFilterRange(target: Doc, key: string, range?: number[]) { - const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []); + const container = target ?? CollectionDockingView.Instance.props.Document; + const docRangeFilters = Cast(container._docRangeFilters, listSpec("string"), []); for (let i = 0; i < docRangeFilters.length; i += 3) { if (docRangeFilters[i] === key) { docRangeFilters.splice(i, 3); @@ -1059,14 +1061,15 @@ export namespace Doc { docRangeFilters.push(key); docRangeFilters.push(range[0].toString()); docRangeFilters.push(range[1].toString()); - target._docRangeFilters = new List(docRangeFilters); + container._docRangeFilters = new List(docRangeFilters); } } // filters document in a container collection: // all documents with the specified value for the specified key are included/excluded // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) { + export function setDocFilter(target: Opt, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) { + const container = target ?? CollectionDockingView.Instance.props.Document; const docFilters = Cast(container._docFilters, listSpec("string"), []); runInAction(() => { for (let i = 0; i < docFilters.length; i += 3) { -- cgit v1.2.3-70-g09d2 From ea5bb0221411596461ae7206b6901ccbb79a1c2f Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 2 Sep 2020 20:07:09 -0400 Subject: fixes to sidebar filter. still need some more work. --- src/client/documents/Documents.ts | 5 +- src/client/util/CurrentUserUtils.ts | 16 +- src/client/views/MainView.tsx | 3 +- .../views/collections/CollectionPileView.tsx | 2 - .../views/collections/CollectionSchemaHeaders.tsx | 8 +- .../views/collections/CollectionTreeView.tsx | 25 +-- src/client/views/collections/CollectionView.tsx | 174 +-------------------- src/client/views/nodes/FilterBox.scss | 76 +++++---- src/client/views/nodes/FilterBox.tsx | 114 ++++++++------ src/client/views/search/SearchBox.tsx | 43 ++--- 10 files changed, 153 insertions(+), 313 deletions(-) (limited to 'src/client/views/search') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fbcecbec6..e00160a70 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -108,7 +108,6 @@ export interface DocumentOptions { layout?: string | Doc; // default layout string for a document childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view) childLayoutString?: string; // template string for collection to use to render its children - hideFilterView?: boolean; // whether to hide the filter popout on collections hideLinkButton?: boolean; // whether the blue link counter button should be hidden hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden _columnsHideIfEmpty?: boolean; // whether stacking view column headings should be hidden @@ -762,7 +761,7 @@ export namespace Docs { } export function PileDocument(documents: Array, options: DocumentOptions, id?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", hideFilterView: true, forceActive: true, ...options, _viewType: CollectionViewType.Pile }, id); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _chromeStatus: "collapsed", backgroundColor: "black", forceActive: true, ...options, _viewType: CollectionViewType.Pile }, id); } export function LinearDocument(documents: Array, options: DocumentOptions, id?: string) { @@ -835,7 +834,7 @@ export namespace Docs { } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); const tabs = TreeDocument(documents, { title: "On-Screen Tabs", freezeChildren: "remove|add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true }); const all = TreeDocument([], { title: "Off-Screen Tabs", freezeChildren: "add", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", system: true }); Doc.GetProto(inst).data = new List([tabs, all]); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 164c3ab67..5a01e9cf9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -55,7 +55,7 @@ export class CurrentUserUtils { Docs.Create.SearchDocument({ _viewType: CollectionViewType.Schema, ignoreClick: true, forceActive: true, _chromeStatus: "disabled", lockedPosition: true, title: "query", _height: 200, system: true }), Docs.Create.FreeformDocument([], { title: "data", _height: 100, system: true }) ], - { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true } + { _width: 400, _height: 300, title: "queryView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, system: true } ); queryTemplate.isTemplateDoc = makeTemplate(queryTemplate); doc["template-button-query"] = CurrentUserUtils.ficon({ @@ -90,7 +90,7 @@ export class CurrentUserUtils { Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), Docs.Create.TextDocument("", { title: "text", _height: 100, system: true }) ], - { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, hideFilterView: true, system: true } + { _width: 400, _height: 300, title: "slideView", _chromeStatus: "disabled", _xMargin: 3, _yMargin: 3, system: true } ); slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); doc["template-button-slides"] = CurrentUserUtils.ficon({ @@ -720,7 +720,7 @@ export class CurrentUserUtils { if (doc.myTools === undefined) { const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { - title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", hideFilterView: true, forceActive: true, system: true + title: "My Tools", _width: 500, _yMargin: 20, lockedPosition: true, _chromeStatus: "disabled", forceActive: true, system: true })) as any as Doc; doc.myTools = toolsStack; @@ -734,7 +734,7 @@ export class CurrentUserUtils { doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "My Dashboards", _height: 400, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); @@ -750,7 +750,7 @@ export class CurrentUserUtils { doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "My Presentations", _height: 100, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const newPresentations = ScriptField.MakeScript(`createNewPresentation()`); @@ -768,7 +768,7 @@ export class CurrentUserUtils { doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { title: "Recently Closed", _height: 500, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const clearAll = ScriptField.MakeScript(`self.data = new List([])`); @@ -783,7 +783,7 @@ export class CurrentUserUtils { doc.myFilter = new PrefetchProxy(Docs.Create.FilterDocument({ title: "FilterDoc", _height: 500, treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })); const clearAll = ScriptField.MakeScript(`self.data = new List([])`); @@ -799,7 +799,7 @@ export class CurrentUserUtils { doc.treeViewExpandedView = "fields"; doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, forceActive: true, title: "My UserDoc", - treeViewTruncateTitleWidth: 150, hideFilterView: true, treeViewPreventOpen: false, + treeViewTruncateTitleWidth: 150, treeViewPreventOpen: false, lockedPosition: true, boxShadow: "0 0", dontRegisterChildViews: true, targetDropAction: "same", system: true })) as any as Doc; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e6e80d9fb..a87a07b62 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -83,6 +83,7 @@ export class MainView extends React.Component { componentDidMount() { new InkStrokeProperties(); + this._sidebarContent.proto = undefined; DocServer.setPlaygroundFields(["dataTransition", "_viewTransition", "_panX", "_panY", "_viewScale", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); @@ -527,7 +528,7 @@ export class MainView extends React.Component { TraceMobx(); return
doc) { } this._originalChrome = StrCast(this.layoutDoc._chromeStatus); this.layoutDoc._chromeStatus = "disabled"; - this.layoutDoc.hideFilterView = true; } componentWillUnmount() { - this.layoutDoc.hideFilterView = false; this.layoutDoc._chromeStatus = this._originalChrome; } diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index 94f9d4f92..d2a1234ed 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -380,7 +380,7 @@ export class KeysDropdown extends React.Component { @action renderFilterOptions = (): JSX.Element[] | JSX.Element => { - if (!this._isOpen) { + if (!this._isOpen || !this.props.dataDoc) { this.defaultMenuHeight = 0; return <>; } @@ -388,7 +388,7 @@ export class KeysDropdown extends React.Component { const colpos = this._searchTerm.indexOf(":"); const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); if (this.docSafe.length === 0) { - this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]); + this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]); } const docs = this.docSafe; docs.forEach((doc) => { @@ -476,8 +476,8 @@ export class KeysDropdown extends React.Component { removeFilters = (e: React.PointerEvent): void => { const keyOptions: string[] = []; - if (this.docSafe.length === 0) { - this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]); + if (this.docSafe.length === 0 && this.props.dataDoc) { + this.docSafe = DocListCast(this.props.dataDoc[this.props.fieldKey]); } const docs = this.docSafe; docs.forEach((doc) => { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 52c3b2793..f13fee776 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -32,6 +32,7 @@ import React = require("react"); import { makeTemplate } from '../../util/DropConverter'; import { TraceMobx } from '../../../fields/util'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { CollectionDockingView } from './CollectionDockingView'; export interface TreeViewProps { document: Doc; @@ -488,7 +489,7 @@ class TreeView extends React.Component { }} > {view}
- {Doc.IsSystem(this.doc) ? (null) : headerElements} + {Doc.IsSystem(this.doc) && Doc.UserDoc().noviceMode ? (null) : headerElements} ; } @@ -875,28 +876,6 @@ export class CollectionTreeView extends CollectionSubView - set.add(Field.toString(child[facetHeader] as Field)), new Set())); - - let nonNumbers = 0; - facetValues.map(val => { - const num = Number(val); - if (Number.isNaN(num)) { - nonNumbers++; - } - }); - const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => { - const doc = new Doc(); - doc.system = true; - doc.title = facetValue.toString(); - doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue }); - return doc; - }); - return new List(facetValueDocSet); -}); - Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []); for (let i = 0; i < docFilters.length; i += 3) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index cb053e85c..ba8e23447 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -271,7 +271,6 @@ export class CollectionView extends Touchable Doc, addExtras: boolean) { const subItems: ContextMenuProps[] = []; @@ -367,6 +366,7 @@ export class CollectionView extends Touchable this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); } + get _facetWidth() { return NumCast(this.props.Document._facetWidth); } set _facetWidth(value) { this.props.Document._facetWidth = value; } @@ -390,9 +390,11 @@ export class CollectionView extends Touchable Doc.GetLayoutDataDocPair(Document, DataDoc, doc)).filter(pair => pair.layout); return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types } + get childDocList() { return Cast(this.dataField, listSpec(Doc)); } + get childDocs() { const dfield = this.dataField; const rawdocs = (dfield instanceof Doc) ? [dfield] : Cast(dfield, listSpec(Doc), Cast(this.props.Document.rootDocument, Doc, null) ? [Cast(this.props.Document.rootDocument, Doc, null)] : []); @@ -401,99 +403,6 @@ export class CollectionView extends Touchable viewSpecScript.script.run({ doc: d }, console.log).result) : docs; } - @computed get _allFacets() { - TraceMobx(); - 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 facetCollection = this.props.Document; - const found = DocListCast(facetCollection[this.props.fieldKey + "-filter"]).findIndex(doc => doc.title === facetHeader); - if (found !== -1) { - (facetCollection[this.props.fieldKey + "-filter"] as List).splice(found, 1); - const docFilter = Cast(this.props.Document._docFilters, listSpec("string")); - if (docFilter) { - let index: number; - while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { - docFilter.splice(index, 3); - } - } - const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string")); - if (docRangeFilters) { - let index: number; - while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { - docRangeFilters.splice(index, 3); - } - } - } else { - const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]); - 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.target = this.props.Document; - 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(this.props.Document, 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; - newFacet.target = this.props.Document; - const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; - newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); - Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); - } else { - newFacet = new Doc(); - newFacet.sytem = true; - newFacet.title = facetHeader; - newFacet.treeViewOpen = true; - newFacet.type = DocumentType.COL; - const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; - newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables); - } - newFacet && Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); - } - } - onPointerDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { this._facetWidth = this.props.PanelWidth() - Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0); @@ -501,78 +410,6 @@ export class CollectionView extends Touchable this._facetWidth = this.facetWidth() < 15 ? Math.min(this.props.PanelWidth() - 25, 200) : 0), false); } - 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(containingTreeView, 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() { - TraceMobx(); - const facetCollection = this.props.Document; - const flyout = ( -
e.stopPropagation()}> - {this._allFacets.map(facet => )} -
- ); - - return !this._facetWidth || this.props.dontRegisterView ? (null) :
-
e.stopPropagation()}> - -
- - Facet Filters -
-
-
-
- -
-
; - } - childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); childLayoutString = this.props.childLayoutString || StrCast(this.props.Document.childLayoutString); @@ -603,11 +440,6 @@ export class CollectionView extends Touchable - } - {Doc.UserDoc()?.noviceMode || this.facetWidth() < 10 ? (null) : this.filterView} ); } } diff --git a/src/client/views/nodes/FilterBox.scss b/src/client/views/nodes/FilterBox.scss index c5d6e2505..b39793f01 100644 --- a/src/client/views/nodes/FilterBox.scss +++ b/src/client/views/nodes/FilterBox.scss @@ -1,44 +1,54 @@ - .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; +.filterBox-flyout { + width: 400px; + display: block; + text-align: left; + .filterBox-flyout-facet { + background-color: lightgray; text-align: left; + display: inline-block; + position: relative; + width: 100%; + } +} +.filterBox-treeView { + display: flex; + flex-direction: column; + width: 200px; + height: 100%; + position: absolute; + right: 0; + top: 0; + border-left: solid 1px; + z-index: 1; - .collectionTimeView-button { - align-items: center; - display: flex; - width: 100%; - height: 100%; + .filterBox-addfacet { + display: inline-block; + width: 200px; + height: 30px; + background: darkGray; + text-align: left; - .collectionTimeView-span { - margin: auto; - } - } + .filterBox-addFacetButton { + display: flex; + margin: auto; - >div, - >div>div { - width: 100%; - height: 100%; + .filterBox-span { + margin-right: 15px; } } - .collectionTimeView-tree { - display: inline-block; + >div, + >div>div { width: 100%; - height: calc(100% - 30px); + height: 100%; } - } \ No newline at end of file + } + + .filterBox-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 index 7bffc9184..7fcbce9e3 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -1,27 +1,24 @@ import React = require("react"); -import { action, computed } from "mobx"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { 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 { DataSym, Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; 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 { List } from "../../../fields/List"; +import { RichTextField } from "../../../fields/RichTextField"; +import { listSpec, makeInterface } from "../../../fields/Schema"; +import { ComputedField, ScriptField } from "../../../fields/ScriptField"; +import { Cast } from "../../../fields/Types"; +import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnZero } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; 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 { ViewBoxBaseComponent } from "../DocComponent"; +import { SearchBox } from "../search/SearchBox"; +import { FieldView, FieldViewProps } from './FieldView'; import './FilterBox.scss'; -import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { Scripting } from "../../util/Scripting"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -33,17 +30,24 @@ const FilterBoxDocument = makeInterface(documentSchema); 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]; + @computed get allDocs() { + const allDocs = new Set(); + if (CollectionDockingView.Instance) { + const activeTabs = DocListCast(CollectionDockingView.Instance.props.Document.data); + SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allDocs.add(doc)); + setTimeout(() => CollectionDockingView.Instance.props.Document.allDocuments = new List(Array.from(allDocs))); + } + return allDocs; + } - // 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))); + @computed get _allFacets() { + const noviceReqFields = ["author", "tags", "text", "type"]; + const noviceLayoutFields: string[] = [];//["_curPage"]; + const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; - // 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(); + const keys = new Set(noviceFields); + this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key))); + return Array.from(keys.keys()).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 @@ -95,7 +99,7 @@ export class FilterBox extends ViewBoxBaseComponent script : undefined; } - @computed get filterView() { + render() { const facetCollection = this.props.Document.proto as Doc; - const flyout = ( -
e.stopPropagation()}> - {this._allFacets.map(facet => )} -
- ); + const flyout =
e.stopPropagation()}> + {this._allFacets.map(facet => )} +
; - return this.props.dontRegisterView ? (null) :
-
e.stopPropagation()}> + return this.props.dontRegisterView ? (null) :
+
e.stopPropagation()}> -
+
- Facet Filters + Choose Facets
-
+
; } - render() { - return this.filterView; - } -} \ No newline at end of file +} + +Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) { + const allCollectionDocs = DocListCast(CollectionDockingView.Instance?.props.Document.allDocuments); + const set = new Set(); + if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key))); + else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field))); + let facetValues = Array.from(set).filter(v => v); + + let nonNumbers = 0; + facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++); + const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => { + const doc = new Doc(); + doc.system = true; + doc.title = facetValue.toString(); + doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", {}, { layoutDoc, facetHeader, facetValue }); + return doc; + }); + return new List(facetValueDocSet); +}); \ No newline at end of file diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 3907d2d5c..b37ae02c3 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -179,6 +179,21 @@ export class SearchBox extends ViewBoxBaseComponent void) { + let newarray: Doc[] = []; + while (docs.length > 0) { + newarray = []; + docs.forEach(d => { + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); + const data = d[annos ? fieldKey + "-annotations" : fieldKey]; + data && newarray.push(...DocListCast(data)); + func(d); + }); + docs = newarray; + } + } + @action searchCollection(query: string) { const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0]; @@ -186,26 +201,14 @@ export class SearchBox extends ViewBoxBaseComponent 0) { - newarray = []; - docs.forEach(d => { - const fieldKey = Doc.LayoutFieldKey(d); - const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); - const data = d[annos ? fieldKey + "-annotations" : fieldKey]; - data && newarray.push(...DocListCast(data)); - const hlights = new Set(); - this.documentKeys(d).forEach(key => - Field.toString(d[key] as Field).toLowerCase().includes(query) && hlights.add(key)); - if (Array.from(hlights.keys()).length > 0) { - found.push([d, Array.from(hlights.keys()), []]); - } - }); - docs = newarray; - } + SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => { + const hlights = new Set(); + SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key)); + Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]); + }); + this._results = found; this.docsforfilter = this._results.map(r => r[0]); this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined); @@ -218,7 +221,7 @@ export class SearchBox extends ViewBoxBaseComponent