From 5b4b916b2700da746c12754ba3238eb1f3bbf5f7 Mon Sep 17 00:00:00 2001 From: usodhi <61431818+usodhi@users.noreply.github.com> Date: Sat, 15 Aug 2020 01:42:57 +0530 Subject: many changes to acls, aliases, sharing sidebar etc --- src/fields/util.ts | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'src/fields') diff --git a/src/fields/util.ts b/src/fields/util.ts index 3d832636f..561d505e3 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -154,14 +154,16 @@ export enum SharingPermissions { /** * Calculates the effective access right to a document for the current user. */ -export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number): symbol { +export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number, user?: string): symbol { if (!target) return AclPrivate; if (in_prop === UpdatingFromServer || target[UpdatingFromServer]) return AclAdmin; if (target[AclSym] && Object.keys(target[AclSym]).length) { + const userChecked = user || Doc.CurrentUserEmail; + // if the current user is the author of the document / the current user is a member of the admin group - if (Doc.CurrentUserEmail === (target.__fields?.author || target.author) || currentUserGroups.includes("admin")) return AclAdmin; + if (userChecked === (target.__fields?.author || target.author) || currentUserGroups.includes("admin")) return AclAdmin; // if the ACL is being overriden or the property being modified is one of the playground fields (which can be freely modified) if (_overrideAcl || (in_prop && DocServer.PlaygroundFields?.includes(in_prop.toString()))) return AclEdit; @@ -178,7 +180,7 @@ export function GetEffectiveAcl(target: any, in_prop?: string | symbol | number) for (const [key, value] of Object.entries(target[AclSym])) { // there are issues with storing fields with . in the name, so they are replaced with _ during creation // as a result we need to restore them again during this comparison. - if (currentUserGroups.includes(key.substring(4)) || Doc.CurrentUserEmail === key.substring(4).replace("_", ".")) { + if (currentUserGroups.includes(key.substring(4).replace("_", ".")) || userChecked === key.substring(4).replace("_", ".")) { if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) { effectiveAcl = value as symbol; if (effectiveAcl === AclAdmin) break; @@ -208,52 +210,53 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc ["Admin", 4] ]); - let changed = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym]) + let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym]) + let dataDocChanged = false; const dataDoc = target[DataSym]; // if it is inheriting from a collection, it only inherits if A) the key doesn't already exist or B) the right being inherited is more restrictive if (!inheritingFromCollection || !target[key] || HierarchyMapping.get(StrCast(target[key]))! > HierarchyMapping.get(acl)!) { target[key] = acl; - changed = true; + layoutDocChanged = true; + } + + if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { + dataDoc[key] = acl; + dataDocChanged = true; // maps over the aliases of the document - const aliases = DocListCast(target.aliases); + const aliases = DocListCast(dataDoc.aliases); if (aliases.length) { aliases.map(alias => { alias !== target && distributeAcls(key, acl, alias, inheritingFromCollection); }); } - } - - if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) { - dataDoc[key] = acl; - changed = true; - // maps over the children of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc)]).map(d => { - if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { + if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d, inheritingFromCollection); } const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { + if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, data, inheritingFromCollection); } }); // maps over the annotations of the document DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => { - if (d.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { + if (GetEffectiveAcl(d) === AclAdmin && (!inheritingFromCollection || !d[key] || HierarchyMapping.get(StrCast(d[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, d, inheritingFromCollection); } const data = d[DataSym]; - if (data && data.author === Doc.CurrentUserEmail && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { + if (data && GetEffectiveAcl(data) === AclAdmin && (!inheritingFromCollection || !data[key] || HierarchyMapping.get(StrCast(data[key]))! > HierarchyMapping.get(acl)!)) { distributeAcls(key, acl, data, inheritingFromCollection); } }); } - changed && fetchProto(target); // updates target[AclSym] when changes to acls have been made + layoutDocChanged && fetchProto(target); // updates target[AclSym] when changes to acls have been made + dataDocChanged && fetchProto(dataDoc); } const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", -- cgit v1.2.3-70-g09d2 From 077a38dc22f6082974e639f8a020ceb179116958 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 14 Aug 2020 18:08:47 -0400 Subject: fixed some edge cses with richTextMenu synchronization. added author field to delegates --- src/client/views/collections/CollectionMenu.tsx | 4 +++- src/client/views/nodes/formattedText/RichTextMenu.tsx | 2 +- src/fields/Doc.ts | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 0b3be8739..97d38d27e 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -429,7 +429,9 @@ export class CollectionFreeFormViewChrome extends React.Component { @action public updateMenu(view: EditorView, lastState: EditorState | undefined, props: any) { - if (!view || !(view as any).TextView?.props.isSelected(true)) { + if (!view || !(view as any).TextView?.props.isSelected(true) || !view.hasFocus()) { return; } this.view = view; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index ea53bc9a2..b530b1cf7 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -781,6 +781,7 @@ export namespace Doc { if (doc) { const delegate = new Doc(id, true); delegate.proto = doc; + delegate.author = Doc.CurrentUserEmail; title && (delegate.title = title); return delegate; } -- cgit v1.2.3-70-g09d2 From cd7b13c2c1086739d866e4a6ad9c239a46d89244 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 14 Aug 2020 18:19:20 -0400 Subject: fix to give an author to template doc protos. --- src/fields/Doc.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/fields') diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index b530b1cf7..5f5fe98bc 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -791,7 +791,9 @@ export namespace Doc { let _applyCount: number = 0; export function ApplyTemplate(templateDoc: Doc) { if (templateDoc) { - const target = Doc.MakeDelegate(new Doc()); + const proto = new Doc(); + proto.author = Doc.CurrentUserEmail; + const target = Doc.MakeDelegate(proto); const targetKey = StrCast(templateDoc.layoutKey, "layout"); const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")"); target.layoutKey = targetKey; -- cgit v1.2.3-70-g09d2 From 4e3ed774c9ac3d1365c67125ddc97df9de245f41 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 14 Aug 2020 21:39:18 -0400 Subject: fixed default documents that were missing the 'system' tag. fixed catalog to have default worksapce documents in it. --- src/client/util/CurrentUserUtils.ts | 3 --- src/client/views/DocumentDecorations.tsx | 9 ++++----- src/client/views/MainView.tsx | 8 ++++++-- src/client/views/search/SearchBox.tsx | 7 +++---- src/fields/ScriptField.ts | 1 + 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src/fields') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aff80da75..8931495dc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -861,9 +861,6 @@ export class CurrentUserUtils { title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true })); } - if (doc.activePresentation === undefined) { - doc.activePresentation = Doc.MakeCopy(doc.emptyPresentation as Doc, true); - } } // Sharing sidebar is where shared documents are contained diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3f16dd7e7..e546ca858 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -659,7 +659,6 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (bounds.y > bounds.b) { bounds.y = bounds.b - (this._resizeBorderWidth + this._linkBoxHeight + this._titleHeight); } - let offset = 0; const useRotation = seldoc.rootDoc.type === DocumentType.INK; return (
@@ -674,10 +673,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
{bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
{maximizeIcon} {titleArea} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5a2488bb6..f7ff895a5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -254,6 +254,8 @@ export class MainView extends React.Component { @action createNewWorkspace = async (id?: string) => { + const myCatalog = Doc.UserDoc().myCatalog as Doc; + const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; const workspaceCount = DocListCast(workspaces.data).length + 1; const freeformOptions: DocumentOptions = { @@ -264,8 +266,10 @@ export class MainView extends React.Component { title: "Untitled Collection", }; const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); - + const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [myCatalog] }], { title: `Workspace ${workspaceCount}` }, id, "row"); + Doc.AddDocToList(myCatalog, "data", freeformDoc); + Doc.AddDocToList(myCatalog, "data", presentation); + Doc.UserDoc().activePresentation = presentation; const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const copyWorkspace = ScriptField.MakeScript(`copyWorkspace()`); diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 770a03cb1..084449d04 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -604,13 +604,12 @@ export class SearchBox extends ViewBoxBaseComponent `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`; // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello - const query = [baseExpr, includeDeleted, includeIcons, typeExpr].join(" AND ").replace(/AND $/, ""); + const query = [baseExpr, includeDeleted, typeExpr].join(" AND ").replace(/AND $/, ""); return query; } diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 9391f56ac..1fb71fefb 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -98,6 +98,7 @@ export class ScriptField extends ObjectField { if (script?.options.capturedVariables) { const doc = Doc.assign(new Doc, script.options.capturedVariables); + doc.system = true; this.captures = new ProxyField(doc); } this.setterscript = setterscript; -- cgit v1.2.3-70-g09d2 From ab89ef62d3f11c11f3f6e7162ca0e112bac1517e Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 17 Aug 2020 15:55:17 -0400 Subject: added sort to solr search options. removed delete field assignment when removing Documents. Allowed ToScriptScript and ToString to go through regardless of ACLs --- solr-8.3.1/server/solr/dash/conf/schema.xml | 2 +- src/client/util/SearchUtil.ts | 3 ++- src/client/views/DocComponent.tsx | 1 - src/client/views/collections/CollectionSchemaCells.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 1 - src/client/views/search/SearchBox.tsx | 17 +++++++++++++++-- src/fields/Doc.ts | 16 +++++----------- src/fields/util.ts | 4 ++-- 8 files changed, 26 insertions(+), 20 deletions(-) (limited to 'src/fields') diff --git a/solr-8.3.1/server/solr/dash/conf/schema.xml b/solr-8.3.1/server/solr/dash/conf/schema.xml index 4f6ae57b2..74c4b494c 100644 --- a/solr-8.3.1/server/solr/dash/conf/schema.xml +++ b/solr-8.3.1/server/solr/dash/conf/schema.xml @@ -51,7 +51,7 @@ - + diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 6dafbd58e..ce96ab67b 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -28,6 +28,7 @@ export namespace SearchUtil { start?: number; rows?: number; fq?: string; + sort?: string; allowAliases?: boolean; onlyAliases?: boolean; "facet"?: string; @@ -42,7 +43,7 @@ export namespace SearchUtil { if (options.onlyAliases) { replacedQuery = `{!join from=id to=proto_i}DEFAULT:${replacedQuery}`; } - const gotten = await rp.get(rpquery, { qs: { ...options, sort: "lastModified_d desc", q: replacedQuery } }); + const gotten = await rp.get(rpquery, { qs: { ...options, q: replacedQuery } }); const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten); if (!returnDocs) { return result; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 8c8bb6fde..23ce71c4f 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -135,7 +135,6 @@ export function ViewBoxAnnotatableComponent

{ Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc); recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); - doc.deleted = true; }); return true; } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index d46aa3574..fa4d8c51b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -297,7 +297,7 @@ export class CollectionSchemaCell extends React.Component { let cfield = props.Document[props.fieldKey]; this.type = props.fieldKey; if (StrCast(this.props.Document._searchString).toLowerCase() !== "") { - let term = Field.toString(cfield as Field); + let term = (cfield instanceof Promise) ? "...promise pending..." : Field.toString(cfield as Field); term = term.toLowerCase(); const search = StrCast(this.props.Document._searchString).toLowerCase(); let start = term.indexOf(search); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 9d305145e..2064b0be4 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -208,7 +208,6 @@ export class CollectionView extends Touchable { Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc); recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); - doc.deleted = true; }); return true; } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 07f1485c3..847bda137 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -4,7 +4,7 @@ import { action, computed, observable, runInAction, reaction, IReactionDisposer import { observer } from 'mobx-react'; import * as React from 'react'; import * as rp from 'request-promise'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -24,6 +24,7 @@ import { ViewBoxBaseComponent } from "../DocComponent"; import { DocumentView } from '../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./SearchBox.scss"; +import { ColumnType } from "../collections/CollectionSchemaView"; export const searchSchema = createSchema({ id: "string", @@ -616,6 +617,18 @@ export class SearchBox extends ViewBoxBaseComponent { + switch (type) { + case ColumnType.Date: return "_d"; + case ColumnType.String: return "_t"; + case ColumnType.Boolean: return "_b"; + case ColumnType.Number: return "_n"; + } + } + const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); + return headers.reduce((p: Opt, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined); + } private NumResults = 50; private lockPromise?: Promise; getResults = async (query: string) => { @@ -624,7 +637,7 @@ export class SearchBox extends ViewBoxBaseComponent { while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) { - this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, fq: this.filterQuery, start: this._maxSearchIndex, rows: this.NumResults, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => { + this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, sort: this.primarySort, fq: this.filterQuery, start: 0, rows: this.NumResults, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => { // happens at the beginning this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound; if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 5f5fe98bc..0dcb8ab42 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -195,6 +195,8 @@ export class Doc extends RefField { @observable private ___fieldKeys: any = {}; + @observable + public [AclSym]: { [key: string]: symbol }; private [UpdatingFromServer]: boolean = false; @@ -204,17 +206,9 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym] = (clear?: boolean) => { - if (clear) { - this.___fields = {}; - this.___fieldKeys = {}; - } - return this.___fields; - } - @observable - public [AclSym]: { [key: string]: symbol }; - public [WidthSym] = () => NumCast(this[SelfProxy]._width); - public [HeightSym] = () => NumCast(this[SelfProxy]._height); + public [FieldsSym](clear?: boolean) { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; } + public [WidthSym]() { return NumCast(this[SelfProxy]._width); } + public [HeightSym]() { return NumCast(this[SelfProxy]._height); } public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } public [ToString]() { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } diff --git a/src/fields/util.ts b/src/fields/util.ts index 3d832636f..e3d9ed2ef 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -5,7 +5,7 @@ import { ProxyField, PrefetchProxy } from "./Proxy"; import { RefField } from "./RefField"; import { ObjectField } from "./ObjectField"; import { action, trace } from "mobx"; -import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate } from "./FieldSymbols"; +import { Parent, OnUpdate, Update, Id, SelfProxy, Self, HandleUpdate, ToString, ToScriptString } from "./FieldSymbols"; import { DocServer } from "../client/DocServer"; import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; @@ -286,7 +286,7 @@ export function setter(target: any, in_prop: string | symbol | number, value: an export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { let prop = in_prop; - if (in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop]; + if (in_prop === ToString || in_prop === ToScriptString || in_prop === FieldsSym || in_prop === Id || in_prop === HandleUpdate || in_prop === CachedUpdates) return target.__fields[prop] || target[prop]; if (in_prop === AclSym) return _overrideAcl ? undefined : target[AclSym]; if (GetEffectiveAcl(target) === AclPrivate && !_overrideAcl) return prop === HeightSym || prop === WidthSym ? returnZero : undefined; if (prop === LayoutSym) { -- cgit v1.2.3-70-g09d2 From 681460e3ef16cd6031447108d62a4d160eb3266f Mon Sep 17 00:00:00 2001 From: Melissa Zhang Date: Mon, 17 Aug 2020 14:55:14 -0700 Subject: Fix SchemaView undefined/empty string values --- src/client/views/collections/CollectionSchemaCells.tsx | 9 +++++---- src/client/views/collections/SchemaTable.tsx | 6 +++--- src/fields/RichTextField.ts | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index d46aa3574..545315cfb 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -257,7 +257,7 @@ export class CollectionSchemaCell extends React.Component { }; let contents: any = "incorrect type"; - if (type === undefined) contents = StrCast(field) === "" ? "--" : ; + if (type === undefined) contents = field === undefined ? undefined : Field.toString(field as Field);//StrCast(field) === "" ? "--" : ; if (type === "number") contents = typeof field === "number" ? NumCast(field) : "--" + typeof field + "--"; if (type === "string") { fieldKey === "text" ? @@ -320,6 +320,7 @@ export class CollectionSchemaCell extends React.Component { search = true; } + const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined"; return (

@@ -329,16 +330,16 @@ export class CollectionSchemaCell extends React.Component { {!search ? 0 ? positions : undefined} - search={StrCast(this.props.Document._searchString) ? StrCast(this.props.Document._searchString) : undefined} + search={Cast(this.props.Document._searchString, "string", null)} editing={this._isEditing} isEditingCallback={this.isEditingCallback} display={"inline"} - contents={contents ? contents : type === "number" ? "0" : "undefined"} + contents={contents} highlight={positions.length > 0 ? true : undefined} //contents={StrCast(contents)} height={"auto"} maxHeight={Number(MAX_ROW_HEIGHT)} - placeholder={"undefined"} + placeholder={placeholder} bing={() => { const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey])); if (cfield !== undefined) { diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 763e5a410..abbb7f6b5 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -42,7 +42,7 @@ enum ColumnType { // this map should be used for keys that should have a const type of value const columnTypes: Map = new Map([ - ["title", ColumnType.String], ["text", ColumnType.String], + ["title", ColumnType.String], ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number], ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean], ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] @@ -322,8 +322,8 @@ export class SchemaTable extends React.Component { const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i); this.props.Document._schemaHeaders = new List(newSchemaHeaders); } else if (this.props.Document._schemaHeaders === undefined) { - this.props.Document._schemaHeaders = new List([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb"), - new SchemaHeaderField("text", "#f1efeb"), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]); + this.props.Document._schemaHeaders = new List([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date), + new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]); } } diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts index 2ca5ac082..9c6233af5 100644 --- a/src/fields/RichTextField.ts +++ b/src/fields/RichTextField.ts @@ -28,7 +28,7 @@ export class RichTextField extends ObjectField { } [ToScriptString]() { - return `new RichTextField("${this.Data}", "${this.Text}")`; + return `new RichTextField("${this.Data.replace(/"/g, "'")}", "${this.Text}")`; } [ToString]() { return this.Text; -- cgit v1.2.3-70-g09d2 From 2563f68413594248823b26e68aa3b866ab1566a4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 17 Aug 2020 23:43:57 -0400 Subject: fixed schema view scripts to allow for layout or data doc assignment. fixed rich text field ToScriptField --- src/client/views/collections/CollectionSchemaCells.tsx | 5 +++-- src/client/views/collections/CollectionView.tsx | 1 - src/client/views/search/SearchBox.tsx | 2 +- src/fields/RichTextField.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index c566be7de..f95de5201 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -381,8 +381,9 @@ export class CollectionSchemaCell extends React.Component { 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); + if (value.startsWith(":=") || value.startsWith("=:=")) { + const script = value.substring(value.startsWith("=:=") ? 3 : 2); + retVal = this.props.setComputed(script, value.startsWith(":=") ? Doc.GetProto(props.Document) : 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) { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2064b0be4..0aaceb7f4 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -289,7 +289,6 @@ export class CollectionView extends Touchable func(CollectionViewType.Tree), icon: "tree" }); subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking), icon: "ellipsis-v" }); subItems.push({ description: "Stacking (AutoHeight)", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" }); - subItems.push({ description: "Staff", event: () => func(CollectionViewType.Staff), icon: "music" }); subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" }); subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" }); subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" }); diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 847bda137..000bdd965 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -625,7 +625,7 @@ export class SearchBox extends ViewBoxBaseComponent, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined); } diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts index 9c6233af5..ae5f301d0 100644 --- a/src/fields/RichTextField.ts +++ b/src/fields/RichTextField.ts @@ -28,7 +28,7 @@ export class RichTextField extends ObjectField { } [ToScriptString]() { - return `new RichTextField("${this.Data.replace(/"/g, "'")}", "${this.Text}")`; + return `new RichTextField("${this.Data.replace(/"/g, "\\\"")}", "${this.Text}")`; } [ToString]() { return this.Text; -- cgit v1.2.3-70-g09d2 From e3f5dc881f4fd9e1e8f70e4343a2e2eddf72f2b5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 19 Aug 2020 19:26:34 -0400 Subject: fixed setterscript loading to re-compile --- src/fields/ScriptField.ts | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/fields') diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 1fb71fefb..52730ed00 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -73,6 +73,13 @@ async function deserializeScript(script: ScriptField) { throw new Error("Couldn't compile loaded script"); } (script as any).script = comp; + if (script.setterscript) { + const compset = CompileScript(script.setterscript?.originalScript, script.setterscript.options); + if (!compset.compiled) { + throw new Error("Couldn't compile setter script"); + } + (script as any).setterscript = compset; + } } @scriptingGlobal -- cgit v1.2.3-70-g09d2 From 093b9b03528a2187ac71db0945a031a8298b8002 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 19 Aug 2020 19:58:57 -0400 Subject: fixed errors caused by Doc symFields not being variable functions and thus not having 'this' bound correctly. --- src/client/views/nodes/formattedText/DashFieldView.tsx | 2 +- src/fields/Doc.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 8ae71c035..924079096 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -183,7 +183,7 @@ export class DashFieldViewInternal extends React.Component(); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 0dcb8ab42..4797fb4cb 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -206,11 +206,11 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym](clear?: boolean) { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; } - public [WidthSym]() { return NumCast(this[SelfProxy]._width); } - public [HeightSym]() { return NumCast(this[SelfProxy]._height); } - public [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } - public [ToString]() { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } + public [FieldsSym] = (clear?: boolean) => { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; } + public [WidthSym] = () => { return NumCast(this[SelfProxy]._width); } + public [HeightSym] = () => { return NumCast(this[SelfProxy]._height); } + public [ToScriptString] = () => { return `DOC-"${this[Self][Id]}"-`; } + public [ToString] = () => { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } public get [DataSym]() { const self = this[SelfProxy]; -- cgit v1.2.3-70-g09d2 From da8901bef2d4a0362354c69fe486076a67f8efc4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 19 Aug 2020 22:39:51 -0400 Subject: cleaning lint errors --- src/client/views/PropertiesButtons.tsx | 2 +- src/client/views/collections/CollectionMenu.tsx | 12 ++++++------ src/client/views/collections/CollectionSchemaCells.tsx | 11 ++--------- src/client/views/collections/CollectionSchemaHeaders.tsx | 2 +- src/client/views/collections/SchemaTable.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/PresBox.tsx | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 10 ++++++---- src/fields/Doc.ts | 10 +++++----- 9 files changed, 24 insertions(+), 29 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index e352db562..9e776c652 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -301,7 +301,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const dragData = new DragManager.DocumentDragData([this.selectedDoc]); const [left, top] = [e.clientX, e.clientY]; dragData.dropAction = "alias"; - DragManager.StartDocumentDrag([this._dragRef.current!], dragData, left, top, { + DragManager.StartDocumentDrag([this._dragRef.current], dragData, left, top, { offsetX: dragData.offset[0], offsetY: dragData.offset[1], hideSource: false diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index cf053e1ca..388eda2b3 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -624,10 +624,10 @@ export class CollectionFreeFormViewChrome extends React.Component { @@ -637,7 +637,7 @@ export class CollectionFreeFormViewChrome extends React.Component([url])); @@ -648,7 +648,7 @@ export class CollectionFreeFormViewChrome extends React.Component(annos)); } - this.selectedDoc && (this.selectedDoc["data"] = new WebField(URLy)); + this.selectedDoc && (this.selectedDoc.data = new WebField(URLy)); this.selectedDoc && (this.selectedDoc["data-annotations"] = new List([])); } catch (e) { console.log("WebBox URL error:" + this._url); @@ -682,7 +682,7 @@ export class CollectionFreeFormViewChrome extends React.Component(DocListCast(this.selectedDoc["data-annotations"]))); - this.selectedDoc && (this.selectedDoc["data"] = new WebField(new URL(this._url = future.pop()!))); + this.selectedDoc && (this.selectedDoc.data = new WebField(new URL(this._url = future.pop()!))); this.selectedDoc && (this.selectedDoc["data-annotations"] = new List(DocListCast(this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)]))); } } @@ -695,7 +695,7 @@ export class CollectionFreeFormViewChrome extends React.Component([this._url])); else future.push(this._url); this.selectedDoc && (this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)] = new List(DocListCast(this.selectedDoc["data-annotations"]))); - this.selectedDoc && (this.selectedDoc["data"] = new WebField(new URL(this._url = history.pop()!))); + this.selectedDoc && (this.selectedDoc.data = new WebField(new URL(this._url = history.pop()!))); this.selectedDoc && (this.selectedDoc["data-annotations"] = new List(DocListCast(this.selectedDoc["data-annotations" + "-" + this.urlHash(this._url)]))); } } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 626ddf288..ea786800c 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -778,17 +778,10 @@ export class CollectionSchemaListCell extends CollectionSchemaCell { @observable private _text = "select an item"; @observable private _selectedNum = 0; - @action - toggleOpened(open: boolean) { - this._opened = open; - } - @action onSetValue = (value: string) => { - this._text = value; - // change if its a document - this._optionsList[this._selectedNum] = this._text; + this._optionsList[this._selectedNum] = this._text = value; (this.prop.Document[this.prop.fieldKey] as List).splice(this._selectedNum, 1, value); } @@ -854,7 +847,7 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
{link ? plainText : textarea}
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index 73f319e9b..34a8bfa24 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -275,7 +275,7 @@ export class KeysDropdown extends React.Component { document.removeEventListener("pointerdown", this.detectClick); const filters = Cast(this.props.Document._docFilters, listSpec("string")); if (filters?.includes(this._key)) { - runInAction(() => { this.closeResultsVisibility = "contents" }); + runInAction(() => this.closeResultsVisibility = "contents"); } } diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx index 489c5e540..6ec9783e2 100644 --- a/src/client/views/collections/SchemaTable.tsx +++ b/src/client/views/collections/SchemaTable.tsx @@ -415,7 +415,7 @@ export class SchemaTable extends React.Component { createRow = action(() => { this.props.addDocument(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 })); this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col }; - }) + }); @undoBatch @action diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7cf6b0f39..590befd86 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -713,7 +713,7 @@ export class DocumentView extends DocComponent(Docu } const cm = ContextMenu.Instance; - if (!cm || ((e as any)?.nativeEvent as any)?.SchemaHandled) return; + if (!cm || (e?.nativeEvent as any)?.SchemaHandled) return; const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []); Cast(this.props.Document.contextMenuLabels, listSpec("string"), []).forEach((label, i) => diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f39413e9f..d30ea03b1 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1061,7 +1061,7 @@ export class PresBox extends ViewBoxBaseComponent DockedFrameRenderer.PinDoc(doc, false); this.gotoDocument(this.childDocs.length, this.itemIndex); } else { - this.props.addDocTab(doc as Doc, "onRight"); + this.props.addDocTab(doc, "onRight"); } } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d58d75226..6b4115e53 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -510,10 +510,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let index = 0, foundAt; const ep = this.getNodeEndpoints(pm.state.doc, node); const regexp = find.replace("*", ""); - if (regexp) while (ep && (foundAt = node.textContent.slice(index).search(regexp, "i")) > -1) { - const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1)); - ret.push(sel); - index = index + foundAt + find.length; + if (regexp) { + while (ep && (foundAt = node.textContent.slice(index).search(regexp)) > -1) { + const sel = new TextSelection(pm.state.doc.resolve(ep.from + index + foundAt + 1), pm.state.doc.resolve(ep.from + index + foundAt + find.length + 1)); + ret.push(sel); + index = index + foundAt + find.length; + } } } else { node.content.forEach((child, i) => ret = ret.concat(this.findInNode(pm, child, find))); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 4797fb4cb..3fdeb8e71 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -206,11 +206,11 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym] = (clear?: boolean) => { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; } - public [WidthSym] = () => { return NumCast(this[SelfProxy]._width); } - public [HeightSym] = () => { return NumCast(this[SelfProxy]._height); } - public [ToScriptString] = () => { return `DOC-"${this[Self][Id]}"-`; } - public [ToString] = () => { return `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; } + public [FieldsSym] = (clear?: boolean) => clear ? this.___fields = this.___fieldKeys = {} : this.___fields; + public [WidthSym] = () => NumCast(this[SelfProxy]._width); + public [HeightSym] = () => NumCast(this[SelfProxy]._height); + public [ToScriptString] = () => `DOC-"${this[Self][Id]}"-`; + public [ToString] = () => `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } public get [DataSym]() { const self = this[SelfProxy]; -- cgit v1.2.3-70-g09d2 From 694cbd8c810068ea27b94cce8d74d6e645939e39 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 20 Aug 2020 11:32:17 -0400 Subject: starting overhaul of SearchBox code. --- src/client/views/MainView.tsx | 21 +- .../views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/search/SearchBox.tsx | 731 ++++++--------------- src/fields/Doc.ts | 4 +- 4 files changed, 200 insertions(+), 558 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f3d8fc181..fbc843d53 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -1,12 +1,14 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faHireAHelper, faBuffer } from '@fortawesome/free-brands-svg-icons'; +import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; +import * as ReactDOM from 'react-dom'; import Measure from 'react-measure'; +import * as rp from 'request-promise'; import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; @@ -14,16 +16,20 @@ import { listSpec } from '../../fields/Schema'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, simulateMouseClick } from '../../Utils'; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; +import { Networking } from '../Network'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { DocumentManager } from '../util/DocumentManager'; import GroupManager from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; +import { Hypothesis } from '../util/HypothesisUtils'; +import { LinkManager } from '../util/LinkManager'; import { Scripting } from '../util/Scripting'; +import { SearchUtil } from '../util/SearchUtil'; import { SelectionManager } from '../util/SelectionManager'; import SettingsManager from '../util/SettingsManager'; import SharingManager from '../util/SharingManager'; @@ -53,18 +59,11 @@ import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview } from './nodes/LinkDocPreview'; import { RadialMenu } from './nodes/RadialMenu'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; +import { WebBox } from './nodes/WebBox'; import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; -import { Hypothesis } from '../util/HypothesisUtils'; -import { WebBox } from './nodes/WebBox'; -import * as ReactDOM from 'react-dom'; import { SearchBox } from './search/SearchBox'; -import { SearchUtil } from '../util/SearchUtil'; -import { Networking } from '../Network'; -import * as rp from 'request-promise'; -import { LinkManager } from '../util/LinkManager'; -import RichTextMenu from './nodes/formattedText/RichTextMenu'; @observer export class MainView extends React.Component { @@ -544,7 +543,7 @@ export class MainView extends React.Component { switch (this.panelContent = title) { case "Tools": panelDoc = Doc.UserDoc()["sidebar-tools"] as Doc ?? undefined; break; case "Workspace": panelDoc = Doc.UserDoc()["sidebar-workspaces"] as Doc ?? undefined; break; - case "Catalog": SearchBox.Instance.searchFullDB = "My Stuff"; + case "Catalog": SearchBox.Instance._searchFullDB = "My Stuff"; SearchBox.Instance.newsearchstring = ""; SearchBox.Instance.enter(undefined); break; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ed8496544..257099783 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -313,7 +313,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { else { this.props.Document._docFilters = undefined; if (this.props.Document.selectedDoc !== undefined) { - const doc = Cast(this.props.Document.selectedDoc, Doc) as Doc; + const doc = Cast(this.props.Document.selectedDoc, Doc, null); doc._docFilters = undefined; } } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 7b3086848..687dcaa93 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -1,30 +1,28 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from '@material-ui/core'; -import { action, computed, observable, runInAction, reaction, IReactionDisposer } from 'mobx'; +import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import * as rp from 'request-promise'; -import { Doc, DocListCast, Opt, Field } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { returnFalse, Utils, returnZero } from '../../../Utils'; +import { returnFalse, returnZero } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { SetupDrag } from '../../util/DragManager'; import { SearchUtil } from '../../util/SearchUtil'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; +import { ColumnType } from "../collections/CollectionSchemaView"; import { CollectionView, CollectionViewType } from '../collections/CollectionView'; import { ViewBoxBaseComponent } from "../DocComponent"; import { DocumentView } from '../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./SearchBox.scss"; -import { ColumnType } from "../collections/CollectionSchemaView"; export const searchSchema = createSchema({ id: "string", @@ -42,126 +40,70 @@ export enum Keys { type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>; const SearchBoxDocument = makeInterface(documentSchema, searchSchema); -//React.Component @observer export class SearchBox extends ViewBoxBaseComponent(SearchBoxDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } + public static Instance: SearchBox; - get _searchString() { return this.layoutDoc.searchQuery; } - @computed set _searchString(value) { this.layoutDoc.searchQuery = (value); } - @observable private _resultsOpen: boolean = false; - @observable _searchbarOpen: boolean = false; - @observable private _results: [Doc, string[], string[]][] = []; - @observable private _openNoResults: boolean = false; - @observable private _visibleElements: JSX.Element[] = []; - @observable private _visibleDocuments: Doc[] = []; - - static NUM_SEARCH_RESULTS_PER_PAGE = 25; - - private _resultsSet = new Map(); - private _resultsRef = React.createRef(); - public inputRef = React.createRef(); - - private _isSearch: ("search" | "placeholder" | undefined)[] = []; - private _isSorted: ("sorted" | "placeholder" | undefined)[] = []; - + private _allIcons: string[] = [DocumentType.INK, DocumentType.AUDIO, DocumentType.COL, DocumentType.IMG, DocumentType.LINK, DocumentType.PDF, DocumentType.RTF, DocumentType.VID, DocumentType.WEB]; + private _numResultsPerPage = 4; private _numTotalResults = -1; private _endIndex = -1; - - static Instance: SearchBox; - + private _lockPromise?: Promise; + private _resultsSet = new Map(); + private _inputRef = React.createRef(); private _maxSearchIndex: number = 0; private _curRequest?: Promise = undefined; - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } + private _disposers: { [name: string]: IReactionDisposer } = {}; - private new_buckets: { [characterName: string]: number } = {}; - //if true, any keywords can be used. if false, all keywords are required. - //this also serves as an indicator if the word status filter is applied - @observable private _basicWordStatus: boolean = false; - @observable private _nodeStatus: boolean = false; - @observable private _keyStatus: boolean = false; + private currentSelectedCollection: DocumentView | undefined = undefined; + private docsforfilter: Doc[] = []; + private realTotalResults: number = 0; + private collectionRef = React.createRef(); - @observable private newAssign: boolean = true; + @observable _icons: string[] = this._allIcons; + @observable _results: [Doc, string[], string[]][] = []; + @observable _visibleElements: JSX.Element[] = []; + @observable _visibleDocuments: Doc[] = []; + @observable _deletedDocsStatus: boolean = false; + @observable _onlyAliases: boolean = true; + @observable _searchbarOpen = false; + @observable _searchFullDB = "DB"; + @observable _noResults = ""; + @observable _pageStart = 0; + @observable open = false; + @observable children = 0; + @observable newsearchstring = ""; + @observable headercount: number = 0; + @observable headerscale: number = 0; + @observable filter = false; constructor(props: any) { super(props); SearchBox.Instance = this; - this.resultsScrolled = this.resultsScrolled.bind(this); - } - @observable setupButtons = false; - private _disposers: { [name: string]: IReactionDisposer } = {}; - - componentDidMount = () => { - this._disposers.filters = reaction(() => Cast(this.props.Document._docFilters, listSpec("string")), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks - newFilters => { - if (this.searchFullDB) { - runInAction(() => this._pageStart = 0); - this.submitSearch(); - // newFilters?.forEach(f => { - // console.log(f); - // }) - } - }); - if (this.setupButtons === false) { - runInAction(() => this.setupButtons = true); - } - if (this.inputRef.current) { - this.inputRef.current.focus(); - runInAction(() => { this._searchbarOpen = true; }); + componentDidMount = action(() => { + if (this._inputRef.current) { + this._inputRef.current.focus(); + this._searchbarOpen = true; } - if (this.rootDoc.searchQuery && this.newAssign) { - const sq = this.rootDoc.searchQuery; - runInAction(() => { - - // this._deletedDocsStatus=this.props.filterQuery!.deletedDocsStatus; - // this._authorFieldStatus=this.props.filterQuery!.authorFieldStatus - // this._titleFieldStatus=this.props.filterQuery!.titleFieldStatus; - // this._basicWordStatus=this.props.filterQuery!.basicWordStatus; - // this._icons=this.props.filterQuery!.icons; - this.newAssign = false; - }); - runInAction(() => { - this.layoutDoc._searchString = StrCast(sq); - this.submitSearch(); - }); - } - } + }) componentWillUnmount() { Object.values(this._disposers).forEach(disposer => disposer?.()); } - @action getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc) - - @observable newsearchstring: string = ""; @action.bound onChange(e: React.ChangeEvent) { this.layoutDoc._searchString = e.target.value; this.newsearchstring = e.target.value; if (e.target.value === "") { - if (this.currentSelectedCollection !== undefined) { - 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(); - d._docFilters = new List(); - DocListCast(d.data).forEach((newdoc) => newarray.push(newdoc)); - } - }); - docs = newarray; - } - - this.currentSelectedCollection.props.Document._searchDocs = new List([]); - this.currentSelectedCollection.props.Document._docFilters = new List(); - this.props.Document.selectedDoc = undefined; + if (this.currentSelectedCollection) { + this.doLoop(this.currentSelectedCollection, undefined); } this._results.forEach(result => { Doc.UnBrushDoc(result[0]); @@ -172,10 +114,8 @@ export class SearchBox extends ViewBoxBaseComponent([]); this.currentSelectedCollection = undefined; this.props.Document.selectedDoc = undefined; - } - runInAction(() => { this.open = false; }); - this._openNoResults = false; + this.open = false; this._results = []; this._resultsSet.clear(); this._visibleElements = []; @@ -190,58 +130,17 @@ export class SearchBox extends ViewBoxBaseComponent { - // if (i === 0) { - // if (i + 1 === limit) { - // query = query + " && " + filters[i] + "_t:" + filters; - // } - // else if (filters[i] === filters[i + 3]) { - // query = query + " && (" + filters[i] + "_t:" + filters; - // } - // else { - // query = query + " && " + filters[i] + "_t:" + filters; - // } - - // } - // else if (i + 3 > filters.length) { - - // } - // else { - - // } - - // }); - - // query = this.applyBasicFieldFilters(query); - - - - query = query.replace(/-\s+/g, ''); - // query = query.replace(/-/g, ""); - return query; + return query.replace(/-\s+/g, ''); } - basicRequireWords(query: string): string { - return query.split(" ").join(" + ").replace(/ + /, ""); - } @action filterDocsByType(docs: Doc[]) { @@ -352,27 +218,6 @@ export class SearchBox extends ViewBoxBaseComponent { - const proto = doc.proto; - const protoId = (proto || doc)[Id]; - const colString: string = "{!join from=data_l to=id}id:" + protoId + " "; - collectionString.push(colString); - }); - - let finalColString = collectionString.join(" "); - finalColString = finalColString.trim(); - return "+(" + finalColString + ")" + query; - } - - get filterTypes() { - return this._icons.length === this._allIcons.length ? undefined : this._icons; - } - //TODO: basically all of this //gets all of the collections of all the docviews that are selected //if a collection is the only thing selected, search only in that collection (not its container) @@ -398,9 +243,6 @@ export class SearchBox extends ViewBoxBaseComponent(docsforFilter); - docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]); - while (docs.length > 0) { - newarray = []; - docs.forEach(d => { - const docs = DocListCast(d.data); - if (docs.length) { - d._searchDocs = new List(docsforFilter); - docs.forEach((newdoc) => newarray.push(newdoc)); - } - }); - docs = newarray; - } + this.doLoop(selectedCollection, docsforFilter); } this._numTotalResults = found.length; this.realTotalResults = found.length; } else { - this.noresults = "No collection selected :("; + this._noResults = "No collection selected :("; } } - documentKeys(doc: Doc) { const keys: { [key: string]: boolean } = {}; // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. @@ -471,17 +301,6 @@ export class SearchBox extends ViewBoxBaseComponent newWords.push(mod + word)); - - query = newWords.join(" "); - - return query; + query.split(" ").forEach(word => newWords.push(mod + word)); + return newWords.join(" "); } - get fieldFiltersApplied() { return !(this._authorFieldStatus && this._titleFieldStatus); } - @action submitSearch = async (reset?: boolean) => { if (this.currentSelectedCollection !== undefined) { - 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(); - //d._docFilters = new List(); - const newdocs = DocListCast(d.data); - newdocs.forEach((newdoc) => { - newarray.push(newdoc); - }); - } - }); - docs = newarray; - } - - this.currentSelectedCollection.props.Document._searchDocs = new List([]); - this.currentSelectedCollection.props.Document._docFilters = new List(); - this.props.Document.selectedDoc = undefined; + this.doLoop(this.currentSelectedCollection, undefined); } if (reset) { this.layoutDoc._searchString = ""; } //this.props.Document._docFilters = new List(); - this.noresults = ""; + this._noResults = ""; this.dataDoc[this.fieldKey] = new List([]); this.headercount = 0; this.children = 0; - this.buckets = []; - this.new_buckets = {}; let query = StrCast(this.layoutDoc._searchString); Doc.SetSearchQuery(query); - this.searchFullDB ? query = this.getFinalQuery(query) : console.log("local"); + this._searchFullDB ? query = this.getFinalQuery(query) : console.log("local"); this._results.forEach(result => { Doc.UnBrushDoc(result[0]); result[0].searchMatch = undefined; }); this._results = []; this._resultsSet.clear(); - this._isSearch = []; - this._isSorted = []; this._visibleElements = []; this._visibleDocuments = []; - if (StrCast(this.props.Document.searchQuery)) { - if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; } - this._timeout = setTimeout(() => { - console.log("Resubmitting search"); - }, 60000); - } - if (query !== "" || this.searchFullDB === "My Stuff") { + if (query !== "" || this._searchFullDB === "My Stuff") { this._endIndex = 12; this._maxSearchIndex = 0; this._numTotalResults = -1; - this.searchFullDB ? await this.getResults(query) : this.searchCollection(query); + this._searchFullDB ? await this.getResults(query) : this.searchCollection(query); runInAction(() => { - this._resultsOpen = true; this._searchbarOpen = true; - this._openNoResults = true; this.resultsScrolled(); - }); } } - @observable searchFullDB = "DB"; - - @observable _timeout: any = undefined; - - @observable firststring: string = ""; - @observable secondstring: string = ""; - - @observable bucketcount: number[] = []; - @observable buckets: Doc[] | undefined; - getAllResults = async (query: string) => { return SearchUtil.Search(query, true, { fq: this.filterQuery, start: 0, rows: 10000000 }); } @@ -590,16 +360,14 @@ export class SearchBox extends ViewBoxBaseComponent `NOT ({!join from=id to=proto_i}type_t:${type}) AND NOT type_t:${type}`).join(" AND ")}`; // fq: type_t:collection OR {!join from=id to=proto_i}type_t:collection q:text_t:hello const query = [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, ""); return query; } - getDataStatus() { return this._deletedDocsStatus; } - @computed get primarySort() { const suffixMap = (type: ColumnType) => { switch (type) { @@ -612,15 +380,11 @@ export class SearchBox extends ViewBoxBaseComponent, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined); } - private NumResults = 500; - private lockPromise?: Promise; getResults = async (query: string) => { - if (this.lockPromise) { - await this.lockPromise; - } - this.lockPromise = new Promise(async res => { + this._lockPromise && (await this._lockPromise); + this._lockPromise = new Promise(async res => { while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) { - this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this.NumResults, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => { + this._curRequest = SearchUtil.Search(query, true, { onlyAliases: true, allowAliases: true, /*sort: this.primarySort,*/ fq: this.filterQuery, start: 0, rows: this._numResultsPerPage, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => { // happens at the beginning this.realTotalResults = res.numFound <= 0 ? 0 : res.numFound; if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) { @@ -635,28 +399,26 @@ export class SearchBox extends ViewBoxBaseComponent highlights[doc[Id]] = highlightList[index]); const filteredDocs = this.filterDocsByType(docs); - runInAction(() => { - filteredDocs.forEach((doc, i) => { - const index = this._resultsSet.get(doc); - const highlight = highlights[doc[Id]]; - const line = lines.get(doc[Id]) || []; - const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : []; - // if (this.findCommonElements(hlights)) { - // } - if (index === undefined) { - this._resultsSet.set(doc, this._results.length); - this._results.push([doc, hlights, line]); - } else { - this._results[index][1].push(...hlights); - this._results[index][2].push(...line); - } + runInAction(() => filteredDocs.forEach((doc, i) => { + const index = this._resultsSet.get(doc); + const highlight = highlights[doc[Id]]; + const line = lines.get(doc[Id]) || []; + const hlights = highlight ? Object.keys(highlight).map(key => key.substring(0, key.length - 2)).filter(k => k) : []; + // if (this.findCommonElements(hlights)) { + // } + if (index === undefined) { + this._resultsSet.set(doc, this._results.length); + this._results.push([doc, hlights, line]); + } else { + this._results[index][1].push(...hlights); + this._results[index][2].push(...line); + } - }); - }); + })); this._curRequest = undefined; })); - this._maxSearchIndex += this.NumResults; + this._maxSearchIndex += this._numResultsPerPage; await this._curRequest; } @@ -664,21 +426,13 @@ export class SearchBox extends ViewBoxBaseComponent(); + startDragCollection = async () => { const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString))); const filtered = this.filterDocsByType(res.docs); - const docs = filtered.map(doc => { - const isProto = Doc.GetT(doc, "isPrototype", "boolean", true); - if (isProto) { - return Doc.MakeDelegate(doc); - } else { - return Doc.MakeAlias(doc); - } - }); + const docs = filtered.map(doc => Doc.GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeDelegate(doc) : Doc.MakeAlias(doc)); let x = 0; let y = 0; for (const doc of docs.map(d => Doc.Layout(d))) { @@ -707,30 +461,22 @@ export class SearchBox extends ViewBoxBaseComponent { - Doc.BrushDoc(result[0]); - }); e.stopPropagation(); - this._openNoResults = false; - this._resultsOpen = true; + this._results.forEach(result => Doc.BrushDoc(result[0])); this._searchbarOpen = true; } - realTotalResults: number = 0; - @action.bound closeSearch = () => { this._results.forEach(result => { Doc.UnBrushDoc(result[0]); result[0].searchMatch = undefined; }); - //this.closeResults(); this._searchbarOpen = false; } @action.bound closeResults() { - this._resultsOpen = false; this._results = []; this._resultsSet.clear(); this._visibleElements = []; @@ -740,21 +486,10 @@ export class SearchBox extends ViewBoxBaseComponent) => { - if (!this._resultsRef.current) return; this._endIndex = 30; const headers = new Set(["title", "author", "text", "type", "data", "*lastModified", "context"]); - // if ((this._numTotalResults === 0 || this._results.length === 0) && this._openNoResults) { - // if (this.noresults === "") { - // this.noresults = "No search results :("; - // } - // return; - // } if (this._numTotalResults <= this._maxSearchIndex) { this._numTotalResults = this._results.length; @@ -766,139 +501,125 @@ export class SearchBox extends ViewBoxBaseComponent(this._numTotalResults === -1 ? 0 : this._numTotalResults); this._visibleDocuments = Array(this._numTotalResults === -1 ? 0 : this._numTotalResults); - // indicates if things are placeholders - this._isSearch = Array(this._numTotalResults === -1 ? 0 : this._numTotalResults); - this._isSorted = Array(this._numTotalResults === -1 ? 0 : this._numTotalResults); - } - let max = this.NumResults; + let max = this._numResultsPerPage; max > this._results.length ? max = this._results.length : console.log(""); for (let i = this._pageStart; i < max; i++) { //if the index is out of the window then put a placeholder in //should ones that have already been found get set to placeholders? - if (this._isSearch[i] !== "search") { - let result: [Doc, string[], string[]] | undefined = undefined; - - result = this._results[i]; - if (result) { - const highlights = Array.from([...Array.from(new Set(result[1]).values())]); - const lines = new List(result[2]); - highlights.forEach((item) => headers.add(item)); - result[0].lines = lines; - result[0].highlighting = highlights.join(", "); - result[0].searchMatch = true; - if (i < this._visibleDocuments.length) { - this._visibleDocuments[i] = result[0]; - this._isSearch[i] = "search"; - Doc.BrushDoc(result[0]); - Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); - this.children++; - } - + let result: [Doc, string[], string[]] | undefined = undefined; + + result = this._results[i]; + if (result) { + const highlights = Array.from([...Array.from(new Set(result[1]).values())]); + const lines = new List(result[2]); + highlights.forEach((item) => headers.add(item)); + result[0].lines = lines; + result[0].highlighting = highlights.join(", "); + result[0].searchMatch = true; + if (i < this._visibleDocuments.length) { + this._visibleDocuments[i] = result[0]; + Doc.BrushDoc(result[0]); + Doc.AddDocToList(this.dataDoc, this.props.fieldKey, result[0]); + this.children++; } - } } this.headerscale = headers.size; - if (Cast(this.props.Document._docFilters, listSpec("string"), []).length === 0) { - const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []); - if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") { - const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i); - headers.forEach(header => { - if (oldSchemaHeaders.includes(header) === false) { - newSchemaHeaders.push(new SchemaHeaderField(header, "#f1efeb")); - } - }); - this.headercount = newSchemaHeaders.length; - this.props.Document._schemaHeaders = new List(newSchemaHeaders); - } else if (this.props.Document._schemaHeaders === undefined) { - this.props.Document._schemaHeaders = new List([new SchemaHeaderField("title", "#f1efeb")]); - } + const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []); + if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") { + const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i); + headers.forEach(header => { + if (oldSchemaHeaders.includes(header) === false) { + newSchemaHeaders.push(new SchemaHeaderField(header, "#f1efeb")); + } + }); + this.headercount = newSchemaHeaders.length; + this.props.Document._schemaHeaders = new List(newSchemaHeaders); + } else if (this.props.Document._schemaHeaders === undefined) { + this.props.Document._schemaHeaders = new List([new SchemaHeaderField("title", "#f1efeb")]); } if (this._maxSearchIndex >= this._numTotalResults) { this._visibleElements.length = this._results.length; this._visibleDocuments.length = this._results.length; - this._isSearch.length = this._results.length; } } - @observable headercount: number = 0; - @observable headerscale: number = 0; findCommonElements(arr2: string[]) { const arr1 = ["layout", "data"]; return arr1.some(item => arr2.includes(item)); } - @computed - get resFull() { return this._numTotalResults <= 8; } - - @computed - get resultHeight() { return this._numTotalResults * 70; } - - addButtonDoc = (doc: Doc) => Doc.AddDocToList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); - remButtonDoc = (doc: Doc) => Doc.RemoveDocFromList(CurrentUserUtils.UserDocument.expandingButtons as Doc, "data", doc); - moveButtonDoc = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => this.remButtonDoc(doc) && addDocument(doc); + getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight + panelHeight = () => this.props.PanelHeight(); - @computed get searchItemTemplate() { return Cast(Doc.UserDoc().searchItemTemplate, Doc, null); } - - @computed get viewspec() { return Cast(this.props.Document._docFilters, listSpec("string"), []); } - - getTransform = () => { - return this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight - } - panelHeight = () => { - return this.props.PanelHeight(); - } selectElement = (doc: Doc) => { //this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); } - - addDocument = (doc: Doc) => { - return null; - } - - @observable filter = false; - - @action newpage() { - this._pageStart += SearchBox.NUM_SEARCH_RESULTS_PER_PAGE; - this.dataDoc[this.fieldKey] = new List([]); - this.resultsScrolled(); - } returnHeight = () => 31 + 31 * 6; - returnLength = () => { - const cols = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length; - return cols * 205 + 51; - } + returnLength = () => 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length; + @action changeSearchScope = (scope: string) => { scope && (this.filter = false); - this.searchFullDB = scope; + this._searchFullDB = scope; this.dataDoc[this.fieldKey] = new List([]); if (this.currentSelectedCollection !== undefined) { - 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(); - d._docFilters = new List(); - const newdocs = DocListCast(d.data); - newdocs.forEach((newdoc) => { - newarray.push(newdoc); - }); - } - }); - docs = newarray; - } - this.currentSelectedCollection.props.Document._docFilters = new List(); - this.currentSelectedCollection.props.Document._searchDocs = undefined; - this.currentSelectedCollection = undefined; + this.doLoop(this.currentSelectedCollection, undefined); } this.submitSearch(); } + + @computed get scopeButtons() { + return
+
+
+
+ +
+
+ +
+
+
+
; + } + + doLoop = (collectionView: DocumentView, filter: Doc[] | undefined) => { + let docs = DocListCast(collectionView.dataDoc[Doc.LayoutFieldKey(collectionView.dataDoc)]); + let newarray: Doc[] = []; + while (docs.length > 0) { + newarray = []; + docs.forEach(d => { + const subDocs = DocListCast(d.data); + if (subDocs.length) { + d._searchDocs = filter ? new List(filter) : undefined; + DocListCast(d.data).forEach(newdoc => newarray.push(newdoc)); + } + }); + docs = newarray; + } + collectionView.props.Document._searchDocs = filter ? new List(filter) : undefined; + this.props.Document.selectedDoc = filter ? collectionView.props.Document : undefined; + } + render() { this.props.Document._chromeStatus === "disabled"; this.props.Document._searchDoc = true; @@ -908,7 +629,7 @@ export class SearchBox extends ViewBoxBaseComponent{Doc.CurrentUserEmail}
-
@@ -923,116 +644,38 @@ export class SearchBox extends ViewBoxBaseComponent
only display documents matching search
} > -
{ e.stopPropagation(); SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined); }} - onClick={action(() => { - ///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(this.docsforfilter); - 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(this.docsforfilter); - const newdocs = DocListCast(d.data); - newdocs.forEach(newdoc => newarray.push(newdoc)); - } - }); - docs = newarray; - } - - this.currentSelectedCollection.props.Document._docFilters = new List(this.viewspec); - this.props.Document.selectedDoc = this.currentSelectedCollection.props.Document; - } - else if (this.filter === false && this.currentSelectedCollection !== undefined) { - 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(); - d._docFilters = new List(); - const newdocs = DocListCast(d.data); - newdocs.forEach(newdoc => newarray.push(newdoc)); - } - }); - docs = newarray; - } - - this.currentSelectedCollection.props.Document._searchDocs = new List([]); - this.currentSelectedCollection.props.Document._docFilters = new List(); - this.props.Document.selectedDoc = undefined; - } - } - )} />
- -
-
-
-
-
- -
-
- -
+
+ { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }} + onClick={action(() => { + this.filter = !this.filter && !this._searchFullDB; + this.currentSelectedCollection && this.doLoop(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined); + })} />
- +
+ {this.scopeButtons}
-
- {this._searchbarOpen === true ? -
- {this.noresults === "" ?
- window.innerWidth || rows > 6 ? true : false} - focus={this.selectElement} - ScreenToLocalTransform={Transform.Identity} - /> -
: -
-
{this.noresults}
-
} -
: undefined} -
- -
-
+ {!this._searchbarOpen ? (null) :
+
+
+ window.innerWidth || rows > 6 ? true : false} + focus={this.selectElement} + ScreenToLocalTransform={Transform.Identity} + /> +
+
+
}
); } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 3fdeb8e71..c4a49962b 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -206,11 +206,11 @@ export class Doc extends RefField { private [Self] = this; private [SelfProxy]: any; - public [FieldsSym] = (clear?: boolean) => clear ? this.___fields = this.___fieldKeys = {} : this.___fields; + public [FieldsSym](clear?: boolean) { return clear ? this.___fields = this.___fieldKeys = {} : this.___fields; } public [WidthSym] = () => NumCast(this[SelfProxy]._width); public [HeightSym] = () => NumCast(this[SelfProxy]._height); public [ToScriptString] = () => `DOC-"${this[Self][Id]}"-`; - public [ToString] = () => `Doc(${GetEffectiveAcl(this) === AclPrivate ? "-inaccessible-" : this.title})`; + public [ToString] = () => `Doc(${GetEffectiveAcl(this[SelfProxy]) === AclPrivate ? "-inaccessible-" : this[SelfProxy].title})`; public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } public get [DataSym]() { const self = this[SelfProxy]; -- cgit v1.2.3-70-g09d2 From c57a215fcca0928522f7d765fc2d8d8120b7a33e Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 21 Aug 2020 19:55:31 -0400 Subject: added a locking option for treeViewExpandedView field. --- src/client/documents/Documents.ts | 8 +++++--- src/client/util/CurrentUserUtils.ts | 8 +++++--- src/client/views/collections/CollectionTreeView.tsx | 17 ++++++++++------- src/fields/documentSchemas.ts | 2 ++ 4 files changed, 22 insertions(+), 13 deletions(-) (limited to 'src/fields') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 697ff858f..f0cae8f0e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -192,6 +192,8 @@ export interface DocumentOptions { treeViewExpandedView?: string; // which field/thing is displayed when this item is opened in tree view treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked treeViewTruncateTitleWidth?: number; + treeViewLockExpandedView?: boolean; // whether the expanded view can be changed + treeViewDefaultExpandedView?: string; // default expanded view limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; pointerHack?: boolean; // for buttons, allows onClick handler to fire onPointerDown @@ -819,9 +821,9 @@ export namespace Docs { } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { - const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); - const tabs = TreeDocument(documents, { title: "Active Tabs" }); - const all = TreeDocument([], { title: "Other Tabs" }); + const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); + const tabs = TreeDocument(documents, { title: "Active Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" }); + const all = TreeDocument([], { title: "Other Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" }); Doc.GetProto(inst).data = new List([tabs, all]); return inst; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b439b8b43..fbcc562da 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -592,7 +592,7 @@ export class CurrentUserUtils { // SEts up mobile buttons for inside mobile menu static setupMobileButtons(doc?: Doc, buttons?: string[]) { const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, ischecked?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ - { title: "SCENES", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " }, + { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " }, { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." }, { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." }, { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." }, @@ -729,7 +729,8 @@ export class CurrentUserUtils { await doc.myDashboards; if (doc.myDashboards === undefined) { doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "SCENES", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true + title: "DASHBOARDS", _height: 100, forceActive: true, boxShadow: "0 0", lockedPosition: true, treeViewOpen: true, system: true, + treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", })); } if (doc["sidebar-dashboards"] === undefined) { @@ -773,7 +774,8 @@ export class CurrentUserUtils { doc.myRecentlyClosed === undefined; if (doc.myRecentlyClosed === undefined) { doc.myRecentlyClosed = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true + title: "RECENTLY CLOSED", _height: 75, forceActive: true, boxShadow: "0 0", treeViewPreventOpen: false, treeViewOpen: true, _stayInCollection: true, system: true, + treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", })); } // this is equivalent to using PrefetchProxies to make sure the recentlyClosed doc is ready diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index b78678bc3..cde86b127 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -88,14 +88,16 @@ class TreeView extends React.Component { get doc() { return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive - get defaultExpandedView() { return this.childDocs.length ? this.fieldKey : StrCast(this.doc.defaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } + get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); } + get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs.length ? this.fieldKey : this.defaultExpandedView); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; else this.doc.treeViewOpen = this._overrideTreeViewOpen = c; } @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && BoolCast(this.doc.treeViewOpen)) || this._overrideTreeViewOpen; } - @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); } + @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.doc[DataSym]; } @computed get layoutDoc() { return Doc.Layout(this.doc); } @@ -422,11 +424,12 @@ class TreeView extends React.Component { { if (this.treeViewOpen) { - this.doc.treeViewExpandedView = this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : - this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : - this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : - (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); + this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : + this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode ? "layout" : "fields") : + this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : + this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : + (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : + this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 848a648e1..4b75b1a32 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -83,6 +83,8 @@ export const documentSchema = createSchema({ textTransform: "string", treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree + treeViewLockExpandedView: "boolean", // whether the expanded view can be changed + treeViewDefaultExpandedView: "string", // name of field whose contents are displayed by default treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) // interaction and linking properties -- cgit v1.2.3-70-g09d2 From cbedd8e1e651843e50f8f27163bf8e2a1a574009 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 22 Aug 2020 00:49:22 -0400 Subject: fixed key value for empty lists to show [], fixed tree view to allow 'data' field .to display for empty lists. Gave untitled Tabs a default height --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/collections/CollectionTreeView.tsx | 16 ++++++++-------- src/client/views/nodes/FieldView.tsx | 2 +- src/fields/Doc.ts | 3 +++ 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src/fields') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b935e28d4..b907a8e4c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -401,7 +401,7 @@ export class CurrentUserUtils { { _nativeWidth: undefined, _nativeHeight: undefined, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List(["system"]) }); } if (doc.emptyPane === undefined) { - doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, title: "Untitled Tab", system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List(["system"]) }); } if (doc.emptyComparison === undefined) { doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8cf8000f7..4503b0fb3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym, DocListCastOrNull } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { PrefetchProxy } from '../../../fields/Proxy'; @@ -90,7 +90,7 @@ class TreeView extends React.Component { get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode ? "layout" : "fields"); } - get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs.length ? this.fieldKey : this.defaultExpandedView); } + get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state set treeViewOpen(c: boolean) { if (this.props.treeViewPreventOpen) this._overrideTreeViewOpen = c; @@ -104,9 +104,9 @@ class TreeView extends React.Component { @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } childDocList(field: string) { const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; - return ((this.props.dataDoc ? DocListCast(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field - (layout ? DocListCast(layout[field]) : undefined) || // else if there's a layout doc, display it's fields - DocListCast(this.doc[field])); // otherwise use the document's data field + return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field + (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields + DocListCastOrNull(this.doc[field])); // otherwise use the document's data field } @computed get childDocs() { return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } @@ -429,7 +429,7 @@ class TreeView extends React.Component { this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs.length ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); + this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> @@ -734,8 +734,8 @@ export class CollectionTreeView extends CollectionSubView Doc.UserDoc().myInactiveDocs = new List(), icon: "plus" }); + } else if (!e.isPropagationStopped() && this.doc === Doc.UserDoc().myRecentlyClosed) { + ContextMenu.Instance.addItem({ description: "Clear All", event: () => Doc.UserDoc().myRecentlyClosed = new List(), icon: "plus" }); e.stopPropagation(); e.preventDefault(); ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 9d61ec6d1..d95951f3a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -133,7 +133,7 @@ export class FieldView extends React.Component { // ); } else if (field instanceof List) { - return
{field.map(f => Field.toString(f)).join(", ")}
; + return
{field.length ? field.map(f => Field.toString(f)).join(", ") : "[]"}
; } // bcz: this belongs here, but it doesn't render well so taking it out for now else if (field instanceof WebField) { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c4a49962b..c78b01def 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -91,6 +91,9 @@ export async function DocCastAsync(field: FieldResult): Promise> { export function DocListCast(field: FieldResult): Doc[] { return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; } +export function DocListCastOrNull(field: FieldResult) { + return Cast(field, listSpec(Doc), null)?.filter(d => d instanceof Doc) as Doc[] | undefined; +} export const WidthSym = Symbol("Width"); export const HeightSym = Symbol("Height"); -- cgit v1.2.3-70-g09d2 From 4ed30f48bd0c87f0a5ccb484d78bf4601ab6ff74 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 22 Aug 2020 09:49:56 -0400 Subject: switched search from writiing searchMatch to matched docs to instead store in a run-time observableMap. This allows simultaneous searches by different users. --- .../views/collections/CollectionSchemaCells.tsx | 26 ++++++--------- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/nodes/FieldView.tsx | 3 -- .../views/nodes/formattedText/FormattedTextBox.tsx | 39 ++++++++++------------ src/client/views/pdf/PDFViewer.tsx | 4 +-- src/client/views/search/SearchBox.tsx | 34 ++++++++----------- src/fields/Doc.ts | 25 ++++++++++++++ 7 files changed, 69 insertions(+), 64 deletions(-) (limited to 'src/fields') diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index ea786800c..4bd69041b 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -894,26 +894,22 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { export class CollectionSchemaButtons extends CollectionSchemaCell { render() { const doc = this.props.rowProps.original; - const searchMatch = (backward: boolean = true) => { doc.searchMatch = undefined; setTimeout(() => doc.searchMatch = backward, 0); }; + const searchMatch = (backward: boolean = true) => Doc.SearchMatchNext(doc, backward); // const reference = React.createRef(); // const onItemDown = (e: React.PointerEvent) => { // (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined : // SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e)); // }; return !BoolCast(this.props.Document._searchDoc) ? <> - : StrCast(doc.type) === DocumentType.PDF ? - - : StrCast(doc.type) === DocumentType.RTF ? -
- - -
: - <>; + : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ? +
+ + +
: + <>; } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 4503b0fb3..8440d3e9b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -472,7 +472,7 @@ class TreeView extends React.Component { return <>
number; childLayoutTemplate?: () => Opt; - highlighting?: string[]; - lines?: string[]; - doc?: Doc; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) height?: number; width?: number; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6b4115e53..5250a8f58 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -376,8 +376,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } public highlightSearchTerms = (terms: string[], backward: 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)); @@ -385,24 +383,19 @@ 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 (backward === 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; - } - + console.log("Search:" + this.rootDoc.title + " " + this._searchIndex + " => " + (this._searchIndex + 1 > flattened.length - 1 ? 0 : this._searchIndex + 1)); + this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; + if (backward === 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); @@ -909,9 +902,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.setupEditor(this.config, this.props.fieldKey); - this._disposers.search = reaction(() => this.rootDoc.searchMatch, - search => search !== undefined ? this.highlightSearchTerms([Doc.SearchQuery()], BoolCast(search)) : this.unhighlightSearchTerms(), - { fireImmediately: this.rootDoc.searchMatch !== undefined ? true : false }); + this._disposers.search = reaction(() => Doc.IsSearchMatch(this.rootDoc), + search => { + search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms(); + }, + { fireImmediately: Doc.IsSearchMatch(this.rootDoc) ? true : false }); this._disposers.record = reaction(() => this._recording, () => { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 0916e8b0c..f2aa9fa3f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -150,9 +150,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent this._showWaiting = this._showCover = true); this.props.startupLive && this.setupPdfJsViewer(); this._mainCont.current && (this._mainCont.current.scrollTop = this.layoutDoc._scrollTop || 0); - this._searchReactionDisposer = reaction(() => this.Document.searchMatch, + this._searchReactionDisposer = reaction(() => Doc.IsSearchMatch(this.rootDoc), m => { - if (m !== undefined) (this._lastSearch = true) && this.search(Doc.SearchQuery(), true); + if (m) (this._lastSearch = true) && this.search(Doc.SearchQuery(), m.searchMatch > 0); else !(this._lastSearch = false) && setTimeout(() => !this._lastSearch && this.search("", false, true), 200); }, { fireImmediately: true }); diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index d7c065a75..c571adefe 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -84,21 +84,17 @@ export class SearchBox extends ViewBoxBaseComponent disposer?.()); } - @action - getViews = (doc: Doc) => SearchUtil.GetViewsOfDocument(doc) - @action.bound onChange(e: React.ChangeEvent) { this.layoutDoc._searchString = e.target.value; this.newsearchstring = e.target.value; if (e.target.value === "") { if (this.currentSelectedCollection) { - this.doLoop(this.currentSelectedCollection, undefined); + this.setSearchDocsRecursive(this.currentSelectedCollection, undefined); } this.closeSearch(false); if (this.currentSelectedCollection !== undefined) { - this.currentSelectedCollection.props.Document._searchDocs = new List([]); this.currentSelectedCollection = undefined; this.props.Document.selectedDoc = undefined; } @@ -242,14 +238,12 @@ export class SearchBox extends ViewBoxBaseComponent 0) { newarray = []; docs.forEach((d) => { - if (d.data !== undefined) { - newarray.push(...DocListCast(d.data)); - } - const hlights: string[] = []; + d.data && newarray.push(...DocListCast(d.data)); + const hlights = new Set(); this.documentKeys(d).forEach(key => - Field.toString(d[key] as Field).toLowerCase().includes(query) && !hlights.includes(key) && hlights.push(key)); - if (hlights.length > 0) { - found.push([d, hlights, []]); + 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()), []]); docsforFilter.push(d); } }); @@ -259,7 +253,7 @@ export class SearchBox extends ViewBoxBaseComponent(docsforFilter); - this.doLoop(selectedCollection, docsforFilter); + this.setSearchDocsRecursive(selectedCollection, docsforFilter); } this._numTotalResults = found.length; this.realTotalResults = found.length; @@ -285,7 +279,7 @@ export class SearchBox extends ViewBoxBaseComponent { if (this.currentSelectedCollection !== undefined) { - this.doLoop(this.currentSelectedCollection, undefined); + this.setSearchDocsRecursive(this.currentSelectedCollection, undefined); } if (reset) { this.layoutDoc._searchString = ""; @@ -434,7 +428,7 @@ export class SearchBox extends ViewBoxBaseComponent { this._results.forEach(result => { Doc.UnBrushDoc(result[0]); - result[0].searchMatch = undefined; + Doc.ClearSearchMatches(); }); closesearchbar && (this._searchbarOpen = false); } @@ -479,9 +473,7 @@ export class SearchBox extends ViewBoxBaseComponent(result[2]); highlights.forEach((item) => headers.add(item)); - result[0].lines = lines; - result[0].highlighting = highlights.join(", "); - result[0].searchMatch = true; + Doc.SetSearchMatch(result[0], { searchMatch: 1 }); if (i < this._visibleDocuments.length) { this._visibleDocuments[i] = result[0]; Doc.BrushDoc(result[0]); @@ -530,7 +522,7 @@ export class SearchBox extends ViewBoxBaseComponent([]); if (this.currentSelectedCollection !== undefined) { - this.doLoop(this.currentSelectedCollection, undefined); + this.setSearchDocsRecursive(this.currentSelectedCollection, undefined); } this.submitSearch(); } @@ -566,7 +558,7 @@ export class SearchBox extends ViewBoxBaseComponent; } - doLoop = (collectionView: DocumentView, filter: Doc[] | undefined) => { + setSearchDocsRecursive = (collectionView: DocumentView, filter: Doc[] | undefined) => { let docs = DocListCast(collectionView.dataDoc[Doc.LayoutFieldKey(collectionView.dataDoc)]); let newarray: Doc[] = []; while (docs.length > 0) { @@ -614,7 +606,7 @@ export class SearchBox extends ViewBoxBaseComponent { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }} onClick={action(() => { this.filter = !this.filter && !this._searchFullDB; - this.currentSelectedCollection && this.doLoop(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined); + this.currentSelectedCollection && this.setSearchDocsRecursive(this.currentSelectedCollection, this.filter ? this.docsforfilter : undefined); })} />
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index c78b01def..dc274a063 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 { result } from "lodash"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -881,6 +882,7 @@ export namespace Doc { export class DocBrush { BrushedDoc: ObservableMap = new ObservableMap(); + SearchMatchDoc: ObservableMap = new ObservableMap(); } const brushManager = new DocBrush(); @@ -906,6 +908,29 @@ export namespace Doc { export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } + + export function IsSearchMatch(doc: Doc) { + return computedFn(function IsSearchMatch(doc: Doc) { + return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) : + brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined; + })(doc); + } + export function SetSearchMatch(doc: Doc, results: { searchMatch: number }) { + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + brushManager.SearchMatchDoc.set(doc, results); + return doc; + } + export function SearchMatchNext(doc: Doc, backward: boolean) { + if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc; + const result = brushManager.SearchMatchDoc.get(doc); + const num = Math.abs(result?.searchMatch || 0) + 1; + runInAction(() => result && brushManager.SearchMatchDoc.set(doc, { searchMatch: backward ? -num : num })); + return doc; + } + export function ClearSearchMatches() { + brushManager.SearchMatchDoc.clear(); + } + export function IsBrushed(doc: Doc) { return computedFn(function IsBrushed(doc: Doc) { return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); -- cgit v1.2.3-70-g09d2 From 88b06945c8a1fcb7bc518de878a4894de70dda94 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 22 Aug 2020 14:21:41 -0400 Subject: major overhaul of search to avoid writing anything to filtered documents and to make the UI work properly. --- src/Utils.ts | 2 + src/client/documents/Documents.ts | 5 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/GestureOverlay.tsx | 3 +- src/client/views/MainView.tsx | 11 +- src/client/views/OverlayView.tsx | 3 +- src/client/views/Palette.tsx | 3 +- src/client/views/TemplateMenu.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 4 +- .../views/collections/CollectionLinearView.tsx | 1 + src/client/views/collections/CollectionMenu.tsx | 14 ++- .../views/collections/CollectionSchemaCells.tsx | 6 +- .../views/collections/CollectionSchemaHeaders.tsx | 26 ++--- .../views/collections/CollectionSchemaView.scss | 3 + .../views/collections/CollectionSchemaView.tsx | 17 +-- .../views/collections/CollectionStackingView.tsx | 1 + src/client/views/collections/CollectionSubView.tsx | 26 ++--- .../views/collections/CollectionTreeView.tsx | 4 +- src/client/views/collections/CollectionView.tsx | 3 +- src/client/views/collections/SchemaTable.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + .../collectionFreeForm/PropertiesView.tsx | 3 +- .../CollectionMulticolumnView.tsx | 1 + .../CollectionMultirowView.tsx | 1 + src/client/views/nodes/DocHolderBox.tsx | 2 + src/client/views/nodes/DocumentView.tsx | 2 + src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/ImageBox.tsx | 1 + src/client/views/nodes/LinkDocPreview.tsx | 3 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 1 + src/client/views/nodes/WebBox.tsx | 1 + .../views/nodes/formattedText/DashDocView.tsx | 3 +- .../formattedText/FormattedTextBoxComment.tsx | 3 +- .../views/nodes/formattedText/RichTextSchema.tsx | 1 + src/client/views/pdf/PDFViewer.tsx | 1 + .../views/presentationview/PresElementBox.tsx | 1 + src/client/views/search/SearchBox.tsx | 128 ++++++++------------- src/fields/Doc.ts | 15 +-- src/fields/List.ts | 25 ++-- src/mobile/AudioUpload.tsx | 3 +- src/mobile/MobileInterface.tsx | 3 +- 42 files changed, 173 insertions(+), 169 deletions(-) (limited to 'src/fields') diff --git a/src/Utils.ts b/src/Utils.ts index d9a5353e8..3ff05104f 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -422,6 +422,8 @@ export function returnEmptyString() { return ""; } export function returnEmptyFilter() { return [] as string[]; } +export function returnEmptyDoclist() { return [] as Doc[]; } + export let emptyPath = []; export function emptyFunction() { } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4544e0e3e..49da4f9eb 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -72,6 +72,7 @@ export interface DocumentOptions { _scrollTop?: number; // scroll location for pdfs _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged _chromeStatus?: string; + _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view) _viewType?: string; // sub type of a collection _gridGap?: number; // gap between items in masonry view _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts @@ -822,8 +823,8 @@ export namespace Docs { export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id); - const tabs = TreeDocument(documents, { title: "Active Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" }); - const all = TreeDocument([], { title: "Inactive Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" }); + const tabs = TreeDocument(documents, { title: "On-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" }); + const all = TreeDocument([], { title: "Off-Screen Tabs", treeViewLockExpandedView: true, treeViewDefaultExpandedView: "data" }); Doc.GetProto(inst).data = new List([tabs, all]); return inst; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b907a8e4c..c09a227cf 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -525,7 +525,7 @@ export class CurrentUserUtils { static setupSearchPanel(doc: Doc) { if (doc["search-panel"] === undefined) { doc["search-panel"] = new PrefetchProxy(Docs.Create.SearchDocument({ - _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true, + _width: 500, _height: 400, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, childDropAction: "alias", lockedPosition: true, _viewType: CollectionViewType.Schema, _chromeStatus: "disabled", title: "sidebar search stack", system: true })) as any as Doc; } diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 2d41343b3..260cffc90 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -7,7 +7,7 @@ import { Cast, FieldValue, NumCast } from "../../fields/Types"; import MobileInkOverlay from "../../mobile/MobileInkOverlay"; import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { MobileInkOverlayContent } from "../../server/Message"; -import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents } from "../../Utils"; +import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter, setupMoveUpEvents, returnEmptyDoclist } from "../../Utils"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; import { DocUtils } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; @@ -935,6 +935,7 @@ export default class GestureOverlay extends Touchable { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index bc5483644..979f803bf 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -16,7 +16,7 @@ import { listSpec } from '../../fields/Schema'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils'; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils, returnEmptyDoclist } from '../../Utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions } from '../documents/Documents'; @@ -206,7 +206,7 @@ export class MainView extends React.Component { } }); if (check === false) { - SearchBox.Instance.closeSearch(); + SearchBox.Instance.resetSearch(true); } } @@ -394,6 +394,7 @@ export class MainView extends React.Component { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} renderDepth={-1} @@ -476,6 +477,7 @@ export class MainView extends React.Component { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} relative={true} @@ -510,6 +512,7 @@ export class MainView extends React.Component { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} relative={true} @@ -703,6 +706,7 @@ export class MainView extends React.Component { focus={emptyFunction} whenActiveChanged={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />
; @@ -775,6 +779,7 @@ export class MainView extends React.Component { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />
@@ -808,6 +813,7 @@ export class MainView extends React.Component { NativeWidth={() => 800} ContentScaling={returnOne} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} /> ; } @@ -878,6 +884,7 @@ export class MainView extends React.Component { NativeWidth={() => 800} ContentScaling={returnOne} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} /> ; , ele); diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 49580cde4..001135340 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -4,7 +4,7 @@ import * as React from "react"; import { Doc, DocListCast, Opt } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { NumCast, Cast } from "../../fields/Types"; -import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter } from "../../Utils"; +import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils, setupMoveUpEvents, returnEmptyFilter, returnEmptyDoclist } from "../../Utils"; import { Transform } from "../util/Transform"; import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView"; import { DocumentView } from "./nodes/DocumentView"; @@ -205,6 +205,7 @@ export class OverlayView extends React.Component { addDocTab={returnFalse} pinToPres={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> ; diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 0a4334302..92c3f09b4 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import { Doc } from "../../fields/Doc"; import { NumCast } from "../../fields/Types"; -import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter } from "../../Utils"; +import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue, returnEmptyFilter, returnEmptyDoclist } from "../../Utils"; import { Transform } from "../util/Transform"; import { DocumentView } from "./nodes/DocumentView"; import "./Palette.scss"; @@ -60,6 +60,7 @@ export default class Palette extends React.Component { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index eb20fc257..e4ba45648 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -10,7 +10,7 @@ import { Doc, DocListCast } from "../../fields/Doc"; import { Docs, DocUtils, } from "../documents/Documents"; import { StrCast, Cast } from "../../fields/Types"; import { CollectionTreeView } from "./collections/CollectionTreeView"; -import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter } from "../../Utils"; +import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero, returnEmptyFilter, returnEmptyDoclist } from "../../Utils"; import { Transform } from "../util/Transform"; import { ScriptField, ComputedField } from "../../fields/ScriptField"; import { Scripting } from "../util/Scripting"; @@ -133,6 +133,7 @@ export class TemplateMenu extends React.Component { ContainingCollectionDoc={undefined} ContainingCollectionView={undefined} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} rootSelected={returnFalse} onCheckedClick={this.scriptField} onChildClick={this.scriptField} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 83321d6e0..74b1e5714 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -14,7 +14,7 @@ import { FieldId } from "../../../fields/RefField"; import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils, returnEmptyDoclist } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; @@ -938,6 +938,7 @@ export class DockedFrameRenderer extends React.Component { addDocTab={this.addDocTab} pinToPres={DockedFrameRenderer.PinDoc} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} fitToBox={true} />
@@ -979,6 +980,7 @@ export class DockedFrameRenderer extends React.Component { addDocTab={this.addDocTab} pinToPres={DockedFrameRenderer.PinDoc} docFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} /> {document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)} diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index e1b07077e..0fd18034f 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -166,6 +166,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { whenActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={this.props.docFilters} + searchFilterDocs={this.props.searchFilterDocs} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />
; diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 388eda2b3..5580c32f2 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -131,7 +131,7 @@ export class CollectionViewBaseChrome extends React.Component Doc.GetProto(this.target).data = new List(source)), // Doc.aliasDocs(source), + immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List(source)), initialize: emptyFunction, }; _onClickCommand = { @@ -180,12 +180,16 @@ export class CollectionViewBaseChrome extends React.Component this.target._docFilters = undefined), - initialize: (button: Doc) => { button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : ""; }, + script: `self.target._docFilters = copyField(self['target-docFilters']); + self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`, + immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }), + initialize: (button: Doc) => { + button['target-docFilters'] = this.target._docFilters instanceof ObjectField ? ObjectField.MakeCopy(this.target._docFilters as any as ObjectField) : undefined; + button['target-searchFilterDocs'] = this.target._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(this.target._searchFilterDocs as any as ObjectField) : undefined; + }, }; - @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } + @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; } @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; } @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; } diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 4bd69041b..2f1f7a90f 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -229,7 +229,7 @@ export class CollectionSchemaCell extends React.Component { const fieldIsDoc = (type === "document" && typeof field === "object") || (typeof field === "object" && doc); const onItemDown = async (e: React.PointerEvent) => { - if (this.props.Document._searchDoc !== undefined) { + if (this.props.Document._searchDoc) { const doc = Doc.GetProto(this.props.rowProps.original); const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc); let targetContext = undefined; @@ -315,7 +315,7 @@ export class CollectionSchemaCell extends React.Component { } } let search = false; - if (this.props.Document._searchDoc !== undefined) { + if (this.props.Document._searchDoc) { search = true; } @@ -900,7 +900,7 @@ export class CollectionSchemaButtons extends CollectionSchemaCell { // (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined : // SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e)); // }; - return !BoolCast(this.props.Document._searchDoc) ? <> + return !this.props.Document._searchDoc ? <> : [DocumentType.PDF, DocumentType.RTF].includes(StrCast(doc.type) as DocumentType) ?
+ ; + } + + @undoBatch + onAlias = () => { + if (this.selectedDoc && this.selectedDocumentView) { + // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true); + // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); + // copy.y = NumCast(this.selectedDoc.y) + 30; + // this.selectedDocumentView.props.addDocument?.(copy); + const alias = Doc.MakeAlias(this.selectedDoc); + alias.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width); + alias.y = NumCast(this.selectedDoc.y) + 30; + this.selectedDocumentView.props.addDocument?.(alias); + } + } + private _dragRef = React.createRef(); + + @observable _aliasDown = false; + onAliasButtonDown = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction); + } + @undoBatch + onAliasButtonMoved = (e: PointerEvent) => { + if (this._dragRef.current && this.selectedDoc) { + const dragData = new DragManager.DocumentDragData([this.selectedDoc]); + const [left, top] = [e.clientX, e.clientY]; + dragData.dropAction = "alias"; + DragManager.StartDocumentDrag([this._dragRef.current], dragData, left, top, { + offsetX: dragData.offset[0], + offsetY: dragData.offset[1], + hideSource: false + }); + return true; + } + return false; + } + + @computed + get aliasButton() { + const targetDoc = this.selectedDoc; + return !targetDoc ? (null) : {"Tap or Drag to create an alias"}
} placement="top"> + + ; + } + + @computed + get pinWithViewButton() { + const targetDoc = this.selectedDoc; + if (targetDoc) { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + } + return !targetDoc ? (null) :
{"Pin to presentation with current view"}
} placement="top"> + +
; + } + render() { return ( @@ -396,6 +486,9 @@ export class CollectionViewBaseChrome extends React.Component } + {this.aliasButton} + {this.pinButton} + {this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? (null) : this.pinWithViewButton} {this.subChrome} @@ -444,25 +537,25 @@ export class CollectionFreeFormViewChrome extends React.Component { - const currentFrame = Cast(this.document.currentFrame, "number", null); + const currentFrame = Cast(this.document._currentFrame, "number", null); if (currentFrame === undefined) { - this.document.currentFrame = 0; + this.document._currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); } CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0); - this.document.currentFrame = Math.max(0, (currentFrame || 0) + 1); - this.document.lastFrame = Math.max(NumCast(this.document.currentFrame), NumCast(this.document.lastFrame)); + this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1); + this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame)); } @undoBatch @action prevKeyframe = (): void => { - const currentFrame = Cast(this.document.currentFrame, "number", null); + const currentFrame = Cast(this.document._currentFrame, "number", null); if (currentFrame === undefined) { - this.document.currentFrame = 0; + this.document._currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); } CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice()); - this.document.currentFrame = Math.max(0, (currentFrame || 0) - 1); + this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1); } @undoBatch @action @@ -784,7 +877,7 @@ export class CollectionFreeFormViewChrome extends React.ComponentToggle View All} placement="bottom">
this.document.editing = !this.document.editing)} > - {NumCast(this.document.currentFrame)} + {NumCast(this.document._currentFrame)}
: null} {!this.isText && !this.props.isDoc ? Forward Frame} placement="bottom"> @@ -1242,11 +1335,11 @@ export class CollectionGridViewChrome extends React.Component = new Map([ ["title", ColumnType.String], ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number], ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean], - ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] + ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] ]); @observer diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index a50dab54d..5386d617c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -193,7 +193,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument) getDisplayDoc(doc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { const height = () => this.getDocHeight(doc); - const opacity = () => this.Document.currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document.currentFrame))?.opacity; + const opacity = () => this.Document._currentFrame === undefined ? this.props.childOpacity?.() : CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity; return = new Map([ ["title", ColumnType.String], ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number], ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean], - ["page", ColumnType.Number], ["curPage", ColumnType.Number], ["currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] + ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] ]); export interface SchemaTableProps { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 75fc297fd..549610e53 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -50,9 +50,9 @@ import React = require("react"); export const panZoomSchema = createSchema({ _panX: "number", _panY: "number", - currentTimecode: "number", + _currentTimecode: "number", displayTimecode: "number", - currentFrame: "number", + _currentFrame: "number", arrangeInit: ScriptField, _useClusters: "boolean", fitToBox: "boolean", @@ -173,8 +173,8 @@ export class CollectionFreeFormView extends CollectionSubView DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectDoc(dv, true)); } - public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } + public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document._currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } public getActiveDocuments = () => { return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); @@ -207,9 +207,9 @@ export class CollectionFreeFormView extends CollectionSubView { @observable layoutFields: boolean = false; - @observable openActions: boolean = true; + @observable openOptions: boolean = true; @observable openSharing: boolean = true; @observable openFields: boolean = true; @observable openLayout: boolean = true; @@ -76,7 +76,7 @@ export class PropertiesView extends React.Component { @observable openAddSlide: boolean = false; @observable openSlideOptions: boolean = false; - @observable inActions: boolean = false; + @observable inOptions: boolean = false; @observable _controlBtn: boolean = false; @observable _lock: boolean = false; @@ -177,7 +177,7 @@ export class PropertiesView extends React.Component { doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)); const rows: JSX.Element[] = []; const noviceReqFields = ["author", "creationDate"]; - const noviceLayoutFields = ["curPage"]; + const noviceLayoutFields = ["_curPage"]; const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("ACL") && key !== "UseCors")), ...noviceReqFields, ...noviceLayoutFields]; for (const key of noviceKeys.sort()) { @@ -843,17 +843,17 @@ export class PropertiesView extends React.Component {
{this.editableTitle}
-
runInAction(() => { this.inActions = true; })} - onPointerLeave={action(() => this.inActions = false)}> +
runInAction(() => { this.inOptions = true; })} + onPointerLeave={action(() => this.inOptions = false)}>
runInAction(() => { this.openActions = !this.openActions; })} - style={{ backgroundColor: this.openActions ? "black" : "" }}> - Actions + onPointerDown={() => runInAction(() => { this.openOptions = !this.openOptions; })} + style={{ backgroundColor: this.openOptions ? "black" : "" }}> + Options
- +
- {!this.openActions ? (null) : + {!this.openOptions ? (null) :
} diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 7b9a32dbe..0e3c4462c 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -171,11 +171,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent this.dataDoc.duration = htmlEle.duration); DocListCast(this.dataDoc.links).map(l => { const { la1, linkTime } = this.getLinkData(l); - if (linkTime > NumCast(this.layoutDoc.currentTimecode) && linkTime < htmlEle.currentTime) { + if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < htmlEle.currentTime) { Doc.linkFollowHighlight(la1); } }); - this.layoutDoc.currentTimecode = htmlEle.currentTime; + this.layoutDoc._currentTimecode = htmlEle.currentTime; } } @@ -225,7 +225,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent this._ele!.currentTime = this.layoutDoc.currentTimecode = toTimeline(e.clientX - rect.x), + () => this._ele!.currentTime = this.layoutDoc._currentTimecode = toTimeline(e.clientX - rect.x), emptyFunction); } @@ -465,7 +465,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent
-
{formatTime(Math.round(NumCast(this.layoutDoc.currentTimecode)))}
+
{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
: