diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 3 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 2 | ||||
-rw-r--r-- | src/client/util/DocumentManager.ts | 1 | ||||
-rw-r--r-- | src/client/views/DocComponent.tsx | 2 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 2 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaCells.tsx | 206 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaHeaders.tsx | 287 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.scss | 87 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 28 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 31 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 103 | ||||
-rw-r--r-- | src/client/views/collections/SchemaTable.tsx | 39 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 41 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 3 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 70 | ||||
-rw-r--r-- | src/server/websocket.ts | 3 |
17 files changed, 644 insertions, 268 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e54c89d9f..6a9701cfa 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1211,5 +1211,4 @@ export namespace DocUtils { } Scripting.addGlobal("Docs", Docs); -Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; }); - +Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
\ No newline at end of file diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7eed4d2b1..ee17c694f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -964,4 +964,4 @@ Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.cre Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); Scripting.addGlobal(function directLinks(doc: any) { return new List(LinkManager.Instance.getAllDirectLinks(doc)); }, - "returns all the links directly to the document", "(doc: any)"); + "returns all the links directly to the document", "(doc: any)");
\ No newline at end of file diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index bd57e7f48..962294933 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -231,6 +231,7 @@ export class DocumentManager { containerDoc.currentTimecode = targetTimecode; const targetContext = await target?.context as Doc; const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined; + console.log(targetNavContext); DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "onRight"), finished), targetNavContext, linkDoc, undefined, doc, finished); } else { finished?.(); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index eea133ed9..ea854c2a3 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -171,6 +171,8 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T added.map(doc => doc.context = this.props.Document); (targetDataDoc[this.annotationKey] as List<Doc>).push(...added); targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now())); + const lastModified = "lastModified"; + targetDataDoc[lastModified] = new DateField(new Date(Date.now())); } } } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 182f6397c..fd9b04128 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -331,6 +331,8 @@ export default class KeyManager { undoBatch(() => { targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]); targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + const lastModified = "lastModified"; + targetDataDoc[lastModified] = new DateField(new Date(Date.now())); })(); } } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4d5dfc99e..5f7abdcb1 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -199,7 +199,7 @@ export class MainView extends React.Component { let check = false; const icon = "icon"; targets.forEach((thing) => { - if (thing.className.toString() === "collectionSchemaView-table" || (thing as any)?.dataset[icon] === "filter" || thing.className.toString() === "beta" || thing.className.toString() === "collectionSchemaView-menuOptions-wrapper") { + if (thing.className.toString() === "collectionSchemaView-searchContainer" || (thing as any)?.dataset[icon] === "filter" || thing.className.toString() === "collectionSchema-header-menuOptions" || thing.className.toString() === "altcollectionTimeView-treeView") { check = true; } }); @@ -904,4 +904,4 @@ Scripting.addGlobal(function copyWorkspace() { Doc.AddDocToList(workspaces, "data", copiedWorkspace); // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) setTimeout(() => MainView.Instance.openWorkspace(copiedWorkspace), 0); -}); +});
\ No newline at end of file diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 8a1dd8472..20ae74b44 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -33,6 +33,8 @@ import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { DateField } from "../../../fields/DateField"; import { RichTextField } from "../../../fields/RichTextField"; +import { DocumentManager } from "../../util/DocumentManager"; +import { SearchUtil } from "../../util/SearchUtil"; const path = require('path'); library.add(faExpand); @@ -159,6 +161,29 @@ export class CollectionSchemaCell extends React.Component<CellProps> { // e.stopPropagation(); // } + returnHighlights(bing: (() => string), positions?: number[]) { + const results = []; + const contents = bing(); + + if (positions !== undefined) { + StrCast(this.props.Document._searchString) + const length = StrCast(this.props.Document._searchString).length; + + results.push(<span style={{ color: contents ? "black" : "grey" }}>{contents ? contents.slice(0, positions[0]) : "undefined"}</span>); + positions.forEach((num, cur) => { + results.push(<span style={{ backgroundColor: "#FFFF00", color: contents ? "black" : "grey" }}>{contents ? contents.slice(num, num + length) : "undefined"}</span>); + let end = 0; + cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1]; + results.push(<span style={{ color: contents ? "black" : "grey" }}>{contents ? contents.slice(num + length, end) : "undefined"}</span>); + } + ); + return results; + } + else { + return <span style={{ color: contents ? "black" : "grey" }}>{contents ? contents?.valueOf() : "undefined"}</span>; + } + } + renderCellWithType(type: string | undefined) { const dragRef: React.RefObject<HTMLDivElement> = React.createRef(); @@ -200,12 +225,24 @@ export class CollectionSchemaCell extends React.Component<CellProps> { const doc = FieldValue(Cast(field, Doc)); const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc); - const onItemDown = (e: React.PointerEvent) => { - //fieldIsDoc && - SetupDrag(this._focusRef, - () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document, - this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument, - this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); + const onItemDown = async (e: React.PointerEvent) => { + if (this.props.Document._searchDoc !== undefined) { + let doc = Doc.GetProto(this.props.rowProps.original); + const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc); + let targetContext = undefined; + if (aliasdoc.length > 0) { + targetContext = Cast(aliasdoc[0].context, Doc) as Doc; + } + console.log(targetContext); + DocumentManager.Instance.jumpToDocument(this.props.rowProps.original, false, undefined, targetContext); + } + else { + fieldIsDoc && + SetupDrag(this._focusRef, + () => this._document[props.fieldKey] instanceof Doc ? this._document[props.fieldKey] : this._document, + this._document[props.fieldKey] instanceof Doc ? (doc: Doc | Doc[], target: Doc | undefined, addDoc: (newDoc: Doc | Doc[]) => any) => addDoc(doc) : this.props.moveDocument, + this._document[props.fieldKey] instanceof Doc ? "alias" : this.props.Document.schemaDoc ? "copy" : undefined)(e); + } }; const onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) { @@ -281,27 +318,98 @@ export class CollectionSchemaCell extends React.Component<CellProps> { positions.pop(); } } + let search = false; + if (this.props.Document._searchDoc !== undefined) { + search = true; + } + + return ( <div className="collectionSchemaView-cellContainer" style={{ cursor: fieldIsDoc ? "grab" : "auto" }} ref={dragRef} onPointerDown={this.onPointerDown} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}> <div className={className} ref={this._focusRef} onPointerDown={onItemDown} tabIndex={-1}> - <div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}> - <EditableView - positions={positions.length > 0 ? positions : undefined} - search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined} - editing={this._isEditing} - isEditingCallback={this.isEditingCallback} - display={"inline"} - contents={contents ? contents : type === "number" ? "0" : "undefined"} - highlight={positions.length > 0 ? true : undefined} - //contents={StrCast(contents)} - height={"auto"} - maxHeight={Number(MAX_ROW_HEIGHT)} - placeholder={"enter value"} - bing={() => { + <div className="collectionSchemaView-cellContents" + ref={type === undefined || type === "document" ? this.dropRef : null} key={props.Document[Id]}> + {!search ? + <EditableView + positions={positions.length > 0 ? positions : undefined} + search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined} + editing={this._isEditing} + isEditingCallback={this.isEditingCallback} + display={"inline"} + contents={contents ? contents : type === "number" ? "0" : "undefined"} + highlight={positions.length > 0 ? true : undefined} + //contents={StrCast(contents)} + height={"auto"} + maxHeight={Number(MAX_ROW_HEIGHT)} + placeholder={"undefined"} + bing={() => { + const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); + if (cfield !== undefined) { + // if (typeof(cfield)===RichTextField) + const a = cfield as RichTextField; + if (a.Text !== undefined) { + return (a.Text); + } + else if (StrCast(cfield)) { + return StrCast(cfield); + } + else { + return String(NumCast(cfield)); + } + } + }} + GetValue={() => { + if (type === "number" && (contents === 0 || contents === "0")) { + return "0"; + } else { + const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); + if (type === "number") { + return StrCast(cfield); + } + const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined; + const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1]; + const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : + Field.IsField(cfield) ? Field.toScriptString(cfield) : ""; + return val; + + } + + }} + SetValue={action((value: string) => { + let retVal = false; + + if (value.startsWith(":=")) { + retVal = this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col); + } else { + const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + if (script.compiled) { + retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run); + } + + } + if (retVal) { + this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' + this.props.setIsEditing(false); + } + return retVal; + + //return true; + })} + OnFillDown={async (value: string) => { + const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); + if (script.compiled) { + DocListCast(this.props.Document[this.props.fieldKey]). + forEach((doc, i) => value.startsWith(":=") ? + this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) : + this.applyToDoc(doc, i, this.props.col, script.run)); + } + }} + /> + : + this.returnHighlights(() => { const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); if (cfield !== undefined) { - console.log(typeof (cfield)); // if (typeof(cfield)===RichTextField) const a = cfield as RichTextField; if (a.Text !== undefined) { @@ -314,56 +422,11 @@ export class CollectionSchemaCell extends React.Component<CellProps> { return String(NumCast(cfield)); } } - }} - GetValue={() => { - if (type === "number" && (contents === 0 || contents === "0")) { - return "0"; - } else { - const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); - if (type === "number") { - return StrCast(cfield); - } - const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined; - const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1]; - const val = cscript !== undefined ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : - Field.IsField(cfield) ? Field.toScriptString(cfield) : ""; - return val; - + else { + return ""; } - - }} - SetValue={action((value: string) => { - let retVal = false; - - if (value.startsWith(":=")) { - retVal = this.props.setComputed(value.substring(2), props.Document, this.props.rowProps.column.id!, this.props.row, this.props.col); - } else { - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - if (script.compiled) { - retVal = this.applyToDoc(props.Document, this.props.row, this.props.col, script.run); - } - - } - if (retVal) { - this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' - this.props.setIsEditing(false); - } - return retVal; - - //return true; - })} - OnFillDown={async (value: string) => { - const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - if (script.compiled) { - DocListCast(this.props.Document[this.props.fieldKey]). - forEach((doc, i) => value.startsWith(":=") ? - this.props.setComputed(value.substring(2), doc, this.props.rowProps.column.id!, i, this.props.col) : - this.applyToDoc(doc, i, this.props.col, script.run)); - } - }} - /> - - + }, positions) + } </div > {/* {fieldIsDoc ? docExpander : null} */} </div> @@ -903,7 +966,6 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { }}><button onClick={() => { doc.searchMatch = false; setTimeout(() => doc.searchMatch = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); }} style={{ padding: 2, left: 77 }}> <FontAwesomeIcon icon="arrow-up" size="sm" /> </button> @@ -911,7 +973,6 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { { doc.searchMatchAlt = false; setTimeout(() => doc.searchMatchAlt = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); } }} style={{ padding: 2 }}> <FontAwesomeIcon icon="arrow-down" size="sm" /> @@ -929,7 +990,6 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { onClick={() => { doc.searchMatch = false; setTimeout(() => doc.searchMatch = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); }}> <FontAwesomeIcon icon="arrow-down" size="sm" /> </button></div >; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index e65adcf76..b7d6f212f 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -1,5 +1,5 @@ import React = require("react"); -import { action, observable } from "mobx"; +import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import "./CollectionSchemaView.scss"; import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl, faCalendar } from '@fortawesome/free-solid-svg-icons'; @@ -9,8 +9,21 @@ import { ColumnType } from "./CollectionSchemaView"; import { faFile } from "@fortawesome/free-regular-svg-icons"; import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; -import { Doc } from "../../../fields/Doc"; -import { StrCast } from "../../../fields/Types"; +import { Transform } from '../../util/Transform'; +import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; +import { StrCast, Cast } from "../../../fields/Types"; +import { optionFocusAriaMessage } from "react-select/src/accessibility"; +import { TraceMobx } from "../../../fields/util"; +import { CollectionTreeView } from "./CollectionTreeView"; +import { returnEmptyFilter, returnFalse, emptyPath, returnZero, emptyFunction, returnOne } from "../../../Utils"; +import { RichTextField } from "../../../fields/RichTextField"; +import { Docs } from "../../documents/Documents"; +import { List } from "../../../fields/List"; +import { listSpec } from "../../../fields/Schema"; +import { ScriptField, ComputedField } from "../../../fields/ScriptField"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { CollectionView } from "./CollectionView"; + const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -286,6 +299,12 @@ export interface KeysDropdownProps { setIsEditing: (isEditing: boolean) => void; width?: string; docs?: Doc[]; + Document?: Doc; + dataDoc?: Doc; + fieldKey?: string; + ContainingCollectionDoc?: Doc; + ContainingCollectionView?: CollectionView; + active?: (outsideReaction?: boolean) => boolean; } @observer export class KeysDropdown extends React.Component<KeysDropdownProps> { @@ -301,8 +320,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { @action onSelect = (key: string): void => { - if (key.slice(0, this._key.length) === this._key && this._key !== key) { - const filter = key.slice(this._key.length - key.length); + if (this._searchTerm.includes(":")) { + const colpos = this._searchTerm.indexOf(":"); + const filter = this._searchTerm.slice(colpos + 1, this._searchTerm.length); + //const filter = key.slice(this._key.length - key.length); this.props.onSelect(this._key, this._key, this.props.addNew, filter); } else { @@ -314,16 +335,21 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { } @action - onSelect2 = (key: string): void => { - this._searchTerm = this._searchTerm.slice(0, this._key.length) + key; + onSelectValue = (key: string): void => { + const colpos = this._searchTerm.indexOf(":"); + this.onSelect(key); + this._searchTerm = this._searchTerm.slice(0, colpos + 1) + key; this._isOpen = false; + this.props.onSelect(this._key, this._key, this.props.addNew, key); } @undoBatch onKeyDown = (e: React.KeyboardEvent): void => { if (e.key === "Enter") { - const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + let blockedkeys = ["_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)); if (keyOptions.length) { this.onSelect(keyOptions[0]); } else if (this._searchTerm !== "" && this.props.canAddNew) { @@ -360,20 +386,25 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { onPointerOut = (e: React.PointerEvent): void => { this._canClose = true; } - + @action renderOptions = (): JSX.Element[] | JSX.Element => { - if (!this._isOpen) return <></>; - + if (!this._isOpen) { + this.defaultMenuHeight = 0; + return <></>; + } const searchTerm = this._searchTerm.trim() === "New field" ? "" : this._searchTerm; - const keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); + let keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 || this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1; + let 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 options = keyOptions.map(key => { return <div key={key} className="key-option" style={{ border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" + width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", }} onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect(key); this.setSearchTerm(""); }}>{key}</div>; }); @@ -382,49 +413,242 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { if (this._key !== this._searchTerm.slice(0, this._key.length)) { if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) { options.push(<div key={""} className="key-option" style={{ - border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" + border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", }} onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}> Create "{this._searchTerm}" key</div>); } } + if (options.length === 0) { + this.defaultMenuHeight = 0; + } + else { + if (this.props.docs) { + let panesize = this.props.docs.length * 30; + options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8; + } + else { + options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8; + } + } return options; } - + @action renderFilterOptions = (): JSX.Element[] | JSX.Element => { - if (!this._isOpen) return <></>; - const keyOptions: string[] = []; - const temp = this._searchTerm.slice(this._key.length); - this.props.docs?.forEach((doc) => { + if (!this._isOpen) { + this.defaultMenuHeight = 0; + return <></>; + } + let keyOptions: string[] = []; + const colpos = this._searchTerm.indexOf(":"); + const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); + let docs = DocListCast(this.props.dataDoc![this.props.fieldKey!]); + docs.forEach((doc) => { const key = StrCast(doc[this._key]); if (keyOptions.includes(key) === false && key.includes(temp)) { keyOptions.push(key); } }); - + //checked={() =>{Cast(this.props.Document!._docRangeFilters, listSpec("string"))!.includes(key)}} const options = keyOptions.map(key => { + //Doc.setDocFilter(this.props.Document!, this._key, key, undefined); return <div key={key} className="key-option" style={{ - border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden" + border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white", }} - onPointerDown={e => e.stopPropagation()} onClick={() => { this.onSelect2(key); }}>{key}</div>; + > + <input type="checkbox" onChange={(e) => { e.target.checked === true ? Doc.setDocFilter(this.props.Document!, this._key, key, "check") : Doc.setDocFilter(this.props.Document!, this._key, key, undefined) }} ></input> + {key} + + </div>; }); + if (options.length === 0) { + this.defaultMenuHeight = 0; + } + else { + if (this.props.docs) { + let panesize = this.props.docs.length * 30; + options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8; + } + else { + options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8; + } + + } return options; } + // @action + // renderFilterOptions = (): JSX.Element[] | JSX.Element => { + // this.facetClick(this._key); + // return <div> + // {this.filterView} + // </div> + // } + @observable defaultMenuHeight = 0; + + + facetClick = (facetHeader: string) => { + facetHeader = this._key; + let newFacet: Opt<Doc>; + if (this.props.Document !== undefined) { + const facetCollection = this.props.Document; + // const found = DocListCast(facetCollection[this.props.fieldKey + "-filter"]).findIndex(doc => doc.title === facetHeader); + // if (found !== -1) { + // console.log("found not n-1"); + // (facetCollection[this.props.fieldKey + "-filter"] as List<Doc>).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); + // } + // } + // } + + + + console.log("found is n-1"); + const allCollectionDocs = DocListCast(this.props.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<string>())); + + 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); + } + }); + if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) { + console.log("Case1"); + 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) { + console.log("Case2"); + 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 { + console.log("Case3"); + newFacet = new Doc(); + newFacet.title = facetHeader; + newFacet.treeViewOpen = true; + newFacet.type = DocumentType.COL; + const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.props.dataDoc! }; + this.props.Document.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, {}, capturedVariables); + // this.props.Document.data + } + //newFacet && Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); + } + } + + + get ignoreFields() { return ["_docFilters", "_docRangeFilters"]; } + + @computed get scriptField() { + console.log("we kinda made it"); + 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; + } + filterBackground = () => "rgba(105, 105, 105, 0.432)"; + + + // @computed get filterView() { + // TraceMobx(); + // if (this.props.Document !== undefined) { + // //const facetCollection = this.props.Document; + // // const flyout = ( + // // <div className="collectionTimeView-flyout" style={{ width: `${this.facetWidth()}`, height: this.props.PanelHeight() - 30 }} onWheel={e => e.stopPropagation()}> + // // {this._allFacets.map(facet => <label className="collectionTimeView-flyout-item" key={`${facet}`} onClick={e => this.facetClick(facet)}> + // // <input type="checkbox" onChange={e => { }} checked={DocListCast(this.props.Document[this.props.fieldKey + "-filter"]).some(d => d.title === facet)} /> + // // <span className="checkmark" /> + // // {facet} + // // </label>)} + // // </div> + // // ); + // return <div className="altcollectionTimeView-treeView"> + // <div className="altcollectionTimeView-tree" key="tree"> + // <CollectionTreeView + // PanelPosition={""} + // Document={this.props.Document} + // DataDoc={this.props.Document} + // fieldKey={this.props.fieldKey!} + // CollectionView={undefined} + // docFilters={returnEmptyFilter} + // ContainingCollectionDoc={this.props.ContainingCollectionDoc!} + // ContainingCollectionView={this.props.ContainingCollectionView!} + // PanelWidth={() => 200} + // PanelHeight={() => 200} + // NativeHeight={returnZero} + // NativeWidth={returnZero} + // LibraryPath={emptyPath} + // rootSelected={returnFalse} + // renderDepth={1} + // dropAction={undefined} + // ScreenToLocalTransform={Transform.Identity} + // addDocTab={returnFalse} + // pinToPres={returnFalse} + // isSelected={returnFalse} + // select={returnFalse} + // bringToFront={emptyFunction} + // active={this.props.active!} + // whenActiveChanged={returnFalse} + // treeViewHideTitle={true} + // ContentScaling={returnOne} + // focus={returnFalse} + // treeViewHideHeaderFields={true} + // onCheckedClick={this.scriptField} + // ignoreFields={this.ignoreFields} + // annotationsKey={""} + // dontRegisterView={true} + // backgroundColor={this.filterBackground} + // moveDocument={returnFalse} + // removeDocument={returnFalse} + // addDocument={returnFalse} /> + // </div> + // </div>; + // } + // } + render() { return ( <div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}> - {this._key === this._searchTerm.slice(0, this._key.length) ? - <div style={{ position: "absolute", marginLeft: "4px", marginTop: "3", color: "grey", pointerEvents: "none", lineHeight: 1.15 }}> - {this._key} - </div> - : undefined} <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)} @@ -433,11 +657,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { e.stopPropagation(); }} onFocus={this.onFocus} onBlur={this.onBlur}></input> <div className="keys-options-wrapper" style={{ - backgroundColor: "white", - width: this.props.width, maxWidth: this.props.width, + width: this.props.width, maxWidth: this.props.width, height: "auto", }} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}> - {this._key === this._searchTerm.slice(0, this._key.length) ? + {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()} </div> </div > diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index ba0a259c5..ee643b86b 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -43,6 +43,49 @@ // } } +.collectionSchemaView-searchContainer { + border-width: $COLLECTION_BORDER_WIDTH; + border-color: $intermediate-color; + border-style: solid; + border-radius: $border-radius; + box-sizing: border-box; + position: relative; + top: 0; + width: 100%; + height: 100%; + margin-top: 0; + transition: top 0.5s; + display: flex; + justify-content: space-between; + flex-wrap: nowrap; + touch-action: none; + + div { + touch-action: none; + } + + + .collectionSchemaView-tableContainer { + width: 100%; + height: 100%; + } + + .collectionSchemaView-dividerDragger { + position: relative; + height: 100%; + width: 20px; + z-index: 20; + right: 0; + top: 0; + background: gray; + cursor: col-resize; + } + + // .documentView-node:first-child { + // background: $light-color; + // } +} + .ReactTable { width: 100%; background: white; @@ -165,7 +208,7 @@ .collectionSchema-header-menu { - height: 100%; + height: auto; z-index: 100; position: absolute; background:white; @@ -280,7 +323,6 @@ button.add-column { background-color: white; .key-option { - //background-color: $light-color; background-color: white; border: 1px solid lightgray; padding: 2px 3px; @@ -315,6 +357,47 @@ button.add-column { } } +.altcollectionTimeView-treeView { + display: flex; + flex-direction: column; + width: 175px; + height: auto; + position: fixed; + 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%; + } + } + + .altcollectionTimeView-tree { + display: inline-block; + width: 100%; + height: calc(100% - 30px); + } +} + .collectionSchema-row { height: 100%; background-color: white; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index a003de0d3..3f879b489 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -11,7 +11,7 @@ import { Doc } from "../../../fields/Doc"; import { List } from "../../../fields/List"; import { listSpec } from "../../../fields/Schema"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; -import { Cast, NumCast } from "../../../fields/Types"; +import { Cast, NumCast, BoolCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents } from "../../../Utils"; import { SnappingManager } from "../../util/SnappingManager"; @@ -71,8 +71,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @observable _pointerY = 0; @observable _openTypes: boolean = false; @computed get menuCoordinates() { - const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)); - const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)); + let searchx = 0; + let searchy = 0; + if (this.props.Document._searchDoc !== undefined) { + let el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0]; + if (el !== undefined) { + let rect = el.getBoundingClientRect(); + searchx = rect.x; + searchy = rect.y + } + } + const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx; + const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy; return this.props.ScreenToLocalTransform().transformPoint(x, y); } @@ -611,14 +621,18 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { onKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => { } render() { + let name = "collectionSchemaView-container"; + if (this.props.Document._searchDoc !== undefined) { + name = "collectionSchemaView-searchContainer"; + } TraceMobx(); const menuContent = this.renderMenuContent; const menu = <div className="collectionSchema-header-menu" ref={this.setNode} onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ - position: "fixed", background: "white", - transform: `translate(${this.menuCoordinates[0] / this.scale}px, ${this.menuCoordinates[1] / this.scale}px)` + position: "fixed", background: "white", border: "black 1px solid", + transform: `translate(${(this.menuCoordinates[0] / this.scale)}px, ${(this.menuCoordinates[1] / this.scale)}px)` }}> <Measure offset onResize={action((r: any) => { const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); @@ -627,9 +641,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>} </Measure> </div>; - return <div className="collectionSchemaView-container" + return <div className={name} style={{ - overflow: this.props.overflow === true ? "auto" : undefined, + overflow: this.props.overflow === true ? "scroll" : undefined, pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined, width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} > diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d1aeb1b65..9be5a5d99 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -112,10 +112,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; } @computed get childDocs() { - let rawdocs: (Doc | Promise<Doc>)[] = DocListCast(this.props.Document._searchDocs); - - if (rawdocs.length !== 0) { - } else if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; + let rawdocs: (Doc | Promise<Doc>)[] = []; + if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; rawdocs = [this.dataField]; } else if (Cast(this.dataField, listSpec(Doc), null)) { // otherwise, if the collection data is a list, then use it. rawdocs = Cast(this.dataField, listSpec(Doc), null); @@ -131,27 +129,17 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: let childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; const searchDocs = DocListCast(this.props.Document._searchDocs); - // if (searchDocs !== undefined && searchDocs.length > 0) { - // let newdocs: Doc[] = []; - // childDocs.forEach((el) => { - // searchDocs.includes(el) ? newdocs.push(el) : undefined; - // }); - // childDocs = newdocs; - // } + let docsforFilter: Doc[] = childDocs; if (searchDocs !== undefined && searchDocs.length > 0) { docsforFilter = []; - // let newdocs: Doc[] = []; - // let newarray: Doc[] = []; - //while (childDocs.length > 0) { - //newarray = []; + childDocs.forEach((d) => { if (d.data !== undefined) { - console.log(d); let newdocs = DocListCast(d.data); if (newdocs.length > 0) { - let vibecheck: boolean | undefined = undefined; + let displaycheck: boolean | undefined = undefined; let newarray: Doc[] = []; while (newdocs.length > 0) { newarray = []; @@ -163,12 +151,12 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: }); } if (searchDocs.includes(t)) { - vibecheck = true; + displaycheck = true; } }); newdocs = newarray; } - if (vibecheck === true) { + if (displaycheck === true) { docsforFilter.push(d); } } @@ -177,14 +165,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: docsforFilter.push(d); } }); - //childDocs = newarray; - //} + } childDocs = docsforFilter; const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); - return this.props.Document.dontRegisterView ? docs : DocUtils.FilterDocs(docs, this.docFilters(), docRangeFilters, viewSpecScript); + return this.props.Document.dontRegisterView ? childDocs : DocUtils.FilterDocs(childDocs, this.docFilters(), docRangeFilters, viewSpecScript); } @action diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 0feec3fbd..fe30cb05a 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -46,6 +46,7 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; import { ContextMenuProps } from '../ContextMenuItem'; +import { table } from 'console'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -182,6 +183,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus // targetDataDoc[this.props.fieldKey] = new List<Doc>([...docList, ...added]); (targetDataDoc[this.props.fieldKey] as List<Doc>).push(...added); targetDataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now())); + const lastModified = "lastModified"; + targetDataDoc[lastModified] = new DateField(new Date(Date.now())); } } } @@ -510,56 +513,56 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus </label>)} </div> ); - return !this._facetWidth || this.props.dontRegisterView ? (null) : - <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}> - <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}> - <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}> - <div className="collectionTimeView-button"> - <FontAwesomeIcon icon={faEdit} size={"lg"} /> - <span className="collectionTimeView-span">Facet Filters</span> - </div> - </Flyout> - </div> - <div className="collectionTimeView-tree" key="tree"> - <CollectionTreeView - PanelPosition={""} - Document={facetCollection} - DataDoc={facetCollection} - fieldKey={`${this.props.fieldKey}-filter`} - CollectionView={this} - docFilters={returnEmptyFilter} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} - ContainingCollectionView={this.props.ContainingCollectionView} - PanelWidth={this.facetWidth} - PanelHeight={this.props.PanelHeight} - NativeHeight={returnZero} - NativeWidth={returnZero} - LibraryPath={emptyPath} - rootSelected={this.props.rootSelected} - renderDepth={1} - dropAction={this.props.dropAction} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} - addDocTab={returnFalse} - pinToPres={returnFalse} - isSelected={returnFalse} - select={returnFalse} - bringToFront={emptyFunction} - active={this.props.active} - whenActiveChanged={returnFalse} - treeViewHideTitle={true} - ContentScaling={returnOne} - focus={returnFalse} - treeViewHideHeaderFields={true} - onCheckedClick={this.scriptField} - ignoreFields={this.ignoreFields} - annotationsKey={""} - dontRegisterView={true} - backgroundColor={this.filterBackground} - moveDocument={returnFalse} - removeDocument={returnFalse} - addDocument={returnFalse} /> - </div> - </div>; + + return !this._facetWidth || this.props.dontRegisterView ? (null) : <div className="collectionTimeView-treeView" style={{ width: `${this.facetWidth()}px`, overflow: this.facetWidth() < 15 ? "hidden" : undefined }}> + <div className="collectionTimeView-addFacet" style={{ width: `${this.facetWidth()}px` }} onPointerDown={e => e.stopPropagation()}> + <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout}> + <div className="collectionTimeView-button"> + <FontAwesomeIcon icon={faEdit} size={"lg"} /> + <span className="collectionTimeView-span">Facet Filters</span> + </div> + </Flyout> + </div> + <div className="collectionTimeView-tree" key="tree"> + <CollectionTreeView + PanelPosition={""} + Document={facetCollection} + DataDoc={facetCollection} + fieldKey={`${this.props.fieldKey}-filter`} + CollectionView={this} + docFilters={returnEmptyFilter} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + ContainingCollectionView={this.props.ContainingCollectionView} + PanelWidth={this.facetWidth} + PanelHeight={this.props.PanelHeight} + NativeHeight={returnZero} + NativeWidth={returnZero} + LibraryPath={emptyPath} + rootSelected={this.props.rootSelected} + renderDepth={1} + dropAction={this.props.dropAction} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} + addDocTab={returnFalse} + pinToPres={returnFalse} + isSelected={returnFalse} + select={returnFalse} + bringToFront={emptyFunction} + active={this.props.active} + whenActiveChanged={returnFalse} + treeViewHideTitle={true} + ContentScaling={returnOne} + focus={returnFalse} + treeViewHideHeaderFields={true} + onCheckedClick={this.scriptField} + ignoreFields={this.ignoreFields} + annotationsKey={""} + dontRegisterView={true} + backgroundColor={this.filterBackground} + moveDocument={returnFalse} + removeDocument={returnFalse} + addDocument={returnFalse} /> + </div> + </div>; } childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index a974c5496..d8756aae3 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -177,9 +177,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } ); } + this.props.active const cols = this.props.columns.map(col => { - const keysDropdown = <KeysDropdown keyValue={col.heading} possibleKeys={possibleKeys} @@ -189,6 +189,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> { onSelect={this.props.changeColumns} setIsEditing={this.props.setHeaderIsEditing} docs={this.props.childDocs} + Document={this.props.Document} + dataDoc={this.props.dataDoc} + fieldKey={this.props.fieldKey} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + ContainingCollectionView={this.props.ContainingCollectionView} + active={this.props.active} + + // try commenting this out width={"100%"} />; @@ -218,19 +226,15 @@ export class SchemaTable extends React.Component<SchemaTableProps> { background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}> - <FontAwesomeIcon icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px" }} /> + <FontAwesomeIcon onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> {/* <div className="keys-dropdown" style={{ display: "inline", zIndex: 1000 }}> */} {keysDropdown} {/* </div> */} <div onClick={e => this.changeSorting(col)} - style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit" }}> + style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}> <FontAwesomeIcon icon={sortIcon} size="lg" /> </div> - {/* <div onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} - style={{ float: "right", paddingRight: "6px", zIndex: 1, background: "inherit" }}> - <FontAwesomeIcon icon={"compass"} size="sm" /> - </div> */} </div>; return { @@ -318,27 +322,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> { } - - @action - nextHighlight = (e: React.MouseEvent, doc: Doc) => { - e.preventDefault(); - e.stopPropagation(); - doc.searchMatch = false; - console.log(doc.searchMatch); - setTimeout(() => doc.searchMatch = true, 0); - console.log(doc.searchMatch); - - doc.searchIndex = NumCast(doc.searchIndex); - } - - @action - nextHighlight2 = (doc: Doc) => { - - doc.searchMatchAlt = false; - setTimeout(() => doc.searchMatchAlt = true, 0); - doc.searchIndex = NumCast(doc.searchIndex); - } - constructor(props: SchemaTableProps) { super(props); // convert old schema columns (list of strings) into new schema columns (list of schema header fields) diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 7d4bd5dd3..31d69327b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -23,7 +23,7 @@ import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from "../../../../fields/RichTextField"; import { RichTextUtils } from '../../../../fields/RichTextUtils'; import { createSchema, makeInterface } from "../../../../fields/Schema"; -import { Cast, DateCast, NumCast, StrCast, ScriptCast } from "../../../../fields/Types"; +import { Cast, DateCast, NumCast, StrCast, ScriptCast, BoolCast } from "../../../../fields/Types"; import { TraceMobx, OVERRIDE_ACL, GetEffectiveAcl } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; @@ -376,6 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public highlightSearchTerms = (terms: string[], alt: boolean) => { if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) { + const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); const activeMark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight, { selected: true }); const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); @@ -383,30 +384,32 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = this._editorView.state.tr; const flattened: TextSelection[] = []; res.map(r => r.map(h => flattened.push(h))); + if (BoolCast(Doc.GetProto(this.dataDoc).resetSearch) === true) { + this._searchIndex = 0; + Doc.GetProto(this.dataDoc).resetSearch = undefined; + } + else { + this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; + if (alt === true) { + if (this._searchIndex > 1) { + this._searchIndex += -2; + } + else if (this._searchIndex === 1) { + this._searchIndex = length - 1; + } + else if (this._searchIndex === 0 && length !== 1) { + this._searchIndex = length - 2; + } + + } + } const lastSel = Math.min(flattened.length - 1, this._searchIndex); flattened.forEach((h: TextSelection, ind: number) => tr = tr.addMark(h.from, h.to, ind === lastSel ? activeMark : mark)); - this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(flattened[lastSel].from), tr.doc.resolve(flattened[lastSel].to))).scrollIntoView()); - if (alt === true) { - if (this._searchIndex > 1) { - this._searchIndex += -2; - } - else if (this._searchIndex === 1) { - this._searchIndex = length - 1; - } - else if (this._searchIndex === 0 && length !== 1) { - this._searchIndex = length - 2; - } - - } - else { - - } - const index = this._searchIndex; - Doc.GetProto(this.dataDoc).searchIndex = index; + console.log(this._searchIndex); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index cfa9a1844..7c1b2f621 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -337,7 +337,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu nextAnnotation = () => { this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1); this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); - this.Document.searchIndex = this.Index; } @@ -411,7 +410,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu phraseSearch: true, query: searchString }); - this.Document.searchIndex = this.Index; } else if (this._mainCont.current) { const executeFind = () => { @@ -425,7 +423,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu }; this._mainCont.current.addEventListener("pagesloaded", executeFind); this._mainCont.current.addEventListener("pagerendered", executeFind); - this.Document.searchIndex = this.Index; } } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index f7b817aa1..6d6f32da7 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -235,7 +235,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc @action filterDocsByType(docs: Doc[]) { const finalDocs: Doc[] = []; - const blockedTypes: string[] = ["preselement", "docholder", "collection", "search", "searchitem", "script", "fonticonbox", "button", "label"]; + const blockedTypes: string[] = ["preselement", "docholder", "search", "searchitem", "script", "fonticonbox", "button", "label"]; docs.forEach(doc => { const layoutresult = Cast(doc.type, "string"); if (layoutresult && !blockedTypes.includes(layoutresult)) { @@ -299,6 +299,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc searchCollection(query: string) { const selectedCollection: DocumentView = SelectionManager.SelectedDocuments()[0]; query = query.toLowerCase(); + if (selectedCollection !== undefined) { this.currentSelectedCollection = selectedCollection; if (this.filter === true) { @@ -312,6 +313,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc while (docs.length > 0) { newarray = []; docs.forEach((d) => { + d ? console.log(Cast(d.context, Doc)) : null; if (d.data !== undefined) { newarray.push(...DocListCast(d.data)); } @@ -586,7 +588,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc y += 300; } } - return Docs.Create.SearchDocument({ _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString), searchQuery: StrCast(this.layoutDoc._searchString) }); + return Docs.Create.SchemaDocument(Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []), DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) }); } @action.bound @@ -628,7 +630,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc const endIndex = 30; //this._endIndex = endIndex === -1 ? 12 : endIndex; this._endIndex = 30; - const headers = new Set<string>(["title", "author", "*lastModified", "text"]); + const headers = new Set<string>(["title", "author", "*lastModified"]); if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) { if (this.noresults === "") { this.noresults = "No search results :("; @@ -749,7 +751,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc @observable filter = false; - //Make id layour document render() { this.props.Document._chromeStatus === "disabled"; this.props.Document._searchDoc = true; @@ -758,44 +759,63 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc cols > 5 ? length = 1076 : length = cols * 205 + 51; let height = 0; const rows = this.children; - rows > 8 ? height = 31 + 31 * 8 : height = 31 * rows + 31; + rows > 6 ? height = 31 + 31 * 6 : height = 31 * rows + 31; return ( <div style={{ pointerEvents: "all" }} className="searchBox-container"> <div className="searchBox-bar"> <div style={{ position: "absolute", left: 15 }}>{Doc.CurrentUserEmail}</div> <div style={{ display: "flex", alignItems: "center" }}> - <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg" - style={{ color: "black", padding: 1, left: 35, position: "relative" }} /> - + <Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>} ><div> + <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg" + style={{ cursor: "hand", color: "black", padding: 1, left: 35, position: "relative" }} /> + </div></Tooltip> <div style={{ cursor: "default", left: 250, position: "relative", }}> <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} ><div> <FontAwesomeIcon icon={"filter"} size="lg" - style={{ padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }} + style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }} onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined); }} onClick={action(() => { - const dofilter = (currentSelectedCollection: DocumentView) => { - let docs = DocListCast(currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(currentSelectedCollection.dataDoc)]); + ///DONT Change without emailing andy r first. + this.filter = !this.filter && !this.searchFullDB; + if (this.filter === true && this.currentSelectedCollection !== undefined) { + this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>(this.docsforfilter); + let newarray: Doc[] = []; + let docs: Doc[] = []; + docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]); while (docs.length > 0) { - const newarray: Doc[] = []; - docs.filter(d => d.data !== undefined).forEach((d) => { - d._searchDocs = new List<Doc>(this.docsforfilter); - newarray.push(...DocListCast(d.data)); + newarray = []; + docs.forEach((d) => { + if (d.data !== undefined) { + d._searchDocs = new List<Doc>(this.docsforfilter); + const newdocs = DocListCast(d.data); + newdocs.forEach((newdoc) => { + newarray.push(newdoc); + }); + } }); docs = newarray; } - }; - this.filter = !this.filter && !this.searchFullDB; - if (this.filter === true && this.currentSelectedCollection !== undefined) { - this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>(this.docsforfilter); - - dofilter(this.currentSelectedCollection); this.currentSelectedCollection.props.Document._docFilters = new List<string>(Cast(this.props.Document._docFilters, listSpec("string"), [])); this.props.Document.selectedDoc = this.currentSelectedCollection.props.Document; } else if (this.filter === false && this.currentSelectedCollection !== undefined) { - - dofilter(this.currentSelectedCollection); + let newarray: Doc[] = []; + let docs: Doc[] = []; + docs = DocListCast(this.currentSelectedCollection.dataDoc[Doc.LayoutFieldKey(this.currentSelectedCollection.dataDoc)]); + while (docs.length > 0) { + newarray = []; + docs.forEach((d) => { + if (d.data !== undefined) { + d._searchDocs = new List<Doc>(); + const newdocs = DocListCast(d.data); + newdocs.forEach((newdoc) => { + newarray.push(newdoc); + }); + } + }); + docs = newarray; + } this.currentSelectedCollection.props.Document._searchDocs = new List<Doc>([]); this.currentSelectedCollection.props.Document._docFilters = undefined; @@ -900,7 +920,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc removeDocument={returnFalse} PanelHeight={this.open === true ? () => height : () => 0} PanelWidth={this.open === true ? () => length : () => 0} - overflow={cols > 5 || rows > 8 ? true : false} + overflow={cols > 5 || rows > 6 ? true : false} focus={this.selectElement} ScreenToLocalTransform={Transform.Identity} /> @@ -920,4 +940,4 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc </div > ); } -} +}
\ No newline at end of file diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 63cfa41f0..798c79164 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -230,8 +230,7 @@ export namespace WebSocket { "script": ["_t", value => value.script.originalScript], "RichTextField": ["_t", value => value.Text], "date": ["_d", value => new Date(value.date).toISOString()], - // "proxy": ["_i", "fieldId"], - // "proxy": ["", "fieldId"], + "proxy": ["_i", "fieldId"], "list": ["_l", list => { const results = []; for (const value of list.fields) { |