aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/documents/Documents.ts1
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx27
-rw-r--r--src/client/views/collections/CollectionMenu.tsx6
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx12
-rw-r--r--src/client/views/collections/CollectionSubView.tsx1
-rw-r--r--src/client/views/collections/SchemaTable.tsx153
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx2
-rw-r--r--src/client/views/search/SearchBox.tsx135
-rw-r--r--src/fields/Doc.ts6
-rw-r--r--src/fields/List.ts2
12 files changed, 114 insertions, 238 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 3ff05104f..5b5a06839 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -422,7 +422,7 @@ export function returnEmptyString() { return ""; }
export function returnEmptyFilter() { return [] as string[]; }
-export function returnEmptyDoclist() { return [] as Doc[]; }
+export function returnEmptyDoclist() { return [] as any[]; }
export let emptyPath = [];
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 49da4f9eb..36712ab3e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -873,6 +873,7 @@ export namespace DocUtils {
}
const filteredDocs = docFilters.length ? childDocs.filter(d => {
+ if (d.z) return true;
for (const facetKey of Object.keys(filterFacets)) {
const facet = filterFacets[facetKey];
const satisfiesFacet = Object.keys(facet).some(value => {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 58c40b1d4..6277fbb14 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -30,14 +30,14 @@ import { DocumentView } from "../nodes/DocumentView";
import { PresBox } from '../nodes/PresBox';
import "./CollectionDockingView.scss";
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { SubCollectionViewProps } from "./CollectionSubView";
+import { SubCollectionViewProps, CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from './CollectionView';
import { DockingViewButtonSelector } from './ParentDocumentSelector';
import React = require("react");
const _global = (window /* browser */ || global /* node */) as any;
@observer
-export class CollectionDockingView extends React.Component<SubCollectionViewProps> {
+export class CollectionDockingView extends CollectionSubView(doc => doc) {
@observable public static Instances: CollectionDockingView[] = [];
@computed public static get Instance() { return CollectionDockingView.Instances[0]; }
public static makeDocumentConfig(document: Doc, width?: number, libraryPath?: Doc[]) {
@@ -73,20 +73,13 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp
DragManager.StartWindowDrag = this.StartOtherDrag;
}
public StartOtherDrag = (e: any, dragDocs: Doc[]) => {
- let config: any;
- if (dragDocs.length === 1) {
- config = CollectionDockingView.makeDocumentConfig(dragDocs[0]);
- } else {
- config = {
+ const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
+ {
type: 'row',
content: dragDocs.map((doc, i) => CollectionDockingView.makeDocumentConfig(doc))
};
- }
- const div = document.createElement("div");
- const dragSource = this._goldenLayout.createDragSource(div, config);
- dragSource._dragListener.on("dragStop", () => {
- dragSource.destroy();
- });
+ const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
+ dragSource._dragListener.on("dragStop", () => dragSource.destroy());
dragSource._dragListener.onMouseDown(e);
}
@@ -939,8 +932,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
addDocTab={this.addDocTab}
pinToPres={DockedFrameRenderer.PinDoc}
- docFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
+ docFilters={CollectionDockingView.Instance.docFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
fitToBox={true}
/>
<div className="miniOverlay" onPointerDown={this.miniDown} >
@@ -981,8 +974,8 @@ export class DockedFrameRenderer extends React.Component<DockedFrameProps> {
backgroundColor={CollectionDockingView.Instance.props.backgroundColor}
addDocTab={this.addDocTab}
pinToPres={DockedFrameRenderer.PinDoc}
- docFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
+ docFilters={CollectionDockingView.Instance.docFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined} />
{document._viewType === CollectionViewType.Freeform && !this._document?.hideMinimap ? this.renderMiniMap() : (null)}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5580c32f2..00415dab1 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -180,12 +180,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
};
_saveFilterCommand = {
params: ["target"], title: "save filter",
- script: `self.target._docFilters = copyField(self['target-docFilters']);
+ script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']);
self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
initialize: (button: Doc) => {
- button['target-docFilters'] = 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;
+ button['target-docFilters'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._docFilters as any as ObjectField) : undefined;
+ button['target-searchFilterDocs'] = Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(Cast(Doc.UserDoc()["search-panel"], Doc, null)._searchFilterDocs as any as ObjectField) : undefined;
},
};
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 2f1f7a90f..e37644f2f 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -3,7 +3,7 @@ import { action, observable, trace, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import { CellInfo } from "react-table";
import "react-table/react-table.css";
-import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, returnOne, returnEmptyFilter, Utils, emptyPath, returnEmptyDoclist } from "../../../Utils";
import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { KeyCodes } from "../../util/KeyCodes";
@@ -199,6 +199,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
rootSelected: returnFalse,
fieldKey: this.props.rowProps.column.id as string,
docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
ContainingCollectionView: this.props.CollectionView,
ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
isSelected: returnFalse,
@@ -511,7 +512,8 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
addDocTab: this.props.addDocTab,
pinToPres: this.props.pinToPres,
ContentScaling: returnOne,
- docFilters: returnEmptyFilter
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
};
@observable private _field = this.prop.Document[this.prop.fieldKey];
@observable private _doc = FieldValue(Cast(this._field, Doc));
@@ -691,7 +693,8 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell {
addDocTab: this.props.addDocTab,
pinToPres: this.props.pinToPres,
ContentScaling: returnOne,
- docFilters: returnEmptyFilter
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
};
let image = true;
@@ -770,7 +773,8 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
addDocTab: this.props.addDocTab,
pinToPres: this.props.pinToPres,
ContentScaling: returnOne,
- docFilters: returnEmptyFilter
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
};
@observable private _field = this.prop.Document[this.prop.fieldKey];
@observable private _optionsList = this._field as List<any>;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index c73159084..71e891045 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -136,6 +136,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
let docsforFilter: Doc[] = childDocs;
if (searchDocs.length > 0) {
+ searchDocs = [...searchDocs, ...docs.filter(d => d.z)];
docsforFilter = [];
const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript);
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index e1c81aeab..7de0b14ac 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -14,7 +14,6 @@ import { ComputedField } from "../../../fields/ScriptField";
import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types";
import { emptyFunction, emptyPath, returnEmptyFilter, returnFalse, returnOne, returnZero, returnEmptyDoclist } from "../../../Utils";
import { Docs, DocumentOptions } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
import { CompileScript, Transformer, ts } from "../../util/Scripting";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
@@ -22,11 +21,12 @@ import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
import { ContentFittingDocumentView } from "../nodes/ContentFittingDocumentView";
-import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaBooleanCell } from "./CollectionSchemaCells";
import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
import "./CollectionSchemaView.scss";
import { CollectionView } from "./CollectionView";
+import { DocumentType } from "../../documents/DocumentTypes";
enum ColumnType {
@@ -91,7 +91,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@observable _cellIsEditing: boolean = false;
@observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 };
- @observable _openCollections: Array<string> = [];
+ @observable _openCollections: Set<number> = new Set;
@observable _showDoc: Doc | undefined;
@observable _showDataDoc: any = "";
@@ -136,16 +136,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeSorting = (col: any) => {
- if (col.desc === undefined) {
- // no sorting
- this.props.changeColumnSort(col, true);
- } else if (col.desc === true) {
- // descending sort
- this.props.changeColumnSort(col, false);
- } else if (col.desc === false) {
- // ascending sort
- this.props.changeColumnSort(col, undefined);
- }
+ this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
}
@action
@@ -153,7 +144,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
@computed get tableColumns(): Column<Doc>[] {
-
const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
const columns: Column<Doc>[] = [];
const tableIsFocused = this.props.isFocused(this.props.Document, false);
@@ -161,27 +151,16 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const focusedCol = this._focusedCell.col;
const isEditable = !this.props.headerIsEditing;
- //if (this.childDocs.reduce((found, doc) => found || doc.type === DocumentType.COL, false)) {
- columns.push(
- {
- expander: true,
- Header: "",
- width: 60,
- Expander: (rowInfo) => {
- if (rowInfo.original.type === "collection") {
- return rowInfo.isExpanded ?
- <div className="collectionSchemaView-expander" onClick={() => this.onCloseCollection(rowInfo.original)}><FontAwesomeIcon icon={"caret-down"} size="lg" /></div> :
- <div className="collectionSchemaView-expander" onClick={() => this.onExpandCollection(rowInfo.original)}><FontAwesomeIcon icon={"caret-right"} size="lg" /></div>;
- } else {
- return null;
- }
- }
+ columns.push({
+ expander: true, Header: "", width: 60,
+ Expander: (rowInfo) => {
+ return rowInfo.original.type !== DocumentType.COL ? (null) :
+ <div className="collectionSchemaView-expander" onClick={action(() => (this._openCollections[rowInfo.isExpanded ? "delete" : "add"])(rowInfo.viewIndex))}>
+ <FontAwesomeIcon icon={rowInfo.isExpanded ? "caret-down" : "caret-right"} size="lg" />
+ </div>;
}
- );
- // }
- this.props.active;
-
- const cols = this.props.columns.map(col => {
+ });
+ columns.push(...this.props.columns.map(col => {
const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" :
this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" :
this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" :
@@ -209,25 +188,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
width={"100%"}
/>;
-
-
const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
-
- const header =
- <div //className="collectionSchemaView-header"
- //onClick={e => this.props.openHeader(col, menuContent, e.clientX, e.clientY)}
- className="collectionSchemaView-menuOptions-wrapper"
- style={{
- background: col.color, padding: "2px",
- display: "flex", cursor: "default", height: "100%",
- }}>
- {/* <FontAwesomeIcon onClick={e => this.props.openHeader(col, e.clientX, e.clientY)} icon={icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} /> */}
- {keysDropdown}
- <div onClick={e => this.changeSorting(col)}
- style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
- <FontAwesomeIcon icon={sortIcon} size="lg" />
- </div>
- </div>;
+ const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}>
+ {keysDropdown}
+ <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
+ <FontAwesomeIcon icon={sortIcon} size="lg" />
+ </div>
+ </div>;
return {
Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
@@ -260,21 +227,20 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
showDoc: this.showDoc,
};
- const colType = this.getColumnType(col);
- if (colType === ColumnType.Number) return <CollectionSchemaNumberCell {...props} />;
- if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />;
- if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />;
- if (colType === ColumnType.Doc) return <CollectionSchemaDocCell {...props} />;
- if (colType === ColumnType.Image) return <CollectionSchemaImageCell {...props} />;
- if (colType === ColumnType.List) return <CollectionSchemaListCell {...props} />;
- if (colType === ColumnType.Date) return <CollectionSchemaDateCell {...props} />;
- return <CollectionSchemaCell {...props} />;
+ switch (this.getColumnType(col)) {
+ case ColumnType.Number: return <CollectionSchemaNumberCell {...props} />;
+ case ColumnType.String: return <CollectionSchemaStringCell {...props} />;
+ case ColumnType.Boolean: return <CollectionSchemaCheckboxCell {...props} />;
+ case ColumnType.Doc: return <CollectionSchemaDocCell {...props} />;
+ case ColumnType.Image: return <CollectionSchemaImageCell {...props} />;
+ case ColumnType.List: return <CollectionSchemaListCell {...props} />;
+ case ColumnType.Date: return <CollectionSchemaDateCell {...props} />;
+ default: return <CollectionSchemaCell {...props} />;
+ }
},
minWidth: 200,
};
- });
- columns.push(...cols);
-
+ }));
columns.push({
Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
accessor: (doc: Doc) => 0,
@@ -283,8 +249,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const rowIndex = rowProps.index;
const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
- const props: CellProps = {
- row: rowIndex,
+ return <CollectionSchemaButtons {...{
+ row: rowProps.index,
col: columnIndex,
rowProps: rowProps,
isFocused: isFocused,
@@ -303,9 +269,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
setComputed: this.setComputed,
getField: this.getField,
showDoc: this.showDoc,
- };
-
- return <CollectionSchemaButtons {...props} />;
+ }} />;
},
width: 28,
resizable: false
@@ -316,12 +280,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
constructor(props: SchemaTableProps) {
super(props);
- // convert old schema columns (list of strings) into new schema columns (list of schema header fields)
- const oldSchemaHeaders = Cast(this.props.Document._schemaHeaders, listSpec("string"), []);
- if (oldSchemaHeaders?.length && typeof oldSchemaHeaders[0] !== "object") {
- const newSchemaHeaders = oldSchemaHeaders.map(i => typeof i === "string" ? new SchemaHeaderField(i, "#f1efeb") : i);
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
- } else if (this.props.Document._schemaHeaders === undefined) {
+ if (this.props.Document._schemaHeaders === undefined) {
this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date),
new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]);
}
@@ -362,19 +321,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
// TODO: editing border doesn't work :(
return {
- style: {
- border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb"
- }
+ style: { border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" }
};
}
- @action
- onCloseCollection = (collection: Doc): void => {
- const index = this._openCollections.findIndex(col => col === collection[Id]);
- if (index > -1) this._openCollections.splice(index, 1);
- }
-
- @action onExpandCollection = (collection: Doc) => this._openCollections.push(collection[Id]);
@action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
@action
@@ -431,21 +381,13 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
getColumnType = (column: SchemaHeaderField): ColumnType => {
- // added functionality to convert old column type stuff to new column type stuff -syip
if (column.type && column.type !== 0) {
return column.type;
}
if (columnTypes.get(column.heading)) {
- column.type = columnTypes.get(column.heading)!;
- return columnTypes.get(column.heading)!;
- }
- const typesDoc = FieldValue(Cast(this.props.Document.schemaColumnTypes, Doc));
- if (!typesDoc) {
- column.type = ColumnType.Any;
- return ColumnType.Any;
+ return column.type = columnTypes.get(column.heading)!;
}
- column.type = NumCast(typesDoc[column.heading]);
- return NumCast(typesDoc[column.heading]);
+ return column.type = ColumnType.Any;
}
@undoBatch
@@ -474,11 +416,9 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@computed
get reactTable() {
const children = this.childDocs;
- const hasCollectionChild = children.reduce((found, doc) => found || doc.type === "collection", false);
- const expandedRowsList = this._openCollections.map(col => children.findIndex(doc => doc[Id] === col).toString());
- const expanded = {};
- //@ts-ignore
- expandedRowsList.forEach(row => expanded[row] = true);
+ const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
+ const expanded: { [name: string]: any } = {};
+ Array.from(this._openCollections.keys()).map(col => expanded[col.toString()] = true);
const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
return <ReactTable
@@ -496,17 +436,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
expanded={expanded}
resized={this.resized}
onResizedChange={this.props.onResizedChange}
- SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== "collection") ? (null) :
+ SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
<div className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>}
/>;
}
onContextMenu = (e: React.MouseEvent): void => {
- if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- // ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" });
- ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
- }
+ ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
}
getField = (row: number, col?: number) => {
@@ -588,9 +525,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
onOpenClick = () => {
- if (this._showDoc) {
- this.props.addDocTab(this._showDoc, "onRight");
- }
+ this._showDoc && this.props.addDocTab(this._showDoc, "onRight");
}
getPreviewTransform = (): Transform => {
@@ -606,7 +541,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
{StrCast(this.props.Document._chromeStatus) !== "disabled" ? <div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
: undefined}
{!this._showDoc ? (null) :
- <div className="collectionSchemaView-documentPreview" //onClick={() => { this.onOpenClick(); }}
+ <div className="collectionSchemaView-documentPreview"
style={{
position: "absolute", width: 150, height: 150,
background: "dimGray", display: "block", top: 0, left: 0,
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 4568a6b16..6dd85b7f5 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -1,7 +1,7 @@
import { action, observable } from 'mobx';
import { observer } from "mobx-react";
import { Doc, Field, Opt } from '../../../fields/Doc';
-import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, returnEmptyFilter, returnEmptyDoclist } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
@@ -56,7 +56,8 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
Document: this.props.doc,
DataDoc: this.props.doc,
LibraryPath: [],
- docFilters:returnEmptyFilter,
+ docFilters: returnEmptyFilter,
+ searchFilterDocs: returnEmptyDoclist,
ContainingCollectionView: undefined,
ContainingCollectionDoc: undefined,
fieldKey: this.props.keyName,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 5250a8f58..ba0ac6293 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -906,7 +906,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
search => {
search ? this.highlightSearchTerms([Doc.SearchQuery()], search.searchMatch < 0) : this.unhighlightSearchTerms();
},
- { fireImmediately: Doc.IsSearchMatch(this.rootDoc) ? true : false });
+ { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false });
this._disposers.record = reaction(() => this._recording,
() => {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 9c9900bfb..e93fc86b5 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { documentSchema } from "../../../fields/documentSchemas";
-import { Id } from '../../../fields/FieldSymbols';
+import { Id, Copy } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -15,12 +15,11 @@ import { Docs } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { SetupDrag } from '../../util/DragManager';
import { SearchUtil } from '../../util/SearchUtil';
-import { SelectionManager } from '../../util/SelectionManager';
import { Transform } from '../../util/Transform';
+import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView";
import { CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
-import { DocumentView } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./SearchBox.scss";
@@ -48,9 +47,8 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
private docsforfilter: Doc[] | undefined = [];
private realTotalResults: number = 0;
- private collectionRef = React.createRef<HTMLSpanElement>();
+ private collectionRef = React.createRef<HTMLDivElement>();
- @observable _currentSelectedCollection: DocumentView | undefined = undefined;
@observable _icons: string[] = this._allIcons;
@observable _results: [Doc, string[], string[]][] = [];
@observable _visibleElements: JSX.Element[] = [];
@@ -64,7 +62,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
@observable open = false;
@observable children = 0;
@observable newsearchstring = "";
- @computed get filter() { return this._results?.length && (this._currentSelectedCollection?.props.Document._searchFilterDocs || this._currentSelectedCollection?.props.Document._docFilters); }
+ @computed get filter() { return this._results?.length && (this.currentSelectedCollection?.props.Document._searchFilterDocs || this.currentSelectedCollection?.props.Document._docFilters); }
constructor(props: any) {
super(props);
@@ -77,25 +75,24 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._searchbarOpen = true;
}
this._disposers.filters = reaction(() => this.props.Document._docFilters,
- (filters: any) => this.setSearchFilter(this._currentSelectedCollection, this.filter ? undefined : this.docsforfilter));
+ (filters: any) => this.setSearchFilter(this.currentSelectedCollection, !this.filter ? undefined : this.docsforfilter));
});
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
}
+ @computed get currentSelectedCollection() { return CollectionDockingView.Instance; }
+
@action.bound
onChange(e: React.ChangeEvent<HTMLInputElement>) {
this.layoutDoc._searchString = e.target.value;
this.newsearchstring = e.target.value;
if (e.target.value === "") {
this.docsforfilter = undefined;
- this.setSearchFilter(this._currentSelectedCollection, undefined);
+ this.setSearchFilter(this.currentSelectedCollection, undefined);
this.resetSearch(false);
- if (this._currentSelectedCollection !== undefined) {
- this._currentSelectedCollection = undefined;
- }
this.open = false;
this._results = [];
this._resultsSet.clear();
@@ -149,9 +146,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const mod = "_t:";
const newWords: string[] = [];
const oldWords = values[0].split(" ");
- oldWords.forEach((word, i) => {
- i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
- });
+ oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\""));
query = `(${query}) AND (${newWords.join(" ")})`;
}
else {
@@ -159,22 +154,12 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const mod = "_t:";
const newWords: string[] = [];
const oldWords = values[i].split(" ");
- oldWords.forEach((word, i) => {
- i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
- });
+ oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\""));
const v = "(" + newWords.join(" ") + ")";
if (i === 0) {
- query = `(${query}) AND (${v}`;
- if (values.length === 1) {
- query = query + ")";
- }
- }
- else if (i === values.length - 1) {
- query = query + " OR " + v + ")";
- }
- else {
- query = query + " OR " + v;
+ query = `(${query}) AND (${v}` + (values.length === 1 ? ")" : "");
}
+ else query = query + " OR " + v + (i === values.length - 1 ? ")" : "");
}
}
}
@@ -194,40 +179,15 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
return finalDocs;
}
- //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)
- getCurCollections(): Doc[] {
- const selectedDocs: DocumentView[] = SelectionManager.SelectedDocuments();
- const collections: Doc[] = [];
- selectedDocs.forEach(async element => {
- const layout: string = StrCast(element.props.Document.layout);
- //checks if selected view (element) is a collection. if it is, adds to list to search through
- if (layout.indexOf("Collection") > -1) {
- //makes sure collections aren't added more than once
- if (!collections.includes(element.props.Document)) {
- collections.push(element.props.Document);
- }
- }
- //makes sure collections aren't added more than once
- if (element.props.ContainingCollectionDoc && !collections.includes(element.props.ContainingCollectionDoc)) {
- collections.push(element.props.ContainingCollectionDoc);
- }
- });
-
- return collections;
- }
-
-
+ @action
searchCollection(query: string) {
- const selectedCollection = SelectionManager.SelectedDocuments()[0];
+ const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
query = query.toLowerCase();
if (selectedCollection !== undefined) {
- this._currentSelectedCollection = selectedCollection;
+ // this._currentSelectedCollection = selectedCollection;
let docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]);
const found: [Doc, string[], string[]][] = [];
- const docsforFilter: Doc[] = [];
let newarray: Doc[] = [];
while (docs.length > 0) {
@@ -239,14 +199,13 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
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);
}
});
docs = newarray;
}
this._results = found;
- this.docsforfilter = docsforFilter;
- this.setSearchFilter(selectedCollection, this.filter && found.length ? docsforFilter : undefined);
+ this.docsforfilter = this._results.map(r => r[0]);
+ this.setSearchFilter(selectedCollection, this.filter && found.length ? this.docsforfilter : undefined);
this._numTotalResults = found.length;
this.realTotalResults = found.length;
}
@@ -285,11 +244,11 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._visibleElements = [];
this._visibleDocuments = [];
- 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.searchDatabase(query) : this.searchCollection(query);
runInAction(() => {
this._searchbarOpen = true;
this.resultsScrolled();
@@ -307,8 +266,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
const includeDeleted = this._deletedDocsStatus ? "" : " NOT deleted_b:true";
const typeExpr = this._onlyAliases ? "NOT {!join from=id to=proto_i}type_t:*" : `(type_t:* OR {!join from=id to=proto_i}type_t:*) ${this._blockedTypes.map(type => `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;
+ return [baseExpr, authorExpr, includeDeleted, typeExpr].filter(q => q).join(" AND ").replace(/AND $/, "");
}
@computed get primarySort() {
@@ -324,7 +282,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
return headers.reduce((p: Opt<string>, header: SchemaHeaderField) => p || (header.desc !== undefined && suffixMap(header.type) ? (header.heading + suffixMap(header.type) + (header.desc ? " desc" : " asc")) : undefined), undefined);
}
- getResults = async (query: string) => {
+ searchDatabase = async (query: string) => {
this._lockPromise && (await this._lockPromise);
this._lockPromise = new Promise(async res => {
while (this._results.length <= this._endIndex && (this._numTotalResults === -1 || this._maxSearchIndex < this._numTotalResults)) {
@@ -368,6 +326,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
this.resultsScrolled();
+
+ const selectedCollection = this.currentSelectedCollection;//SelectionManager.SelectedDocuments()[0];
+ this.docsforfilter = this._results.map(r => r[0]);
+ this.setSearchFilter(selectedCollection, this.filter ? this.docsforfilter : undefined);
res();
});
return this._lockPromise;
@@ -400,7 +362,8 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
y += 300;
}
}
- return Docs.Create.SchemaDocument(Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []), DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
+ const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).map(h => { const v = h[Copy](); v.color = "#f1efeb"; return v; });
+ return Docs.Create.SchemaDocument(headers, DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) });
}
@action.bound
@@ -416,9 +379,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
Doc.UnBrushDoc(result[0]);
Doc.ClearSearchMatches();
});
- if (close) {
- this._searchbarOpen = false;
- }
+ close && (this._searchbarOpen = false);
}
@action.bound
@@ -470,16 +431,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
}
}
- 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.props.Document._schemaHeaders = new List<SchemaHeaderField>(newSchemaHeaders);
- } else if (this.props.Document._schemaHeaders === undefined) {
+ if (this.props.Document._schemaHeaders === undefined) {
this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb")]);
}
if (this._maxSearchIndex >= this._numTotalResults) {
@@ -488,44 +440,29 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
}
- findCommonElements(arr2: string[]) {
- const arr1 = ["layout", "data"];
- return arr1.some(item => arr2.includes(item));
- }
-
getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
panelHeight = () => this.props.PanelHeight();
-
- selectElement = (doc: Doc) => {
- //this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex));
- }
+ selectElement = (doc: Doc) => { /* this.gotoDocument(this.childDocs.indexOf(doc), NumCasst(this.layoutDoc._itemIndex)); */ }
returnHeight = () => 31 + 31 * 6;
returnLength = () => Math.min(window.innerWidth, 51 + 205 * Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length);
@action
changeSearchScope = (scope: string) => {
this.docsforfilter = undefined;
- this.setSearchFilter(this._currentSelectedCollection, undefined);
+ this.setSearchFilter(this.currentSelectedCollection, undefined);
this._searchFullDB = scope;
this.dataDoc[this.fieldKey] = new List<Doc>([]);
this.submitSearch();
}
@computed get scopeButtons() {
- return <div style={{
- height: 25,
- paddingLeft: "4px",
- paddingRight: "4px",
- border: "1px solid gray",
- borderRadius: "0.3em",
- borderBottom: !this.open ? "1px solid" : "none",
- }}>
+ return <div style={{ height: 25, paddingLeft: "4px", paddingRight: "4px", border: "1px solid gray", borderRadius: "0.3em", borderBottom: !this.open ? "1px solid" : "none", }}>
<form className="beta" style={{ justifyContent: "space-evenly", display: "flex" }}>
<div style={{ display: "contents" }}>
<div className="radio" style={{ margin: 0 }}>
<label style={{ fontSize: 12, marginTop: 6 }} >
<input type="radio" style={{ marginLeft: -16, marginTop: -1 }} checked={!this._searchFullDB} onChange={() => this.changeSearchScope("")} />
- Collection
+ Dashboard
</label>
</div>
<div className="radio" style={{ margin: 0 }}>
@@ -542,7 +479,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
</div>;
}
- setSearchFilter = (collectionView: DocumentView | undefined, docsForFilter: Doc[] | undefined) => {
+ setSearchFilter = (collectionView: { props: { Document: Doc } }, docsForFilter: Doc[] | undefined) => {
if (collectionView) {
const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), null);
collectionView.props.Document._searchFilterDocs = docsForFilter?.length ? new List<Doc>(docsForFilter) : undefined;
@@ -561,7 +498,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
<div style={{ display: "flex", alignItems: "center" }}>
<div style={{ position: "absolute", left: 10 }}>
<Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>}>
- <div><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
+ <div ref={this.collectionRef}><FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div>
</Tooltip>
</div>
@@ -574,7 +511,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
<FontAwesomeIcon icon={"filter"} size="lg"
style={{ cursor: "hand", padding: 1, backgroundColor: this.filter ? "white" : "lightgray", color: this.filter ? "black" : "white" }}
onPointerDown={e => { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }}
- onClick={action(() => this.setSearchFilter(this._currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
+ onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} />
</div>
</Tooltip>
</div>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index af2ea73d5..c2b491454 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -915,6 +915,10 @@ export namespace Doc {
brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
})(doc);
}
+ export function IsSearchMatchUnmemoized(doc: Doc) {
+ return brushManager.SearchMatchDoc.has(doc) ? brushManager.SearchMatchDoc.get(doc) :
+ brushManager.SearchMatchDoc.has(Doc.GetProto(doc)) ? brushManager.SearchMatchDoc.get(Doc.GetProto(doc)) : undefined;
+ }
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);
@@ -1276,7 +1280,7 @@ Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) {
Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
}
return ndoc;
-})
+});
Scripting.addGlobal(function copyField(field: any) { return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field; });
Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 1c2b89b01..3601f282b 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -334,5 +334,5 @@ Scripting.addGlobal("List", List);
Scripting.addGlobal(function compareLists(l1: any, l2: any) {
const L1 = Cast(l1, listSpec("string"), []);
const L2 = Cast(l2, listSpec("string"), []);
- return L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
+ return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
}, "compare two lists"); \ No newline at end of file