aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/DocServer.ts126
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx42
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx45
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx10
-rw-r--r--src/client/views/collections/CollectionSubView.tsx6
-rw-r--r--src/client/views/collections/SchemaTable.tsx14
-rw-r--r--src/client/views/nodes/FieldView.tsx8
-rw-r--r--src/client/views/nodes/PDFBox.tsx10
-rw-r--r--src/client/views/nodes/WebBox.tsx6
-rw-r--r--src/client/views/pdf/PDFViewer.tsx4
-rw-r--r--src/client/views/search/SearchBox.tsx64
11 files changed, 156 insertions, 179 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index a36dfaf69..63e01bc5b 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -330,72 +330,76 @@ export namespace DocServer {
}
}
- // 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string)
- // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
- // the fields have been returned from the server
- const getSerializedFields: Promise<any> = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
-
- // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
- // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all
- // future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
- const deserializeFields = getSerializedFields.then(async fields => {
- const fieldMap: { [id: string]: RefField } = {};
- const proms: Promise<void>[] = [];
- runInAction(() => {
- for (const field of fields) {
- if (field !== undefined && field !== null && !_cache[field.id]) {
- // deserialize
- const cached = _cache[field.id];
- if (!cached) {
- const prom = SerializationHelper.Deserialize(field).then(deserialized => {
- fieldMap[field.id] = deserialized;
-
- //overwrite or delete any promises (that we inserted as flags
- // to indicate that the field was in the process of being fetched). Now everything
- // should be an actual value within or entirely absent from the cache.
- if (deserialized !== undefined) {
- _cache[field.id] = deserialized;
- } else {
- delete _cache[field.id];
- }
- return deserialized;
- });
- // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
- // we set the value at the field's id to a promise that will resolve to the field.
- // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
- // The mapping in the .then call ensures that when other callers await these promises, they'll
- // get the resolved field
- _cache[field.id] = prom;
-
- // adds to a list of promises that will be awaited asynchronously
- proms.push(prom);
- } else if (cached instanceof Promise) {
- proms.push(cached as any);
+ if (requestedIds.length) {
+
+ // 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string)
+ // fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
+ // the fields have been returned from the server
+ const getSerializedFields: Promise<any> = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
+
+ // 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
+ // Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all
+ // future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
+ const deserializeFields = getSerializedFields.then(async fields => {
+ const fieldMap: { [id: string]: RefField } = {};
+ const proms: Promise<void>[] = [];
+ runInAction(() => {
+ for (const field of fields) {
+ if (field !== undefined && field !== null && !_cache[field.id]) {
+ // deserialize
+ const cached = _cache[field.id];
+ if (!cached) {
+ const prom = SerializationHelper.Deserialize(field).then(deserialized => {
+ fieldMap[field.id] = deserialized;
+
+ //overwrite or delete any promises (that we inserted as flags
+ // to indicate that the field was in the process of being fetched). Now everything
+ // should be an actual value within or entirely absent from the cache.
+ if (deserialized !== undefined) {
+ _cache[field.id] = deserialized;
+ } else {
+ delete _cache[field.id];
+ }
+ return deserialized;
+ });
+ // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
+ // we set the value at the field's id to a promise that will resolve to the field.
+ // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
+ // The mapping in the .then call ensures that when other callers await these promises, they'll
+ // get the resolved field
+ _cache[field.id] = prom;
+
+ // adds to a list of promises that will be awaited asynchronously
+ proms.push(prom);
+ } else if (cached instanceof Promise) {
+ proms.push(cached as any);
+ }
+ } else if (_cache[field.id] instanceof Promise) {
+ proms.push(_cache[field.id] as any);
+ (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f);
+ } else if (field) {
+ proms.push(_cache[field.id] as any);
+ fieldMap[field.id] = field;
}
- } else if (_cache[field.id] instanceof Promise) {
- proms.push(_cache[field.id] as any);
- (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f);
- } else if (field) {
- proms.push(_cache[field.id] as any);
- fieldMap[field.id] = field;
}
- }
+ });
+ await Promise.all(proms);
+ return fieldMap;
});
- await Promise.all(proms);
- return fieldMap;
- });
- // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
- // prototype documents, if any, have also been fetched and cached.
- const fields = await deserializeFields;
+ // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
+ // prototype documents, if any, have also been fetched and cached.
+ const fields = await deserializeFields;
- // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
- // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
- // id to the soon-to-be-returned field mapping.
- requestedIds.forEach(id => {
- const field = fields[id];
- map[id] = field;
- });
+ // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
+ // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
+ // id to the soon-to-be-returned field mapping.
+ requestedIds.forEach(id => {
+ const field = fields[id];
+ map[id] = field;
+ });
+
+ }
// 7) those promises we encountered in the else if of 1), which represent
// other callers having already submitted a request to the server for (a) document(s)
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index 20ae74b44..49d75e6de 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -166,15 +166,16 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const contents = bing();
if (positions !== undefined) {
- StrCast(this.props.Document._searchString)
+ StrCast(this.props.Document._searchString);
const length = StrCast(this.props.Document._searchString).length;
+ const color = contents ? "black" : "grey";
- results.push(<span style={{ color: contents ? "black" : "grey" }}>{contents ? contents.slice(0, positions[0]) : "undefined"}</span>);
+ results.push(<span key="-1" style={{ color }}>{contents?.slice(0, positions[0])}</span>);
positions.forEach((num, cur) => {
- results.push(<span style={{ backgroundColor: "#FFFF00", color: contents ? "black" : "grey" }}>{contents ? contents.slice(num, num + length) : "undefined"}</span>);
+ results.push(<span key={"start" + cur} style={{ backgroundColor: "#FFFF00", color }}>{contents?.slice(num, num + length)}</span>);
let end = 0;
cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1];
- results.push(<span style={{ color: contents ? "black" : "grey" }}>{contents ? contents.slice(num + length, end) : "undefined"}</span>);
+ results.push(<span key={"end" + cur} style={{ color }}>{contents?.slice(num + length, end)}</span>);
}
);
return results;
@@ -227,13 +228,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const onItemDown = async (e: React.PointerEvent) => {
if (this.props.Document._searchDoc !== undefined) {
- let doc = Doc.GetProto(this.props.rowProps.original);
+ const doc = Doc.GetProto(this.props.rowProps.original);
const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc);
let targetContext = undefined;
if (aliasdoc.length > 0) {
targetContext = Cast(aliasdoc[0].context, Doc) as Doc;
}
- console.log(targetContext);
DocumentManager.Instance.jumpToDocument(this.props.rowProps.original, false, undefined, targetContext);
}
else {
@@ -289,18 +289,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const positions = [];
if (StrCast(this.props.Document._searchString).toLowerCase() !== "") {
const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- let term = "";
- if (cfield !== undefined) {
- if (cfield.Text !== undefined) {
- term = cfield.Text;
- }
- else if (StrCast(cfield)) {
- term = StrCast(cfield);
- }
- else {
- term = String(NumCast(cfield));
- }
- }
+ let term = Field.toString(cfield as Field);
term = term.toLowerCase();
const search = StrCast(this.props.Document._searchString).toLowerCase();
let start = term.indexOf(search);
@@ -409,22 +398,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
:
this.returnHighlights(() => {
const cfield = ComputedField.WithoutComputed(() => FieldValue(props.Document[props.fieldKey]));
- if (cfield !== undefined) {
- // if (typeof(cfield)===RichTextField)
- const a = cfield as RichTextField;
- if (a.Text !== undefined) {
- return (a.Text);
- }
- else if (StrCast(cfield)) {
- return StrCast(cfield);
- }
- else {
- return String(NumCast(cfield));
- }
- }
- else {
- return "";
- }
+ return Field.toString(cfield as Field);
}, positions)
}
</div >
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index 0ad1c5fa1..fa260bee1 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -3,7 +3,7 @@ import { IconProp, library } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
import { listSpec } from "../../../fields/Schema";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { ScriptField } from "../../../fields/ScriptField";
@@ -277,10 +277,10 @@ export interface KeysDropdownProps {
width?: string;
docs?: Doc[];
Document: Doc;
- dataDoc: Doc;
+ dataDoc: Doc | undefined;
fieldKey: string;
- ContainingCollectionDoc: Doc;
- ContainingCollectionView: CollectionView;
+ ContainingCollectionDoc: Doc | undefined;
+ ContainingCollectionView: Opt<CollectionView>;
active?: (outsideReaction?: boolean) => boolean;
openHeader: (column: any, screenx: number, screeny: number) => void;
col: SchemaHeaderField;
@@ -310,7 +310,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
onKeyDown = (e: React.KeyboardEvent): void => {
if (e.key === "Enter") {
let keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- let blockedkeys = ["_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
+ const blockedkeys = ["_scrollTop", "customTitle", "limitHeight", "proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
if (keyOptions.length) {
this.onSelect(keyOptions[0]);
@@ -362,7 +362,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
- let blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
+ const blockedkeys = ["proto", "x", "y", "_width", "_height", "_autoHeight", "_fontSize", "_fontFamily", "context", "zIndex", "_timeStampOnEnter", "lines", "highlighting", "searchMatch", "creationDate", "isPrototype", "text-annotations", "aliases", "text-lastModified", "text-noTemplate", "layoutKey", "baseProto", "_xMargin", "_yMargin", "layout", "layout_keyValue", "links"];
keyOptions = keyOptions.filter(n => !blockedkeys.includes(n));
const options = keyOptions.map(key => {
@@ -391,7 +391,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}
else {
if (this.props.docs) {
- let panesize = this.props.docs.length * 30;
+ const panesize = this.props.docs.length * 30;
options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
}
else {
@@ -401,7 +401,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
return options;
}
- docSafe: Doc[] = []
+ docSafe: Doc[] = [];
@action
renderFilterOptions = (): JSX.Element[] | JSX.Element => {
@@ -409,25 +409,32 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
this.defaultMenuHeight = 0;
return <></>;
}
+
let keyOptions: string[] = [];
+ const colpos = this._searchTerm.indexOf(":");
+ const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
if (this.docSafe.length === 0) {
- this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey!]);
+ this.docSafe = DocListCast(this.props.dataDoc![this.props.fieldKey]);
}
- let docs = this.docSafe;
+ const docs = this.docSafe;
docs.forEach((doc) => {
const key = StrCast(doc[this._key]);
- if (keyOptions.includes(key) === false) {
+ if (keyOptions.includes(key) === false && key.includes(temp)) {
keyOptions.push(key);
}
});
- let filters = Cast(this.props.Document!._docFilters, listSpec("string"));
+ const filters = Cast(this.props.Document._docFilters, listSpec("string"));
for (let i = 0; i < (filters?.length ?? 0) - 1; i += 3) {
if (filters![i] === this.props.col.heading && keyOptions.includes(filters![i + 1]) === false) {
keyOptions.push(filters![i + 1]);
}
}
+ if (filters === undefined || filters.length === 0 || filters.includes(this._key) === false) {
+ this.props.col.setColor("rgb(241, 239, 235)");
+ }
+
const options = keyOptions.map(key => {
//Doc.setDocFilter(this.props.Document!, this._key, key, undefined);
let bool = false;
@@ -442,8 +449,9 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}}
>
<input type="checkbox" onChange={(e) => {
- e.target.checked === true ? Doc.setDocFilter(this.props.Document!, this._key, key, "check") : Doc.setDocFilter(this.props.Document!, this._key, key, undefined);
- e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs![0], this._key, key, "check") : Doc.setDocFilter(docs![0], this._key, key, undefined);
+ e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, undefined);
+ e.target.checked === true ? this.props.col.setColor("green") : "";
+ e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, undefined);
}}
checked={bool} ></input>
<span style={{ paddingLeft: 4 }}>
@@ -457,7 +465,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}
else {
if (this.props.docs) {
- let panesize = this.props.docs.length * 30;
+ const panesize = this.props.docs.length * 30;
options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
}
else {
@@ -484,14 +492,13 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
@observable filterOpen: boolean | undefined = undefined;
render() {
- console.log(this._isOpen, this._key, this._searchTerm);
return (
<div style={{ display: "flex" }}>
<FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY) }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
- <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
+ {/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen })
- }} />
+ }} /> */}
<div className="keys-dropdown" style={{ zIndex: 10, width: this.props.width, maxWidth: this.props.width }}>
<input className="keys-search" style={{ width: "100%" }}
@@ -505,7 +512,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
width: this.props.width, maxWidth: this.props.width, height: "auto",
}}
onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerOut}>
- {this._key === this._searchTerm ? this.renderFilterOptions() : this.renderOptions()}
+ {this._searchTerm.includes(":") ? this.renderFilterOptions() : this.renderOptions()}
</div>
</div >
</div>
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index ef7b1c4f7..1de881f6d 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -74,11 +74,11 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
let searchx = 0;
let searchy = 0;
if (this.props.Document._searchDoc !== undefined) {
- let el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
+ const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
if (el !== undefined) {
- let rect = el.getBoundingClientRect();
+ const rect = el.getBoundingClientRect();
searchx = rect.x;
- searchy = rect.y
+ searchy = rect.y;
}
}
const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
@@ -420,7 +420,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
super.CreateDropTarget(ele);
}
- isFocused = (doc: Doc): boolean => this.props.isSelected() && doc === this._focusedTable;
+ isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
@action setFocused = (doc: Doc) => this._focusedTable = doc;
@@ -631,7 +631,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
style={{
overflow: this.props.overflow === true ? "scroll" : undefined,
pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+ width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
}} >
<div className="collectionSchemaView-tableContainer"
style={{ backgroundColor: "white", width: `calc(100% - ${this.previewWidth()}px)` }}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a43685a5e..3f2ad47a5 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -126,7 +126,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const docs = rawdocs.filter(d => !(d instanceof Promise)).map(d => d as Doc);
const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
- let childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
+ const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
let searchDocs = DocListCast(this.props.Document._searchDocs);
@@ -137,7 +137,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
docsforFilter = [];
const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
console.log(searchDocs);
- searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript)
+ searchDocs = DocUtils.FilterDocs(searchDocs, this.docFilters(), docRangeFilters, viewSpecScript);
console.log(this.docFilters());
console.log(searchDocs);
childDocs.forEach((d) => {
@@ -240,6 +240,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
} else added = res;
+ !added && alert("You don't have permission to perform this move");
+ e.stopPropagation();
} else {
ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
added = this.addDocument(docDragData.droppedDocuments);
diff --git a/src/client/views/collections/SchemaTable.tsx b/src/client/views/collections/SchemaTable.tsx
index 2866bb497..8d87a3ba2 100644
--- a/src/client/views/collections/SchemaTable.tsx
+++ b/src/client/views/collections/SchemaTable.tsx
@@ -63,12 +63,12 @@ export interface SchemaTableProps {
addDocument: (document: Doc | Doc[]) => boolean;
moveDocument: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
ScreenToLocalTransform: () => Transform;
- active: (outsideReaction: boolean) => boolean;
+ active: (outsideReaction: boolean | undefined) => boolean;
onDrop: (e: React.DragEvent<Element>, options: DocumentOptions, completed?: (() => void) | undefined) => void;
addDocTab: (document: Doc, where: string) => boolean;
pinToPres: (document: Doc) => void;
isSelected: (outsideReaction?: boolean) => boolean;
- isFocused: (document: Doc) => boolean;
+ isFocused: (document: Doc, outsideReaction: boolean) => boolean;
setFocused: (document: Doc) => void;
setPreviewDoc: (document: Doc) => void;
columns: SchemaHeaderField[];
@@ -155,7 +155,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
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);
+ const tableIsFocused = this.props.isFocused(this.props.Document, false);
const focusedRow = this._focusedCell.row;
const focusedCol = this._focusedCell.col;
const isEditable = !this.props.headerIsEditing;
@@ -177,7 +177,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
);
}
- this.props.active
+ this.props.active;
const cols = this.props.columns.map(col => {
const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" :
@@ -342,7 +342,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
addDoc: this.tableAddDoc,
removeDoc: this.props.deleteDocument,
rowInfo,
- rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document),
+ rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
textWrapRow: this.toggleTextWrapRow,
rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
dropAction: StrCast(this.props.Document.childDropAction),
@@ -356,7 +356,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const row = rowInfo.index;
//@ts-ignore
const col = this.columns.map(c => c.heading).indexOf(column!.id);
- const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document);
+ 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: {
@@ -376,7 +376,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
onKeyDown = (e: KeyboardEvent): void => {
- if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected(true)) {
+ if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {// && this.props.isSelected(true)) {
const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index fe0ea80d5..3f2a590ab 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -5,7 +5,7 @@ import { DateField } from "../../../fields/DateField";
import { Doc, FieldResult, Opt, Field } from "../../../fields/Doc";
import { List } from "../../../fields/List";
import { ScriptField } from "../../../fields/ScriptField";
-import { AudioField, VideoField } from "../../../fields/URLField";
+import { AudioField, VideoField, WebField } from "../../../fields/URLField";
import { Transform } from "../../util/Transform";
import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
@@ -135,9 +135,9 @@ export class FieldView extends React.Component<FieldViewProps> {
return <div> {field.map(f => Field.toString(f)).join(", ")} </div>;
}
// bcz: this belongs here, but it doesn't render well so taking it out for now
- // else if (field instanceof HtmlField) {
- // return <WebBox {...this.props} />
- // }
+ else if (field instanceof WebField) {
+ return <p>{Field.toString(field.url.href)}</p>;
+ }
else if (!(field instanceof Promise)) {
return <p>{Field.toString(field)}</p>;
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 2fdb87e63..255a1b2d0 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -60,19 +60,19 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
if (href) {
const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g;
const matches = pathCorrectionTest.exec(href);
- console.log("\nHere's the { url } being fed into the outer regex:");
- console.log(href);
- console.log("And here's the 'properPath' build from the captured filename:\n");
+ // console.log("\nHere's the { url } being fed into the outer regex:");
+ // console.log(href);
+ // console.log("And here's the 'properPath' build from the captured filename:\n");
if (matches !== null && href.startsWith(window.location.origin)) {
const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`);
- console.log(properPath);
+ //console.log(properPath);
if (!properPath.includes(href)) {
console.log(`The two (url and proper path) were not equal`);
const proto = Doc.GetProto(Document);
proto[this.props.fieldKey] = new PdfField(properPath);
proto[backup] = href;
} else {
- console.log(`The two (url and proper path) were equal`);
+ //console.log(`The two (url and proper path) were equal`);
}
} else {
console.log("Outer matches was null!");
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index f37909e6e..1393e7868 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -454,9 +454,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
} else if (field instanceof WebField) {
const url = this.layoutDoc.UseCors ? Utils.CorsProxy(field.url.href) : field.url.href;
- view = <iframe className="webBox-iframe" enable-annotation={true} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded} />;
+ view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded}
+ // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
+ sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
} else {
- view = <iframe className="webBox-iframe" enable-annotation={true} ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
+ view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
}
return view;
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index cca86adb5..f9ae78778 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -399,10 +399,10 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
search = (searchString: string, fwd: boolean, clear: boolean = false) => {
if (clear) {
- this._pdfViewer.findController.executeCommand('reset', { query: "" });
+ this._pdfViewer?.findController.executeCommand('reset', { query: "" });
} else if (!searchString) {
fwd ? this.nextAnnotation() : this.prevAnnotation();
- } else if (this._pdfViewer.pageViewsReady) {
+ } else if (this._pdfViewer?.pageViewsReady) {
this._pdfViewer.findController.executeCommand('findagain', {
caseSensitive: false,
findPrevious: !fwd,
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index da3bafa95..35b383a27 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -54,6 +54,8 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
@observable private _visibleElements: JSX.Element[] = [];
@observable private _visibleDocuments: Doc[] = [];
+ static NUM_SEARCH_RESULTS_PER_PAGE = 25;
+
private _resultsSet = new Map<Doc, number>();
private _resultsRef = React.createRef<HTMLDivElement>();
public inputRef = React.createRef<HTMLInputElement>();
@@ -244,11 +246,11 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
//alters the query so it looks in the correct fields
//if this is true, th`en not all of the field boxes are checked
//TODO: data
- let initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
+ const initialfilters = Cast(this.props.Document._docFilters, listSpec("string"), []);
- let type: string[] = [];
+ const type: string[] = [];
- let filters: string[] = []
+ const filters: string[] = [];
for (let i = 0; i < initialfilters.length; i = i + 3) {
if (initialfilters[i + 2] !== undefined) {
@@ -258,7 +260,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
}
- let finalfilters: { [key: string]: string[] } = {};
+ const finalfilters: { [key: string]: string[] } = {};
for (let i = 0; i < filters.length; i = i + 3) {
if (finalfilters[filters[i]] !== undefined) {
@@ -269,26 +271,26 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
}
}
- for (var key in finalfilters) {
- let values = finalfilters[key];
+ for (const key in finalfilters) {
+ const values = finalfilters[key];
if (values.length === 1) {
- let mod = "_t:"
+ 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 + "\"")
+ i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
});
query = `(${query}) AND (${newWords.join(" ")})`;
}
else {
for (let i = 0; i < values.length; i++) {
- let mod = "_t:"
+ 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 + "\"")
+ i === 0 ? newWords.push(key + mod + "\"" + word + "\"") : newWords.push("AND " + key + mod + "\"" + word + "\"");
});
- let v = "(" + newWords.join(" ") + ")";
+ const v = "(" + newWords.join(" ") + ")";
if (i === 0) {
query = `(${query}) AND (${v}`;
if (values.length === 1) {
@@ -336,7 +338,6 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
query = query.replace(/-\s+/g, '');
query = query.replace(/-/g, "");
- console.log(query);
return query;
}
@@ -347,7 +348,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
@action
filterDocsByType(docs: Doc[]) {
const finalDocs: Doc[] = [];
- const blockedTypes: string[] = ["preselement", "docholder", "search", "searchitem", "script", "fonticonbox", "button", "label"];
+ const blockedTypes: string[] = [DocumentType.PRESELEMENT, DocumentType.DOCHOLDER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
docs.forEach(doc => {
const layoutresult = Cast(doc.type, "string");
if (layoutresult && !blockedTypes.includes(layoutresult)) {
@@ -618,18 +619,16 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
private NumResults = 50;
private lockPromise?: Promise<void>;
getResults = async (query: string) => {
- console.log("Get");
if (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, { fq: this.filterQuery, start: this._maxSearchIndex, rows: 10000000, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
+ this._curRequest = SearchUtil.Search(query, true, { fq: this.filterQuery, start: this._maxSearchIndex, rows: this.NumResults, hl: true, "hl.fl": "*", }).then(action(async (res: SearchUtil.DocSearchResult) => {
// happens at the beginning
if (res.numFound !== this._numTotalResults && this._numTotalResults === -1) {
this._numTotalResults = res.numFound;
}
- console.log(res.numFound);
const highlighting = res.highlighting || {};
const highlightList = res.docs.map(doc => highlighting[doc[Id]]);
const lines = new Map<string, string[]>();
@@ -641,18 +640,15 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
runInAction(() => {
filteredDocs.forEach((doc, i) => {
- console.log(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) : [];
- doc ? console.log(Cast(doc.context, Doc)) : null;
// if (this.findCommonElements(hlights)) {
// }
if (index === undefined) {
this._resultsSet.set(doc, this._results.length);
this._results.push([doc, hlights, line]);
- console.log(i);
} else {
this._results[index][1].push(...hlights);
this._results[index][2].push(...line);
@@ -738,21 +734,15 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._curRequest = undefined;
}
- @observable _pageStart: number = 0
- @observable _pageCount: number = 5;
+ @observable _pageStart: number = 0;
+ @observable _pageCount: number = SearchBox.NUM_SEARCH_RESULTS_PER_PAGE;
@observable children: number = 0;
@action
resultsScrolled = (e?: React.UIEvent<HTMLDivElement>) => {
if (!this._resultsRef.current) return;
-
- const scrollY = e ? e.currentTarget.scrollTop : this._resultsRef.current ? this._resultsRef.current.scrollTop : 0;
- const itemHght = 53;
- //const endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (this._resultsRef.current.getBoundingClientRect().height / itemHght)));
- const endIndex = 30;
- //this._endIndex = endIndex === -1 ? 12 : endIndex;
this._endIndex = 30;
- const headers = new Set<string>(["title", "author", "*lastModified", "type"]);
+ const headers = new Set<string>(["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 :(";
@@ -775,7 +765,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this._isSorted = Array<undefined>(this._numTotalResults === -1 ? 0 : this._numTotalResults);
}
- let max = this._pageStart + this._pageCount;
+ let max = this.NumResults;
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
@@ -811,7 +801,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
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"))
+ newSchemaHeaders.push(new SchemaHeaderField(header, "#f1efeb"));
}
});
this.headercount = newSchemaHeaders.length;
@@ -846,7 +836,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
@computed get searchItemTemplate() { return Cast(Doc.UserDoc().searchItemTemplate, Doc, null); }
- @computed get viewspec() { return Cast(this.props.Document._docFilters, listSpec("string"), []) }
+ @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
@@ -865,7 +855,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
@observable filter = false;
@action newpage() {
- this._pageStart += 5;
+ this._pageStart += SearchBox.NUM_SEARCH_RESULTS_PER_PAGE;
this.dataDoc[this.fieldKey] = new List<Doc>([]);
this.resultsScrolled();
}
@@ -874,7 +864,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
this.props.Document._searchDoc = true;
const cols = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).length;
let length = 0;
- cols > 5 ? length = 1076 : length = cols * 205 + 51;
+ length = cols * 205 + 51;
let height = 0;
const rows = this.children;
height = 31 + 31 * 6;
@@ -884,9 +874,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
<div style={{ position: "absolute", left: 15 }}>{Doc.CurrentUserEmail}</div>
<div style={{ display: "flex", alignItems: "center" }}>
<Tooltip title={<div className="dash-tooltip" >drag search results as collection</div>} ><div>
- {/* <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
- style={{ cursor: "hand", color: "black", padding: 1, left: 35, position: "relative" }} /> */}
- <FontAwesomeIcon onPointerDown={() => { this.newpage(); }} icon={"search"} size="lg"
+ <FontAwesomeIcon onPointerDown={SetupDrag(this.collectionRef, () => StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg"
style={{ cursor: "hand", color: "black", padding: 1, left: 35, position: "relative" }} />
</div></Tooltip>
<div style={{ cursor: "default", left: 250, position: "relative", }}>
@@ -1008,7 +996,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
docs.forEach((d) => {
if (d.data !== undefined) {
d._searchDocs = new List<Doc>();
- d._docFilters = new List()
+ d._docFilters = new List();
const newdocs = DocListCast(d.data);
newdocs.forEach((newdoc) => {
newarray.push(newdoc);
@@ -1043,7 +1031,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
removeDocument={returnFalse}
PanelHeight={this.open === true ? () => height : () => 0}
PanelWidth={this.open === true ? () => length : () => 0}
- overflow={cols > 5 || rows > 6 ? true : false}
+ overflow={length > screen.width || rows > 6 ? true : false}
focus={this.selectElement}
ScreenToLocalTransform={Transform.Identity}
/>